Bug 883920 - use templates for {Hold,Drop}JSObjects. r=peterv
authorAndrew McCreight <amccreight@mozilla.com>
Fri, 16 Aug 2013 13:10:17 -0700
changeset 145346 40b6e77191019a552bcf5848d120cfe3e63a911f
parent 145345 810c464191d4686eaede172cacefb50fe091e2eb
child 145347 98c209ce24081449952d9bbecfc2651ddecb319d
push id33248
push useramccreight@mozilla.com
push dateTue, 03 Sep 2013 20:24:06 +0000
treeherdermozilla-inbound@40b6e7719101 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs883920
milestone26.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 883920 - use templates for {Hold,Drop}JSObjects. r=peterv
content/base/public/nsContentUtils.h
content/base/src/nsContentUtils.cpp
content/base/src/nsDOMFileReader.cpp
content/base/src/nsDocument.cpp
content/base/src/nsXMLHttpRequest.cpp
content/canvas/src/ImageData.cpp
content/events/src/nsDOMMessageEvent.cpp
content/events/src/nsDOMNotifyAudioAvailableEvent.cpp
content/media/webaudio/AudioBuffer.cpp
content/media/webaudio/MediaBufferDecoder.cpp
content/media/webaudio/WaveShaperNode.cpp
content/xbl/src/nsXBLDocumentInfo.cpp
content/xul/content/src/nsXULElement.cpp
content/xul/document/src/nsXULPrototypeDocument.cpp
dom/base/DOMRequest.cpp
dom/base/DOMRequest.h
dom/base/nsGlobalWindow.cpp
dom/base/nsJSEnvironment.cpp
dom/base/nsJSTimeoutHandler.cpp
dom/base/nsWrapperCache.cpp
dom/bindings/CallbackObject.h
dom/bluetooth/BluetoothAdapter.cpp
dom/bluetooth/BluetoothDevice.cpp
dom/indexedDB/IDBCursor.cpp
dom/indexedDB/IDBFactory.cpp
dom/indexedDB/IDBIndex.cpp
dom/indexedDB/IDBKeyRange.cpp
dom/indexedDB/IDBObjectStore.cpp
dom/indexedDB/IDBWrapperCache.cpp
dom/promise/Promise.cpp
dom/src/events/nsJSEventListener.cpp
xpcom/base/nsCycleCollector.cpp
xpcom/base/nsCycleCollector.h
xpcom/glue/moz.build
xpcom/glue/nsCycleCollectionHoldDrop.h
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1240,38 +1240,16 @@ public:
    */
   static void DestroyMatchString(void* aData);
 
   /**
    * Unbinds the content from the tree and nulls it out if it's not null.
    */
   static void DestroyAnonymousContent(nsCOMPtr<nsIContent>* aContent);
 
-  /**
-   * Keep the JS objects held by aScriptObjectHolder alive.
-   *
-   * @param aScriptObjectHolder the object that holds JS objects that we want to
-   *                            keep alive
-   * @param aTracer the tracer for aScriptObject
-   */
-  static void HoldJSObjects(void* aScriptObjectHolder,
-                            nsScriptObjectTracer* aTracer);
-
-  /**
-   * Drop the JS objects held by aScriptObjectHolder.
-   *
-   * @param aScriptObjectHolder the object that holds JS objects that we want to
-   *                            drop
-   */
-  static void DropJSObjects(void* aScriptObjectHolder);
-
-#ifdef DEBUG
-  static bool AreJSObjectsHeld(void* aScriptObjectHolder); 
-#endif
-
   static void DeferredFinalize(nsISupports* aSupports);
   static void DeferredFinalize(mozilla::DeferredFinalizeAppendFunction aAppendFunc,
                                mozilla::DeferredFinalizeFunction aFunc,
                                void* aThing);
 
   /*
    * Notify when the first XUL menu is opened and when the all XUL menus are
    * closed. At opening, aInstalling should be TRUE, otherwise, it should be
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -4320,40 +4320,16 @@ nsContentUtils::DestroyAnonymousContent(
 {
   if (*aContent) {
     AddScriptRunner(new AnonymousContentDestroyer(aContent));
   }
 }
 
 /* static */
 void
-nsContentUtils::HoldJSObjects(void* aScriptObjectHolder,
-                              nsScriptObjectTracer* aTracer)
-{
-  cyclecollector::AddJSHolder(aScriptObjectHolder, aTracer);
-}
-
-/* static */
-void
-nsContentUtils::DropJSObjects(void* aScriptObjectHolder)
-{
-  cyclecollector::RemoveJSHolder(aScriptObjectHolder);
-}
-
-#ifdef DEBUG
-/* static */
-bool
-nsContentUtils::AreJSObjectsHeld(void* aScriptObjectHolder)
-{
-  return cyclecollector::IsJSHolder(aScriptObjectHolder);
-}
-#endif
-
-/* static */
-void
 nsContentUtils::NotifyInstalledMenuKeyboardListener(bool aInstalling)
 {
   nsIMEStateManager::OnInstalledMenuKeyboardListener(aInstalling);
 }
 
 static bool SchemeIs(nsIURI* aURI, const char* aScheme)
 {
   nsCOMPtr<nsIURI> baseURI = NS_GetInnermostURI(aURI);
--- a/content/base/src/nsDOMFileReader.cpp
+++ b/content/base/src/nsDOMFileReader.cpp
@@ -89,17 +89,17 @@ NS_IMPL_EVENT_HANDLER(nsDOMFileReader, l
 NS_IMPL_EVENT_HANDLER(nsDOMFileReader, loadstart)
 NS_IMPL_FORWARD_EVENT_HANDLER(nsDOMFileReader, abort, FileIOObject)
 NS_IMPL_FORWARD_EVENT_HANDLER(nsDOMFileReader, progress, FileIOObject)
 NS_IMPL_FORWARD_EVENT_HANDLER(nsDOMFileReader, error, FileIOObject)
 
 void
 nsDOMFileReader::RootResultArrayBuffer()
 {
-  NS_HOLD_JS_OBJECTS(this, nsDOMFileReader);
+  mozilla::HoldJSObjects(this);
 }
 
 //nsDOMFileReader constructors/initializers
 
 nsDOMFileReader::nsDOMFileReader()
   : mFileData(nullptr),
     mDataLen(0), mDataFormat(FILE_AS_BINARY),
     mResultArrayBuffer(nullptr)
@@ -107,17 +107,17 @@ nsDOMFileReader::nsDOMFileReader()
   SetDOMStringToNull(mResult);
   SetIsDOMBinding();
 }
 
 nsDOMFileReader::~nsDOMFileReader()
 {
   FreeFileData();
   mResultArrayBuffer = nullptr;
-  NS_DROP_JS_OBJECTS(this, nsDOMFileReader);
+  mozilla::DropJSObjects(this);
 }
 
 
 /**
  * This Init method is called from the factory constructor.
  */
 nsresult
 nsDOMFileReader::Init()
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1465,20 +1465,17 @@ nsDocument::~nsDocument()
     }
   }
 
   mInDestructor = true;
   mInUnlinkOrDeletion = true;
 
   mCustomPrototypes.Clear();
 
-  nsISupports* supports;
-  QueryInterface(NS_GET_IID(nsCycleCollectionISupports), reinterpret_cast<void**>(&supports));
-  NS_ASSERTION(supports, "Failed to QI to nsCycleCollectionISupports?!");
-  nsContentUtils::DropJSObjects(supports);
+  mozilla::DropJSObjects(this);
 
   // Clear mObservers to keep it in sync with the mutationobserver list
   mObservers.Clear();
 
   if (mStyleSheetSetList) {
     mStyleSheetSetList->Disconnect();
   }
 
@@ -1981,24 +1978,17 @@ nsDocument::Init()
   mScopeObject = do_GetWeakReference(global);
   MOZ_ASSERT(mScopeObject);
 
   mScriptLoader = new nsScriptLoader(this);
 
   mImageTracker.Init();
   mPlugins.Init();
 
