Backed out 4 changesets (bug 1648453) for hazzard bustages on CycleCollectedJSContext.cpp. CLOSED TREE
authorRazvan Maries <rmaries@mozilla.com>
Fri, 17 Jul 2020 00:01:11 +0300
changeset 540812 5b891bc9d106cee46ad8ef2dc85da684406ac785
parent 540811 c24a785142fb17fc98c4631bb4f67f5260427ed8
child 540813 f7f54765e90e21c3e06618b372e8b4d511cf1582
push id121929
push userrmaries@mozilla.com
push dateThu, 16 Jul 2020 21:01:50 +0000
treeherderautoland@5b891bc9d106 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1648453
milestone80.0a1
backs out9598a75cca47bdfbdcf5853c95fd6368aa988d11
473e7d55a25ef12ae8e5b70642b36a73f28bf459
0afcee198ecd3feea00496becacbb575972cd157
623252539387bff544d97309104c7006d00c99f7
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
Backed out 4 changesets (bug 1648453) for hazzard bustages on CycleCollectedJSContext.cpp. CLOSED TREE Backed out changeset 9598a75cca47 (bug 1648453) Backed out changeset 473e7d55a25e (bug 1648453) Backed out changeset 0afcee198ecd (bug 1648453) Backed out changeset 623252539387 (bug 1648453)
dom/base/nsGlobalWindowOuter.cpp
dom/base/nsJSEnvironment.cpp
dom/webidl/FinalizationRegistry.webidl
dom/webidl/moz.build
js/public/GCAPI.h
js/src/builtin/FinalizationRegistryObject.cpp
js/src/builtin/FinalizationRegistryObject.h
js/src/gc/FinalizationRegistry.cpp
js/src/gc/GC.cpp
js/src/gc/GCRuntime.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/shell/js.cpp
js/src/shell/jsshell.h
js/xpconnect/tests/mochitest/mochitest.ini
js/xpconnect/tests/mochitest/test_finalizationRegistry_incumbent.html
js/xpconnect/wrappers/AccessCheck.h
xpcom/base/CycleCollectedJSContext.cpp
xpcom/base/CycleCollectedJSContext.h
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -230,26 +230,32 @@
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/NavigatorBinding.h"
 #include "mozilla/dom/ImageBitmap.h"
 #include "mozilla/dom/ImageBitmapBinding.h"
 #include "mozilla/dom/ServiceWorkerRegistration.h"
 #include "mozilla/dom/U2F.h"
 #include "mozilla/dom/WebIDLGlobalNameHash.h"
 #include "mozilla/dom/Worklet.h"
-#include "AccessCheck.h"
 
 #ifdef HAVE_SIDEBAR
 #  include "mozilla/dom/ExternalBinding.h"
 #endif
 
 #ifdef MOZ_WEBSPEECH
 #  include "mozilla/dom/SpeechSynthesis.h"
 #endif
 
+// Apple system headers seem to have a check() macro.  <sigh>
+#ifdef check
+class nsIScriptTimeoutHandler;
+#  undef check
+#endif  // check
+#include "AccessCheck.h"
+
 #ifdef ANDROID
 #  include <android/log.h>
 #endif
 
 #ifdef XP_WIN
 #  include <process.h>
 #  define getpid _getpid
 #else
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -59,17 +59,23 @@
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ErrorEvent.h"
 #include "mozilla/dom/FetchUtil.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/SerializedStackHolder.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "nsRefreshDriver.h"
 #include "nsJSPrincipals.h"
+
+#ifdef XP_MACOSX
+// AssertMacros.h defines 'check' and conflicts with AccessCheck.h
+#  undef check
+#endif
 #include "AccessCheck.h"
+
 #include "mozilla/Logging.h"
 #include "prthread.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
