Bug 1412726: Clean up XPCOM singleton constructor refcount handling. r?froydnj draft
authorKris Maglione <maglione.k@gmail.com>
Sun, 29 Oct 2017 16:02:40 -0700
changeset 688385 e56275f3212b06590cf4c52981679c8fe40ded66
parent 688384 f584176734b9390854147d5c1c6200f482557d41
child 738056 0ec23cd0bb739e5d5c99edd02f40b9b08d3f2298
push id86734
push usermaglione.k@gmail.com
push dateMon, 30 Oct 2017 01:46:21 +0000
reviewersfroydnj
bugs1412726, 1409249
milestone58.0a1
Bug 1412726: Clean up XPCOM singleton constructor refcount handling. r?froydnj This is a follow-up to bug 1409249. There are a lot of places where our factory singleton constructors either don't correctly handle their returned references being released by the component manager, or do handle it, but in ways that are not obvious. This patch handles a few places where we can sometimes wind up with dangling singleton pointers, adds some explanatory comments and sanity check assertions, and replaces some uses of manual refcounting with StaticRefPtr and ClearOnShutdown. There are still some places where we may wind up with odd behavior if the first QI for a getService call fails, and we wind up destroying the first instance of a service that we create, and re-creating a new one later. MozReview-Commit-ID: ANYndvd7aZx
dom/flyweb/FlyWebService.cpp
dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
dom/media/webspeech/synth/nsSynthVoiceRegistry.h
dom/media/webspeech/synth/speechd/SpeechDispatcherModule.cpp
dom/media/webspeech/synth/speechd/SpeechDispatcherService.cpp
dom/media/webspeech/synth/speechd/SpeechDispatcherService.h
dom/plugins/base/nsPluginHost.cpp
dom/plugins/base/nsPluginHost.h
dom/workers/WorkerDebuggerManager.cpp
extensions/cookie/nsPermissionManager.cpp
js/xpconnect/src/nsXPConnect.cpp
layout/build/nsLayoutStatics.cpp
modules/libjar/nsJARChannel.cpp
modules/libjar/nsJARChannel.h
modules/libjar/nsJARFactory.cpp
modules/libjar/nsJARProtocolHandler.cpp
modules/libjar/nsJARProtocolHandler.h
netwerk/base/nsIOService.cpp
netwerk/dns/ChildDNSService.cpp
netwerk/dns/nsDNSService2.cpp
storage/VacuumManager.cpp
storage/mozStorageService.cpp
toolkit/components/downloads/ApplicationReputation.cpp
toolkit/components/downloads/nsDownloadManager.cpp
toolkit/components/places/History.cpp
toolkit/mozapps/extensions/AddonPathService.cpp
uriloader/prefetch/nsOfflineCacheUpdateService.cpp
--- a/dom/flyweb/FlyWebService.cpp
+++ b/dom/flyweb/FlyWebService.cpp
@@ -916,21 +916,22 @@ FlyWebService::GetExisting()
   return gFlyWebService;
 }
 
 FlyWebService*
 FlyWebService::GetOrCreate()
 {
   if (!gFlyWebService) {
     gFlyWebService = new FlyWebService();
-    ClearOnShutdown(&gFlyWebService);
     ErrorResult rv = gFlyWebService->Init();
-    if (rv.Failed()) {
+    if (NS_WARN_IF(rv.Failed())) {
+      rv.SuppressException();
       gFlyWebService = nullptr;
-      return nullptr;
+    } else {
+      ClearOnShutdown(&gFlyWebService);
     }
   }
   return gFlyWebService;
 }
 
 ErrorResult
 FlyWebService::Init()
 {
--- a/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
+++ b/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp
@@ -11,16 +11,17 @@
 #include "MediaPrefs.h"
 #include "SpeechSynthesisUtterance.h"
 #include "SpeechSynthesisVoice.h"
 #include "nsSynthVoiceRegistry.h"
 #include "nsSpeechTask.h"
 #include "AudioChannelService.h"
 
 #include "nsString.h"
+#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/intl/LocaleService.h"
 #include "mozilla/Unused.h"
 
 #include "SpeechSynthesisChild.h"
 #include "SpeechSynthesisParent.h"
@@ -166,16 +167,17 @@ nsSynthVoiceRegistry::~nsSynthVoiceRegis
 
 nsSynthVoiceRegistry*
 nsSynthVoiceRegistry::GetInstance()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!gSynthVoiceRegistry) {
     gSynthVoiceRegistry = new nsSynthVoiceRegistry();
+    ClearOnShutdown(&gSynthVoiceRegistry);
     if (XRE_IsParentProcess()) {
       // Start up all speech synth services.
       NS_CreateServicesFromCategory(NS_SPEECH_SYNTH_STARTED, nullptr,
         NS_SPEECH_SYNTH_STARTED);
     }
   }
 
   return gSynthVoiceRegistry;
@@ -184,24 +186,16 @@ nsSynthVoiceRegistry::GetInstance()
 already_AddRefed<nsSynthVoiceRegistry>
 nsSynthVoiceRegistry::GetInstanceForService()
 {
   RefPtr<nsSynthVoiceRegistry> registry = GetInstance();
 
   return registry.forget();
 }
 
-void
-nsSynthVoiceRegistry::Shutdown()
-{
-  LOG(LogLevel::Debug, ("[%s] nsSynthVoiceRegistry::Shutdown()",
-                        (XRE_IsContentProcess()) ? "Content" : "Default"));
-  gSynthVoiceRegistry = nullptr;
-}
-
 bool
 nsSynthVoiceRegistry::SendInitialVoicesAndState(SpeechSynthesisParent* aParent)
 {
   MOZ_ASSERT(XRE_IsParentProcess());
 
   InfallibleTArray<RemoteVoice> voices;
   InfallibleTArray<nsString> defaults;
 
--- a/dom/media/webspeech/synth/nsSynthVoiceRegistry.h
+++ b/dom/media/webspeech/synth/nsSynthVoiceRegistry.h
@@ -63,18 +63,16 @@ public:
   static void RecvAddVoice(const RemoteVoice& aVoice);
 
   static void RecvSetDefaultVoice(const nsAString& aUri, bool aIsDefault);
 
   static void RecvIsSpeakingChanged(bool aIsSpeaking);
 
   static void RecvNotifyVoicesChanged();
 
-  static void Shutdown();
-
 private:
   virtual ~nsSynthVoiceRegistry();
 
   VoiceData* FindBestMatch(const nsAString& aUri, const nsAString& lang);
 
   bool FindVoiceByLang(const nsAString& aLang, VoiceData** aRetval);
 
   nsresult AddVoiceImpl(nsISpeechService* aService,
--- a/dom/media/webspeech/synth/speechd/SpeechDispatcherModule.cpp
+++ b/dom/media/webspeech/synth/speechd/SpeechDispatcherModule.cpp
@@ -32,25 +32,19 @@ static const mozilla::Module::ContractID
   { nullptr }
 };
 
 static const mozilla::Module::CategoryEntry kCategories[] = {
   { "speech-synth-started", "SpeechDispatcher Speech Synth", SPEECHDISPATCHERSERVICE_CONTRACTID },
   { nullptr }
 };
 
-static void
-UnloadSpeechDispatcherModule()
-{
-  SpeechDispatcherService::Shutdown();
-}
-
 static const mozilla::Module kModule = {
   mozilla::Module::kVersion,
   kCIDs,
   kContracts,
   kCategories,
   nullptr,
   nullptr,
-  UnloadSpeechDispatcherModule
+  nullptr,
 };
 
 NSMODULE_DEFN(synthspeechdispatcher) = &kModule;
--- a/dom/media/webspeech/synth/speechd/SpeechDispatcherService.cpp
+++ b/dom/media/webspeech/synth/speechd/SpeechDispatcherService.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "SpeechDispatcherService.h"
 
 #include "mozilla/dom/nsSpeechTask.h"
 #include "mozilla/dom/nsSynthVoiceRegistry.h"
+#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Preferences.h"
 #include "nsEscape.h"
 #include "nsISupports.h"
 #include "nsPrintfCString.h"
 #include "nsReadableUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "prlink.h"
@@ -563,16 +564,17 @@ SpeechDispatcherService::GetInstance(boo
     MOZ_ASSERT(false,
                "SpeechDispatcherService can only be started on main gecko process");
     return nullptr;
   }
 
   if (!sSingleton && create) {
     sSingleton = new SpeechDispatcherService();
     sSingleton->Init();
+    ClearOnShutdown(&sSingleton);
   }
 
   return sSingleton;
 }
 
 already_AddRefed<SpeechDispatcherService>
 SpeechDispatcherService::GetInstanceForService()
 {
@@ -588,20 +590,10 @@ SpeechDispatcherService::EventNotify(uin
 
   if (callback) {
     if (callback->OnSpeechEvent((SPDNotificationType)aState)) {
       mCallbacks.Remove(aMsgId);
     }
   }
 }
 
-void
-SpeechDispatcherService::Shutdown()
-{
-  if (!sSingleton) {
-    return;
-  }
-
-  sSingleton = nullptr;
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/webspeech/synth/speechd/SpeechDispatcherService.h
+++ b/dom/media/webspeech/synth/speechd/SpeechDispatcherService.h
@@ -37,18 +37,16 @@ public:
 
   void Setup();
 
   void EventNotify(uint32_t aMsgId, uint32_t aState);
 
   static SpeechDispatcherService* GetInstance(bool create = true);
   static already_AddRefed<SpeechDispatcherService> GetInstanceForService();
 
-  static void Shutdown();
-
   static StaticRefPtr<SpeechDispatcherService> sSingleton;
 
 private:
   virtual ~SpeechDispatcherService();
 
   void RegisterVoices();
 
   bool mInitialized;
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -47,16 +47,17 @@
 #include "nsVersionComparator.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsIWritablePropertyBag2.h"
 #include "nsICategoryManager.h"
 #include "nsPluginStreamListenerPeer.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/FakePluginTagInitBinding.h"
+#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/plugins/PluginTypes.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ipc/URIUtils.h"
 
 #include "nsEnumeratorUtils.h"
 #include "nsXPCOM.h"
@@ -148,17 +149,17 @@ LazyLogModule nsPluginLogging::gPluginLo
 
 // #defines for plugin cache and prefs
 #define NS_PREF_MAX_NUM_CACHED_INSTANCES "browser.plugins.max_num_cached_plugins"
 // Raise this from '10' to '50' to work around a bug in Apple's current Java
 // plugins on OS X Lion and SnowLeopard.  See bug 705931.
 #define DEFAULT_NUMBER_OF_STOPPED_INSTANCES 50
 
 nsIFile *nsPluginHost::sPluginTempDir;
-nsPluginHost *nsPluginHost::sInst;
+StaticRefPtr<nsPluginHost> nsPluginHost::sInst;
 
 /* to cope with short read */
 /* we should probably put this into a global library now that this is the second
    time we need this. */
 static
 int32_t
 busy_beaver_PR_Read(PRFileDesc *fd, void * start, int32_t len)
 {
@@ -294,38 +295,34 @@ nsPluginHost::nsPluginHost()
   }
 }
 
 nsPluginHost::~nsPluginHost()
 {
   PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::dtor\n"));
 
   UnloadPlugins();
-  sInst = nullptr;
 }
 
 NS_IMPL_ISUPPORTS(nsPluginHost,
                   nsIPluginHost,
                   nsIObserver,
                   nsITimerCallback,
                   nsISupportsWeakReference,
                   nsINamed)
 
 already_AddRefed<nsPluginHost>
 nsPluginHost::GetInst()
 {
   if (!sInst) {
     sInst = new nsPluginHost();
-    if (!sInst)
-      return nullptr;
-    NS_ADDREF(sInst);
+    ClearOnShutdown(&sInst);
   }
 
-  RefPtr<nsPluginHost> inst = sInst;
-  return inst.forget();
+  return do_AddRef(sInst);
 }
 
 bool nsPluginHost::IsRunningPlugin(nsPluginTag * aPluginTag)
 {
   if (!aPluginTag || !aPluginTag->mPlugin) {
     return false;
   }
 
@@ -3402,17 +3399,16 @@ void nsPluginHost::CreateWidget(nsPlugin
 }
 
 NS_IMETHODIMP nsPluginHost::Observe(nsISupports *aSubject,
                                     const char *aTopic,
                                     const char16_t *someData)
 {
   if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
     UnloadPlugins();
-    sInst->Release();
   }
   if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
     mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
     // Unload or load plugins as needed
     if (mPluginsDisabled) {
       UnloadPlugins();
     } else {
       LoadPlugins();
--- a/dom/plugins/base/nsPluginHost.h
+++ b/dom/plugins/base/nsPluginHost.h
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsPluginHost_h_
 #define nsPluginHost_h_
 
 #include "mozilla/LinkedList.h"
+#include "mozilla/StaticPtr.h"
 
 #include "nsIPluginHost.h"
 #include "nsIObserver.h"
 #include "nsCOMPtr.h"
 #include "prlink.h"
 #include "nsIPluginTag.h"
 #include "nsPluginsDir.h"
 #include "nsPluginDirServiceProvider.h"
@@ -410,17 +411,17 @@ private:
   // In the content process, this stores the last epoch value observed
   // when reading plugins from chrome.
   uint32_t mPluginEpoch;
 
   static nsIFile *sPluginTempDir;
 
   // We need to hold a global ptr to ourselves because we register for
   // two different CIDs for some reason...
-  static nsPluginHost* sInst;
+  static mozilla::StaticRefPtr<nsPluginHost> sInst;
 };
 
 class PluginDestructionGuard : public mozilla::LinkedListElement<PluginDestructionGuard>
 {
 public:
   explicit PluginDestructionGuard(nsNPAPIPluginInstance *aInstance);
   explicit PluginDestructionGuard(NPP npp);
 
--- a/dom/workers/WorkerDebuggerManager.cpp
+++ b/dom/workers/WorkerDebuggerManager.cpp
@@ -4,16 +4,17 @@
  * 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 "WorkerDebuggerManager.h"
 
 #include "nsISimpleEnumerator.h"
 
 #include "mozilla/ClearOnShutdown.h"
+#include "mozilla/StaticPtr.h"
 
 #include "WorkerPrivate.h"
 
 USING_WORKERS_NAMESPACE
 
 namespace {
 
 class RegisterDebuggerMainThreadRunnable final : public mozilla::Runnable
@@ -64,18 +65,17 @@ private:
     WorkerDebuggerManager* manager = WorkerDebuggerManager::Get();
     MOZ_ASSERT(manager);
 
     manager->UnregisterDebuggerMainThread(mWorkerPrivate);
     return NS_OK;
   }
 };
 
-// Does not hold an owning reference.
-static WorkerDebuggerManager* gWorkerDebuggerManager;
+static StaticRefPtr<WorkerDebuggerManager> gWorkerDebuggerManager;
 
 } /* anonymous namespace */
 
 BEGIN_WORKERS_NAMESPACE
 
 class WorkerDebuggerEnumerator final : public nsISimpleEnumerator
 {
   nsTArray<RefPtr<WorkerDebugger>> mDebuggers;
@@ -138,20 +138,21 @@ WorkerDebuggerManager::GetInstance()
 WorkerDebuggerManager*
 WorkerDebuggerManager::GetOrCreate()
 {
   AssertIsOnMainThread();
 
   if (!gWorkerDebuggerManager) {
     // The observer service now owns us until shutdown.
     gWorkerDebuggerManager = new WorkerDebuggerManager();
-    if (NS_FAILED(gWorkerDebuggerManager->Init())) {
+    if (NS_SUCCEEDED(gWorkerDebuggerManager->Init())) {
+      ClearOnShutdown(&gWorkerDebuggerManager);
+    } else {
       NS_WARNING("Failed to initialize worker debugger manager!");
       gWorkerDebuggerManager = nullptr;
-      return nullptr;
     }
   }
 
   return gWorkerDebuggerManager;
 }
 
 WorkerDebuggerManager*
 WorkerDebuggerManager::Get()
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -917,17 +917,20 @@ nsPermissionManager::~nsPermissionManage
   for (auto iter = mPermissionKeyPromiseMap.Iter(); !iter.Done(); iter.Next()) {
     if (iter.Data()) {
       iter.Data()->Reject(NS_ERROR_FAILURE, __func__);
     }
   }
   mPermissionKeyPromiseMap.Clear();
 
   RemoveAllFromMemory();
-  gPermissionManager = nullptr;
+  if (gPermissionManager) {
+    MOZ_ASSERT(gPermissionManager == this);
+    gPermissionManager = nullptr;
+  }
 }
 
 // static
 already_AddRefed<nsIPermissionManager>
 nsPermissionManager::GetXPCOMSingleton()
 {
   if (gPermissionManager) {
     return do_AddRef(gPermissionManager);
@@ -936,21 +939,22 @@ nsPermissionManager::GetXPCOMSingleton()
   // Create a new singleton nsPermissionManager.
   // We AddRef only once since XPCOM has rules about the ordering of module
   // teardowns - by the time our module destructor is called, it's too late to
   // Release our members, since GC cycles have already been completed and
   // would result in serious leaks.
   // See bug 209571.
   auto permManager = MakeRefPtr<nsPermissionManager>();
   if (NS_SUCCEEDED(permManager->Init())) {
+    // Note: This is cleared in the nsPermissionManager destructor.
     gPermissionManager = permManager.get();
     return permManager.forget();
   }
 
-  return nullptr;;
+  return nullptr;
 }
 
 nsresult
 nsPermissionManager::Init()
 {
   // If the 'permissions.memory_only' pref is set to true, then don't write any
   // permission settings to disk, but keep them in a memory-only database.
   mMemoryOnlyDB = mozilla::Preferences::GetBool("permissions.memory_only", false);
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -105,16 +105,17 @@ nsXPConnect::~nsXPConnect()
     NS_RELEASE(gSystemPrincipal);
     gScriptSecurityManager = nullptr;
 
     // shutdown the logging system
     XPC_LOG_FINISH();
 
     delete gPrimaryContext;
 
+    MOZ_ASSERT(gSelf == this);
     gSelf = nullptr;
     gOnceAliveNowDead = true;
 }
 
 // static
 void
 nsXPConnect::InitStatics()
 {
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -83,20 +83,16 @@
 #include "nsXULPrototypeCache.h"
 #include "nsXULTooltipListener.h"
 
 #include "inDOMView.h"
 
 #include "nsMenuBarListener.h"
 #endif
 
-#ifdef MOZ_WEBSPEECH
-#include "nsSynthVoiceRegistry.h"
-#endif
-
 #include "CubebUtils.h"
 #include "Latency.h"
 #include "WebAudioUtils.h"
 
 #include "nsError.h"
 
 #include "nsJSEnvironment.h"
 #include "nsContentSink.h"
@@ -387,20 +383,16 @@ nsLayoutStatics::Shutdown()
   nsXBLService::Shutdown();
   nsAutoCopyListener::Shutdown();
   FrameLayerBuilder::Shutdown();
 
   CubebUtils::ShutdownLibrary();
   AsyncLatencyLogger::ShutdownLogger();
   WebAudioUtils::Shutdown();
 
-#ifdef MOZ_WEBSPEECH
-  nsSynthVoiceRegistry::Shutdown();
-#endif
-
   nsCORSListenerProxy::Shutdown();
 
   PointerEventHandler::ReleaseStatics();
 
   TouchManager::ReleaseStatics();
 
   nsTreeSanitizer::ReleaseStatics();
 
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -199,27 +199,23 @@ nsJARChannel::nsJARChannel()
     , mStatus(NS_OK)
     , mIsPending(false)
     , mIsUnsafe(true)
     , mBlockRemoteFiles(false)
 {
     mBlockRemoteFiles = Preferences::GetBool("network.jar.block-remote-files", false);
 
     // hold an owning reference to the jar handler
-    NS_ADDREF(gJarHandler);
+    mJarHandler = gJarHandler;
 }
 
 nsJARChannel::~nsJARChannel()
 {
     NS_ReleaseOnMainThreadSystemGroup("nsJARChannel::mLoadInfo",
                                       mLoadInfo.forget());
-
-    // release owning reference to the jar handler
-    nsJARProtocolHandler *handler = gJarHandler;
-    NS_RELEASE(handler); // nullptr parameter
 }
 
 NS_IMPL_ISUPPORTS_INHERITED(nsJARChannel,
                             nsHashPropertyBag,
                             nsIRequest,
                             nsIChannel,
                             nsIStreamListener,
                             nsIRequestObserver,
--- a/modules/libjar/nsJARChannel.h
+++ b/modules/libjar/nsJARChannel.h
@@ -21,16 +21,17 @@
 #include "nsHashPropertyBag.h"
 #include "nsIFile.h"
 #include "nsIURI.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "mozilla/Logging.h"
 
 class nsJARInputThunk;
+class nsJARProtocolHandler;
 class nsInputStreamPump;
 
 //-----------------------------------------------------------------------------
 
 class nsJARChannel final : public nsIJARChannel
                          , public mozilla::net::MemoryDownloader::IObserver
                          , public nsIStreamListener
                          , public nsIThreadRetargetableRequest
@@ -67,16 +68,17 @@ private:
                                     nsresult aStatus,
                                     mozilla::net::MemoryDownloader::Data aData)
         override;
 
     nsCString                       mSpec;
 
     bool                            mOpened;
 
+    RefPtr<nsJARProtocolHandler>    mJarHandler;
     nsCOMPtr<nsIJARURI>             mJarURI;
     nsCOMPtr<nsIURI>                mOriginalURI;
     nsCOMPtr<nsISupports>           mOwner;
     nsCOMPtr<nsILoadInfo>           mLoadInfo;
     nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
     nsCOMPtr<nsISupports>           mSecurityInfo;
     nsCOMPtr<nsIProgressEventSink>  mProgressSink;
     nsCOMPtr<nsILoadGroup>          mLoadGroup;
--- a/modules/libjar/nsJARFactory.cpp
+++ b/modules/libjar/nsJARFactory.cpp
@@ -37,28 +37,19 @@ static const mozilla::Module::CIDEntry k
 
 static const mozilla::Module::ContractIDEntry kJARContracts[] = {
     { "@mozilla.org/libjar/zip-reader;1", &kNS_ZIPREADER_CID },
     { "@mozilla.org/libjar/zip-reader-cache;1", &kNS_ZIPREADERCACHE_CID },
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "jar", &kNS_JARPROTOCOLHANDLER_CID },
     { nullptr }
 };
 
-// Jar module shutdown hook
-static void nsJarShutdown()
-{
-    // Make sure to not null out gJarHandler here, because we may have
-    // still-live nsJARChannels that will want to release it.
-    nsJARProtocolHandler *handler = gJarHandler;
-    NS_IF_RELEASE(handler);
-}
-
 static const mozilla::Module kJARModule = {
     mozilla::Module::kVersion,
     kJARCIDs,
     kJARContracts,
     nullptr,
     nullptr,
     nullptr,
-    nsJarShutdown
+    nullptr
 };
 
 NSMODULE_DEFN(nsJarModule) = &kJARModule;
--- a/modules/libjar/nsJARProtocolHandler.cpp
+++ b/modules/libjar/nsJARProtocolHandler.cpp
@@ -1,13 +1,14 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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/ClearOnShutdown.h"
 #include "nsAutoPtr.h"
 #include "nsJARProtocolHandler.h"
 #include "nsIIOService.h"
 #include "nsCRT.h"
 #include "nsIComponentManager.h"
 #include "nsIServiceManager.h"
 #include "nsJARURI.h"
 #include "nsIURL.h"
@@ -19,28 +20,25 @@
 #include "nsThreadUtils.h"
 
 static NS_DEFINE_CID(kZipReaderCacheCID, NS_ZIPREADERCACHE_CID);
 
 #define NS_JAR_CACHE_SIZE 32
 
 //-----------------------------------------------------------------------------
 
-nsJARProtocolHandler *gJarHandler = nullptr;
+StaticRefPtr<nsJARProtocolHandler> gJarHandler;
 
 nsJARProtocolHandler::nsJARProtocolHandler()
 {
     MOZ_ASSERT(NS_IsMainThread());
 }
 
 nsJARProtocolHandler::~nsJARProtocolHandler()
-{
-    MOZ_ASSERT(gJarHandler == this);
-    gJarHandler = nullptr;
-}
+{}
 
 nsresult
 nsJARProtocolHandler::Init()
 {
     nsresult rv;
 
     mJARCache = do_CreateInstance(kZipReaderCacheCID, &rv);
     if (NS_FAILED(rv)) return rv;
@@ -62,25 +60,22 @@ NS_IMPL_ISUPPORTS(nsJARProtocolHandler,
                   nsIJARProtocolHandler,
                   nsIProtocolHandler,
                   nsISupportsWeakReference)
 
 already_AddRefed<nsJARProtocolHandler>
 nsJARProtocolHandler::GetSingleton()
 {
     if (!gJarHandler) {
-        auto jar = MakeRefPtr<nsJARProtocolHandler>();
-        gJarHandler = jar.get();
-        if (NS_FAILED(jar->Init())) {
+        gJarHandler = new nsJARProtocolHandler();
+        if (NS_SUCCEEDED(gJarHandler->Init())) {
+            ClearOnShutdown(&gJarHandler);
+        } else {
             gJarHandler = nullptr;
-            return nullptr;
         }
-        // We release this reference on module shutdown.
-        NS_ADDREF(gJarHandler);
-        return jar.forget();
     }
     return do_AddRef(gJarHandler);
 }
 
 NS_IMETHODIMP
 nsJARProtocolHandler::GetJARCache(nsIZipReaderCache* *result)
 {
     *result = mJARCache;
--- a/modules/libjar/nsJARProtocolHandler.h
+++ b/modules/libjar/nsJARProtocolHandler.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 nsJARProtocolHandler_h__
 #define nsJARProtocolHandler_h__
 
+#include "mozilla/StaticPtr.h"
 #include "nsIJARProtocolHandler.h"
 #include "nsIProtocolHandler.h"
 #include "nsIJARURI.h"
 #include "nsIZipReader.h"
 #include "nsIMIMEService.h"
 #include "nsWeakReference.h"
 #include "nsCOMPtr.h"
 
@@ -34,17 +35,17 @@ public:
     nsIZipReaderCache *JarCache() { return mJARCache; }
 protected:
     virtual ~nsJARProtocolHandler();
 
     nsCOMPtr<nsIZipReaderCache> mJARCache;
     nsCOMPtr<nsIMIMEService> mMimeService;
 };
 
-extern nsJARProtocolHandler *gJarHandler;
+extern mozilla::StaticRefPtr<nsJARProtocolHandler> gJarHandler;
 
 #define NS_JARPROTOCOLHANDLER_CID                    \
 { /* 0xc7e410d4-0x85f2-11d3-9f63-006008a6efe9 */     \
     0xc7e410d4,                                      \
     0x85f2,                                          \
     0x11d3,                                          \
     {0x9f, 0x63, 0x00, 0x60, 0x08, 0xa6, 0xef, 0xe9} \
 }
--- a/netwerk/base/nsIOService.cpp
+++ b/netwerk/base/nsIOService.cpp
@@ -73,17 +73,17 @@ namespace net {
 // but the old names are still used to preserve backward compatibility.
 #define NECKO_BUFFER_CACHE_COUNT_PREF "network.buffer.cache.count"
 #define NECKO_BUFFER_CACHE_SIZE_PREF  "network.buffer.cache.size"
 #define NETWORK_NOTIFY_CHANGED_PREF   "network.notify.changed"
 #define NETWORK_CAPTIVE_PORTAL_PREF   "network.captive-portal-service.enabled"
 
 #define MAX_RECURSION_COUNT 50
 
-nsIOService* gIOService = nullptr;
+nsIOService* gIOService;
 static bool gHasWarnedUploadChannel2;
 static bool gCaptivePortalEnabled = false;
 static LazyLogModule gIOServiceLog("nsIOService");
 #undef LOG
 #define LOG(args)     MOZ_LOG(gIOServiceLog, LogLevel::Debug, args)
 
 // A general port blacklist.  Connections to these ports will not be allowed
 // unless the protocol overrides.
@@ -264,17 +264,20 @@ nsIOService::Init()
     SetOffline(false);
 
     return NS_OK;
 }
 
 
 nsIOService::~nsIOService()
 {
-    gIOService = nullptr;
+    if (gIOService) {
+        MOZ_ASSERT(gIOService == this);
+        gIOService = nullptr;
+    }
 }
 
 nsresult
 nsIOService::InitializeCaptivePortalService()
 {
     if (XRE_GetProcessType() != GeckoProcessType_Default) {
         // We only initalize a captive portal service in the main process
         return NS_OK;
@@ -350,23 +353,20 @@ nsIOService::InitializeProtocolProxyServ
 
     return rv;
 }
 
 already_AddRefed<nsIOService>
 nsIOService::GetInstance() {
     if (!gIOService) {
         RefPtr<nsIOService> ios = new nsIOService();
-        gIOService = ios.get();
-        if (NS_FAILED(ios->Init())) {
-            gIOService = nullptr;
-            return nullptr;
+        if (NS_SUCCEEDED(ios->Init())) {
+            MOZ_ASSERT(gIOService == ios.get());
+            return ios.forget();
         }
-
-        return ios.forget();
     }
     return do_AddRef(gIOService);
 }
 
 NS_IMPL_ISUPPORTS(nsIOService,
                   nsIIOService,
                   nsIIOService2,
                   nsINetUtil,
--- a/netwerk/dns/ChildDNSService.cpp
+++ b/netwerk/dns/ChildDNSService.cpp
@@ -6,37 +6,40 @@
 #include "nsIDNSListener.h"
 #include "nsIIOService.h"
 #include "nsIThread.h"
 #include "nsThreadUtils.h"
 #include "nsIXPConnect.h"
 #include "nsIPrefService.h"
 #include "nsIProtocolProxyService.h"
 #include "nsNetCID.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/StaticPtr.h"
 #include "mozilla/SystemGroup.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/net/DNSListenerProxy.h"
 #include "nsServiceManagerUtils.h"
 
 namespace mozilla {
 namespace net {
 
 //-----------------------------------------------------------------------------
 // ChildDNSService
 //-----------------------------------------------------------------------------
 
-static ChildDNSService *gChildDNSService;
+static StaticRefPtr<ChildDNSService> gChildDNSService;
 static const char kPrefNameDisablePrefetch[] = "network.dns.disablePrefetch";
 
 already_AddRefed<ChildDNSService> ChildDNSService::GetSingleton()
 {
   MOZ_ASSERT(IsNeckoChild());
 
   if (!gChildDNSService) {
     gChildDNSService = new ChildDNSService();
+    ClearOnShutdown(&gChildDNSService);
   }
 
   return do_AddRef(gChildDNSService);
 }
 
 NS_IMPL_ISUPPORTS(ChildDNSService,
                   nsIDNSService,
                   nsPIDNSService,
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -29,20 +29,22 @@
 #include "nsIOService.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsNetAddr.h"
 #include "nsProxyRelease.h"
 #include "nsIObserverService.h"
 #include "nsINetworkLinkService.h"
 
 #include "mozilla/Attributes.h"
+#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/net/NeckoCommon.h"
 #include "mozilla/net/ChildDNSService.h"
 #include "mozilla/net/DNSListenerProxy.h"
 #include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
 
 using namespace mozilla;
 using namespace mozilla::net;
 
 static const char kPrefDnsCacheEntries[]     = "network.dnsCacheEntries";
 static const char kPrefDnsCacheExpiration[]  = "network.dnsCacheExpiration";
 static const char kPrefDnsCacheGrace[]       = "network.dnsCacheExpirationGracePeriod";
 static const char kPrefIPv4OnlyDomains[]     = "network.dns.ipv4OnlyDomains";
@@ -492,45 +494,43 @@ nsDNSService::~nsDNSService() = default;
 
 NS_IMPL_ISUPPORTS(nsDNSService, nsIDNSService, nsPIDNSService, nsIObserver,
                   nsIMemoryReporter)
 
 /******************************************************************************
  * nsDNSService impl:
  * singleton instance ctor/dtor methods
  ******************************************************************************/
-static nsDNSService *gDNSService;
+static StaticRefPtr<nsDNSService> gDNSService;
 
 already_AddRefed<nsIDNSService>
 nsDNSService::GetXPCOMSingleton()
 {
     if (IsNeckoChild()) {
         return ChildDNSService::GetSingleton();
     }
 
     return GetSingleton();
 }
 
 already_AddRefed<nsDNSService>
 nsDNSService::GetSingleton()
 {
     NS_ASSERTION(!IsNeckoChild(), "not a parent process");
 
-    if (gDNSService) {
-        return do_AddRef(gDNSService);
+    if (!gDNSService) {
+        gDNSService = new nsDNSService();
+        if (NS_SUCCEEDED(gDNSService->Init())) {
+            ClearOnShutdown(&gDNSService);
+        } else {
+            gDNSService = nullptr;
+        }
     }
 
-    auto dns = MakeRefPtr<nsDNSService>();
-    gDNSService = dns.get();
-    if (NS_FAILED(dns->Init())) {
-        gDNSService = nullptr;
-        return nullptr;
-    }
-
-    return dns.forget();
+    return do_AddRef(gDNSService);
 }
 
 NS_IMETHODIMP
 nsDNSService::Init()
 {
     if (mResolver)
         return NS_OK;
     NS_ENSURE_TRUE(!mResolver, NS_ERROR_ALREADY_INITIALIZED);
--- a/storage/VacuumManager.cpp
+++ b/storage/VacuumManager.cpp
@@ -315,17 +315,19 @@ already_AddRefed<VacuumManager>
 VacuumManager::getSingleton()
 {
   //Don't allocate it in the child Process.
   if (!XRE_IsParentProcess()) {
     return nullptr;
   }
 
   if (!gVacuumManager) {
-    gVacuumManager = new VacuumManager();
+    auto manager = MakeRefPtr<VacuumManager>();
+    MOZ_ASSERT(gVacuumManager == manager.get());
+    return manager.forget();
   }
   return do_AddRef(gVacuumManager);
 }
 
 VacuumManager::VacuumManager()
   : mParticipants("vacuum-participant")
 {
   MOZ_ASSERT(!gVacuumManager,
--- a/storage/mozStorageService.cpp
+++ b/storage/mozStorageService.cpp
@@ -217,23 +217,23 @@ Service::getSingleton()
     }
     MOZ_CRASH("SQLite Version Error");
   }
 
   // The first reference to the storage service must be obtained on the
   // main thread.
   NS_ENSURE_TRUE(NS_IsMainThread(), nullptr);
   RefPtr<Service> service = new Service();
-  gService = service.get();
-  if (NS_FAILED(service->initialize())) {
-    gService = nullptr;
-    return nullptr;
+  if (NS_SUCCEEDED(service->initialize())) {
+    // Note: This is cleared in the Service destructor.
+    gService = service.get();
+    return service.forget();
   }
 
-  return service.forget();
+  return nullptr;
 }
 
 nsIXPConnect *Service::sXPConnect = nullptr;
 
 // static
 already_AddRefed<nsIXPConnect>
 Service::getXPConnect()
 {
--- a/toolkit/components/downloads/ApplicationReputation.cpp
+++ b/toolkit/components/downloads/ApplicationReputation.cpp
@@ -1551,16 +1551,17 @@ NS_IMPL_ISUPPORTS(ApplicationReputationS
 
 ApplicationReputationService*
   ApplicationReputationService::gApplicationReputationService = nullptr;
 
 already_AddRefed<ApplicationReputationService>
 ApplicationReputationService::GetSingleton()
 {
   if (!gApplicationReputationService) {
+    // Note: This is cleared in the new ApplicationReputationService destructor.
     gApplicationReputationService = new ApplicationReputationService();
   }
   return do_AddRef(gApplicationReputationService);
 }
 
 ApplicationReputationService::ApplicationReputationService()
 {
   LOG(("Application reputation service started up"));
--- a/toolkit/components/downloads/nsDownloadManager.cpp
+++ b/toolkit/components/downloads/nsDownloadManager.cpp
@@ -28,26 +28,27 @@ nsDownloadManager *nsDownloadManager::gD
 already_AddRefed<nsDownloadManager>
 nsDownloadManager::GetSingleton()
 {
   if (gDownloadManagerService) {
     return do_AddRef(gDownloadManagerService);
   }
 
   auto serv = MakeRefPtr<nsDownloadManager>();
+  // Note: This is cleared in the nsDownloadManager constructor.
   gDownloadManagerService = serv.get();
-  if (NS_FAILED(serv->Init())) {
-    gDownloadManagerService = nullptr;
-    return nullptr;
+  if (NS_SUCCEEDED(serv->Init())) {
+    return serv.forget();
   }
-  return serv.forget();
+  return nullptr;
 }
 
 nsDownloadManager::~nsDownloadManager()
 {
+  MOZ_ASSERT(gDownloadManagerService == this);
   gDownloadManagerService = nullptr;
 }
 
 nsresult
 nsDownloadManager::Init()
 {
   nsresult rv;
 
--- a/toolkit/components/places/History.cpp
+++ b/toolkit/components/places/History.cpp
@@ -1936,16 +1936,17 @@ History::History()
     (void)os->AddObserver(this, TOPIC_PLACES_SHUTDOWN, false);
   }
 }
 
 History::~History()
 {
   UnregisterWeakMemoryReporter(this);
 
+  MOZ_ASSERT(gService == this);
   gService = nullptr;
 }
 
 void
 History::InitMemoryReporter()
 {
   RegisterWeakMemoryReporter(this);
 }
@@ -2420,19 +2421,20 @@ History::GetService()
   return gService;
 }
 
 /* static */
 already_AddRefed<History>
 History::GetSingleton()
 {
   if (!gService) {
-    gService = new History();
-    NS_ENSURE_TRUE(gService, nullptr);
-    gService->InitMemoryReporter();
+    RefPtr<History> svc = new History();
+    MOZ_ASSERT(gService == svc.get());
+    svc->InitMemoryReporter();
+    return svc.forget();
   }
 
   return do_AddRef(gService);
 }
 
 mozIStorageConnection*
 History::GetDBConn()
 {
--- a/toolkit/mozapps/extensions/AddonPathService.cpp
+++ b/toolkit/mozapps/extensions/AddonPathService.cpp
@@ -45,16 +45,17 @@ struct PathEntryComparator
 };
 
 AddonPathService::AddonPathService()
 {
 }
 
 AddonPathService::~AddonPathService()
 {
+  MOZ_ASSERT(sInstance == this);
   sInstance = nullptr;
 }
 
 NS_IMPL_ISUPPORTS(AddonPathService, amIAddonPathService)
 
 AddonPathService *AddonPathService::sInstance;
 
 /* static */ already_AddRefed<AddonPathService>
--- a/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
+++ b/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
@@ -251,16 +251,17 @@ nsOfflineCacheUpdateService::nsOfflineCa
     MOZ_ASSERT(NS_IsMainThread());
     Preferences::AddBoolVarCache(&sAllowOfflineCache,
                                  "browser.cache.offline.enable",
                                  true);
 }
 
 nsOfflineCacheUpdateService::~nsOfflineCacheUpdateService()
 {
+    MOZ_ASSERT(gOfflineCacheUpdateService == this);
     gOfflineCacheUpdateService = nullptr;
 }
 
 nsresult
 nsOfflineCacheUpdateService::Init()
 {
     // Observe xpcom-shutdown event
     nsCOMPtr<nsIObserverService> observerService =
@@ -292,21 +293,19 @@ nsOfflineCacheUpdateService::Init()
 }
 
 /* static */
 already_AddRefed<nsOfflineCacheUpdateService>
 nsOfflineCacheUpdateService::GetInstance()
 {
     if (!gOfflineCacheUpdateService) {
         auto serv = MakeRefPtr<nsOfflineCacheUpdateService>();
-        gOfflineCacheUpdateService = serv.get();
-        if (NS_FAILED(serv->Init())) {
-            gOfflineCacheUpdateService = nullptr;
-            return nullptr;
-        }
+        if (NS_FAILED(serv->Init()))
+            serv = nullptr;
+        MOZ_ASSERT(gOfflineCacheUpdateService == serv.get());
         return serv.forget();
     }
 
     return do_AddRef(gOfflineCacheUpdateService);
 }
 
 /* static */
 nsOfflineCacheUpdateService *