-  nsXPCOMCycleCollectionParticipant* participant;
-  CallQueryInterface(this, &participant);
-  NS_ASSERTION(participant, "Failed to QI to nsXPCOMCycleCollectionParticipant!");
-
-  nsISupports* thisSupports;
-  QueryInterface(NS_GET_IID(nsCycleCollectionISupports), reinterpret_cast<void**>(&thisSupports));
-  NS_ASSERTION(thisSupports, "Failed to QI to nsCycleCollectionISupports!");
-  nsContentUtils::HoldJSObjects(thisSupports, participant);
+  mozilla::HoldJSObjects(this);
 
   return NS_OK;
 }
 
 void
 nsIDocument::DeleteAllProperties()
 {
   for (uint32_t i = 0; i < GetPropertyTableCount(); ++i) {
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -318,23 +318,23 @@ nsXMLHttpRequest::~nsXMLHttpRequest()
     Abort();
   }
 
   NS_ABORT_IF_FALSE(!(mState & XML_HTTP_REQUEST_SYNCLOOPING), "we rather crash than hang");
   mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
 
   mResultJSON = JSVAL_VOID;
   mResultArrayBuffer = nullptr;
-  NS_DROP_JS_OBJECTS(this, nsXMLHttpRequest);
+  mozilla::DropJSObjects(this);
 }
 
 void
 nsXMLHttpRequest::RootJSResultObjects()
 {
-  NS_HOLD_JS_OBJECTS(this, nsXMLHttpRequest);
+  mozilla::HoldJSObjects(this);
 }
 
 /**
  * This Init method is called from the factory constructor.
  */
 nsresult
 nsXMLHttpRequest::Init()
 {
--- a/content/canvas/src/ImageData.cpp
+++ b/content/canvas/src/ImageData.cpp
@@ -1,18 +1,18 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 et tw=78: */
 /* 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 "mozilla/dom/ImageData.h"
 
-#include "nsContentUtils.h" // for NS_HOLD_JS_OBJECTS, NS_DROP_JS_OBJECTS
 #include "mozilla/dom/ImageDataBinding.h"
+#include "nsCycleCollectionHoldDrop.h"
 
 #include "jsapi.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ImageData)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ImageData)
@@ -33,25 +33,25 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ImageData)
   tmp->DropData();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 void
 ImageData::HoldData()
 {
-  NS_HOLD_JS_OBJECTS(this, ImageData);
+  mozilla::HoldJSObjects(this);
 }
 
 void
 ImageData::DropData()
 {
   if (mData) {
     mData = nullptr;
-    NS_DROP_JS_OBJECTS(this, ImageData);
+    mozilla::DropJSObjects(this);
   }
 }
 
 JSObject*
 ImageData::WrapObject(JSContext* cx, JS::Handle<JSObject*> scope)
 {
   return ImageDataBinding::Wrap(cx, scope, this);
 }
--- a/content/events/src/nsDOMMessageEvent.cpp
+++ b/content/events/src/nsDOMMessageEvent.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; 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/. */
 
 #include "nsDOMMessageEvent.h"
-#include "nsContentUtils.h" // for NS_HOLD_JS_OBJECTS, NS_DROP_JS_OBJECTS
+
+#include "nsCycleCollectionHoldDrop.h"
 #include "jsapi.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMMessageEvent)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMMessageEvent, nsDOMEvent)
@@ -38,17 +39,17 @@ nsDOMMessageEvent::nsDOMMessageEvent(moz
   : nsDOMEvent(aOwner, aPresContext, aEvent),
     mData(JSVAL_VOID)
 {
 }
 
 nsDOMMessageEvent::~nsDOMMessageEvent()
 {
   mData = JSVAL_VOID;
-  NS_DROP_JS_OBJECTS(this, nsDOMMessageEvent);
+  mozilla::DropJSObjects(this);
 }
 
 NS_IMETHODIMP
 nsDOMMessageEvent::GetData(JSContext* aCx, JS::Value* aData)
 {
   ErrorResult rv;
   *aData = GetData(aCx, rv);
   return rv.ErrorCode();
@@ -93,17 +94,17 @@ nsDOMMessageEvent::InitMessageEvent(cons
                                     const nsAString& aOrigin,
                                     const nsAString& aLastEventId,
                                     nsIDOMWindow* aSource)
 {
   nsresult rv = nsDOMEvent::InitEvent(aType, aCanBubble, aCancelable);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mData = aData;
-  NS_HOLD_JS_OBJECTS(this, nsDOMMessageEvent);
+  mozilla::HoldJSObjects(this);
   mOrigin = aOrigin;
   mLastEventId = aLastEventId;
   mSource = aSource;
 
   return NS_OK;
 }
 
 nsresult
--- a/content/events/src/nsDOMNotifyAudioAvailableEvent.cpp
+++ b/content/events/src/nsDOMNotifyAudioAvailableEvent.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "nsError.h"
 #include "nsDOMNotifyAudioAvailableEvent.h"
-#include "nsContentUtils.h" // NS_DROP_JS_OBJECTS
+#include "nsCycleCollectionHoldDrop.h"
 #include "jsfriendapi.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 nsDOMNotifyAudioAvailableEvent::nsDOMNotifyAudioAvailableEvent(EventTarget* aOwner,
                                                                nsPresContext* aPresContext,
                                                                nsEvent* aEvent,
@@ -35,17 +35,17 @@ nsDOMNotifyAudioAvailableEvent::nsDOMNot
 NS_IMPL_ADDREF_INHERITED(nsDOMNotifyAudioAvailableEvent, nsDOMEvent)
 NS_IMPL_RELEASE_INHERITED(nsDOMNotifyAudioAvailableEvent, nsDOMEvent)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMNotifyAudioAvailableEvent)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMNotifyAudioAvailableEvent, nsDOMEvent)
   if (tmp->mCachedArray) {
     tmp->mCachedArray = nullptr;
-    NS_DROP_JS_OBJECTS(tmp, nsDOMNotifyAudioAvailableEvent);
+    mozilla::DropJSObjects(tmp);
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMNotifyAudioAvailableEvent, nsDOMEvent)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsDOMNotifyAudioAvailableEvent, nsDOMEvent)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedArray)
@@ -55,17 +55,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_
   NS_INTERFACE_MAP_ENTRY(nsIDOMNotifyAudioAvailableEvent)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
 
 nsDOMNotifyAudioAvailableEvent::~nsDOMNotifyAudioAvailableEvent()
 {
   MOZ_COUNT_DTOR(nsDOMNotifyAudioAvailableEvent);
   if (mCachedArray) {
     mCachedArray = nullptr;
-    NS_DROP_JS_OBJECTS(this, nsDOMNotifyAudioAvailableEvent);
+    mozilla::DropJSObjects(this);
   }
 }
 
 NS_IMETHODIMP
 nsDOMNotifyAudioAvailableEvent::GetFrameBuffer(JSContext* aCx, JS::Value* aResult)
 {
   if (!mAllowAudioData) {
     // Media is not same-origin, don't allow the data out.
@@ -73,21 +73,21 @@ nsDOMNotifyAudioAvailableEvent::GetFrame
   }
 
   if (mCachedArray) {
     *aResult = OBJECT_TO_JSVAL(mCachedArray);
     return NS_OK;
   }
 
   // Cache this array so we don't recreate on next call.
-  NS_HOLD_JS_OBJECTS(this, nsDOMNotifyAudioAvailableEvent);
+  mozilla::HoldJSObjects(this);
 
   mCachedArray = JS_NewFloat32Array(aCx, mFrameBufferLength);
   if (!mCachedArray) {
-    NS_DROP_JS_OBJECTS(this, nsDOMNotifyAudioAvailableEvent);
+    mozilla::DropJSObjects(this);
     return NS_ERROR_FAILURE;
   }
   memcpy(JS_GetFloat32ArrayData(mCachedArray), mFrameBuffer.get(), mFrameBufferLength * sizeof(float));
 
   *aResult = OBJECT_TO_JSVAL(mCachedArray);
   return NS_OK;
 }
 
