Backed out changeset c18e29c1b369 (bug 1164581) for cpp unit tests test failures
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 08 Feb 2016 11:17:38 +0100
changeset 283493 a71b79b9c3ef990e218c42d06c96027bc932b249
parent 283492 4c2e56373aab1b1a556f46f94642d4fd69957ae2
child 283494 faf39373fc660fb0416ad1d4ab6786074cd982ac
push id17508
push usercbook@mozilla.com
push dateTue, 09 Feb 2016 12:11:04 +0000
treeherderfx-team@683c1c3ca832 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1164581
milestone47.0a1
backs outc18e29c1b369e431a40fd079cdbbc39f0b909423
Backed out changeset c18e29c1b369 (bug 1164581) for cpp unit tests test failures
dom/archivereader/ArchiveEvent.cpp
dom/base/Console.cpp
dom/base/WebSocket.cpp
dom/base/nsScriptLoader.cpp
dom/cache/ManagerId.cpp
dom/devicestorage/DeviceStorageStatics.cpp
dom/devicestorage/nsDeviceStorage.cpp
dom/media/webspeech/recognition/SpeechStreamListener.cpp
dom/workers/WorkerPrivate.cpp
extensions/gio/nsGIOProtocolHandler.cpp
extensions/spellcheck/src/mozPersonalDictionary.cpp
image/Decoder.cpp
image/decoders/icon/mac/nsIconChannelCocoa.mm
image/decoders/icon/win/nsIconChannel.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
modules/libjar/nsJARChannel.cpp
netwerk/base/TLSServerSocket.cpp
netwerk/base/nsBaseChannel.cpp
netwerk/base/nsProtocolProxyService.cpp
netwerk/base/nsServerSocket.cpp
netwerk/base/nsStreamListenerTee.cpp
netwerk/base/nsTransportUtils.cpp
netwerk/base/nsUDPSocket.cpp
netwerk/cache/nsCacheService.cpp
netwerk/cache2/CacheIndex.h
netwerk/cache2/CacheStorageService.h
netwerk/ipc/RemoteOpenFileChild.cpp
netwerk/protocol/file/nsFileChannel.cpp
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/rtsp/controller/RtspControllerParent.cpp
netwerk/protocol/websocket/BaseWebSocketChannel.cpp
netwerk/protocol/websocket/WebSocketChannel.cpp
netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp
netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp
netwerk/sctp/datachannel/DataChannel.cpp
security/manager/ssl/nsNSSCallbacks.cpp
storage/StorageBaseStatementInternal.cpp
storage/mozStorageAsyncStatement.cpp
storage/mozStorageConnection.cpp
storage/mozStorageService.cpp
storage/mozStorageStatementData.h
toolkit/components/osfile/NativeOSFileInternals.cpp
toolkit/components/places/AsyncFaviconHelpers.cpp
toolkit/components/places/Database.cpp
toolkit/components/places/Helpers.h
toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
widget/gonk/nsScreenManagerGonk.cpp
xpcom/base/nsConsoleService.cpp
xpcom/base/nsInterfaceRequestorAgg.cpp
xpcom/glue/nsProxyRelease.cpp
xpcom/glue/nsProxyRelease.h
xpcom/glue/objs.mozbuild
xpcom/libxpcomrt/moz.build
--- a/dom/archivereader/ArchiveEvent.cpp
+++ b/dom/archivereader/ArchiveEvent.cpp
@@ -44,17 +44,27 @@ ArchiveReaderEvent::ArchiveReaderEvent(A
 : mArchiveReader(aArchiveReader)
 {
   MOZ_COUNT_CTOR(ArchiveReaderEvent);
 }
 
 ArchiveReaderEvent::~ArchiveReaderEvent()
 {
   if (!NS_IsMainThread()) {
-    NS_ReleaseOnMainThread(mMimeService.forget());
+    nsIMIMEService* mimeService;
+    mMimeService.forget(&mimeService);
+
+    if (mimeService) {
+      nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+      NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread! Leaking!");
+
+      if (mainThread) {
+        NS_ProxyRelease(mainThread, mimeService);
+      }
+    }
   }
 
   MOZ_COUNT_DTOR(ArchiveReaderEvent);
 }
 
 // From the filename to the mimetype:
 nsresult
 ArchiveReaderEvent::GetType(nsCString& aExt,
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -773,21 +773,21 @@ Console::Console(nsPIDOMWindowInner* aWi
 }
 
 Console::~Console()
 {
   MOZ_ASSERT(mConsoleCallDataArray.IsEmpty());
 
   if (!NS_IsMainThread()) {
     if (mStorage) {
-      NS_ReleaseOnMainThread(mStorage.forget());
+      NS_ReleaseOnMainThread(mStorage);
     }
 
     if (mSandbox) {
-      NS_ReleaseOnMainThread(mSandbox.forget());
+      NS_ReleaseOnMainThread(mSandbox);
     }
   }
 
   mozilla::DropJSObjects(this);
 }
 
 NS_IMETHODIMP
 Console::Observe(nsISupports* aSubject, const char* aTopic,
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -624,18 +624,18 @@ WebSocketImpl::Disconnect()
     // where to, exactly?
     rv.SuppressException();
   }
 
   // DontKeepAliveAnyMore() can release the object. So hold a reference to this
   // until the end of the method.
   RefPtr<WebSocketImpl> kungfuDeathGrip = this;
 
-  NS_ReleaseOnMainThread(mChannel.forget());
-  NS_ReleaseOnMainThread(mService.forget());
+  NS_ReleaseOnMainThread(mChannel);
+  NS_ReleaseOnMainThread(static_cast<nsIWebSocketEventService*>(mService.forget().take()));
 
   mWebSocket->DontKeepAliveAnyMore();
   mWebSocket->mImpl = nullptr;
 
   if (mWorkerPrivate && mWorkerFeature) {
     UnregisterFeature();
   }
 
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -777,18 +777,27 @@ nsScriptLoader::ProcessOffThreadRequest(
   nsresult rv = ProcessRequest(aRequest);
   mDocument->UnblockOnload(false);
   return rv;
 }
 
 NotifyOffThreadScriptLoadCompletedRunnable::~NotifyOffThreadScriptLoadCompletedRunnable()
 {
   if (MOZ_UNLIKELY(mRequest || mLoader) && !NS_IsMainThread()) {
-    NS_ReleaseOnMainThread(mRequest.forget());
-    NS_ReleaseOnMainThread(mLoader.forget());
+    nsCOMPtr<nsIThread> mainThread;
+    NS_GetMainThread(getter_AddRefs(mainThread));
+    if (mainThread) {
+      NS_ProxyRelease(mainThread, mRequest);
+      NS_ProxyRelease(mainThread, mLoader);
+    } else {
+      MOZ_ASSERT(false, "We really shouldn't leak!");
+      // Better to leak than crash.
+      Unused << mRequest.forget();
+      Unused << mLoader.forget();
+    }
   }
 }
 
 NS_IMETHODIMP
 NotifyOffThreadScriptLoadCompletedRunnable::Run()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
--- a/dom/cache/ManagerId.cpp
+++ b/dom/cache/ManagerId.cpp
@@ -57,14 +57,17 @@ ManagerId::~ManagerId()
   if (NS_IsMainThread()) {
     return;
   }
 
   // Otherwise we need to proxy to main thread to do the release
 
   // The PBackground worker thread shouldn't be running after the main thread
   // is stopped.  So main thread is guaranteed to exist here.
-  NS_ReleaseOnMainThread(mPrincipal.forget());
+  nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+  MOZ_ASSERT(mainThread);
+
+  NS_ProxyRelease(mainThread, mPrincipal.forget().take());
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/devicestorage/DeviceStorageStatics.cpp
+++ b/dom/devicestorage/DeviceStorageStatics.cpp
@@ -855,17 +855,17 @@ DeviceStorageStatics::ListenerWrapper::L
   : mListener(do_GetWeakReference(static_cast<DOMEventTargetHelper*>(aListener)))
   , mOwningThread(NS_GetCurrentThread())
 {
 }
 
 DeviceStorageStatics::ListenerWrapper::~ListenerWrapper()
 {
   // Even weak pointers are not thread safe
-  NS_ProxyRelease(mOwningThread, mListener.forget());
+  NS_ProxyRelease(mOwningThread, mListener);
 }
 
 bool
 DeviceStorageStatics::ListenerWrapper::Equals(nsDOMDeviceStorage* aListener)
 {
   bool current = false;
   mOwningThread->IsOnCurrentThread(&current);
   if (current) {
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -3711,17 +3711,18 @@ DeviceStorageRequestManager::~DeviceStor
   DS_LOG_INFO("%p pending %zu", this, mPending.Length());
 
   if (!mPending.IsEmpty()) {
     MOZ_ASSERT_UNREACHABLE("Should not destroy, still has pending requests");
     ListIndex i = mPending.Length();
     while (i > 0) {
       --i;
       DS_LOG_ERROR("terminate %u", mPending[i].mId);
-      NS_ProxyRelease(mOwningThread, mPending[i].mRequest.forget());
+      NS_ProxyRelease(mOwningThread,
+        NS_ISUPPORTS_CAST(EventTarget*, mPending[i].mRequest.forget().take()));
     }
   }
 }
 
 void
 DeviceStorageRequestManager::StorePermission(size_t aAccess, bool aAllow)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/media/webspeech/recognition/SpeechStreamListener.cpp
+++ b/dom/media/webspeech/recognition/SpeechStreamListener.cpp
@@ -17,17 +17,20 @@ SpeechStreamListener::SpeechStreamListen
 {
 }
 
 SpeechStreamListener::~SpeechStreamListener()
 {
   nsCOMPtr<nsIThread> mainThread;
   NS_GetMainThread(getter_AddRefs(mainThread));
 
-  NS_ProxyRelease(mainThread, mRecognition.forget());
+  SpeechRecognition* forgottenRecognition = nullptr;
+  mRecognition.swap(forgottenRecognition);
+  NS_ProxyRelease(mainThread,
+                  static_cast<DOMEventTargetHelper*>(forgottenRecognition));
 }
 
 void
 SpeechStreamListener::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
                                                TrackID aID,
                                                StreamTime aTrackOffset,
                                                uint32_t aTrackEvents,
                                                const MediaSegment& aQueuedMedia,
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -3621,18 +3621,27 @@ WorkerDebugger::WorkerDebugger(WorkerPri
   AssertIsOnMainThread();
 }
 
 WorkerDebugger::~WorkerDebugger()
 {
   MOZ_ASSERT(!mWorkerPrivate);
 
   if (!NS_IsMainThread()) {
+    nsCOMPtr<nsIThread> mainThread;
+    if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread)))) {
+      NS_WARNING("Failed to proxy release of listeners, leaking instead!");
+    }
+
     for (size_t index = 0; index < mListeners.Length(); ++index) {
-      NS_ReleaseOnMainThread(mListeners[index].forget());
+      nsIWorkerDebuggerListener* listener = nullptr;
+      mListeners[index].forget(&listener);
+      if (NS_FAILED(NS_ProxyRelease(mainThread, listener))) {
+        NS_WARNING("Failed to proxy release of listener, leaking instead!");
+      }
     }
   }
 }
 
 NS_IMPL_ISUPPORTS(WorkerDebugger, nsIWorkerDebugger)
 
 NS_IMETHODIMP
 WorkerDebugger::GetIsClosed(bool* aResult)