deleted file mode 100644
--- a/dom/webidl/FinalizationRegistry.webidl
+++ /dev/null
@@ -1,10 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; 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/.
- *
- * This IDL file contains a callback used to integrate JS FinalizationRegistry
- * objects with the browser.
- */
-
-callback FinalizationRegistryCleanupCallback = void();
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -528,17 +528,16 @@ WEBIDL_FILES = [
     'FileMode.webidl',
     'FileReader.webidl',
     'FileReaderSync.webidl',
     'FileSystem.webidl',
     'FileSystemDirectoryEntry.webidl',
     'FileSystemDirectoryReader.webidl',
     'FileSystemEntry.webidl',
     'FileSystemFileEntry.webidl',
-    'FinalizationRegistry.webidl',
     'FocusEvent.webidl',
     'FontFace.webidl',
     'FontFaceSet.webidl',
     'FontFaceSource.webidl',
     'FormData.webidl',
     'Function.webidl',
     'GainNode.webidl',
     'Gamepad.webidl',
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -399,24 +399,24 @@ typedef void (*JSFinalizeCallback)(JSFre
 
 typedef void (*JSWeakPointerZonesCallback)(JSContext* cx, void* data);
 
 typedef void (*JSWeakPointerCompartmentCallback)(JSContext* cx,
                                                  JS::Compartment* comp,
                                                  void* data);
 
 /*
- * This is called to tell the embedding that a FinalizationRegistry object has
- * cleanup work, and that the engine should be called back at an appropriate
- * later time to perform this cleanup, by calling the function |doCleanup|.
+ * This is called to tell the embedding that the FinalizationRegistry object
+ * |registry| has cleanup work, and that then engine should be called back at an
+ * appropriate later time to perform this cleanup.
  *
  * This callback must not do anything that could cause GC.
  */
-using JSHostCleanupFinalizationRegistryCallback =
-    void (*)(JSFunction* doCleanup, JSObject* incumbentGlobal, void* data);
+using JSHostCleanupFinalizationRegistryCallback = void (*)(JSObject* registry,
+                                                           void* data);
 
 /**
  * Each external string has a pointer to JSExternalStringCallbacks. Embedders
  * can use this to implement custom finalization or memory reporting behavior.
  */
 struct JSExternalStringCallbacks {
   /**
    * Finalizes external strings created by JS_NewExternalString. The finalizer
--- a/js/src/builtin/FinalizationRegistryObject.cpp
+++ b/js/src/builtin/FinalizationRegistryObject.cpp
@@ -290,30 +290,27 @@ const JSPropertySpec FinalizationRegistr
 bool FinalizationRegistryObject::construct(JSContext* cx, unsigned argc,
                                            Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   if (!ThrowIfNotConstructing(cx, args, "FinalizationRegistry")) {
     return false;
   }
 
-  RootedObject proto(cx);
-  if (!GetPrototypeFromBuiltinConstructor(
-          cx, args, JSProto_FinalizationRegistry, &proto)) {
-    return false;
-  }
-
   RootedObject cleanupCallback(
       cx, ValueToCallable(cx, args.get(0), 1, NO_CONSTRUCT));
   if (!cleanupCallback) {
     return false;
   }
 
-  Rooted<GlobalObject*> incumbentGlobal(cx,
-                                        cx->runtime()->getIncumbentGlobal(cx));
+  RootedObject proto(cx);
+  if (!GetPrototypeFromBuiltinConstructor(
+          cx, args, JSProto_FinalizationRegistry, &proto)) {
+    return false;
+  }
 
   Rooted<UniquePtr<ObjectWeakMap>> registrations(
       cx, cx->make_unique<ObjectWeakMap>(cx));
   if (!registrations) {
     return false;
   }
 
   Rooted<UniquePtr<FinalizationRecordSet>> activeRecords(
@@ -323,47 +320,32 @@ bool FinalizationRegistryObject::constru
   }
 
   Rooted<UniquePtr<FinalizationRecordVector>> recordsToBeCleanedUp(
       cx, cx->make_unique<FinalizationRecordVector>(cx->zone()));
   if (!recordsToBeCleanedUp) {
     return false;
   }
 
-  HandlePropertyName funName = cx->names().empty;
-  RootedFunction doCleanupFunction(
-      cx, NewNativeFunction(cx, doCleanup, 0, funName,
-                            gc::AllocKind::FUNCTION_EXTENDED));
-  if (!doCleanupFunction) {
-    return false;
-  }
-
   FinalizationRegistryObject* registry =
       NewObjectWithClassProto<FinalizationRegistryObject>(cx, proto);
   if (!registry) {
     return false;
   }
 
   registry->initReservedSlot(CleanupCallbackSlot,
                              ObjectValue(*cleanupCallback));
-  registry->initReservedSlot(IncumbentGlobalSlot,
-                             ObjectValue(*incumbentGlobal));
   InitReservedSlot(registry, RegistrationsSlot, registrations.release(),
                    MemoryUse::FinalizationRegistryRegistrations);
   InitReservedSlot(registry, ActiveRecords, activeRecords.release(),
                    MemoryUse::FinalizationRegistryRecordSet);
   InitReservedSlot(registry, RecordsToBeCleanedUpSlot,
                    recordsToBeCleanedUp.release(),
                    MemoryUse::FinalizationRegistryRecordVector);
   registry->initReservedSlot(IsQueuedForCleanupSlot, BooleanValue(false));
-  registry->initReservedSlot(DoCleanupFunctionSlot,
-                             ObjectValue(*doCleanupFunction));
-
-  doCleanupFunction->setExtendedSlot(DoCleanupFunction_RegistrySlot,
-                                     ObjectValue(*registry));
 
   if (!cx->runtime()->gc.addFinalizationRegistry(cx, registry)) {
     return false;
   }
 
   args.rval().setObject(*registry);
   return true;
 }
@@ -431,32 +413,24 @@ void FinalizationRegistryObject::finaliz
   fop->delete_(obj, registry->registrations(),
                MemoryUse::FinalizationRegistryRegistrations);
   fop->delete_(obj, registry->activeRecords(),
                MemoryUse::FinalizationRegistryRecordSet);
   fop->delete_(obj, registry->recordsToBeCleanedUp(),
                MemoryUse::FinalizationRegistryRecordVector);
 }
 
-JSObject* FinalizationRegistryObject::cleanupCallback() const {
+inline JSObject* FinalizationRegistryObject::cleanupCallback() const {
   Value value = getReservedSlot(CleanupCallbackSlot);
   if (value.isUndefined()) {
     return nullptr;
   }
   return &value.toObject();
 }
 
-GlobalObject* FinalizationRegistryObject::incumbentGlobal() const {
-  Value value = getReservedSlot(IncumbentGlobalSlot);
-  if (value.isUndefined()) {
-    return nullptr;
-  }
-  return &value.toObject().as<GlobalObject>();
-}
-
 ObjectWeakMap* FinalizationRegistryObject::registrations() const {
   Value value = getReservedSlot(RegistrationsSlot);
   if (value.isUndefined()) {
     return nullptr;
   }
   return static_cast<ObjectWeakMap*>(value.toPrivate());
 }
 
@@ -476,24 +450,16 @@ FinalizationRecordVector* FinalizationRe
   }
   return static_cast<FinalizationRecordVector*>(value.toPrivate());
 }
 
 bool FinalizationRegistryObject::isQueuedForCleanup() const {
   return getReservedSlot(IsQueuedForCleanupSlot).toBoolean();
 }
 
-JSFunction* FinalizationRegistryObject::doCleanupFunction() const {
-  Value value = getReservedSlot(DoCleanupFunctionSlot);
-  if (value.isUndefined()) {
-    return nullptr;
-  }
-  return &value.toObject().as<JSFunction>();
-}
-
 void FinalizationRegistryObject::queueRecordToBeCleanedUp(
     FinalizationRecordObject* record) {
   AutoEnterOOMUnsafeRegion oomUnsafe;
   if (!recordsToBeCleanedUp()->append(record)) {
     oomUnsafe.crash("FinalizationRegistryObject::queueRecordsToBeCleanedUp");
   }
 }
 
@@ -799,31 +765,16 @@ bool FinalizationRegistryObject::cleanup
   if (!cleanupQueuedRecords(cx, registry, cleanupCallback)) {
     return false;
   }
 
   args.rval().setUndefined();
   return true;
 }
 
-/* static */
-bool FinalizationRegistryObject::doCleanup(JSContext* cx, unsigned argc,
-                                           Value* vp) {
-  CallArgs args = CallArgsFromVp(argc, vp);
-
-  RootedFunction callee(cx, &args.callee().as<JSFunction>());
-
-  Value value = callee->getExtendedSlot(DoCleanupFunction_RegistrySlot);
-  RootedFinalizationRegistryObject registry(
-      cx, &value.toObject().as<FinalizationRegistryObject>());
-
-  registry->setQueuedForCleanup(false);
-  return cleanupQueuedRecords(cx, registry);
-}
-
 // CleanupFinalizationRegistry ( finalizationRegistry [ , callback ] )
 // https://tc39.es/proposal-weakrefs/#sec-cleanup-finalization-registry
 /* static */
 bool FinalizationRegistryObject::cleanupQueuedRecords(
     JSContext* cx, HandleFinalizationRegistryObject registry,
     HandleObject callbackArg) {
   MOZ_ASSERT(cx->compartment() == registry->compartment());
 
--- a/js/src/builtin/FinalizationRegistryObject.h
+++ b/js/src/builtin/FinalizationRegistryObject.h
@@ -170,40 +170,32 @@ using FinalizationRecordVector =
 
 using FinalizationRecordSet =
     GCHashSet<HeapPtrObject, MovableCellHasher<HeapPtrObject>, ZoneAllocPolicy>;
 
 // The FinalizationRegistry object itself.
 class FinalizationRegistryObject : public NativeObject {
   enum {
     CleanupCallbackSlot = 0,
-    IncumbentGlobalSlot,
     RegistrationsSlot,
     ActiveRecords,
     RecordsToBeCleanedUpSlot,
     IsQueuedForCleanupSlot,
-    DoCleanupFunctionSlot,
     SlotCount
   };
 
-  enum DoCleanupFunctionSlots {
-    DoCleanupFunction_RegistrySlot = 0,
-  };
-
  public:
   static const JSClass class_;
   static const JSClass protoClass_;
 
   JSObject* cleanupCallback() const;
-  GlobalObject* incumbentGlobal() const;
   ObjectWeakMap* registrations() const;
   FinalizationRecordSet* activeRecords() const;
   FinalizationRecordVector* recordsToBeCleanedUp() const;
   bool isQueuedForCleanup() const;
-  JSFunction* doCleanupFunction() const;
 
   void queueRecordToBeCleanedUp(FinalizationRecordObject* record);
   void setQueuedForCleanup(bool value);
 
   void sweep();
 
   static bool unregisterRecord(FinalizationRecordObject* record);
 
@@ -227,17 +219,15 @@ class FinalizationRegistryObject : publi
                               HandleObject unregisterToken,
                               HandleFinalizationRecordObject record);
   static void removeRegistrationOnError(
       HandleFinalizationRegistryObject registry, HandleObject unregisterToken,
       HandleFinalizationRecordObject record);
 
   static bool preserveDOMWrapper(JSContext* cx, HandleObject obj);
 
-  static bool doCleanup(JSContext* cx, unsigned argc, Value* vp);
-
   static void trace(JSTracer* trc, JSObject* obj);
   static void finalize(JSFreeOp* fop, JSObject* obj);
 };
 
 }  // namespace js
 
 #endif /* builtin_FinalizationRegistryObject_h */
--- a/js/src/gc/FinalizationRegistry.cpp
+++ b/js/src/gc/FinalizationRegistry.cpp
@@ -113,13 +113,19 @@ void GCRuntime::sweepFinalizationRegistr
     }
   }
 }
 
 void GCRuntime::queueFinalizationRegistryForCleanup(
     FinalizationRegistryObject* registry) {
   // Prod the embedding to call us back later to run the finalization callbacks.
   if (!registry->isQueuedForCleanup()) {
-    callHostCleanupFinalizationRegistryCallback(registry->doCleanupFunction(),
-                                                registry->incumbentGlobal());
+    callHostCleanupFinalizationRegistryCallback(registry);
     registry->setQueuedForCleanup(true);
   }
 }