--- a/content/media/webaudio/AudioBuffer.cpp
+++ b/content/media/webaudio/AudioBuffer.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "AudioBuffer.h"
 #include "mozilla/dom/AudioBufferBinding.h"
-#include "nsContentUtils.h"
 #include "jsfriendapi.h"
 #include "mozilla/ErrorResult.h"
 #include "AudioSegment.h"
 #include "AudioChannelFormat.h"
 #include "mozilla/PodOperations.h"
 #include "AudioNodeEngine.h"
 
 namespace mozilla {
@@ -43,30 +42,29 @@ NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(A
 
 AudioBuffer::AudioBuffer(AudioContext* aContext, uint32_t aLength,
                          float aSampleRate)
   : mContext(aContext),
     mLength(aLength),
     mSampleRate(aSampleRate)
 {
   SetIsDOMBinding();
-
-  nsContentUtils::HoldJSObjects(this, NS_CYCLE_COLLECTION_PARTICIPANT(AudioBuffer));
+  mozilla::HoldJSObjects(this);
 }
 
 AudioBuffer::~AudioBuffer()
 {
   ClearJSChannels();
 }
 
 void
 AudioBuffer::ClearJSChannels()
 {
   mJSChannels.Clear();
-  nsContentUtils::DropJSObjects(this);
+  mozilla::DropJSObjects(this);
 }
 
 bool
 AudioBuffer::InitializeBuffers(uint32_t aNumberOfChannels, JSContext* aJSContext)
 {
   if (!mJSChannels.SetCapacity(aNumberOfChannels)) {
     return false;
   }
--- a/content/media/webaudio/MediaBufferDecoder.cpp
+++ b/content/media/webaudio/MediaBufferDecoder.cpp
@@ -844,25 +844,25 @@ WebAudioDecodeJob::WebAudioDecodeJob(con
   MOZ_COUNT_CTOR(WebAudioDecodeJob);
 
   mArrayBuffer = aBuffer.Obj();
 
   MOZ_ASSERT(aSuccessCallback ||
              (!aSuccessCallback && !aFailureCallback),
              "If a success callback is not passed, no failure callback should be passed either");
 
-  nsContentUtils::HoldJSObjects(this, NS_CYCLE_COLLECTION_PARTICIPANT(WebAudioDecodeJob));
+  mozilla::HoldJSObjects(this);
 }
 
 WebAudioDecodeJob::~WebAudioDecodeJob()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_COUNT_DTOR(WebAudioDecodeJob);
   mArrayBuffer = nullptr;
-  nsContentUtils::DropJSObjects(this);
+  mozilla::DropJSObjects(this);
 }
 
 void
 WebAudioDecodeJob::OnSuccess(ErrorCode aErrorCode)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aErrorCode == NoError);
 
--- a/content/media/webaudio/WaveShaperNode.cpp
+++ b/content/media/webaudio/WaveShaperNode.cpp
@@ -253,32 +253,32 @@ private:
 WaveShaperNode::WaveShaperNode(AudioContext* aContext)
   : AudioNode(aContext,
               2,
               ChannelCountMode::Max,
               ChannelInterpretation::Speakers)
   , mCurve(nullptr)
   , mType(OverSampleType::None)
 {
-  NS_HOLD_JS_OBJECTS(this, WaveShaperNode);
+  mozilla::HoldJSObjects(this);
 
   WaveShaperNodeEngine* engine = new WaveShaperNodeEngine(this);
   mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
 }
 
 WaveShaperNode::~WaveShaperNode()
 {
   ClearCurve();
 }
 
 void
 WaveShaperNode::ClearCurve()
 {
   mCurve = nullptr;
-  NS_DROP_JS_OBJECTS(this, WaveShaperNode);
+  mozilla::DropJSObjects(this);
 }
 
 JSObject*
 WaveShaperNode::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aScope)
 {
   return WaveShaperNodeBinding::Wrap(aCx, aScope, this);
 }
 
--- a/content/xbl/src/nsXBLDocumentInfo.cpp
+++ b/content/xbl/src/nsXBLDocumentInfo.cpp
@@ -232,17 +232,17 @@ nsXBLDocGlobalObject::GetCompilationGlob
          .setInvisibleToDebugger(true);
   mJSObject = JS_NewGlobalObject(cx, &gSharedGlobalClass,
                                  nsJSPrincipals::get(GetPrincipal()),
                                  JS::DontFireOnNewGlobalHook,
                                  options);
   if (!mJSObject)
       return nullptr;
 
-  NS_HOLD_JS_OBJECTS(this, nsXBLDocGlobalObject);
+  mozilla::HoldJSObjects(this);
 
   // Set the location information for the new global, so that tools like
   // about:memory may use that information
   nsIURI *ownerURI = mGlobalObjectOwner->DocumentURI();
   xpc::SetLocationForGlobal(mJSObject, ownerURI);
 
   // Add an owning reference from JS back to us. This'll be
   // released when the JSObject is finalized.