@@ -4144,17 +4153,21 @@ WorkerPrivate::GetLoadInfo(JSContext* aC
     // Now that we've spun the loop there's no guarantee that our parent is
     // still alive.  We may have received control messages initiating shutdown.
     {
       MutexAutoLock lock(aParent->mMutex);
       parentStatus = aParent->mStatus;
     }
 
     if (parentStatus > Running) {
-      NS_ReleaseOnMainThread(loadInfo.mChannel.forget());
+      nsCOMPtr<nsIThread> mainThread;
+      if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread))) ||
+          NS_FAILED(NS_ProxyRelease(mainThread, loadInfo.mChannel))) {
+        NS_WARNING("Failed to proxy release of channel, leaking instead!");
+      }
       return NS_ERROR_FAILURE;
     }
 
     loadInfo.mDomain = aParent->Domain();
     loadInfo.mFromWindow = aParent->IsFromWindow();
     loadInfo.mWindowID = aParent->WindowID();
     loadInfo.mStorageAllowed = aParent->IsStorageAllowed();
     loadInfo.mPrivateBrowsing = aParent->IsInPrivateBrowsing();
--- a/extensions/gio/nsGIOProtocolHandler.cpp
+++ b/extensions/gio/nsGIOProtocolHandler.cpp
@@ -610,20 +610,27 @@ nsGIOInputStream::Close()
   {
     // Destroy the list of GIOFileInfo objects...
     g_list_foreach(mDirList, (GFunc) g_object_unref, nullptr);
     g_list_free(mDirList);
     mDirList = nullptr;
     mDirListPtr = nullptr;
   }
 
-  if (mChannel) {
-    NS_ReleaseOnMainThread(dont_AddRef(mChannel));
+  if (mChannel)
+  {
+    nsresult rv = NS_OK;
 
+    nsCOMPtr<nsIThread> thread = do_GetMainThread();
+    if (thread)
+      rv = NS_ProxyRelease(thread, mChannel);
+
+    NS_ASSERTION(thread && NS_SUCCEEDED(rv), "leaking channel reference");
     mChannel = nullptr;
+    (void) rv;
   }
 
   mSpec.Truncate(); // free memory
 
   // Prevent future reads from re-opening the handle.
   if (NS_SUCCEEDED(mStatus))
     mStatus = NS_BASE_STREAM_CLOSED;
 
--- a/extensions/spellcheck/src/mozPersonalDictionary.cpp
+++ b/extensions/spellcheck/src/mozPersonalDictionary.cpp
@@ -60,17 +60,26 @@ public:
   {
   }
 
   NS_IMETHOD Run() override
   {
     mDict->SyncLoad();
 
     // Release the dictionary on the main thread
-    NS_ReleaseOnMainThread(mDict.forget());
+    mozPersonalDictionary *dict;
+    mDict.forget(&dict);
+
+    nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+    if (mainThread) {
+      NS_ProxyRelease(mainThread, static_cast<mozIPersonalDictionary *>(dict));
+    } else {
+      // It's better to leak the dictionary than to release it on a wrong thread
+      NS_WARNING("Cannot get main thread, leaking mozPersonalDictionary.");
+    }
 
     return NS_OK;
   }
 
 private:
   RefPtr<mozPersonalDictionary> mDict;
 };
 
@@ -131,18 +140,26 @@ public:
       mDict->mSavePending = false;
       mon.Notify();
 
       // Leaving the block where 'mon' was declared will call the destructor
       // and unlock.
     }
 
     // Release the dictionary on the main thread.
-    NS_ReleaseOnMainThread(mDict.forget());
+    mozPersonalDictionary *dict;
+    mDict.forget(&dict);
 
+    nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+    if (mainThread) {
+      NS_ProxyRelease(mainThread, static_cast<mozIPersonalDictionary *>(dict));
+    } else {
+      // It's better to leak the dictionary than to release it on a wrong thread.
+      NS_WARNING("Cannot get main thread, leaking mozPersonalDictionary.");
+    }
     return NS_OK;
   }
 
 private:
   nsTArray<nsString> mDictWords;
   nsCOMPtr<nsIFile> mFile;
   RefPtr<mozPersonalDictionary> mDict;
 };
--- a/image/Decoder.cpp
+++ b/image/Decoder.cpp
@@ -50,17 +50,26 @@ Decoder::~Decoder()
              "Destroying Decoder without taking all its progress changes");
   MOZ_ASSERT(mInvalidRect.IsEmpty() || !mImage,
              "Destroying Decoder without taking all its invalidations");
   mInitialized = false;
 
   if (mImage && !NS_IsMainThread()) {
     // Dispatch mImage to main thread to prevent it from being destructed by the
     // decode thread.
-    NS_ReleaseOnMainThread(mImage.forget());
+    nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+    NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!");
+    if (mainThread) {
+      // Handle ambiguous nsISupports inheritance.
+      RasterImage* rawImg = nullptr;
+      mImage.swap(rawImg);
+      DebugOnly<nsresult> rv =
+        NS_ProxyRelease(mainThread, NS_ISUPPORTS_CAST(ImageResource*, rawImg));
+      MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to proxy release to main thread");
+    }
   }
 }
 
 /*
  * Common implementation of the decoder interface.
  */
 
 void
--- a/image/decoders/icon/mac/nsIconChannelCocoa.mm
+++ b/image/decoders/icon/mac/nsIconChannelCocoa.mm
@@ -33,17 +33,22 @@
 // nsIconChannel methods
 nsIconChannel::nsIconChannel()
 {
 }
 
 nsIconChannel::~nsIconChannel()
 {
   if (mLoadInfo) {
-    NS_ReleaseOnMainThread(mLoadInfo.forget());
+    nsCOMPtr<nsIThread> mainThread;
+    NS_GetMainThread(getter_AddRefs(mainThread));
+
+    nsILoadInfo* forgetableLoadInfo;
+    mLoadInfo.forget(&forgetableLoadInfo);
+    NS_ProxyRelease(mainThread, forgetableLoadInfo, false);
   }
 }
 
 NS_IMPL_ISUPPORTS(nsIconChannel,
                   nsIChannel,
                   nsIRequest,
                   nsIRequestObserver,
                   nsIStreamListener)