+
+bool GCRuntime::cleanupQueuedFinalizationRegistry(
+    JSContext* cx, HandleFinalizationRegistryObject registry) {
+  registry->setQueuedForCleanup(false);
+  bool ok = FinalizationRegistryObject::cleanupQueuedRecords(cx, registry);
+  return ok;
+}
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -1586,21 +1586,21 @@ void GCRuntime::callFinalizeCallbacks(JS
 }
 
 void GCRuntime::setHostCleanupFinalizationRegistryCallback(
     JSHostCleanupFinalizationRegistryCallback callback, void* data) {
   hostCleanupFinalizationRegistryCallback.ref() = {callback, data};
 }
 
 void GCRuntime::callHostCleanupFinalizationRegistryCallback(
-    JSFunction* doCleanup, GlobalObject* incumbentGlobal) {
+    FinalizationRegistryObject* registry) {
   JS::AutoSuppressGCAnalysis nogc;
   const auto& callback = hostCleanupFinalizationRegistryCallback.ref();
   if (callback.op) {
-    callback.op(doCleanup, incumbentGlobal, callback.data);
+    callback.op(registry, callback.data);
   }
 }
 
 bool GCRuntime::addWeakPointerZonesCallback(JSWeakPointerZonesCallback callback,
                                             void* data) {
   return updateWeakPointerZonesCallbacks.ref().append(
       Callback<JSWeakPointerZonesCallback>(callback, data));
 }
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -416,17 +416,17 @@ class GCRuntime {
   void setObjectsTenuredCallback(JSObjectsTenuredCallback callback, void* data);
   void callObjectsTenuredCallback();
   MOZ_MUST_USE bool addFinalizeCallback(JSFinalizeCallback callback,
                                         void* data);
   void removeFinalizeCallback(JSFinalizeCallback func);
   void setHostCleanupFinalizationRegistryCallback(
       JSHostCleanupFinalizationRegistryCallback callback, void* data);
   void callHostCleanupFinalizationRegistryCallback(
-      JSFunction* doCleanup, GlobalObject* incumbentGlobal);
+      FinalizationRegistryObject* registry);
   MOZ_MUST_USE bool addWeakPointerZonesCallback(
       JSWeakPointerZonesCallback callback, void* data);
   void removeWeakPointerZonesCallback(JSWeakPointerZonesCallback callback);
   MOZ_MUST_USE bool addWeakPointerCompartmentCallback(
       JSWeakPointerCompartmentCallback callback, void* data);
   void removeWeakPointerCompartmentCallback(
       JSWeakPointerCompartmentCallback callback);
   JS::GCSliceCallback setSliceCallback(JS::GCSliceCallback callback);