@@ -254,17 +254,17 @@ nsXBLDocGlobalObject::GetCompilationGlob
 void
 nsXBLDocGlobalObject::Destroy()
 {
   // Maintain indempotence.
   mDestroyed = true;
   if (!mJSObject)
     return;
   mJSObject = nullptr;
-  NS_DROP_JS_OBJECTS(this, nsXBLDocGlobalObject);
+  mozilla::DropJSObjects(this);
 }
 
 
 nsIPrincipal*
 nsXBLDocGlobalObject::GetPrincipal()
 {
   if (!mGlobalObjectOwner) {
     // XXXbz this should really save the principal when
@@ -416,17 +416,17 @@ nsXBLDocumentInfo::~nsXBLDocumentInfo()
 {
   /* destructor code */
   if (mGlobalObject) {
     mGlobalObject->ClearGlobalObjectOwner(); // just in case
   }
   if (mBindingTable) {
     delete mBindingTable;
     mBindingTable = nullptr;
-    NS_DROP_JS_OBJECTS(this, nsXBLDocumentInfo);
+    mozilla::DropJSObjects(this);
   }
 }
 
 nsXBLPrototypeBinding*
 nsXBLDocumentInfo::GetPrototypeBinding(const nsACString& aRef)
 {
   if (!mBindingTable)
     return nullptr;
@@ -450,17 +450,17 @@ DeletePrototypeBinding(nsHashKey* aKey, 
 }
 
 nsresult
 nsXBLDocumentInfo::SetPrototypeBinding(const nsACString& aRef, nsXBLPrototypeBinding* aBinding)
 {
   if (!mBindingTable) {
     mBindingTable = new nsObjectHashtable(nullptr, nullptr, DeletePrototypeBinding, nullptr);
 
-    NS_HOLD_JS_OBJECTS(this, nsXBLDocumentInfo);
+    mozilla::HoldJSObjects(this);
   }
 
   const nsPromiseFlatCString& flat = PromiseFlatCString(aRef);
   nsCStringKey key(flat.get());
   NS_ENSURE_STATE(!mBindingTable->Get(&key));
   mBindingTable->Put(&key, aBinding);
 
   return NS_OK;
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -2659,32 +2659,31 @@ nsXULPrototypeScript::Compile(const PRUn
     return NS_OK;
 }
 
 void
 nsXULPrototypeScript::UnlinkJSObjects()
 {
     if (mScriptObject) {
         mScriptObject = nullptr;
-        nsContentUtils::DropJSObjects(this);
+        mozilla::DropJSObjects(this);
     }
 }
 
 void
 nsXULPrototypeScript::Set(JSScript* aObject)
 {
     MOZ_ASSERT(!mScriptObject, "Leaking script object.");
     if (!aObject) {
         mScriptObject = nullptr;
         return;
     }
 
     mScriptObject = aObject;
-    nsContentUtils::HoldJSObjects(
-        this, NS_CYCLE_COLLECTION_PARTICIPANT(nsXULPrototypeNode));
+    mozilla::HoldJSObjects(this);
 }
 
 //----------------------------------------------------------------------
 //
 // nsXULPrototypeText
 //
 
 nsresult
--- a/content/xul/document/src/nsXULPrototypeDocument.cpp
+++ b/content/xul/document/src/nsXULPrototypeDocument.cpp
@@ -739,17 +739,17 @@ nsXULPDGlobalObject::GetCompilationGloba
   JS::CompartmentOptions options;
   options.setZone(JS::SystemZone)
          .setInvisibleToDebugger(true);
   mJSObject = JS_NewGlobalObject(cx, &gSharedGlobalClass,
                                  nsJSPrincipals::get(GetPrincipal()),
                                  JS::DontFireOnNewGlobalHook, options);
   NS_ENSURE_TRUE(mJSObject, nullptr);
 
-  NS_HOLD_JS_OBJECTS(this, nsXULPDGlobalObject);
+  mozilla::HoldJSObjects(this);
 
   // Add an owning reference from JS back to us. This'll be
   // released when the JSObject is finalized.
   JS_SetPrivate(mJSObject, this);
   NS_ADDREF(this);
 
   // Set the location information for the new global, so that tools like
   // about:memory may use that information
@@ -774,17 +774,17 @@ nsXULPDGlobalObject::ClearGlobalObjectOw
 void
 nsXULPDGlobalObject::Destroy()
 {
   mDestroyed = true;
   if (!mJSObject) {
     return;
   }
   mJSObject = nullptr;
-  NS_DROP_JS_OBJECTS(this, nsXULPDGlobalObject);
+  mozilla::DropJSObjects(this);
 }
 
 nsIPrincipal*
 nsXULPDGlobalObject::GetPrincipal()
 {
     if (!mGlobalObjectOwner) {
         // See nsXULPrototypeDocument::NewXULPDGlobalObject, the comment
         // about gSystemGlobal implying gSystemPrincipal.
--- a/dom/base/DOMRequest.cpp
+++ b/dom/base/DOMRequest.cpp
@@ -2,17 +2,16 @@
 /* vim: set ts=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 "DOMRequest.h"
 
 #include "DOMError.h"
-#include "nsContentUtils.h"
 #include "nsCxPusher.h"
 #include "nsThreadUtils.h"
 #include "DOMCursor.h"
 
 using mozilla::dom::DOMRequest;
 using mozilla::dom::DOMRequestService;
 using mozilla::dom::DOMCursor;
 using mozilla::AutoPushJSContext;
@@ -184,20 +183,17 @@ DOMRequest::FireEvent(const nsAString& a
 
   bool dummy;
   DispatchEvent(event, &dummy);
 }
 
 void
 DOMRequest::RootResultVal()
 {
-  nsXPCOMCycleCollectionParticipant *participant;
-  CallQueryInterface(this, &participant);
-  nsContentUtils::HoldJSObjects(NS_CYCLE_COLLECTION_UPCAST(this, DOMRequest),
-                                participant);
+  mozilla::HoldJSObjects(this);
 }
 
 NS_IMPL_ISUPPORTS1(DOMRequestService, nsIDOMRequestService)
 
 NS_IMETHODIMP
 DOMRequestService::CreateRequest(nsIDOMWindow* aWindow,
                                  nsIDOMDOMRequest** aRequest)
 {
--- a/dom/base/DOMRequest.h
+++ b/dom/base/DOMRequest.h
@@ -73,17 +73,17 @@ public:
   void FireDetailedError(nsISupports* aError);
 
   DOMRequest(nsIDOMWindow* aWindow);
   DOMRequest();
 
   virtual ~DOMRequest()
   {
     mResult = JSVAL_VOID;
-    NS_DROP_JS_OBJECTS(this, DOMRequest);
+    mozilla::DropJSObjects(this);
   }
 
 protected:
   void FireEvent(const nsAString& aType, bool aBubble, bool aCancelable);
 
   void RootResultVal();
 
   void Init(nsIDOMWindow* aWindow);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1281,23 +1281,17 @@ nsGlobalWindow::ShutDown()
 
 // static
 void
 nsGlobalWindow::CleanupCachedXBLHandlers(nsGlobalWindow* aWindow)
 {
   if (aWindow->mCachedXBLPrototypeHandlers.IsInitialized() &&
       aWindow->mCachedXBLPrototypeHandlers.Count() > 0) {
     aWindow->mCachedXBLPrototypeHandlers.Clear();
-
-    nsISupports* supports;
-    aWindow->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
-                            reinterpret_cast<void**>(&supports));
-    NS_ASSERTION(supports, "Failed to QI to nsCycleCollectionISupports?!");
-
-    nsContentUtils::DropJSObjects(supports);
+    mozilla::DropJSObjects(aWindow);
   }
 }
 
 void
 nsGlobalWindow::MaybeForgiveSpamCount()
 {
   if (IsOuterWindow() &&
       IsPopupSpamWindow())
@@ -7630,29 +7624,17 @@ void
 nsGlobalWindow::CacheXBLPrototypeHandler(nsXBLPrototypeHandler* aKey,
                                          JS::Handle<JSObject*> aHandler)
 {
   if (!mCachedXBLPrototypeHandlers.IsInitialized()) {
     mCachedXBLPrototypeHandlers.Init();
   }
 
   if (!mCachedXBLPrototypeHandlers.Count()) {
-    // Can't use macros to get the participant because nsGlobalChromeWindow also
-    // runs through this code. Use QueryInterface to get the correct objects.
-    nsXPCOMCycleCollectionParticipant* participant;
-    CallQueryInterface(this, &participant);
-    NS_ASSERTION(participant,
-                 "Failed to QI to nsXPCOMCycleCollectionParticipant!");
-
-    nsISupports* thisSupports;
-    QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
-                   reinterpret_cast<void**>(&thisSupports));
-    NS_ASSERTION(thisSupports, "Failed to QI to nsCycleCollectionISupports!");
-
-    nsContentUtils::HoldJSObjects(thisSupports, participant);
+    mozilla::HoldJSObjects(this);
   }
 
   mCachedXBLPrototypeHandlers.Put(aKey, aHandler);
 }
 
 /**
  * GetScriptableFrameElement is called when script reads
  * nsIGlobalWindow::frameElement.
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -2989,17 +2989,17 @@ nsJSArgArray::nsJSArgArray(JSContext *aC
   // Callers are allowed to pass in a null argv even for argc > 0. They can
   // then use GetArgs to initialize the values.
   if (argv) {
     for (uint32_t i = 0; i < argc; ++i)
       mArgv[i] = argv[i];
   }
 
   if (argc > 0) {
-    NS_HOLD_JS_OBJECTS(this, nsJSArgArray);
+    mozilla::HoldJSObjects(this);
   }
 
   *prv = NS_OK;
 }
 
 nsJSArgArray::~nsJSArgArray()
 {
   ReleaseJSObjects();
@@ -3008,17 +3008,17 @@ nsJSArgArray::~nsJSArgArray()
 void
 nsJSArgArray::ReleaseJSObjects()
 {
   if (mArgv) {
     delete [] mArgv;
   }
   if (mArgc > 0) {
     mArgc = 0;
-    NS_DROP_JS_OBJECTS(this, nsJSArgArray);
+    mozilla::DropJSObjects(this);
   }
 }
 
 // QueryInterface implementation for nsJSArgArray
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSArgArray)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSArgArray)
   tmp->ReleaseJSObjects();
--- a/dom/base/nsJSTimeoutHandler.cpp
+++ b/dom/base/nsJSTimeoutHandler.cpp
@@ -143,17 +143,17 @@ void
 nsJSScriptTimeoutHandler::ReleaseJSObjects()
 {
   if (mExpr) {
     mExpr = nullptr;
   } else {
     mFunction = nullptr;
     mArgs.Clear();
   }
-  NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
+  mozilla::DropJSObjects(this);
 }
 
 nsresult
 nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, bool *aIsInterval,
                                int32_t *aInterval)
 {
   if (!aWindow->GetContextInternal() || !aWindow->FastGetGlobalJSObject()) {
     // This window was already closed, or never properly initialized,
@@ -274,27 +274,27 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW
 
         if (!allowsEval) {
           // Note: Our only caller knows to turn NS_ERROR_DOM_TYPE_ERR into NS_OK.
           return NS_ERROR_DOM_TYPE_ERR;
         }
       }
     } // if there's no document, we don't have to do anything.
 
-    NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
+    mozilla::HoldJSObjects(this);
 
     mExpr = JS_FORGET_STRING_FLATNESS(expr);
 
     // Get the calling location.
     const char *filename;
     if (nsJSUtils::GetCallingLocation(cx, &filename, &mLineNo)) {
       mFileName.Assign(filename);
     }
   } else if (funobj) {
-    NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
+    mozilla::HoldJSObjects(this);
 
     mFunction = new Function(funobj);
 
     // Create our arg array.  argc is the number of arguments passed
     // to setTimeout or setInterval; the first two are our callback
     // and the delay, so only arguments after that need to go in our
     // array.
     // std::max(argc - 2, 0) wouldn't work right because argc is unsigned.
--- a/dom/base/nsWrapperCache.cpp
+++ b/dom/base/nsWrapperCache.cpp
@@ -3,42 +3,43 @@
 /* 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 "nsWrapperCacheInlines.h"
 
 #include "jsproxy.h"
 #include "mozilla/dom/DOMJSProxyHandler.h"
+#include "nsCycleCollectionHoldDrop.h"
 #include "nsCycleCollectionTraversalCallback.h"
 #include "nsCycleCollector.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 /* static */ void
 nsWrapperCache::HoldJSObjects(void* aScriptObjectHolder,
                               nsScriptObjectTracer* aTracer)
 {
-  cyclecollector::AddJSHolder(aScriptObjectHolder, aTracer);
+  cyclecollector::HoldJSObjectsImpl(aScriptObjectHolder, aTracer);
 }
 
 void
 nsWrapperCache::ReleaseWrapper(void* aScriptObjectHolder)
 {
   if (PreservingWrapper()) {
     // PreserveWrapper puts new DOM bindings in the JS holders hash, but they
     // can also be in the DOM expando hash, so we need to try to remove them
     // from both here.
     JSObject* obj = GetWrapperPreserveColor();
     if (IsDOMBinding() && obj && js::IsProxy(obj)) {
       DOMProxyHandler::GetAndClearExpandoObject(obj);
     }
     SetPreservingWrapper(false);
-    cyclecollector::RemoveJSHolder(aScriptObjectHolder);
+    cyclecollector::DropJSObjectsImpl(aScriptObjectHolder);
   }
 }
 
 #ifdef DEBUG
 
 class DebugWrapperTraversalCallback : public nsCycleCollectionTraversalCallback
 {
 public:
--- a/dom/bindings/CallbackObject.h
+++ b/dom/bindings/CallbackObject.h
@@ -14,16 +14,17 @@
  * that do the call into JS as needed.
  */
 
 #ifndef mozilla_dom_CallbackObject_h
 #define mozilla_dom_CallbackObject_h
 
 #include "nsISupports.h"
 #include "nsISupportsImpl.h"
+#include "nsCycleCollectionHoldDrop.h"
 #include "nsCycleCollectionParticipant.h"
 #include "jswrapper.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Util.h"
 #include "nsContentUtils.h"
 #include "nsCxPusher.h"
 #include "nsWrapperCache.h"
@@ -89,25 +90,25 @@ protected:
 
 private:
   inline void Init(JSObject* aCallback)
   {
     MOZ_ASSERT(aCallback && !mCallback);
     // Set mCallback before we hold, on the off chance that a GC could somehow
     // happen in there... (which would be pretty odd, granted).
     mCallback = aCallback;
-    NS_HOLD_JS_OBJECTS(this, CallbackObject);
+    mozilla::HoldJSObjects(this);
   }
 
 protected:
   void DropCallback()
   {
     if (mCallback) {
       mCallback = nullptr;
-      NS_DROP_JS_OBJECTS(this, CallbackObject);
+      mozilla::DropJSObjects(this);
     }
   }
 
   JS::Heap<JSObject*> mCallback;
 
   class MOZ_STACK_CLASS CallSetup
   {
     /**
--- a/dom/bluetooth/BluetoothAdapter.cpp
+++ b/dom/bluetooth/BluetoothAdapter.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=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 "base/basictypes.h"
 #include "GeneratedEvents.h"
-#include "nsContentUtils.h"
 #include "nsCxPusher.h"
 #include "nsDOMClassInfo.h"
 #include "nsIDOMBluetoothDeviceEvent.h"
 #include "nsIDOMBluetoothStatusChangedEvent.h"
 #include "nsTArrayHelpers.h"
 #include "DOMRequest.h"
 #include "nsThreadUtils.h"
 
@@ -200,27 +199,27 @@ BluetoothAdapter::~BluetoothAdapter()
 void
 BluetoothAdapter::Unroot()
 {
   if (!mIsRooted) {
     return;
   }
   mJsUuids = nullptr;
   mJsDeviceAddresses = nullptr;
-  NS_DROP_JS_OBJECTS(this, BluetoothAdapter);
+  mozilla::DropJSObjects(this);
   mIsRooted = false;
 }
 
 void
 BluetoothAdapter::Root()
 {
   if (mIsRooted) {
     return;
   }
-  NS_HOLD_JS_OBJECTS(this, BluetoothAdapter);
+  mozilla::HoldJSObjects(this);
   mIsRooted = true;
 }
 
 void
 BluetoothAdapter::SetPropertyByValue(const BluetoothNamedValue& aValue)
 {
   const nsString& name = aValue.name();
   const BluetoothValue& value = aValue.value();
--- a/dom/bluetooth/BluetoothDevice.cpp
+++ b/dom/bluetooth/BluetoothDevice.cpp
@@ -6,17 +6,16 @@
 
 #include "base/basictypes.h"
 #include "BluetoothDevice.h"
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothService.h"
 #include "BluetoothUtils.h"
 
 #include "nsDOMClassInfo.h"
-#include "nsContentUtils.h"
 #include "nsTArrayHelpers.h"
 
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/dom/BluetoothDeviceBinding.h"
 
 USING_BLUETOOTH_NAMESPACE
 
 DOMCI_DATA(BluetoothDevice, BluetoothDevice)
@@ -80,28 +79,28 @@ BluetoothDevice::~BluetoothDevice()
   bs->UnregisterBluetoothSignalHandler(mAddress, this);
   Unroot();
 }
 
 void
 BluetoothDevice::Root()
 {
   if (!mIsRooted) {
-    NS_HOLD_JS_OBJECTS(this, BluetoothDevice);
+    mozilla::HoldJSObjects(this);
     mIsRooted = true;
   }
 }
 
 void
 BluetoothDevice::Unroot()
 {
   if (mIsRooted) {
     mJsUuids = nullptr;
     mJsServices = nullptr;
-    NS_DROP_JS_OBJECTS(this, BluetoothDevice);
+    mozilla::DropJSObjects(this);
     mIsRooted = false;
   }
 }
 
 void
 BluetoothDevice::SetPropertyByValue(const BluetoothNamedValue& aValue)
 {
   const nsString& name = aValue.name();
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -319,17 +319,17 @@ IDBCursor::CreateCommon(IDBRequest* aReq
                "Empty query!");
 
   nsRefPtr<IDBCursor> cursor = new IDBCursor();
 
   IDBDatabase* database = aTransaction->Database();
   cursor->mScriptOwner = database->GetScriptOwner();
 
   if (cursor->mScriptOwner) {
-    NS_HOLD_JS_OBJECTS(cursor, IDBCursor);
+    mozilla::HoldJSObjects(cursor.get());
     cursor->mRooted = true;
   }
 
   cursor->mRequest = aRequest;
   cursor->mTransaction = aTransaction;
   cursor->mObjectStore = aObjectStore;
   cursor->mDirection = aDirection;
   cursor->mContinueQuery = aContinueQuery;
@@ -385,17 +385,17 @@ IDBCursor::DropJSObjects()
   mCachedKey = JSVAL_VOID;
   mCachedPrimaryKey = JSVAL_VOID;
   mCachedValue = JSVAL_VOID;
   mHaveCachedKey = false;
   mHaveCachedPrimaryKey = false;
   mHaveCachedValue = false;
   mRooted = false;
   mHaveValue = false;
-  NS_DROP_JS_OBJECTS(this, IDBCursor);
+  mozilla::DropJSObjects(this);
 }
 
 void
 IDBCursor::ContinueInternal(const Key& aKey, int32_t aCount, ErrorResult& aRv)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aCount > 0, "Must have a count!");
 
@@ -541,17 +541,17 @@ IDBCursor::GetKey(JSContext* aCx, ErrorR
   NS_ASSERTION(!mKey.IsUnset() || !mHaveValue, "Bad key!");
 
   if (!mHaveValue) {
     return JSVAL_VOID;
   }
 
   if (!mHaveCachedKey) {
     if (!mRooted) {
-      NS_HOLD_JS_OBJECTS(this, IDBCursor);
+      mozilla::HoldJSObjects(this);
       mRooted = true;
     }
 
     aRv = mKey.ToJSVal(aCx, mCachedKey);
     ENSURE_SUCCESS(aRv, JSVAL_VOID);
 
     mHaveCachedKey = true;
   }
@@ -565,17 +565,17 @@ IDBCursor::GetPrimaryKey(JSContext* aCx,
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mHaveValue) {
     return JSVAL_VOID;
   }
 
   if (!mHaveCachedPrimaryKey) {
     if (!mRooted) {
-      NS_HOLD_JS_OBJECTS(this, IDBCursor);
+      mozilla::HoldJSObjects(this);
       mRooted = true;
     }
 
     JSAutoRequest ar(aCx);
 
     NS_ASSERTION(mType == OBJECTSTORE ? !mKey.IsUnset() :
                                         !mObjectKey.IsUnset(), "Bad key!");
 
@@ -597,17 +597,17 @@ IDBCursor::GetValue(JSContext* aCx, Erro
   NS_ASSERTION(mType != INDEXKEY, "GetValue shouldn't exist on index keys");
 
   if (!mHaveValue) {
     return JSVAL_VOID;
   }
 
   if (!mHaveCachedValue) {
     if (!mRooted) {
-      NS_HOLD_JS_OBJECTS(this, IDBCursor);
+      mozilla::HoldJSObjects(this);
       mRooted = true;
     }
 
     JS::Rooted<JS::Value> val(aCx);
     if (!IDBObjectStore::DeserializeValue(aCx, mCloneReadInfo, &val)) {
       aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
       return JSVAL_VOID;
     }
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -83,17 +83,17 @@ IDBFactory::~IDBFactory()
   NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
   if (mActorChild) {
     NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
     mActorChild->Send__delete__(mActorChild);
     NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!");
   }
   if (mRootedOwningObject) {
     mOwningObject = nullptr;
-    NS_DROP_JS_OBJECTS(this, IDBFactory);
+    mozilla::DropJSObjects(this);
   }
 }
 
 // static
 nsresult
 IDBFactory::Create(nsPIDOMWindow* aWindow,
                    const nsACString& aASCIIOrigin,
                    ContentParent* aContentParent,
@@ -226,17 +226,17 @@ IDBFactory::Create(ContentParent* aConte
   global = js::UncheckedUnwrap(global);
 
   JSAutoCompartment ac(cx, global);
 
   nsRefPtr<IDBFactory> factory;
   rv = Create(cx, global, aContentParent, getter_AddRefs(factory));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  NS_HOLD_JS_OBJECTS(factory, IDBFactory);
+  mozilla::HoldJSObjects(factory.get());
   factory->mRootedOwningObject = true;
 
   factory.forget(aFactory);
   return NS_OK;
 }
 
 // static
 already_AddRefed<nsIFileURL>
@@ -505,17 +505,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBFactory)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   if (tmp->mOwningObject) {
     tmp->mOwningObject = nullptr;
   }
   if (tmp->mRootedOwningObject) {
-    NS_DROP_JS_OBJECTS(tmp, IDBFactory);
+    mozilla::DropJSObjects(tmp);
     tmp->mRootedOwningObject = false;
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBFactory)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mOwningObject)
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -411,17 +411,17 @@ IDBIndex::IDBIndex()
 
 IDBIndex::~IDBIndex()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
 
   if (mRooted) {
     mCachedKeyPath = JSVAL_VOID;
-    NS_DROP_JS_OBJECTS(this, IDBIndex);
+    mozilla::DropJSObjects(this);
   }
 
   if (mActorChild) {
     NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
     mActorChild->Send__delete__(mActorChild);
     NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!");
   }
 }
@@ -801,17 +801,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBIndex)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 
   // Don't unlink mObjectStore!
 
   tmp->mCachedKeyPath = JSVAL_VOID;
 
   if (tmp->mRooted) {
-    NS_DROP_JS_OBJECTS(tmp, IDBIndex);
+    mozilla::DropJSObjects(tmp);
     tmp->mRooted = false;
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBIndex)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
@@ -833,17 +833,17 @@ IDBIndex::GetKeyPath(JSContext* aCx, Err
   if (!JSVAL_IS_VOID(mCachedKeyPath)) {
     return mCachedKeyPath;
   }
 
   aRv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath);
   ENSURE_SUCCESS(aRv, JSVAL_VOID);
 
   if (JSVAL_IS_GCTHING(mCachedKeyPath)) {
-    NS_HOLD_JS_OBJECTS(this, IDBIndex);
+    mozilla::HoldJSObjects(this);
     mRooted = true;
   }
 
   return mCachedKeyPath;
 }
 
 already_AddRefed<IDBRequest>
 IDBIndex::Get(JSContext* aCx, JS::Handle<JS::Value> aKey, ErrorResult& aRv)
--- a/dom/indexedDB/IDBKeyRange.cpp
+++ b/dom/indexedDB/IDBKeyRange.cpp
@@ -340,33 +340,33 @@ IDBKeyRange::DropJSObjects()
   if (!mRooted) {
     return;
   }
   mCachedLowerVal = JSVAL_VOID;
   mCachedUpperVal = JSVAL_VOID;
   mHaveCachedLowerVal = false;
   mHaveCachedUpperVal = false;
   mRooted = false;
-  NS_DROP_JS_OBJECTS(this, IDBKeyRange);
+  mozilla::DropJSObjects(this);
 }
 
 IDBKeyRange::~IDBKeyRange()
 {
   DropJSObjects();
 }
 
 NS_IMETHODIMP
 IDBKeyRange::GetLower(JSContext* aCx,
                       jsval* aLower)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mHaveCachedLowerVal) {
     if (!mRooted) {
-      NS_HOLD_JS_OBJECTS(this, IDBKeyRange);
+      mozilla::HoldJSObjects(this);
       mRooted = true;
     }
 
     nsresult rv = Lower().ToJSVal(aCx, mCachedLowerVal);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mHaveCachedLowerVal = true;
   }
@@ -378,17 +378,17 @@ IDBKeyRange::GetLower(JSContext* aCx,
 NS_IMETHODIMP
 IDBKeyRange::GetUpper(JSContext* aCx,
                       jsval* aUpper)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mHaveCachedUpperVal) {
     if (!mRooted) {
-      NS_HOLD_JS_OBJECTS(this, IDBKeyRange);
+      mozilla::HoldJSObjects(this);
       mRooted = true;
     }
 
     nsresult rv = Upper().ToJSVal(aCx, mCachedUpperVal);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mHaveCachedUpperVal = true;
   }
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -1744,17 +1744,17 @@ IDBObjectStore::~IDBObjectStore()
   if (mActorChild) {
     NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
     mActorChild->Send__delete__(mActorChild);
     NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!");
   }
 
   if (mRooted) {
     mCachedKeyPath = JSVAL_VOID;
-    NS_DROP_JS_OBJECTS(this, IDBObjectStore);
+    mozilla::DropJSObjects(this);
   }
 }
 
 nsresult
 IDBObjectStore::GetAddInfo(JSContext* aCx,
                            JS::Handle<JS::Value> aValue,
                            JS::Handle<JS::Value> aKeyVal,
                            StructuredCloneWriteInfo& aCloneWriteInfo,
@@ -2413,17 +2413,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ID
 
   // Don't unlink mTransaction!
 
   tmp->mCreatedIndexes.Clear();
 
   tmp->mCachedKeyPath = JSVAL_VOID;
 
   if (tmp->mRooted) {
-    NS_DROP_JS_OBJECTS(tmp, IDBObjectStore);
+    mozilla::DropJSObjects(tmp);
     tmp->mRooted = false;
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBObjectStore)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
@@ -2445,17 +2445,17 @@ IDBObjectStore::GetKeyPath(JSContext* aC
   if (!JSVAL_IS_VOID(mCachedKeyPath)) {
     return mCachedKeyPath;
   }
 
   aRv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath);
   ENSURE_SUCCESS(aRv, JSVAL_VOID);
 
   if (JSVAL_IS_GCTHING(mCachedKeyPath)) {
-    NS_HOLD_JS_OBJECTS(this, IDBObjectStore);
+    mozilla::HoldJSObjects(this);
     mRooted = true;
   }
 
   return mCachedKeyPath;
 }
 
 already_AddRefed<nsIDOMDOMStringList>
 IDBObjectStore::GetIndexNames(ErrorResult& aRv)
--- a/dom/indexedDB/IDBWrapperCache.cpp
+++ b/dom/indexedDB/IDBWrapperCache.cpp
@@ -1,32 +1,32 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=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 "IDBWrapperCache.h"
-#include "nsContentUtils.h"
+#include "nsCycleCollector.h"
 
 USING_INDEXEDDB_NAMESPACE
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBWrapperCache)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBWrapperCache,
                                                   nsDOMEventTargetHelper)
   // Don't need NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS because
   // nsDOMEventTargetHelper does it for us.
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBWrapperCache,
                                                 nsDOMEventTargetHelper)
   if (tmp->mScriptOwner) {
     tmp->mScriptOwner = nullptr;
-    NS_DROP_JS_OBJECTS(tmp, IDBWrapperCache);
+    mozilla::DropJSObjects(tmp);
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(IDBWrapperCache,
                                                nsDOMEventTargetHelper)
   // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
   // nsDOMEventTargetHelper does it for us.
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScriptOwner)
@@ -37,32 +37,28 @@ NS_INTERFACE_MAP_END_INHERITING(nsDOMEve
 
 NS_IMPL_ADDREF_INHERITED(IDBWrapperCache, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(IDBWrapperCache, nsDOMEventTargetHelper)
 
 IDBWrapperCache::~IDBWrapperCache()
 {
   mScriptOwner = nullptr;
   ReleaseWrapper(this);
-  NS_DROP_JS_OBJECTS(this, IDBWrapperCache);
+  mozilla::DropJSObjects(this);
 }
 
 void
 IDBWrapperCache::SetScriptOwner(JSObject* aScriptOwner)
 {
   NS_ASSERTION(aScriptOwner, "This should never be null!");
 
   mScriptOwner = aScriptOwner;
-
-  nsISupports* thisSupports = NS_CYCLE_COLLECTION_UPCAST(this, IDBWrapperCache);
-  nsXPCOMCycleCollectionParticipant* participant;
-  CallQueryInterface(this, &participant);
-  nsContentUtils::HoldJSObjects(thisSupports, participant);
+  mozilla::HoldJSObjects(this);
 }
 
 #ifdef DEBUG
 void
 IDBWrapperCache::AssertIsRooted() const
 {
-  NS_ASSERTION(nsContentUtils::AreJSObjectsHeld(const_cast<IDBWrapperCache*>(this)),
-               "Why aren't we rooted?!");
+  MOZ_ASSERT(cyclecollector::IsJSHolder(const_cast<IDBWrapperCache*>(this)),
+             "Why aren't we rooted?!");
 }
 #endif
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -89,27 +89,27 @@ NS_INTERFACE_MAP_END
 Promise::Promise(nsPIDOMWindow* aWindow)
   : mWindow(aWindow)
   , mResult(JS::UndefinedValue())
   , mState(Pending)
   , mTaskPending(false)
   , mHadRejectCallback(false)
 {
   MOZ_COUNT_CTOR(Promise);
-  NS_HOLD_JS_OBJECTS(this, Promise);
+  mozilla::HoldJSObjects(this);
   SetIsDOMBinding();
 
   mResolver = new PromiseResolver(this);
 }
 
 Promise::~Promise()
 {
   MaybeReportRejected();
   mResult = JS::UndefinedValue();
-  NS_DROP_JS_OBJECTS(this, Promise);
+  mozilla::DropJSObjects(this);
   MOZ_COUNT_DTOR(Promise);
 }
 
 JSObject*
 Promise::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return PromiseBinding::Wrap(aCx, aScope, this);
 }
--- a/dom/src/events/nsJSEventListener.cpp
+++ b/dom/src/events/nsJSEventListener.cpp
@@ -6,17 +6,16 @@
 #include "nsJSUtils.h"
 #include "nsString.h"
 #include "nsIServiceManager.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIXPConnect.h"
 #include "nsGUIEvent.h"
-#include "nsContentUtils.h"
 #include "nsIMutableArray.h"
 #include "nsVariant.h"
 #include "nsIDOMBeforeUnloadEvent.h"
 #include "nsGkAtoms.h"
 #include "xpcpublic.h"
 #include "nsJSEnvironment.h"
 #include "nsDOMJSUtils.h"
 #include "mozilla/Likely.h"
@@ -46,47 +45,47 @@ using namespace mozilla::dom;
 nsJSEventListener::nsJSEventListener(nsIScriptContext *aContext,
                                      JSObject* aScopeObject,
                                      nsISupports *aTarget,
                                      nsIAtom* aType,
                                      const nsEventHandler& aHandler)
   : nsIJSEventListener(aContext, aScopeObject, aTarget, aType, aHandler)
 {
   if (mScopeObject) {
-    NS_HOLD_JS_OBJECTS(this, nsJSEventListener);
+    mozilla::HoldJSObjects(this);
   }
 }
 
 nsJSEventListener::~nsJSEventListener() 
 {
   if (mScopeObject) {
     mScopeObject = nullptr;
-    NS_DROP_JS_OBJECTS(this, nsJSEventListener);
+    mozilla::DropJSObjects(this);
   }
 }
 
 /* virtual */
 void
 nsJSEventListener::UpdateScopeObject(JS::Handle<JSObject*> aScopeObject)
 {
   if (mScopeObject && !aScopeObject) {
     mScopeObject = nullptr;
-    NS_DROP_JS_OBJECTS(this, nsJSEventListener);
+    mozilla::DropJSObjects(this);
   } else if (aScopeObject && !mScopeObject) {
-    NS_HOLD_JS_OBJECTS(this, nsJSEventListener);
+    mozilla::HoldJSObjects(this);
   }
   mScopeObject = aScopeObject;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSEventListener)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSEventListener)
   if (tmp->mScopeObject) {
     tmp->mScopeObject = nullptr;
-    NS_DROP_JS_OBJECTS(tmp, nsJSEventListener);
+    mozilla::DropJSObjects(tmp);
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
   }
   tmp->mHandler.ForgetHandler();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsJSEventListener)
   if (MOZ_UNLIKELY(cb.WantDebugInfo()) && tmp->mEventName) {
     nsAutoCString name;
     name.AppendLiteral("nsJSEventListener handlerName=");
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -2938,90 +2938,123 @@ nsCycleCollector_currentJSRuntime()
 {
     CollectorData* data = sCollectorData.get();
     if (data) {
         return data->mRuntime;
     }
     return nullptr;
 }
 
+
+namespace mozilla {
+namespace cyclecollector {
+
 void
-cyclecollector::AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer)
+HoldJSObjectsImpl(void* aHolder, nsScriptObjectTracer* aTracer)
 {
-    CollectorData *data = sCollectorData.get();
+    CollectorData* data = sCollectorData.get();
 
     // We should have started the cycle collector by now.
     MOZ_ASSERT(data);
     MOZ_ASSERT(data->mCollector);
     // And we should have a runtime.
     MOZ_ASSERT(data->mRuntime);
 
     data->mRuntime->AddJSHolder(aHolder, aTracer);
 }
 
 void
-cyclecollector::RemoveJSHolder(void* aHolder)
+HoldJSObjectsImpl(nsISupports* aHolder)
 {
-    CollectorData *data = sCollectorData.get();
+    nsXPCOMCycleCollectionParticipant* participant;
+    CallQueryInterface(aHolder, &participant);
+    MOZ_ASSERT(participant, "Failed to QI to nsXPCOMCycleCollectionParticipant!");
+    MOZ_ASSERT(participant->CheckForRightISupports(aHolder),
+               "The result of QIing a JS holder should be the same as ToSupports");
+
+    HoldJSObjectsImpl(aHolder, participant);
+}
+
+void
+DropJSObjectsImpl(void* aHolder)
+{
+    CollectorData* data = sCollectorData.get();
 
     // We should have started the cycle collector by now, and not completely
     // shut down.
     MOZ_ASSERT(data);
     // And we should have a runtime.
     MOZ_ASSERT(data->mRuntime);
 
     data->mRuntime->RemoveJSHolder(aHolder);
 }
 
+void
+DropJSObjectsImpl(nsISupports* aHolder)
+{
+#ifdef DEBUG
+    nsXPCOMCycleCollectionParticipant* participant;
+    CallQueryInterface(aHolder, &participant);
+    MOZ_ASSERT(participant, "Failed to QI to nsXPCOMCycleCollectionParticipant!");
+    MOZ_ASSERT(participant->CheckForRightISupports(aHolder),
+               "The result of QIing a JS holder should be the same as ToSupports");
+#endif
+    DropJSObjectsImpl(static_cast<void*>(aHolder));
+}
+
 #ifdef DEBUG
 bool
-cyclecollector::IsJSHolder(void* aHolder)
+IsJSHolder(void* aHolder)
 {
     CollectorData *data = sCollectorData.get();
 
     // We should have started the cycle collector by now, and not completely
     // shut down.
     MOZ_ASSERT(data);
     // And we should have a runtime.
     MOZ_ASSERT(data->mRuntime);
 
     return data->mRuntime->IsJSHolder(aHolder);
 }
 #endif
 
 void
-cyclecollector::DeferredFinalize(nsISupports* aSupports)
+DeferredFinalize(nsISupports* aSupports)
 {
     CollectorData *data = sCollectorData.get();
 
     // We should have started the cycle collector by now, and not completely
     // shut down.
     MOZ_ASSERT(data);
     // And we should have a runtime.
     MOZ_ASSERT(data->mRuntime);
 
     data->mRuntime->DeferredFinalize(aSupports);
 }
 
 void
-cyclecollector::DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
-                                 DeferredFinalizeFunction aFunc,
-                                 void* aThing)
+DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
+                 DeferredFinalizeFunction aFunc,
+                 void* aThing)
 {
     CollectorData *data = sCollectorData.get();
 
     // We should have started the cycle collector by now, and not completely
     // shut down.
     MOZ_ASSERT(data);
     // And we should have a runtime.
     MOZ_ASSERT(data->mRuntime);
 
     data->mRuntime->DeferredFinalize(aAppendFunc, aFunc, aThing);
 }
 
+} // namespace cyclecollector
+} // namespace mozilla
+
+
 void
 NS_CycleCollectorSuspect3(void *n, nsCycleCollectionParticipant *cp,
                           nsCycleCollectingAutoRefCnt *aRefCnt,
                           bool* aShouldDelete)
 {
     CollectorData *data = sCollectorData.get();
 
     // We should have started the cycle collector by now.
--- a/xpcom/base/nsCycleCollector.h
+++ b/xpcom/base/nsCycleCollector.h
@@ -73,18 +73,16 @@ mozilla::CycleCollectedJSRuntime* nsCycl
 extern nsresult
 nsCycleCollectorLoggerConstructor(nsISupports* outer,
                                   const nsIID& aIID,
                                   void* *aInstancePtr);
 
 namespace mozilla {
 namespace cyclecollector {
 
-void AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer);
-void RemoveJSHolder(void* aHolder);
 #ifdef DEBUG
 bool IsJSHolder(void* aHolder);
 #endif
 
 void DeferredFinalize(DeferredFinalizeAppendFunction aAppendFunc,
                       DeferredFinalizeFunction aFunc,
                       void* aThing);
 void DeferredFinalize(nsISupports* aSupports);
--- a/xpcom/glue/moz.build
+++ b/xpcom/glue/moz.build
@@ -18,16 +18,17 @@ EXPORTS += [
     'nsArrayUtils.h',
     'nsBaseHashtable.h',
     'nsCOMArray.h',
     'nsCOMPtr.h',
     'nsCRTGlue.h',
     'nsCategoryCache.h',
     'nsClassHashtable.h',
     'nsComponentManagerUtils.h',
+    'nsCycleCollectionHoldDrop.h',
     'nsCycleCollectionNoteChild.h',
     'nsCycleCollectionNoteRootCallback.h',
     'nsCycleCollectionParticipant.h',
     'nsCycleCollectionTraversalCallback.h',
     'nsDataHashtable.h',
     'nsDebug.h',
     'nsDeque.h',
     'nsEnumeratorUtils.h',
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/nsCycleCollectionHoldDrop.h
@@ -0,0 +1,71 @@
+/* -*- 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 nsCycleCollectionHoldDrop_h_
+#define nsCycleCollectionHoldDrop_h_
+
+#include "mozilla/TypeTraits.h"
+#include "nsCycleCollectionParticipant.h"
+
+class nsISupports;
+class nsScriptObjectTracer;
+
+// Only HoldJSObjects and DropJSObjects should be called directly.
+
+namespace mozilla {
+namespace cyclecollector {
+
+// These methods are defined in nsCycleCollector.cpp
+void HoldJSObjectsImpl(void* aHolder, nsScriptObjectTracer* aTracer);
+void HoldJSObjectsImpl(nsISupports* aHolder);
+void DropJSObjectsImpl(void* aHolder);
+void DropJSObjectsImpl(nsISupports* aHolder);
+
+} // namespace cyclecollector
+
+
+template<class T, bool isISupports=IsBaseOf<nsISupports, T>::value>
+struct HoldDropJSObjectsHelper
+{
+  static void Hold(T* aHolder)
+  {
+    cyclecollector::HoldJSObjectsImpl(aHolder, NS_CYCLE_COLLECTION_PARTICIPANT(T));
+  }
+  static void Drop(T* aHolder)
+  {
+    cyclecollector::DropJSObjectsImpl(aHolder);
+  }
+};
+
+template<class T>
+struct HoldDropJSObjectsHelper<T, true>
+{
+  static void Hold(T* aHolder)
+  {
+    cyclecollector::HoldJSObjectsImpl(ToSupports(aHolder));
+  }
+  static void Drop(T* aHolder)
+  {
+    cyclecollector::DropJSObjectsImpl(ToSupports(aHolder));
+  }
+};
+
+
+template<class T>
+void HoldJSObjects(T* aHolder)
+{
+  HoldDropJSObjectsHelper<T>::Hold(aHolder);
+}
+
+template<class T>
+void DropJSObjects(T* aHolder)
+{
+  HoldDropJSObjectsHelper<T>::Drop(aHolder);
+}
+
+} // namespace mozilla
+
+#endif // nsCycleCollectionHoldDrop_h_