--- a/image/decoders/icon/win/nsIconChannel.cpp
+++ b/image/decoders/icon/win/nsIconChannel.cpp
@@ -71,17 +71,22 @@ GetStockIconIDForName(const nsACString& 
 // nsIconChannel methods
 nsIconChannel::nsIconChannel()
 {
 }
 
 nsIconChannel::~nsIconChannel()
 {
   if (mLoadInfo) {
-    NS_ReleaseOnMainThread(mLoadInfo.forget());
+    nsCOMPtr<nsIThread> mainThread;
+    NS_GetMainThread(getter_AddRefs(mainThread));
+
+    nsILoadInfo* forgetableLoadInfo;
+    mLoadInfo.forget(&forgetableLoadInfo);
+    NS_ProxyRelease(mainThread, forgetableLoadInfo, false);
   }
 }
 
 NS_IMPL_ISUPPORTS(nsIconChannel,
                   nsIChannel,
                   nsIRequest,
                   nsIRequestObserver,
                   nsIStreamListener)
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -1124,17 +1124,20 @@ PeerConnectionMedia::AddTransportFlow(in
     WrapRunnable(this, &PeerConnectionMedia::ConnectDtlsListener_s, aFlow),
     NS_DISPATCH_NORMAL);
 }
 
 void
 PeerConnectionMedia::RemoveTransportFlow(int aIndex, bool aRtcp)
 {
   int index_inner = GetTransportFlowIndex(aIndex, aRtcp);
-  NS_ProxyRelease(GetSTSThread(), mTransportFlows[index_inner].forget());
+  TransportFlow* flow = mTransportFlows[index_inner].forget().take();
+  if (flow) {
+    NS_ProxyRelease(GetSTSThread(), flow);
+  }
 }
 
 void
 PeerConnectionMedia::ConnectDtlsListener_s(const RefPtr<TransportFlow>& aFlow)
 {
   TransportLayer* dtls = aFlow->GetLayer(TransportLayerDtls::ID());
   if (dtls) {
     dtls->SignalStateChange.connect(this, &PeerConnectionMedia::DtlsConnected_s);
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -209,17 +209,17 @@ nsJARChannel::nsJARChannel()
     mBlockRemoteFiles = Preferences::GetBool("network.jar.block-remote-files", false);
 
     // hold an owning reference to the jar handler
     NS_ADDREF(gJarHandler);
 }
 
 nsJARChannel::~nsJARChannel()
 {
-    NS_ReleaseOnMainThread(mLoadInfo.forget());
+    NS_ReleaseOnMainThread(mLoadInfo);
 
     // release owning reference to the jar handler
     nsJARProtocolHandler *handler = gJarHandler;
     NS_RELEASE(handler); // nullptr parameter
 }
 
 NS_IMPL_ISUPPORTS_INHERITED(nsJARChannel,
                             nsHashPropertyBag,
--- a/netwerk/base/TLSServerSocket.cpp
+++ b/netwerk/base/TLSServerSocket.cpp
@@ -324,24 +324,26 @@ TLSServerConnectionInfo::TLSServerConnec
 }
 
 TLSServerConnectionInfo::~TLSServerConnectionInfo()
 {
   if (!mSecurityObserver) {
     return;
   }
 
-  RefPtr<nsITLSServerSecurityObserver> observer;
+  nsITLSServerSecurityObserver* observer;
   {
     MutexAutoLock lock(mLock);
-    observer = mSecurityObserver.forget();
+    mSecurityObserver.forget(&observer);
   }
 
   if (observer) {
-    NS_ReleaseOnMainThread(observer.forget());
+    nsCOMPtr<nsIThread> mainThread;
+    NS_GetMainThread(getter_AddRefs(mainThread));
+    NS_ProxyRelease(mainThread, observer);
   }
 }
 
 NS_IMETHODIMP
 TLSServerConnectionInfo::SetSecurityObserver(nsITLSServerSecurityObserver* aObserver)
 {
   {
     MutexAutoLock lock(mLock);
--- a/netwerk/base/nsBaseChannel.cpp
+++ b/netwerk/base/nsBaseChannel.cpp
@@ -61,17 +61,17 @@ nsBaseChannel::nsBaseChannel()
   , mContentLength(-1)
   , mWasOpened(false)
 {
   mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
 }
 
 nsBaseChannel::~nsBaseChannel()
 {
-  NS_ReleaseOnMainThread(mLoadInfo.forget());
+  NS_ReleaseOnMainThread(mLoadInfo);
 }
 
 nsresult
 nsBaseChannel::Redirect(nsIChannel *newChannel, uint32_t redirectFlags,
                         bool openNewChannel)
 {
   SUSPEND_PUMP_FOR_SCOPE();
 
--- a/netwerk/base/nsProtocolProxyService.cpp
+++ b/netwerk/base/nsProtocolProxyService.cpp
@@ -123,30 +123,41 @@ public:
 private:
     ~nsAsyncResolveRequest()
     {
         if (!NS_IsMainThread()) {
             // these xpcom pointers might need to be proxied back to the
             // main thread to delete safely, but if this request had its
             // callbacks called normally they will all be null and this is a nop
 
+            nsCOMPtr<nsIThread> mainThread;
+            NS_GetMainThread(getter_AddRefs(mainThread));
+
             if (mChannel) {
-                NS_ReleaseOnMainThread(mChannel.forget());
+                nsIChannel *forgettable;
+                mChannel.forget(&forgettable);
+                NS_ProxyRelease(mainThread, forgettable, false);
             }
 
             if (mCallback) {
-                NS_ReleaseOnMainThread(mCallback.forget());
+                nsIProtocolProxyCallback *forgettable;
+                mCallback.forget(&forgettable);
+                NS_ProxyRelease(mainThread, forgettable, false);
             }
 
             if (mProxyInfo) {
-                NS_ReleaseOnMainThread(mProxyInfo.forget());
+                nsIProxyInfo *forgettable;
+                mProxyInfo.forget(&forgettable);
+                NS_ProxyRelease(mainThread, forgettable, false);
             }
 
             if (mXPComPPS) {
-                NS_ReleaseOnMainThread(mXPComPPS.forget());
+                nsIProtocolProxyService *forgettable;
+                mXPComPPS.forget(&forgettable);
+                NS_ProxyRelease(mainThread, forgettable, false);
             }
         }
     }
 
 public:
     void SetResult(nsresult status, nsIProxyInfo *pi)
     {
         mStatus = status;
--- a/netwerk/base/nsServerSocket.cpp
+++ b/netwerk/base/nsServerSocket.cpp
@@ -228,27 +228,25 @@ nsServerSocket::OnSocketDetached(PRFileD
     mFD = nullptr;
   }
 
   if (mListener)
   {
     mListener->OnStopListening(this, mCondition);
 
     // need to atomically clear mListener.  see our Close() method.
-    RefPtr<nsIServerSocketListener> listener = nullptr;
+    nsIServerSocketListener *listener = nullptr;
     {
       MutexAutoLock lock(mLock);
-      listener = mListener.forget();
+      mListener.swap(listener);
     }
-
     // XXX we need to proxy the release to the listener's target thread to work
     // around bug 337492.
-    if (listener) {
-      NS_ProxyRelease(mListenerTarget, listener.forget());
-    }
+    if (listener)
+      NS_ProxyRelease(mListenerTarget, listener);
   }
 }
 
 void
 nsServerSocket::IsLocal(bool *aIsLocal)
 {
 #if defined(XP_UNIX)
   // Unix-domain sockets are always local.
--- a/netwerk/base/nsStreamListenerTee.cpp
+++ b/netwerk/base/nsStreamListenerTee.cpp
@@ -34,17 +34,22 @@ nsStreamListenerTee::OnStopRequest(nsIRe
     // it is critical that we close out the input stream tee
     if (mInputTee) {
         mInputTee->SetSink(nullptr);
         mInputTee = 0;
     }
 
     // release sink on the same thread where the data was written (bug 716293)
     if (mEventTarget) {
-      NS_ProxyRelease(mEventTarget, mSink.forget());
+        nsIOutputStream *sink = nullptr;
+        mSink.swap(sink);
+        if (NS_FAILED(NS_ProxyRelease(mEventTarget, sink))) {
+            NS_WARNING("Releasing sink on the current thread!");
+            NS_RELEASE(sink);
+        }
     }
     else {
         mSink = 0;
     }
 
     nsresult rv = mListener->OnStopRequest(request, context, status);
     if (mObserver)
         mObserver->OnStopRequest(request, context, status);
--- a/netwerk/base/nsTransportUtils.cpp
+++ b/netwerk/base/nsTransportUtils.cpp
@@ -32,17 +32,17 @@ public:
         NS_ADDREF(mSink);
     }
 
 private:
     virtual ~nsTransportEventSinkProxy()
     {
         // our reference to mSink could be the last, so be sure to release
         // it on the target thread.  otherwise, we could get into trouble.
-        NS_ProxyRelease(mTarget, dont_AddRef(mSink));
+        NS_ProxyRelease(mTarget, mSink);
     }
 
 public:
     nsITransportEventSink           *mSink;
     nsCOMPtr<nsIEventTarget>         mTarget;
     Mutex                            mLock;
     nsTransportStatusEvent          *mLastEvent;
 };
--- a/netwerk/base/nsUDPSocket.cpp
+++ b/netwerk/base/nsUDPSocket.cpp
@@ -518,23 +518,26 @@ nsUDPSocket::OnSocketDetached(PRFileDesc
     NS_ASSERTION(mFD == fd, "wrong file descriptor");
     CloseSocket();
   }
   SaveNetworkStats(true);
 
   if (mListener)
   {
     // need to atomically clear mListener.  see our Close() method.
-    RefPtr<nsIUDPSocketListener> listener = nullptr;
+    nsCOMPtr<nsIUDPSocketListener> listener;
     {
       MutexAutoLock lock(mLock);
-      listener = mListener.forget();
+      mListener.swap(listener);
     }
 
-    NS_ProxyRelease(mListenerTarget, listener.forget());
+    if (listener) {
+      listener->OnStopListening(this, mCondition);
+      NS_ProxyRelease(mListenerTarget, listener);
+    }
   }
 }
 
 void
 nsUDPSocket::IsLocal(bool *aIsLocal)
 {
   // If bound to loopback, this UDP socket only accepts local connections.
   *aIsLocal = mAddr.raw.family == nsINetAddr::FAMILY_LOCAL;
--- a/netwerk/cache/nsCacheService.cpp
+++ b/netwerk/cache/nsCacheService.cpp
@@ -2686,17 +2686,17 @@ nsCacheService::ReleaseObject_Locked(nsI
                                      nsIEventTarget * target)
 {
     gService->mLock.AssertCurrentThreadOwns();
 
     bool isCur;
     if (!target || (NS_SUCCEEDED(target->IsOnCurrentThread(&isCur)) && isCur)) {
         gService->mDoomedObjects.AppendElement(obj);
     } else {
-        NS_ProxyRelease(target, dont_AddRef(obj));
+        NS_ProxyRelease(target, obj);
     }
 }
 
 
 nsresult
 nsCacheService::SetCacheElement(nsCacheEntry * entry, nsISupports * element)
 {
     entry->SetData(element);
--- a/netwerk/cache2/CacheIndex.h
+++ b/netwerk/cache2/CacheIndex.h
@@ -1041,17 +1041,26 @@ private:
       NS_DispatchToMainThread(this);
     }
 
   private:
     explicit DiskConsumptionObserver(nsWeakPtr const &aWeakObserver)
       : mObserver(aWeakObserver) { }
     virtual ~DiskConsumptionObserver() {
       if (mObserver && !NS_IsMainThread()) {
-        NS_ReleaseOnMainThread(mObserver.forget());
+        nsIWeakReference *obs;
+        mObserver.forget(&obs);
+
+        nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+        if (mainThread) {
+          NS_ProxyRelease(mainThread, obs);
+        } else {
+          NS_WARNING("Cannot get main thread, leaking weak reference to "
+                     "CacheStorageConsumptionObserver.");
+        }
       }
     }
 
     NS_IMETHODIMP Run()
     {
       MOZ_ASSERT(NS_IsMainThread());
 
       nsCOMPtr<nsICacheStorageConsumptionObserver> observer =
--- a/netwerk/cache2/CacheStorageService.h
+++ b/netwerk/cache2/CacheStorageService.h
@@ -378,17 +378,20 @@ private:
   };
 
   RefPtr<IOThreadSuspender> mActiveIOSuspender;
 };
 
 template<class T>
 void ProxyRelease(nsCOMPtr<T> &object, nsIThread* thread)
 {
-  NS_ProxyRelease(thread, object.forget());
+  T* release;
+  object.forget(&release);
+
+  NS_ProxyRelease(thread, release);
 }
 
 template<class T>
 void ProxyReleaseMainThread(nsCOMPtr<T> &object)
 {
   nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
   ProxyRelease(object, mainThread);
 }
--- a/netwerk/ipc/RemoteOpenFileChild.cpp
+++ b/netwerk/ipc/RemoteOpenFileChild.cpp
@@ -102,20 +102,45 @@ RemoteOpenFileChild::RemoteOpenFileChild
 
 RemoteOpenFileChild::~RemoteOpenFileChild()
 {
   if (NS_IsMainThread()) {
     if (mListener) {
       NotifyListener(NS_ERROR_UNEXPECTED);
     }
   } else {
-    NS_ReleaseOnMainThread(mURI.forget(), true);
-    NS_ReleaseOnMainThread(mAppURI.forget(), true);
-    NS_ReleaseOnMainThread(mListener.forget(), true);
-    NS_ReleaseOnMainThread(mTabChild.forget(), true);
+    nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+    if (mainThread) {
+      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_ProxyRelease(mainThread, mURI, true)));
+      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_ProxyRelease(mainThread, mAppURI, true)));
+      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_ProxyRelease(mainThread, mListener,
+                                                   true)));
+
+      TabChild* tabChild;
+      mTabChild.forget(&tabChild);
+
+      if (tabChild) {
+        nsCOMPtr<nsIRunnable> runnable =
+          NS_NewNonOwningRunnableMethod(tabChild, &TabChild::Release);
+        MOZ_ASSERT(runnable);
+
+        MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mainThread->Dispatch(runnable,
+                                                          NS_DISPATCH_NORMAL)));
+      }
+    } else {
+      using mozilla::Unused;
+
+      NS_WARNING("RemoteOpenFileChild released after thread shutdown, leaking "
+                 "its members!");
+
+      Unused << mURI.forget();
+      Unused << mAppURI.forget();
+      Unused << mListener.forget();
+      Unused << mTabChild.forget();
+    }
   }
 
   if (mNSPRFileDesc) {
     // PR_Close both closes the file and deallocates the PRFileDesc
     PR_Close(mNSPRFileDesc);
   }
 }
 