@@ -434,16 +434,18 @@ class GCRuntime {
       JS::GCNurseryCollectionCallback callback);
   JS::DoCycleCollectionCallback setDoCycleCollectionCallback(
       JS::DoCycleCollectionCallback callback);
 
   bool addFinalizationRegistry(JSContext* cx,
                                FinalizationRegistryObject* registry);
   bool registerWithFinalizationRegistry(JSContext* cx, HandleObject target,
                                         HandleObject record);
+  bool cleanupQueuedFinalizationRegistry(
+      JSContext* cx, Handle<FinalizationRegistryObject*> registry);
 
   void setFullCompartmentChecks(bool enable);
 
   JS::Zone* getCurrentSweepGroup() { return currentSweepGroup; }
   unsigned getCurrentSweepGroupIndex() {
     return state() == State::Sweep ? sweepGroupIndex : 0;
   }
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1391,16 +1391,25 @@ JS_PUBLIC_API void JS_RemoveFinalizeCall
 }
 
 JS_PUBLIC_API void JS::SetHostCleanupFinalizationRegistryCallback(
     JSContext* cx, JSHostCleanupFinalizationRegistryCallback cb, void* data) {
   AssertHeapIsIdle();
   cx->runtime()->gc.setHostCleanupFinalizationRegistryCallback(cb, data);
 }
 
+JS_PUBLIC_API bool JS::CleanupQueuedFinalizationRegistry(
+    JSContext* cx, HandleObject registry) {
+  AssertHeapIsIdle();
+  CHECK_THREAD(cx);
+  cx->check(registry);
+  return cx->runtime()->gc.cleanupQueuedFinalizationRegistry(
+      cx, registry.as<FinalizationRegistryObject>());
+}
+
 JS_PUBLIC_API void JS::ClearKeptObjects(JSContext* cx) {
   gc::GCRuntime* gc = &cx->runtime()->gc;
 
   for (ZonesIter zone(gc, ZoneSelector::WithAtoms); !zone.done(); zone.next()) {
     zone->clearKeptObjects();
   }
 }
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3064,16 +3064,23 @@ extern JS_PUBLIC_API bool BuildStackStri
 extern JS_PUBLIC_API bool IsMaybeWrappedSavedFrame(JSObject* obj);
 
 /**
  * Return true iff the given object is a SavedFrame object and not the
  * SavedFrame.prototype object.
  */
 extern JS_PUBLIC_API bool IsUnwrappedSavedFrame(JSObject* obj);
 
+/**
+ * Clean up a finalization registry in response to the engine calling the
+ * HostCleanupFinalizationRegistry callback.
+ */
+extern JS_PUBLIC_API bool CleanupQueuedFinalizationRegistry(
+    JSContext* cx, HandleObject registry);
+
 } /* namespace JS */
 
 namespace js {
 
 /**
  * Hint that we expect a crash. Currently, the only thing that cares is the
  * breakpad injector, which (if loaded) will suppress minidump generation.
  */
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -653,17 +653,17 @@ ShellContext::ShellContext(JSContext* cx
       unhandledRejectedPromises(cx),
       watchdogLock(mutexid::ShellContextWatchdog),
       exitCode(0),
       quitting(false),
       readLineBufPos(0),
       errFilePtr(nullptr),
       outFilePtr(nullptr),
       offThreadMonitor(mutexid::ShellOffThreadState),
-      finalizationRegistryCleanupCallbacks(cx) {}
+      finalizationRegistriesToCleanUp(cx) {}
 
 ShellContext::~ShellContext() { MOZ_ASSERT(offThreadJobs.empty()); }
 
 ShellContext* js::shell::GetShellContext(JSContext* cx) {
   ShellContext* sc = static_cast<ShellContext*>(JS_GetContextPrivate(cx));
   MOZ_ASSERT(sc);
   return sc;
 }
@@ -975,51 +975,46 @@ static MOZ_MUST_USE bool RunModule(JSCon
   path = ResolvePath(cx, path, RootRelative);
   if (!path) {
     return false;
   }
 
   return sc->moduleLoader->loadRootModule(cx, path);
 }
 
-static void ShellCleanupFinalizationRegistryCallback(JSFunction* doCleanup,
-                                                     JSObject* incumbentGlobal,
+static void ShellCleanupFinalizationRegistryCallback(JSObject* registry,
                                                      void* data) {
   // In the browser this queues a task. Shell jobs correspond to microtasks so
-  // we arrange for cleanup to happen after all jobs/microtasks have run. The
-  // incumbent global is ignored in the shell.
-
+  // we arrange for cleanup to happen after all jobs/microtasks have run.
   auto sc = static_cast<ShellContext*>(data);
   AutoEnterOOMUnsafeRegion oomUnsafe;
-  if (!sc->finalizationRegistryCleanupCallbacks.append(doCleanup)) {
+  if (!sc->finalizationRegistriesToCleanUp.append(registry)) {
     oomUnsafe.crash("ShellCleanupFinalizationRegistryCallback");
   }
 }
 
 // Run any FinalizationRegistry cleanup tasks and return whether any ran.
 static bool MaybeRunFinalizationRegistryCleanupTasks(JSContext* cx) {
   ShellContext* sc = GetShellContext(cx);
   MOZ_ASSERT(!sc->quitting);
 
-  Rooted<ShellContext::FunctionVector> callbacks(cx);
-  std::swap(callbacks.get(), sc->finalizationRegistryCleanupCallbacks.get());
+  Rooted<ShellContext::ObjectVector> registries(cx);
+  std::swap(registries.get(), sc->finalizationRegistriesToCleanUp.get());
 
   bool ranTasks = false;
 
-  RootedFunction callback(cx);
-  for (JSFunction* f : callbacks) {
-    callback = f;
-
-    AutoRealm ar(cx, f);
+  RootedObject registry(cx);
+  for (const auto& r : registries) {
+    registry = r;
+
+    AutoRealm ar(cx, registry);
 
     {
       AutoReportException are(cx);
-      RootedValue unused(cx);
-      mozilla::Unused << JS_CallFunction(cx, nullptr, callback,
-                                         HandleValueArray::empty(), &unused);
+      mozilla::Unused << JS::CleanupQueuedFinalizationRegistry(cx, registry);
     }
 
     ranTasks = true;
 
     if (sc->quitting) {
       break;
     }
   }
--- a/js/src/shell/jsshell.h
+++ b/js/src/shell/jsshell.h
@@ -246,18 +246,18 @@ struct ShellContext {
 
   UniquePtr<MarkBitObservers> markObservers;
 
   // Off-thread parse state.
   js::Monitor offThreadMonitor;
   Vector<OffThreadJob*, 0, SystemAllocPolicy> offThreadJobs;
 
   // Queued finalization registry cleanup jobs.
-  using FunctionVector = GCVector<JSFunction*, 0, SystemAllocPolicy>;
-  JS::PersistentRooted<FunctionVector> finalizationRegistryCleanupCallbacks;
+  using ObjectVector = GCVector<JSObject*, 0, SystemAllocPolicy>;
+  JS::PersistentRooted<ObjectVector> finalizationRegistriesToCleanUp;
 };
 
 extern ShellContext* GetShellContext(JSContext* cx);
 
 extern MOZ_MUST_USE bool PrintStackTrace(JSContext* cx,
                                          JS::Handle<JSObject*> stackObj);
 
 extern JSObject* CreateScriptPrivate(JSContext* cx,
--- a/js/xpconnect/tests/mochitest/mochitest.ini
+++ b/js/xpconnect/tests/mochitest/mochitest.ini
@@ -111,12 +111,11 @@ skip-if = (debug == false)
 [test_sameOriginPolicy.html]
 [test_sandbox_fetch.html]
   support-files =
     ../../../../dom/tests/mochitest/fetch/test_fetch_basic.js
 [test_weakmaps.html]
 [test_finalizationRegistry.html]
 [test_finalizationRegistryInWorker.html]
 [test_finalizationRegistry_cleanupSome.html]
-[test_finalizationRegistry_incumbent.html]
 [test_weakRefs.html]
 [test_weakRefs_cross_compartment.html]
 [test_weakRefs_collected_wrapper.html]
deleted file mode 100644
--- a/js/xpconnect/tests/mochitest/test_finalizationRegistry_incumbent.html
+++ /dev/null
@@ -1,62 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <title>Test FinalizationRegistry tracks its incumbent global</title>
-    <script src="/tests/SimpleTest/SimpleTest.js"></script>
-    <script type="application/javascript">
-      let resolvePromise, rejectPromise;
-
-      async function runTest(global, callback) {
-        let fr = new global.FinalizationRegistry(callback);
-        fr.register({}, undefined);
-
-        SpecialPowers.DOMWindowUtils.garbageCollect();
-
-        let promise = new Promise((resolve, reject) => {
-          resolvePromise = resolve;
-          rejectPromise = reject;
-        });
-
-        return promise;
-      }
-
-      function receiveMessage(event) {
-        resolvePromise(event.source.sourceName);
-      }
-
-      async function go() {
-        // This test uses FinalizationRegistry to trigger a callback and reports
-        // the incumbent global in the callback using postMessage. In all cases
-        // the author function that scheduled the callback is runTest(), so the
-        // incumbent global should be the main window.
-
-        SimpleTest.waitForExplicitFinish();
-
-        window.sourceName = "main";
-        window.addEventListener("message", receiveMessage, false);
-
-        let other = window.frames[0];
-        other.sourceName = "other";
-        other.addEventListener("message", receiveMessage, false);
-
-        is(await runTest(window, v => window.postMessage(v)), "main");
-        is(await runTest(window, window.postMessage.bind(window)), "main");
-        is(await runTest(other, v => other.postMessage(v)), "main");
-        is(await runTest(other, other.postMessage.bind(other)), "main");
-        is(await runTest(window, v => other.postMessage(v)), "main");
-        is(await runTest(window, other.postMessage.bind(other)), "main");
-        is(await runTest(other, v => window.postMessage(v)), "main");
-        is(await runTest(other, window.postMessage.bind(window)), "main");
-
-        SimpleTest.finish();
-      }
-    </script>
-  </head>
-  <body onload="go()">
-    <div style="display: none">
-      <!-- A subframe so we have another global to work with -->
-      <iframe></iframe>
-    </div>
-  </body>
-</html>
--- a/js/xpconnect/wrappers/AccessCheck.h
+++ b/js/xpconnect/wrappers/AccessCheck.h
@@ -6,22 +6,16 @@
 
 #ifndef __AccessCheck_h__
 #define __AccessCheck_h__
 
 #include "js/Id.h"
 #include "js/Wrapper.h"
 #include "nsString.h"
 
-#ifdef XP_MACOSX
-// AssertMacros.h defines 'check' which conflicts with the method declarations
-// in this file.
-#  undef check
-#endif
-
 namespace xpc {
 
 class AccessCheck {
  public:
   static bool subsumes(JSObject* a, JSObject* b);
   static bool wrapperSubsumes(JSObject* wrapper);
   static bool subsumesConsideringDomain(JS::Realm* a, JS::Realm* b);
   static bool subsumesConsideringDomainIgnoringFPD(JS::Realm* a, JS::Realm* b);
--- a/xpcom/base/CycleCollectedJSContext.cpp
+++ b/xpcom/base/CycleCollectedJSContext.cpp
@@ -21,17 +21,16 @@
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimelineConsumers.h"
 #include "mozilla/TimelineMarker.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/DOMJSClass.h"
-#include "mozilla/dom/FinalizationRegistryBinding.h"
 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
 #include "mozilla/dom/PromiseBinding.h"
 #include "mozilla/dom/PromiseDebugging.h"
 #include "mozilla/dom/PromiseRejectionEvent.h"
 #include "mozilla/dom/PromiseRejectionEventBinding.h"
 #include "mozilla/dom/RootedDictionary.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/UserActivation.h"
@@ -56,33 +55,33 @@ namespace mozilla {
 
 CycleCollectedJSContext::CycleCollectedJSContext()
     : mRuntime(nullptr),
       mJSContext(nullptr),
       mDoingStableStates(false),
       mTargetedMicroTaskRecursionDepth(0),
       mMicroTaskLevel(0),
       mDebuggerRecursionDepth(0),
-      mMicroTaskRecursionDepth(0),
-      mFinalizationRegistryCleanup(this) {
+      mMicroTaskRecursionDepth(0) {
   MOZ_COUNT_CTOR(CycleCollectedJSContext);
 
   nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
   mOwningThread = thread.forget().downcast<nsThread>().take();
   MOZ_RELEASE_ASSERT(mOwningThread);
 }
 
 CycleCollectedJSContext::~CycleCollectedJSContext() {
   MOZ_COUNT_DTOR(CycleCollectedJSContext);
   // If the allocation failed, here we are.
   if (!mJSContext) {
     return;
   }
 
   JS::SetHostCleanupFinalizationRegistryCallback(mJSContext, nullptr, nullptr);
+  mFinalizationRegistriesToCleanUp.reset();
 
   JS_SetContextPrivate(mJSContext, nullptr);
 
   mRuntime->SetContext(nullptr);
   mRuntime->Shutdown(mJSContext);
 
   // Last chance to process any events.
   CleanupIDBTransactions(mBaseRecursionDepth);
@@ -98,18 +97,16 @@ CycleCollectedJSContext::~CycleCollected
   MOZ_ASSERT(mPendingMicroTaskRunnables.empty());
 
   mUncaughtRejections.reset();
   mConsumedRejections.reset();
 
   mAboutToBeNotifiedRejectedPromises.Clear();
   mPendingUnhandledRejections.Clear();
 
-  mFinalizationRegistryCleanup.Destroy();
-
   JS_DestroyContext(mJSContext);
   mJSContext = nullptr;
 
   nsCycleCollector_forgetJSContext();
 
   mozilla::dom::DestroyScriptSettings();
 
   mOwningThread->SetScriptObserver(nullptr);
@@ -143,17 +140,19 @@ nsresult CycleCollectedJSContext::Initia
                                          PromiseRejectionTrackerCallback, this);
   mUncaughtRejections.init(mJSContext,
                            JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>(
                                js::SystemAllocPolicy()));
   mConsumedRejections.init(mJSContext,
                            JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>(
                                js::SystemAllocPolicy()));
 
-  mFinalizationRegistryCleanup.Init();
+  mFinalizationRegistriesToCleanUp.init(mJSContext);
+  JS::SetHostCleanupFinalizationRegistryCallback(
+      mJSContext, CleanupFinalizationRegistryCallback, this);
 
   // Cast to PerThreadAtomCache for dom::GetAtomCache(JSContext*).
   JS_SetContextPrivate(mJSContext, static_cast<PerThreadAtomCache*>(this));
 
   nsCycleCollector_registerJSContext(this);
 
   return NS_OK;
 }
@@ -744,100 +743,61 @@ nsresult CycleCollectedJSContext::Notify
     }
 
     JS::RootedObject promiseObj(mCx->RootingCx(), promise->PromiseObj());
     mCx->mPendingUnhandledRejections.Remove(JS::GetPromiseID(promiseObj));
   }
   return NS_OK;
 }
 
-class FinalizationRegistryCleanup::CleanupRunnable : public CancelableRunnable {
+class CleanupFinalizationRegistriesRunnable : public CancelableRunnable {
  public:
-  explicit CleanupRunnable(FinalizationRegistryCleanup* aCleanupWork)
-      : CancelableRunnable("CleanupRunnable"), mCleanupWork(aCleanupWork) {}
+  explicit CleanupFinalizationRegistriesRunnable(
+      CycleCollectedJSContext* aContext)
+      : CancelableRunnable("CleanupFinalizationRegistriesRunnable"),
+        mContext(aContext) {}
+  NS_DECL_NSIRUNNABLE
+ private:
+  CycleCollectedJSContext* mContext;
+};
 
-  // MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT.  See
-  // bug 1535398.
-  MOZ_CAN_RUN_SCRIPT_BOUNDARY
-  NS_IMETHODIMP Run() {
-    mCleanupWork->DoCleanup();
+NS_IMETHODIMP
+CleanupFinalizationRegistriesRunnable::Run() {
+  if (mContext->mFinalizationRegistriesToCleanUp.empty()) {
     return NS_OK;
   }
 
- private:
-  FinalizationRegistryCleanup* mCleanupWork;
-};
+  JS::RootingContext* cx = mContext->RootingCx();
 
-FinalizationRegistryCleanup::FinalizationRegistryCleanup(
-    CycleCollectedJSContext* aContext)
-    : mContext(aContext) {}
+  JS::Rooted<CycleCollectedJSContext::ObjectVector> registries(cx);
+  std::swap(registries.get(), mContext->mFinalizationRegistriesToCleanUp.get());
 
-void FinalizationRegistryCleanup::Destroy() {
-  // This must happen before the CycleCollectedJSContext destructor calls
-  // JS_DestroyContext().
-  mCallbacks.reset();
-}
+  JS::Rooted<JSObject*> registry(cx);
+  for (const auto& r : registries) {
+    registry = r;
 
-void FinalizationRegistryCleanup::Init() {
-  JSContext* cx = mContext->Context();
-  mCallbacks.init(cx);
-  JS::SetHostCleanupFinalizationRegistryCallback(cx, QueueCallback, this);
+    AutoEntryScript aes(registry, "cleanupFinalizationRegistry");
+    mozilla::Unused << JS::CleanupQueuedFinalizationRegistry(aes.cx(),
+                                                             registry);
+  }
+
+  return NS_OK;
 }
 
 /* static */
-void FinalizationRegistryCleanup::QueueCallback(JSFunction* aDoCleanup,
-                                                JSObject* aIncumbentGlobal,
-                                                void* aData) {
-  FinalizationRegistryCleanup* cleanup =
-      static_cast<FinalizationRegistryCleanup*>(aData);
-  cleanup->QueueCallback(aDoCleanup, aIncumbentGlobal);
+void CycleCollectedJSContext::CleanupFinalizationRegistryCallback(
+    JSObject* aRegistry, void* aData) {
+  CycleCollectedJSContext* ccjs = static_cast<CycleCollectedJSContext*>(aData);
+  ccjs->QueueFinalizationRegistryForCleanup(aRegistry);
 }
 
-void FinalizationRegistryCleanup::QueueCallback(JSFunction* aDoCleanup,
-                                                JSObject* aIncumbentGlobal) {
-  bool firstCallback = mCallbacks.empty();
-
-  MOZ_ALWAYS_TRUE(mCallbacks.append(Callback{aDoCleanup, aIncumbentGlobal}));
-
-  if (firstCallback) {
-    RefPtr<CleanupRunnable> cleanup = new CleanupRunnable(this);
+void CycleCollectedJSContext::QueueFinalizationRegistryForCleanup(
+    JSObject* aRegistry) {
+  bool firstRegistry = mFinalizationRegistriesToCleanUp.empty();
+  MOZ_ALWAYS_TRUE(mFinalizationRegistriesToCleanUp.append(aRegistry));
+  if (firstRegistry) {
+    RefPtr<CleanupFinalizationRegistriesRunnable> cleanup =
+        new CleanupFinalizationRegistriesRunnable(this);
     NS_DispatchToCurrentThread(cleanup.forget());
   }
 }
 
-void FinalizationRegistryCleanup::DoCleanup() {
-  if (mCallbacks.empty()) {
-    return;
-  }
-
-  JS::RootingContext* cx = mContext->RootingCx();
-
-  JS::Rooted<CallbackVector> callbacks(cx);
-  std::swap(callbacks.get(), mCallbacks.get());
-
-  for (const Callback& callback : callbacks) {
-    JSObject* functionObj = JS_GetFunctionObject(callback.mCallbackFunction);
-    JS::RootedObject globalObj(cx, JS::GetNonCCWObjectGlobal(functionObj));
-
-    nsIGlobalObject* incumbentGlobal =
-        xpc::NativeGlobal(callback.mIncumbentGlobal);
-    if (!incumbentGlobal) {
-      continue;
-    }
-
-    RefPtr<FinalizationRegistryCleanupCallback> cleanupCallback(
-        new FinalizationRegistryCleanupCallback(functionObj, globalObj, nullptr,
-                                                incumbentGlobal));
-
-    nsIGlobalObject* global =
-        xpc::NativeGlobal(cleanupCallback->CallbackPreserveColor());
-    if (global) {
-      cleanupCallback->Call("FinalizationRegistryCleanup::DoCleanup");
-    }
-  }
-}
-
-void FinalizationRegistryCleanup::Callback::trace(JSTracer* trc) {
-  JS::UnsafeTraceRoot(trc, &mCallbackFunction, "mCallbackFunction");
-  JS::UnsafeTraceRoot(trc, &mIncumbentGlobal, "mIncumbentGlobal");
-}
-
 }  // namespace mozilla
--- a/xpcom/base/CycleCollectedJSContext.h
+++ b/xpcom/base/CycleCollectedJSContext.h
@@ -27,17 +27,16 @@
 class nsCycleCollectionNoteRootCallback;
 class nsIRunnable;
 class nsThread;
 class nsWrapperCache;
 
 namespace mozilla {
 class AutoSlowOperation;
 
-class CycleCollectedJSContext;
 class CycleCollectedJSRuntime;
 
 namespace dom {
 class Exception;
 class WorkerJSContext;
 class WorkletJSContext;
 }  // namespace dom
 
@@ -81,50 +80,16 @@ class MicroTaskRunnable {
   NS_INLINE_DECL_REFCOUNTING(MicroTaskRunnable)
   MOZ_CAN_RUN_SCRIPT virtual void Run(AutoSlowOperation& aAso) = 0;
   virtual bool Suppressed() { return false; }
 
  protected:
   virtual ~MicroTaskRunnable() = default;
 };
 
-// Support for JS FinalizationRegistry objects, which allow a JS callback to be
-// registered that is called when objects die.
-//
-// We keep a vector of functions that call back into the JS engine along
-// with their associated incumbent globals, one per FinalizationRegistry object
-// that has pending cleanup work. These are run in their own task.
-class FinalizationRegistryCleanup {
- public:
-  explicit FinalizationRegistryCleanup(CycleCollectedJSContext* aContext);
-  void Init();
-  void Destroy();
-  void QueueCallback(JSFunction* aDoCleanup, JSObject* aIncumbentGlobal);
-  MOZ_CAN_RUN_SCRIPT void DoCleanup();
-
- private:
-  static void QueueCallback(JSFunction* aDoCleanup, JSObject* aIncumbentGlobal,
-                            void* aData);
-
-  class CleanupRunnable;
-
-  struct Callback {
-    JSFunction* mCallbackFunction;
-    JSObject* mIncumbentGlobal;
-    void trace(JSTracer* trc);
-  };
-
-  // This object is part of CycleCollectedJSContext, so it's safe to have a raw
-  // pointer to its containing context here.
-  CycleCollectedJSContext* mContext;
-
-  using CallbackVector = JS::GCVector<Callback, 0, InfallibleAllocPolicy>;
-  JS::PersistentRooted<CallbackVector> mCallbacks;
-};
-
 class CycleCollectedJSContext : dom::PerThreadAtomCache, private JS::JobQueue {
   friend class CycleCollectedJSRuntime;
 
  protected:
   CycleCollectedJSContext();
   virtual ~CycleCollectedJSContext();
 
   MOZ_IS_CLASS_INIT
@@ -292,16 +257,20 @@ class CycleCollectedJSContext : dom::Per
   // headers.  The caller presumably knows this can run script (like everything
   // in SpiderMonkey!) and will deal.
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void runJobs(JSContext* cx) override;
   bool empty() const override;
   class SavedMicroTaskQueue;
   js::UniquePtr<SavedJobQueue> saveJobQueue(JSContext*) override;
 
+  static void CleanupFinalizationRegistryCallback(JSObject* aRegistry,
+                                                  void* aData);
+  void QueueFinalizationRegistryForCleanup(JSObject* aRegistry);
+
  private:
   CycleCollectedJSRuntime* mRuntime;
 
   JSContext* mJSContext;
 
   nsCOMPtr<dom::Exception> mPendingException;
   nsThread* mOwningThread;  // Manual refcounting to avoid include hell.
 
@@ -366,17 +335,24 @@ class CycleCollectedJSContext : dom::Per
 
     nsresult Cancel() final;
 
    private:
     CycleCollectedJSContext* mCx;
     PromiseArray mUnhandledRejections;
   };
 
-  FinalizationRegistryCleanup mFinalizationRegistryCleanup;
+  // Support for JS FinalizationRegistry objects.
+  //
+  // These allow a JS callback to be registered that is called when an object
+  // dies. The browser part of the implementation keeps a vector of
+  // FinalizationRegistries with pending callbacks here.
+  friend class CleanupFinalizationRegistriesRunnable;
+  using ObjectVector = JS::GCVector<JSObject*, 0, InfallibleAllocPolicy>;
+  JS::PersistentRooted<ObjectVector> mFinalizationRegistriesToCleanUp;
 };
 
 class MOZ_STACK_CLASS nsAutoMicroTask {
  public:
   nsAutoMicroTask() {
     CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
     if (ccjs) {
       ccjs->EnterMicroTask();