--- a/netwerk/protocol/file/nsFileChannel.cpp
+++ b/netwerk/protocol/file/nsFileChannel.cpp
@@ -124,17 +124,19 @@ nsFileCopyEvent::DoCopy()
   mDest->Close();
 
   // Notify completion
   if (mCallback) {
     mCallbackTarget->Dispatch(mCallback, NS_DISPATCH_NORMAL);
 
     // Release the callback on the target thread to avoid destroying stuff on
     // the wrong thread.
-    NS_ProxyRelease(mCallbackTarget, mCallback.forget());
+    nsIRunnable *doomed = nullptr;
+    mCallback.swap(doomed);
+    NS_ProxyRelease(mCallbackTarget, doomed);
   }
 }
 
 nsresult
 nsFileCopyEvent::Dispatch(nsIRunnable *callback,
                           nsITransportEventSink *sink,
                           nsIEventTarget *target)
 {
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -122,17 +122,17 @@ HttpBaseChannel::HttpBaseChannel()
   mPeerAddr.raw.family = PR_AF_UNSPEC;
   mSchedulingContextID.Clear();
 }
 
 HttpBaseChannel::~HttpBaseChannel()
 {
   LOG(("Destroying HttpBaseChannel @%x\n", this));
 
-  NS_ReleaseOnMainThread(mLoadInfo.forget());
+  NS_ReleaseOnMainThread(mLoadInfo);
 
   // Make sure we don't leak
   CleanRedirectCacheChainIfNecessary();
 }
 
 nsresult
 HttpBaseChannel::Init(nsIURI *aURI,
                       uint32_t aCaps,
--- a/netwerk/protocol/rtsp/controller/RtspControllerParent.cpp
+++ b/netwerk/protocol/rtsp/controller/RtspControllerParent.cpp
@@ -35,18 +35,23 @@ LazyLogModule gRtspLog("nsRtsp");
 void
 RtspControllerParent::Destroy()
 {
   // If we're being destroyed on a non-main thread, we AddRef again and use a
   // proxy to release the RtspControllerParent on the main thread, where the
   // RtspControllerParent is deleted. This ensures we only delete the
   // RtspControllerParent on the main thread.
   if (!NS_IsMainThread()) {
+    nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+    NS_ENSURE_TRUE_VOID(mainThread);
     RefPtr<RtspControllerParent> doomed(this);
-    NS_ReleaseOnMainThread(doomed.forget(), true);
+    if (NS_FAILED(NS_ProxyRelease(mainThread,
+            static_cast<nsIStreamingProtocolListener*>(doomed), true))) {
+      NS_WARNING("Failed to proxy release to main thread!");
+    }
   } else {
     delete this;
   }
 }
 
 NS_IMPL_ADDREF(RtspControllerParent)
 NS_IMPL_RELEASE_WITH_DESTROY(RtspControllerParent, Destroy())
 NS_IMPL_QUERY_INTERFACE(RtspControllerParent,
--- a/netwerk/protocol/websocket/BaseWebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/BaseWebSocketChannel.cpp
@@ -355,14 +355,17 @@ BaseWebSocketChannel::ListenerAndContext
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mListener);
 }
 
 BaseWebSocketChannel::ListenerAndContextContainer::~ListenerAndContextContainer()
 {
   MOZ_ASSERT(mListener);
 
-  NS_ReleaseOnMainThread(mListener.forget());
-  NS_ReleaseOnMainThread(mContext.forget());
+  nsCOMPtr<nsIThread> mainThread;
+  NS_GetMainThread(getter_AddRefs(mainThread));
+
+  NS_ProxyRelease(mainThread, mListener, false);
+  NS_ProxyRelease(mainThread, mContext, false);
 }
 
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/websocket/WebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannel.cpp
@@ -1211,24 +1211,24 @@ WebSocketChannel::~WebSocketChannel()
 
   while ((mCurrentOut = (OutboundMessage *) mOutgoingPingMessages.PopFront()))
     delete mCurrentOut;
   while ((mCurrentOut = (OutboundMessage *) mOutgoingPongMessages.PopFront()))
     delete mCurrentOut;
   while ((mCurrentOut = (OutboundMessage *) mOutgoingMessages.PopFront()))
     delete mCurrentOut;
 
-  NS_ReleaseOnMainThread(mURI.forget());
-  NS_ReleaseOnMainThread(mOriginalURI.forget());
+  NS_ReleaseOnMainThread(mURI);
+  NS_ReleaseOnMainThread(mOriginalURI);
 
   mListenerMT = nullptr;
 
-  NS_ReleaseOnMainThread(mLoadGroup.forget());
-  NS_ReleaseOnMainThread(mLoadInfo.forget());
-  NS_ReleaseOnMainThread(mService.forget());
+  NS_ReleaseOnMainThread(mLoadGroup);
+  NS_ReleaseOnMainThread(mLoadInfo);
+  NS_ReleaseOnMainThread(static_cast<nsIWebSocketEventService*>(mService.forget().take()));
 }
 
 NS_IMETHODIMP
 WebSocketChannel::Observe(nsISupports *subject,
                           const char *topic,
                           const char16_t *data)
 {
   LOG(("WebSocketChannel::Observe [topic=\"%s\"]\n", topic));
--- a/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp
+++ b/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp
@@ -51,17 +51,22 @@ WyciwygChannelChild::WyciwygChannelChild
   LOG(("Creating WyciwygChannelChild @%x\n", this));
   mEventQ = new ChannelEventQueue(NS_ISUPPORTS_CAST(nsIWyciwygChannel*, this));
 }
 
 WyciwygChannelChild::~WyciwygChannelChild()
 {
   LOG(("Destroying WyciwygChannelChild @%x\n", this));
   if (mLoadInfo) {
-    NS_ReleaseOnMainThread(mLoadInfo.forget());
+    nsCOMPtr<nsIThread> mainThread;
+    NS_GetMainThread(getter_AddRefs(mainThread));
+
+    nsILoadInfo *forgetableLoadInfo;
+    mLoadInfo.forget(&forgetableLoadInfo);
+    NS_ProxyRelease(mainThread, forgetableLoadInfo, false);
   }
 }
 
 void
 WyciwygChannelChild::AddIPDLReference()
 {
   MOZ_ASSERT(!mIPCOpen, "Attempt to retain more than one IPDL reference");
   mIPCOpen = true;
--- a/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp
+++ b/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp
@@ -35,17 +35,23 @@ typedef mozilla::net::LoadContextInfo Lo
 
 // Must release mChannel on the main thread
 class nsWyciwygAsyncEvent : public nsRunnable {
 public:
   explicit nsWyciwygAsyncEvent(nsWyciwygChannel *aChannel) : mChannel(aChannel) {}
 
   ~nsWyciwygAsyncEvent()
   {
-    NS_ReleaseOnMainThread(mChannel.forget());
+    nsCOMPtr<nsIThread> thread = do_GetMainThread();
+    NS_WARN_IF_FALSE(thread, "Couldn't get the main thread!");
+    if (thread) {
+      nsIWyciwygChannel *chan = static_cast<nsIWyciwygChannel *>(mChannel);
+      mozilla::Unused << mChannel.forget();
+      NS_ProxyRelease(thread, chan);
+    }
   }
 protected:
   RefPtr<nsWyciwygChannel> mChannel;
 };
 
 class nsWyciwygSetCharsetandSourceEvent : public nsWyciwygAsyncEvent {
 public:
   explicit nsWyciwygSetCharsetandSourceEvent(nsWyciwygChannel *aChannel)
@@ -98,17 +104,22 @@ nsWyciwygChannel::nsWyciwygChannel()
     mContentLength(-1),
     mLoadFlags(LOAD_NORMAL)
 {
 }
 
 nsWyciwygChannel::~nsWyciwygChannel() 
 {
   if (mLoadInfo) {
-    NS_ReleaseOnMainThread(mLoadInfo.forget(), false);
+    nsCOMPtr<nsIThread> mainThread;
+    NS_GetMainThread(getter_AddRefs(mainThread));
+
+    nsILoadInfo *forgetableLoadInfo;
+    mLoadInfo.forget(&forgetableLoadInfo);
+    NS_ProxyRelease(mainThread, forgetableLoadInfo, false);
   }
 }
 
 NS_IMPL_ISUPPORTS(nsWyciwygChannel,
                   nsIChannel,
                   nsIRequest,
                   nsIStreamListener,
                   nsIRequestObserver,
--- a/netwerk/sctp/datachannel/DataChannel.cpp
+++ b/netwerk/sctp/datachannel/DataChannel.cpp
@@ -191,17 +191,17 @@ DataChannelConnection::~DataChannelConne
   MOZ_ASSERT(mPending.GetSize() == 0);
 
   // Already disconnected from sigslot/mTransportFlow
   // TransportFlows must be released from the STS thread
   if (!IsSTSThread()) {
     ASSERT_WEBRTC(NS_IsMainThread());
     if (mTransportFlow) {
       ASSERT_WEBRTC(mSTS);
-      NS_ProxyRelease(mSTS, mTransportFlow.forget());
+      NS_ProxyRelease(mSTS, mTransportFlow);
     }
 
     if (mInternalIOThread) {
       // Avoid spinning the event thread from here (which if we're mainthread
       // is in the event loop already)
       NS_DispatchToMainThread(WrapRunnable(nsCOMPtr<nsIThread>(mInternalIOThread),
                                            &nsIThread::Shutdown),
                               NS_DISPATCH_NORMAL);
@@ -2412,17 +2412,17 @@ DataChannelConnection::ReadBlob(already_
   RefPtr<DataChannelBlobSendRunnable> runnable = new DataChannelBlobSendRunnable(aThis,
                                                                                    aStream);
   // avoid copying the blob data by passing the mData from the runnable
   if (NS_FAILED(aBlob->Available(&len)) ||
       NS_FAILED(NS_ReadInputStreamToString(aBlob, runnable->mData, len))) {
     // Bug 966602:  Doesn't return an error to the caller via onerror.
     // We must release DataChannelConnection on MainThread to avoid issues (bug 876167)
     // aThis is now owned by the runnable; release it there
-    NS_ProxyRelease(mainThread, runnable.forget());
+    NS_ProxyRelease(mainThread, runnable);
     return;
   }
   aBlob->Close();
   NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
 }
 
 void
 DataChannelConnection::GetStreamIds(std::vector<uint16_t>* aStreamList)
--- a/security/manager/ssl/nsNSSCallbacks.cpp
+++ b/security/manager/ssl/nsNSSCallbacks.cpp
@@ -605,17 +605,18 @@ nsHTTPListener::~nsHTTPListener()
   if (mResponsibleForDoneSignal)
     send_done_signal();
 
   if (mResultData) {
     free(const_cast<uint8_t *>(mResultData));
   }
 
   if (mLoader) {
-    NS_ReleaseOnMainThread(mLoader.forget());
+    nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
+    NS_ProxyRelease(mainThread, mLoader);
   }
 }
 
 NS_IMPL_ISUPPORTS(nsHTTPListener, nsIStreamLoaderObserver)
 
 void
 nsHTTPListener::FreeLoadGroup(bool aCancelLoad)
 {
--- a/storage/StorageBaseStatementInternal.cpp
+++ b/storage/StorageBaseStatementInternal.cpp
@@ -40,22 +40,20 @@ public:
   : mStatement(aStatement)
   , mConnection(aConnection)
   {
   }
 
   NS_IMETHOD Run()
   {
     if (mStatement->mAsyncStatement) {
-      sqlite3_finalize(mStatement->mAsyncStatement);
+      (void)::sqlite3_finalize(mStatement->mAsyncStatement);
       mStatement->mAsyncStatement = nullptr;
     }
-
-    nsCOMPtr<nsIThread> targetThread(mConnection->threadOpenedOn);
-    NS_ProxyRelease(targetThread, mStatement.forget());
+    (void)::NS_ProxyRelease(mConnection->threadOpenedOn, mStatement);
     return NS_OK;
   }
 private:
   RefPtr<StorageBaseStatementInternal> mStatement;
   RefPtr<Connection> mConnection;
 };
 
 /**
@@ -88,18 +86,23 @@ public:
     NS_PRECONDITION(aConnection, "You must provide a Connection");
   }
 
   NS_IMETHOD Run()
   {
     (void)::sqlite3_finalize(mAsyncStatement);
     mAsyncStatement = nullptr;
 
-    nsCOMPtr<nsIThread> target(mConnection->threadOpenedOn);
-    (void)::NS_ProxyRelease(target, mConnection.forget());
+    // Because of our ambiguous nsISupports we cannot use the NS_ProxyRelease
+    // template helpers.
+    Connection *rawConnection = nullptr;
+    mConnection.swap(rawConnection);
+    (void)::NS_ProxyRelease(
+      rawConnection->threadOpenedOn,
+      NS_ISUPPORTS_CAST(mozIStorageConnection *, rawConnection));
     return NS_OK;
   }
 private:
   RefPtr<Connection> mConnection;
   sqlite3_stmt *mAsyncStatement;
 };
 
 ////////////////////////////////////////////////////////////////////////////////
--- a/storage/mozStorageAsyncStatement.cpp
+++ b/storage/mozStorageAsyncStatement.cpp
@@ -215,18 +215,20 @@ AsyncStatement::~AsyncStatement()
 
   // If we are getting destroyed on the wrong thread, proxy the connection
   // release to the right thread.  I'm not sure why we do this.
   bool onCallingThread = false;
   (void)mDBConnection->threadOpenedOn->IsOnCurrentThread(&onCallingThread);
   if (!onCallingThread) {
     // NS_ProxyRelase only magic forgets for us if mDBConnection is an
     // nsCOMPtr.  Which it is not; it's an nsRefPtr.
-    nsCOMPtr<nsIThread> targetThread(mDBConnection->threadOpenedOn);
-    NS_ProxyRelease(targetThread, mDBConnection.forget());
+    Connection *forgottenConn = nullptr;
+    mDBConnection.swap(forgottenConn);
+    (void)::NS_ProxyRelease(forgottenConn->threadOpenedOn,
+                            static_cast<mozIStorageConnection *>(forgottenConn));
   }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsISupports
 
 NS_IMPL_ADDREF(AsyncStatement)
 NS_IMPL_RELEASE(AsyncStatement)
--- a/storage/mozStorageConnection.cpp
+++ b/storage/mozStorageConnection.cpp
@@ -377,18 +377,25 @@ public:
       (void)NS_GetMainThread(getter_AddRefs(thread));
       (void)thread->Dispatch(mCallbackEvent, NS_DISPATCH_NORMAL);
     }
 
     return NS_OK;
   }
 
   ~AsyncCloseConnection() {
-    NS_ReleaseOnMainThread(mConnection.forget());
-    NS_ReleaseOnMainThread(mCallbackEvent.forget());
+    nsCOMPtr<nsIThread> thread;
+    (void)NS_GetMainThread(getter_AddRefs(thread));
+    // Handle ambiguous nsISupports inheritance.
+    Connection *rawConnection = nullptr;
+    mConnection.swap(rawConnection);
+    (void)NS_ProxyRelease(thread,
+                          NS_ISUPPORTS_CAST(mozIStorageConnection *,
+                                            rawConnection));
+    (void)NS_ProxyRelease(thread, mCallbackEvent);
   }
 private:
   RefPtr<Connection> mConnection;
   sqlite3 *mNativeConnection;
   nsCOMPtr<nsIRunnable> mCallbackEvent;
   nsCOMPtr<nsIThread> mAsyncExecutionThread;
 };
 
@@ -440,23 +447,32 @@ private:
   }
 
   ~AsyncInitializeClone() {
     nsCOMPtr<nsIThread> thread;
     DebugOnly<nsresult> rv = NS_GetMainThread(getter_AddRefs(thread));
     MOZ_ASSERT(NS_SUCCEEDED(rv));
 
     // Handle ambiguous nsISupports inheritance.
-    NS_ProxyRelease(thread, mConnection.forget());
-    NS_ProxyRelease(thread, mClone.forget());
+    Connection *rawConnection = nullptr;
+    mConnection.swap(rawConnection);
+    (void)NS_ProxyRelease(thread, NS_ISUPPORTS_CAST(mozIStorageConnection *,
+                                                    rawConnection));
+
+    Connection *rawClone = nullptr;
+    mClone.swap(rawClone);
+    (void)NS_ProxyRelease(thread, NS_ISUPPORTS_CAST(mozIStorageConnection *,
+                                                    rawClone));
 
     // Generally, the callback will be released by CallbackComplete.
     // However, if for some reason Run() is not executed, we still
     // need to ensure that it is released here.
-    NS_ProxyRelease(thread, mCallback.forget());
+    mozIStorageCompletionCallback *rawCallback = nullptr;
+    mCallback.swap(rawCallback);
+    (void)NS_ProxyRelease(thread, rawCallback);
   }
 
   RefPtr<Connection> mConnection;
   RefPtr<Connection> mClone;
   const bool mReadOnly;
   nsCOMPtr<mozIStorageCompletionCallback> mCallback;
 };
 
--- a/storage/mozStorageService.cpp
+++ b/storage/mozStorageService.cpp
@@ -327,17 +327,18 @@ Service::unregisterConnection(Connection
 
     for (uint32_t i = 0 ; i < mConnections.Length(); ++i) {
       if (mConnections[i] == aConnection) {
         nsCOMPtr<nsIThread> thread = mConnections[i]->threadOpenedOn;
 
         // Ensure the connection is released on its opening thread.  Note, we
         // must use .forget().take() so that we can manually cast to an
         // unambiguous nsISupports type.
-        NS_ProxyRelease(thread, mConnections[i].forget());
+        NS_ProxyRelease(thread,
+          static_cast<mozIStorageConnection*>(mConnections[i].forget().take()));
 
         mConnections.RemoveElementAt(i);
         return;
       }
     }
 
     MOZ_ASSERT_UNREACHABLE("Attempt to unregister unknown storage connection!");
   }
@@ -727,23 +728,33 @@ private:
       new CallbackComplete(aStatus,
                            aValue,
                            mCallback.forget());
     return NS_DispatchToMainThread(event);
   }
 
   ~AsyncInitDatabase()
   {
-    NS_ReleaseOnMainThread(mStorageFile.forget());
-    NS_ReleaseOnMainThread(mConnection.forget());
+    nsCOMPtr<nsIThread> thread;
+    DebugOnly<nsresult> rv = NS_GetMainThread(getter_AddRefs(thread));
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+    (void)NS_ProxyRelease(thread, mStorageFile);
+
+    // Handle ambiguous nsISupports inheritance.
+    Connection *rawConnection = nullptr;
+    mConnection.swap(rawConnection);
+    (void)NS_ProxyRelease(thread, NS_ISUPPORTS_CAST(mozIStorageConnection *,
+                                                    rawConnection));
 
     // Generally, the callback will be released by CallbackComplete.
     // However, if for some reason Run() is not executed, we still
     // need to ensure that it is released here.
-    NS_ReleaseOnMainThread(mCallback.forget());
+    mozIStorageCompletionCallback *rawCallback = nullptr;
+    mCallback.swap(rawCallback);
+    (void)NS_ProxyRelease(thread, rawCallback);
   }
 
   RefPtr<Connection> mConnection;
   nsCOMPtr<nsIFile> mStorageFile;
   int32_t mGrowthIncrement;
   RefPtr<mozIStorageCompletionCallback> mCallback;
 };
 
--- a/storage/mozStorageStatementData.h
+++ b/storage/mozStorageStatementData.h
@@ -47,17 +47,18 @@ public:
   : mStatement(nullptr)
   {
   }
   ~StatementData()
   {
     // We need to ensure that mParamsArray is released on the main thread,
     // as the binding arguments may be XPConnect values, which are safe
     // to release only on the main thread.
-    NS_ReleaseOnMainThread(mParamsArray.forget());
+    nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+    (void)NS_ProxyRelease(mainThread, mParamsArray);
   }
 
   /**
    * Return the sqlite statement, fetching it from the storage statement.  In
    * the case of AsyncStatements this may actually create the statement 
    */
   inline int getSqliteStatement(sqlite3_stmt **_stmt)
   {
--- a/toolkit/components/osfile/NativeOSFileInternals.cpp
+++ b/toolkit/components/osfile/NativeOSFileInternals.cpp
@@ -524,34 +524,36 @@ public:
                                                 aDiscardedResult,
                                                 aOperation,
                                                 aOSError);
     nsresult rv = NS_DispatchToMainThread(event);
     if (NS_FAILED(rv)) {
       // Last ditch attempt to release on the main thread - some of
       // the members of event are not thread-safe, so letting the
       // pointer go out of scope would cause a crash.
-      NS_ReleaseOnMainThread(event.forget());
+      nsCOMPtr<nsIThread> main = do_GetMainThread();
+      NS_ProxyRelease(main, event);
     }
   }
 
   /**
    * Succeed, asynchronously.
    */
   void Succeed(already_AddRefed<nsINativeOSFileResult>&& aResult) {
     Resolve();
     RefPtr<SuccessEvent> event = new SuccessEvent(mOnSuccess,
                                                     mOnError,
                                                     aResult);
     nsresult rv = NS_DispatchToMainThread(event);
     if (NS_FAILED(rv)) {
       // Last ditch attempt to release on the main thread - some of
       // the members of event are not thread-safe, so letting the
       // pointer go out of scope would cause a crash.
-      NS_ReleaseOnMainThread(event.forget());
+      nsCOMPtr<nsIThread> main = do_GetMainThread();
+      NS_ProxyRelease(main, event);
     }
 
   }
 
 private:
 
   /**
    * Mark the event as complete, for debugging purposes.
@@ -742,17 +744,18 @@ public:
   { }
 
   ~DoReadToTypedArrayEvent() {
     // If AbstractReadEvent::Run() has bailed out, we may need to cleanup
     // mResult, which is main-thread only data
     if (!mResult) {
       return;
     }
-    NS_ReleaseOnMainThread(mResult.forget());
+    nsCOMPtr<nsIThread> main = do_GetMainThread();
+    (void)NS_ProxyRelease(main, mResult);
   }
 
 protected:
   void AfterRead(TimeStamp aDispatchDate,
                  ScopedArrayBufferContents& aBuffer) override {
     MOZ_ASSERT(!NS_IsMainThread());
     mResult->Init(aDispatchDate, TimeStamp::Now() - aDispatchDate, aBuffer.forget());
     Succeed(mResult.forget());
@@ -779,17 +782,18 @@ public:
   { }
 
   ~DoReadToStringEvent() {
     // If AbstraactReadEvent::Run() has bailed out, we may need to cleanup
     // mResult, which is main-thread only data
     if (!mResult) {
       return;
     }
-    NS_ReleaseOnMainThread(mResult.forget());
+    nsCOMPtr<nsIThread> main = do_GetMainThread();
+    (void)NS_ProxyRelease(main, mResult);
   }
 
 protected:
   nsresult BeforeRead() override {
     // Obtain the decoder. We do this before reading to avoid doing
     // any unnecessary I/O in case the name of the encoding is incorrect.
     MOZ_ASSERT(!NS_IsMainThread());
     nsAutoCString encodingName;
--- a/toolkit/components/places/AsyncFaviconHelpers.cpp
+++ b/toolkit/components/places/AsyncFaviconHelpers.cpp
@@ -352,18 +352,20 @@ AsyncFaviconHelperBase::AsyncFaviconHelp
 )
 {
   // Don't AddRef or Release in runnables for thread-safety.
   mCallback.swap(aCallback);
 }
 
 AsyncFaviconHelperBase::~AsyncFaviconHelperBase()
 {
+  nsCOMPtr<nsIThread> thread;
+  (void)NS_GetMainThread(getter_AddRefs(thread));
   if (mCallback) {
-    NS_ReleaseOnMainThread(mCallback.forget(), true);
+    (void)NS_ProxyRelease(thread, mCallback, true);
   }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// AsyncFetchAndSetIconForPage
 
 // static
 nsresult
--- a/toolkit/components/places/Database.cpp
+++ b/toolkit/components/places/Database.cpp
@@ -538,19 +538,23 @@ DatabaseShutdown::Complete(nsresult, nsI
                              nullptr);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
   }
   mState = NOTIFIED_OBSERVERS_PLACES_CONNECTION_CLOSED;
 
   if (NS_WARN_IF(!mBarrier)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
+  nsCOMPtr<nsIAsyncShutdownBarrier> barrier = mBarrier.forget();
+  nsCOMPtr<nsIAsyncShutdownClient> parentClient = mParentClient.forget();
+  nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+  MOZ_ASSERT(mainThread);
 
-  NS_ReleaseOnMainThread(mBarrier.forget());
-  NS_ReleaseOnMainThread(mParentClient.forget());
+  NS_ProxyRelease(mainThread, barrier);
+  NS_ProxyRelease(mainThread, parentClient);
 
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS(
   DatabaseShutdown
 , nsIAsyncShutdownBlocker
 , nsIAsyncShutdownCompletionCallback
--- a/toolkit/components/places/Helpers.h
+++ b/toolkit/components/places/Helpers.h
@@ -191,17 +191,17 @@ public:
   , mCallingThread(do_GetCurrentThread())
   {
   }
 
   NS_IMETHOD Run()
   {
     mStatementCache.FinalizeStatements();
     // Release the owner back on the calling thread.
-    NS_ProxyRelease(mCallingThread, mOwner.forget());
+    (void)NS_ProxyRelease(mCallingThread, mOwner);
     return NS_OK;
   }
 
 protected:
   mozilla::storage::StatementCache<StatementType>& mStatementCache;
   nsCOMPtr<nsISupports> mOwner;
   nsCOMPtr<nsIThread> mCallingThread;
 };
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -793,18 +793,21 @@ private:
 };
 
 NS_IMPL_ISUPPORTS(nsUrlClassifierLookupCallback,
                   nsIUrlClassifierLookupCallback,
                   nsIUrlClassifierHashCompleterCallback)
 
 nsUrlClassifierLookupCallback::~nsUrlClassifierLookupCallback()
 {
+  nsCOMPtr<nsIThread> thread;
+  (void)NS_GetMainThread(getter_AddRefs(thread));
+
   if (mCallback) {
-    NS_ReleaseOnMainThread(mCallback.forget());
+    (void)NS_ProxyRelease(thread, mCallback, false);
   }
 }
 
 NS_IMETHODIMP
 nsUrlClassifierLookupCallback::LookupComplete(nsTArray<LookupResult>* results)
 {
   NS_ASSERTION(mResults == nullptr,
                "Should only get one set of results per nsUrlClassifierLookupCallback!");
--- a/widget/gonk/nsScreenManagerGonk.cpp
+++ b/widget/gonk/nsScreenManagerGonk.cpp
@@ -731,17 +731,17 @@ nsScreenGonk::ClearMirroringScreen(nsScr
 void
 nsScreenGonk::UpdateMirroringWidget(already_AddRefed<nsWindow>& aWindow)
 {
     MOZ_ASSERT(CompositorParent::IsInCompositorThread());
     MOZ_ASSERT(IsPrimaryScreen());
 
     if (mMirroringWidget) {
         nsCOMPtr<nsIWidget> widget = mMirroringWidget.forget();
-        NS_ReleaseOnMainThread(widget.forget());
+        NS_ReleaseOnMainThread(widget);
     }
     mMirroringWidget = aWindow;
 }
 
 nsWindow*
 nsScreenGonk::GetMirroringWidget()
 {
     MOZ_ASSERT(CompositorParent::IsInCompositorThread());
--- a/xpcom/base/nsConsoleService.cpp
+++ b/xpcom/base/nsConsoleService.cpp
@@ -309,17 +309,17 @@ nsConsoleService::LogMessageWithMode(nsI
       r = new LogMessageRunnable(aMessage, this);
     }
   }
 
   if (retiredMessage) {
     // Release |retiredMessage| on the main thread in case it is an instance of
     // a mainthread-only class like nsScriptErrorWithStack and we're off the
     // main thread.
-    NS_ReleaseOnMainThread(retiredMessage.forget());
+    NS_ReleaseOnMainThread(retiredMessage);
   }
 
   if (r) {
     // avoid failing in XPCShell tests
     nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
     if (mainThread) {
       NS_DispatchToMainThread(r.forget());
     }
--- a/xpcom/base/nsInterfaceRequestorAgg.cpp
+++ b/xpcom/base/nsInterfaceRequestorAgg.cpp
@@ -49,18 +49,26 @@ nsInterfaceRequestorAgg::GetInterface(co
   if (mSecond && NS_FAILED(rv)) {
     rv = mSecond->GetInterface(aIID, aResult);
   }
   return rv;
 }
 
 nsInterfaceRequestorAgg::~nsInterfaceRequestorAgg()
 {
-  NS_ProxyRelease(mConsumerTarget, mFirst.forget());
-  NS_ProxyRelease(mConsumerTarget, mSecond.forget());
+  nsIInterfaceRequestor* iir = nullptr;
+  mFirst.swap(iir);
+  if (iir) {
+    NS_ProxyRelease(mConsumerTarget, iir);
+  }
+  iir = nullptr;
+  mSecond.swap(iir);
+  if (iir) {
+    NS_ProxyRelease(mConsumerTarget, iir);
+  }
 }
 
 nsresult
 NS_NewInterfaceRequestorAggregation(nsIInterfaceRequestor* aFirst,
                                     nsIInterfaceRequestor* aSecond,
                                     nsIInterfaceRequestor** aResult)
 {
   *aResult = new nsInterfaceRequestorAgg(aFirst, aSecond);
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/nsProxyRelease.cpp
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsProxyRelease.h"
+#include "nsThreadUtils.h"
+#include "nsAutoPtr.h"
+
+class nsProxyReleaseEvent : public nsRunnable
+{
+public:
+  explicit nsProxyReleaseEvent(nsISupports* aDoomed) : mDoomed(aDoomed) {}
+
+  NS_IMETHOD Run()
+  {
+    mDoomed->Release();
+    return NS_OK;
+  }
+
+private:
+  nsISupports* MOZ_OWNING_REF mDoomed;
+};
+
+nsresult
+NS_ProxyRelease(nsIEventTarget* aTarget, nsISupports* aDoomed,
+                bool aAlwaysProxy)
+{
+  nsresult rv;
+
+  if (!aDoomed) {
+    // nothing to do
+    return NS_OK;
+  }
+
+  if (!aTarget) {
+    NS_RELEASE(aDoomed);
+    return NS_OK;
+  }
+
+  if (!aAlwaysProxy) {
+    bool onCurrentThread = false;
+    rv = aTarget->IsOnCurrentThread(&onCurrentThread);
+    if (NS_SUCCEEDED(rv) && onCurrentThread) {
+      NS_RELEASE(aDoomed);
+      return NS_OK;
+    }
+  }
+
+  nsCOMPtr<nsIRunnable> ev = new nsProxyReleaseEvent(aDoomed);
+  if (!ev) {
+    // we do not release aDoomed here since it may cause a delete on the
+    // wrong thread.  better to leak than crash.
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  rv = aTarget->Dispatch(ev, NS_DISPATCH_NORMAL);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("failed to post proxy release event");
+    // again, it is better to leak the aDoomed object than risk crashing as
+    // a result of deleting it on the wrong thread.
+  }
+  return rv;
+}
--- a/xpcom/glue/nsProxyRelease.h
+++ b/xpcom/glue/nsProxyRelease.h
@@ -7,116 +7,123 @@
 #ifndef nsProxyRelease_h__
 #define nsProxyRelease_h__
 
 #include "nsIEventTarget.h"
 #include "nsIThread.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "MainThreadUtils.h"
-#include "nsThreadUtils.h"
 #include "mozilla/Likely.h"
-#include "mozilla/Move.h"
 
 #ifdef XPCOM_GLUE_AVOID_NSPR
 #error NS_ProxyRelease implementation depends on NSPR.
 #endif
 
-
+/**
+ * Ensure that a nsCOMPtr is released on the target thread.
+ *
+ * @see NS_ProxyRelease(nsIEventTarget*, nsISupports*, bool)
+ */
 template<class T>
-class nsProxyReleaseEvent : public nsRunnable
+inline NS_HIDDEN_(nsresult)
+NS_ProxyRelease(nsIEventTarget* aTarget, nsCOMPtr<T>& aDoomed,
+                bool aAlwaysProxy = false)
 {
-public:
-  explicit nsProxyReleaseEvent(already_AddRefed<T> aDoomed)
-  : mDoomed(aDoomed.take()) {}
-
-  NS_IMETHOD Run()
-  {
-    NS_IF_RELEASE(mDoomed);
-    return NS_OK;
-  }
-
-private:
-  T* MOZ_OWNING_REF mDoomed;
-};
+  T* raw = nullptr;
+  aDoomed.swap(raw);
+  return NS_ProxyRelease(aTarget, raw, aAlwaysProxy);
+}
 
 /**
- * Ensures that the delete of a smart pointer occurs on the target thread.
+ * Ensure that a nsRefPtr is released on the target thread.
+ *
+ * @see NS_ProxyRelease(nsIEventTarget*, nsISupports*, bool)
+ */
+template<class T>
+inline NS_HIDDEN_(nsresult)
+NS_ProxyRelease(nsIEventTarget* aTarget, RefPtr<T>& aDoomed,
+                bool aAlwaysProxy = false)
+{
+  T* raw = nullptr;
+  aDoomed.swap(raw);
+  return NS_ProxyRelease(aTarget, raw, aAlwaysProxy);
+}
+
+/**
+ * Ensures that the delete of a nsISupports object occurs on the target thread.
  *
  * @param aTarget
  *        the target thread where the doomed object should be released.
  * @param aDoomed
  *        the doomed object; the object to be released on the target thread.
  * @param aAlwaysProxy
  *        normally, if NS_ProxyRelease is called on the target thread, then the
  *        doomed object will be released directly. However, if this parameter is
  *        true, then an event will always be posted to the target thread for
  *        asynchronous release.
  */
-template<class T>
-inline NS_HIDDEN_(void)
-NS_ProxyRelease(nsIEventTarget* aTarget, already_AddRefed<T> aDoomed,
-                bool aAlwaysProxy = false)
-{
-  // Auto-managing release of the pointer.
-  RefPtr<T> doomed = aDoomed;
-  nsresult rv;
-
-  if (!doomed || !aTarget) {
-    return;
-  }
+nsresult
+NS_ProxyRelease(nsIEventTarget* aTarget, nsISupports* aDoomed,
+                bool aAlwaysProxy = false);
 
-  if (!aAlwaysProxy) {
-    bool onCurrentThread = false;
-    rv = aTarget->IsOnCurrentThread(&onCurrentThread);
-    if (NS_SUCCEEDED(rv) && onCurrentThread) {
-      return;
-    }
-  }
-
-  nsCOMPtr<nsIRunnable> ev = new nsProxyReleaseEvent<T>(doomed.forget());
-
-  rv = aTarget->Dispatch(ev, NS_DISPATCH_NORMAL);
-  if (NS_FAILED(rv)) {
-    NS_WARNING("failed to post proxy release event, leaking!");
-    // It is better to leak the aDoomed object than risk crashing as
-    // a result of deleting it on the wrong thread.
-  }
+/**
+ * Ensure that a nsCOMPtr is released on the main thread.
+ *
+ * @see NS_ReleaseOnMainThread( nsISupports*, bool)
+ */
+template<class T>
+inline NS_HIDDEN_(nsresult)
+NS_ReleaseOnMainThread(nsCOMPtr<T>& aDoomed,
+                       bool aAlwaysProxy = false)
+{
+  T* raw = nullptr;
+  aDoomed.swap(raw);
+  return NS_ReleaseOnMainThread(raw, aAlwaysProxy);
 }
 
 /**
- * Ensures that the delete of a smart pointer occurs on the main thread.
+ * Ensure that a nsRefPtr is released on the main thread.
+ *
+ * @see NS_ReleaseOnMainThread(nsISupports*, bool)
+ */
+template<class T>
+inline NS_HIDDEN_(nsresult)
+NS_ReleaseOnMainThread(RefPtr<T>& aDoomed,
+                       bool aAlwaysProxy = false)
+{
+  T* raw = nullptr;
+  aDoomed.swap(raw);
+  return NS_ReleaseOnMainThread(raw, aAlwaysProxy);
+}
+
+/**
+ * Ensures that the delete of a nsISupports object occurs on the main thread.
  *
  * @param aDoomed
  *        the doomed object; the object to be released on the main thread.
  * @param aAlwaysProxy
  *        normally, if NS_ReleaseOnMainThread is called on the main thread,
  *        then the doomed object will be released directly. However, if this
  *        parameter is true, then an event will always be posted to the main
  *        thread for asynchronous release.
  */
-template<class T>
-inline NS_HIDDEN_(void)
-NS_ReleaseOnMainThread(already_AddRefed<T> aDoomed,
+inline nsresult
+NS_ReleaseOnMainThread(nsISupports* aDoomed,
                        bool aAlwaysProxy = false)
 {
   // NS_ProxyRelease treats a null event target as "the current thread".  So a
   // handle on the main thread is only necessary when we're not already on the
   // main thread or the release must happen asynchronously.
   nsCOMPtr<nsIThread> mainThread;
   if (!NS_IsMainThread() || aAlwaysProxy) {
-    nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
-
-    if (NS_FAILED(rv)) {
-      NS_WARNING("Could not get main thread! Leaking.");
-      return;
-    }
+    NS_GetMainThread(getter_AddRefs(mainThread));
   }
 
-  NS_ProxyRelease(mainThread, mozilla::Move(aDoomed), aAlwaysProxy);
+  return NS_ProxyRelease(mainThread, aDoomed, aAlwaysProxy);
 }
 
 /**
  * Class to safely handle main-thread-only pointers off the main thread.
  *
  * Classes like XPCWrappedJS are main-thread-only, which means that it is
  * forbidden to call methods on instances of these classes off the main thread.
  * For various reasons (see bug 771074), this restriction recently began to
@@ -173,17 +180,23 @@ public:
 
 private:
   // We can be released on any thread.
   ~nsMainThreadPtrHolder()
   {
     if (NS_IsMainThread()) {
       NS_IF_RELEASE(mRawPtr);
     } else if (mRawPtr) {
-      NS_ReleaseOnMainThread(dont_AddRef(mRawPtr));
+      nsCOMPtr<nsIThread> mainThread;
+      NS_GetMainThread(getter_AddRefs(mainThread));
+      if (!mainThread) {
+        NS_WARNING("Couldn't get main thread! Leaking pointer.");
+        return;
+      }
+      NS_ProxyRelease(mainThread, mRawPtr);
     }
   }
 
 public:
   T* get()
   {
     // Nobody should be touching the raw pointer off-main-thread.
     if (mStrict && MOZ_UNLIKELY(!NS_IsMainThread())) {
--- a/xpcom/glue/objs.mozbuild
+++ b/xpcom/glue/objs.mozbuild
@@ -34,14 +34,15 @@ xpcom_glue_src_lcppsrcs = [
 
 xpcom_glue_src_cppsrcs = [
     '/xpcom/glue/%s' % s for s in xpcom_glue_src_lcppsrcs
 ]
 
 xpcom_gluens_src_lcppsrcs = [
     'BlockingResourceBase.cpp',
     'GenericFactory.cpp',
+    'nsProxyRelease.cpp',
     'nsTextFormatter.cpp',
 ]
 
 xpcom_gluens_src_cppsrcs = [
     '/xpcom/glue/%s' % s for s in xpcom_gluens_src_lcppsrcs
 ]
--- a/xpcom/libxpcomrt/moz.build
+++ b/xpcom/libxpcomrt/moz.build
@@ -56,16 +56,17 @@ xpcom_glue_src = [
     'nsCOMPtr.cpp',
     'nsCRTGlue.cpp',
     'nsComponentManagerUtils.cpp',
     'nsEnumeratorUtils.cpp',
     'GenericFactory.cpp',
     'nsID.cpp',
     'nsISupportsImpl.cpp',
     'nsMemory.cpp',
+    'nsProxyRelease.cpp',
     'nsQuickSort.cpp',
     'nsTArray.cpp',
     'nsTObserverArray.cpp',
     'nsThreadUtils.cpp',
     'nsWeakReference.cpp',
     'PLDHashTable.cpp',
 ]
 src_list += [