Bug 1477579: Part 3 - Avoid duplicating static strings in category manager entries. r?froydnj draft
authorKris Maglione <maglione.k@gmail.com>
Sat, 21 Jul 2018 22:39:42 -0700
changeset 821293 c7af0b00fe92fadfb47d161588fd7f307db74140
parent 821292 055b89d30b88312c0f50e771b46b35c1774ccc8f
push id117051
push usermaglione.k@gmail.com
push dateSun, 22 Jul 2018 18:01:56 +0000
reviewersfroydnj
bugs1477579
milestone63.0a1
Bug 1477579: Part 3 - Avoid duplicating static strings in category manager entries. r?froydnj Much like the component manager, many of the strings that we use for category manager entries are statically allocated. There's no need to duplicate these strings. This patch changes the category manager APIs to take nsACStrings rather than raw pointers, and to pass literal nsCStrings when we know we have a literal string to begin with. When adding the category entry, it then skips making copies of any strings with the LITERAL flag. MozReview-Commit-ID: EJEcYSdNMWs
dom/base/nsContentUtils.cpp
dom/base/nsDocument.cpp
dom/plugins/base/nsPluginHost.cpp
dom/push/PushNotifier.cpp
gfx/thebes/gfxSVGGlyphs.cpp
image/SVGDocumentWrapper.cpp
layout/base/nsStyleSheetService.cpp
toolkit/components/commandlines/nsCommandLine.cpp
toolkit/xre/nsAppStartupNotifier.cpp
uriloader/base/nsURILoader.cpp
uriloader/exthandler/nsExternalHelperAppService.cpp
xpcom/components/nsCategoryCache.cpp
xpcom/components/nsCategoryCache.h
xpcom/components/nsCategoryManager.cpp
xpcom/components/nsCategoryManager.h
xpcom/components/nsComponentManager.cpp
xpcom/components/nsICategoryManager.idl
xpcom/components/nsServiceManagerUtils.h
xpcom/io/nsDirectoryService.cpp
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -6947,18 +6947,17 @@ nsContentUtils::FindInternalContentViewe
   nsCOMPtr<nsICategoryManager> catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
   if (!catMan)
     return nullptr;
 
   nsCOMPtr<nsIDocumentLoaderFactory> docFactory;
 
   nsCString contractID;
   nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers",
-                                         PromiseFlatCString(aType).get(),
-                                         getter_Copies(contractID));
+                                         aType, contractID);
   if (NS_SUCCEEDED(rv)) {
     docFactory = do_GetService(contractID.get());
     if (docFactory && aLoaderType) {
       if (contractID.EqualsLiteral(CONTENT_DLF_CONTRACTID))
         *aLoaderType = TYPE_CONTENT;
       else if (contractID.EqualsLiteral(PLUGIN_DLF_CONTRACTID))
         *aLoaderType = TYPE_PLUGIN;
       else
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1083,18 +1083,18 @@ nsExternalResourceMap::PendingLoad::Setu
     new LoadgroupCallbacks(callbacks);
   newLoadGroup->SetNotificationCallbacks(newCallbacks);
 
   // This is some serious hackery cribbed from docshell
   nsCOMPtr<nsICategoryManager> catMan =
     do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
   NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
   nsCString contractId;
-  nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", type.get(),
-                                         getter_Copies(contractId));
+  nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", type,
+                                         contractId);
   NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
     do_GetService(contractId.get());
   NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
 
   nsCOMPtr<nsIContentViewer> viewer;
   nsCOMPtr<nsIStreamListener> listener;
   rv = docLoaderFactory->CreateInstance("external-resource", chan, newLoadGroup,
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -2766,45 +2766,42 @@ nsPluginHost::RegisterWithCategoryManage
               aMimeType.get(), aType == ePluginUnregister ? "yes" : "no"));
 
   nsCOMPtr<nsICategoryManager> catMan =
     do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
   if (!catMan) {
     return;
   }
 
-  const char *contractId =
-    "@mozilla.org/content/plugin/document-loader-factory;1";
+  NS_NAMED_LITERAL_CSTRING(contractId,
+                           "@mozilla.org/content/plugin/document-loader-factory;1");
 
   if (aType == ePluginRegister) {
     catMan->AddCategoryEntry("Gecko-Content-Viewers",
-                             aMimeType.get(),
+                             aMimeType,
                              contractId,
                              false, /* persist: broken by bug 193031 */
-                             mOverrideInternalTypes,
-                             nullptr);
+                             mOverrideInternalTypes);
   } else {
     if (aType == ePluginMaybeUnregister) {
       // Bail out if this type is still used by an enabled plugin
       if (HavePluginForType(aMimeType)) {
         return;
       }
     } else {
       MOZ_ASSERT(aType == ePluginUnregister, "Unknown nsRegisterType");
     }
 
     // Only delete the entry if a plugin registered for it
     nsCString value;
     nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers",
-                                           aMimeType.get(),
-                                           getter_Copies(value));
-    if (NS_SUCCEEDED(rv) && strcmp(value.get(), contractId) == 0) {
+                                           aMimeType, value);
+    if (NS_SUCCEEDED(rv) && value == contractId) {
       catMan->DeleteCategoryEntry("Gecko-Content-Viewers",
-                                  aMimeType.get(),
-                                  true);
+                                  aMimeType, true);
     }
   }
 }
 
 nsresult
 nsPluginHost::WritePluginInfo()
 {
   MOZ_ASSERT(XRE_IsParentProcess());
--- a/dom/push/PushNotifier.cpp
+++ b/dom/push/PushNotifier.cpp
@@ -315,19 +315,17 @@ PushDispatcher::DoNotifyObservers(nsISup
   if (!obsService) {
     return NS_ERROR_FAILURE;
   }
   // If there's a service for this push category, make sure it is alive.
   nsCOMPtr<nsICategoryManager> catMan =
     do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
   if (catMan) {
     nsCString contractId;
-    nsresult rv = catMan->GetCategoryEntry("push",
-                                           mScope.BeginReading(),
-                                           getter_Copies(contractId));
+    nsresult rv = catMan->GetCategoryEntry("push", mScope, contractId);
     if (NS_SUCCEEDED(rv)) {
       // Ensure the service is created - we don't need to do anything with
       // it though - we assume the service constructor attaches a listener.
       nsCOMPtr<nsISupports> service = do_GetService(contractId.get());
     }
   }
   return obsService->NotifyObservers(aSubject, aTopic,
                                      NS_ConvertUTF8toUTF16(mScope).get());
--- a/gfx/thebes/gfxSVGGlyphs.cpp
+++ b/gfx/thebes/gfxSVGGlyphs.cpp
@@ -132,17 +132,17 @@ gfxSVGGlyphs::FindOrCreateGlyphsDocument
     return result;
 }
 
 nsresult
 gfxSVGGlyphsDocument::SetupPresentation()
 {
     nsCOMPtr<nsICategoryManager> catMan = do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
     nsCString contractId;
-    nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "image/svg+xml", getter_Copies(contractId));
+    nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", "image/svg+xml", contractId);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
       do_GetService(contractId.get());
     NS_ASSERTION(docLoaderFactory, "Couldn't get DocumentLoaderFactory");
 
     nsCOMPtr<nsIContentViewer> viewer;
     rv = docLoaderFactory->CreateInstanceForDocument(nullptr, mDocument, nullptr, getter_AddRefs(viewer));
--- a/image/SVGDocumentWrapper.cpp
+++ b/image/SVGDocumentWrapper.cpp
@@ -327,17 +327,17 @@ SVGDocumentWrapper::SetupViewer(nsIReque
   NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
   newLoadGroup->SetLoadGroup(loadGroup);
 
   nsCOMPtr<nsICategoryManager> catMan =
     do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
   NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
   nsCString contractId;
   nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers", IMAGE_SVG_XML,
-                                         getter_Copies(contractId));
+                                         contractId);
   NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
     do_GetService(contractId.get());
   NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
 
   nsCOMPtr<nsIContentViewer> viewer;
   nsCOMPtr<nsIStreamListener> listener;
   rv = docLoaderFactory->CreateInstance("external-resource", chan,
--- a/layout/base/nsStyleSheetService.cpp
+++ b/layout/base/nsStyleSheetService.cpp
@@ -69,17 +69,17 @@ nsStyleSheetService::RegisterFromEnumera
     nsCOMPtr<nsISupportsCString> icStr = do_QueryInterface(element);
     NS_ASSERTION(icStr,
                  "category manager entries must be nsISupportsCStrings");
 
     nsAutoCString name;
     icStr->GetData(name);
 
     nsCString spec;
-    aManager->GetCategoryEntry(aCategory, name.get(), getter_Copies(spec));
+    aManager->GetCategoryEntry(nsDependentCString(aCategory), name, spec);
 
     nsCOMPtr<nsIURI> uri;
     NS_NewURI(getter_AddRefs(uri), spec);
     if (uri)
       LoadAndRegisterSheetInternal(uri, aSheetType);
   }
 }
 
--- a/toolkit/components/commandlines/nsCommandLine.cpp
+++ b/toolkit/components/commandlines/nsCommandLine.cpp
@@ -510,19 +510,18 @@ nsCommandLine::EnumerateHandlers(Enumera
   NS_ENSURE_TRUE(strenum, NS_ERROR_UNEXPECTED);
 
   nsAutoCString entry;
   bool hasMore;
   while (NS_SUCCEEDED(strenum->HasMore(&hasMore)) && hasMore) {
     strenum->GetNext(entry);
 
     nsCString contractID;
-    rv = catman->GetCategoryEntry("command-line-handler",
-				  entry.get(),
-				  getter_Copies(contractID));
+    rv = catman->GetCategoryEntry("command-line-handler", entry,
+				  contractID);
     if (NS_FAILED(rv))
       continue;
 
     nsCOMPtr<nsICommandLineHandler> clh(do_GetService(contractID.get()));
     if (!clh) {
       LogConsoleMessage(u"Contract ID '%s' was registered as a command line handler for entry '%s', but could not be created.",
                         contractID.get(), entry.get());
       continue;
@@ -557,18 +556,17 @@ nsCommandLine::EnumerateValidators(Enume
 
   nsAutoCString entry;
   bool hasMore;
   while (NS_SUCCEEDED(strenum->HasMore(&hasMore)) && hasMore) {
     strenum->GetNext(entry);
 
     nsCString contractID;
     rv = catman->GetCategoryEntry("command-line-validator",
-				  entry.get(),
-				  getter_Copies(contractID));
+				  entry, contractID);
     if (contractID.IsVoid())
       continue;
 
     nsCOMPtr<nsICommandLineValidator> clv(do_GetService(contractID.get()));
     if (!clv)
       continue;
 
     rv = (aCallback)(clv, this, aClosure);
--- a/toolkit/xre/nsAppStartupNotifier.cpp
+++ b/toolkit/xre/nsAppStartupNotifier.cpp
@@ -25,33 +25,34 @@ NS_IMETHODIMP nsAppStartupNotifier::Obse
     NS_ENSURE_ARG(aTopic);
     nsresult rv;
 
     // now initialize all startup listeners
     nsCOMPtr<nsICategoryManager> categoryManager =
                     do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
+    nsDependentCString topic(aTopic);
+
     nsCOMPtr<nsISimpleEnumerator> enumerator;
-    rv = categoryManager->EnumerateCategory(aTopic,
+    rv = categoryManager->EnumerateCategory(topic,
                                getter_AddRefs(enumerator));
     if (NS_FAILED(rv)) return rv;
 
     nsCOMPtr<nsISupports> entry;
     while (NS_SUCCEEDED(enumerator->GetNext(getter_AddRefs(entry)))) {
         nsCOMPtr<nsISupportsCString> category = do_QueryInterface(entry, &rv);
 
         if (NS_SUCCEEDED(rv)) {
             nsAutoCString categoryEntry;
             rv = category->GetData(categoryEntry);
 
             nsCString contractId;
-            categoryManager->GetCategoryEntry(aTopic,
-                                              categoryEntry.get(),
-                                              getter_Copies(contractId));
+            categoryManager->GetCategoryEntry(topic, categoryEntry,
+                                              contractId);
 
             if (NS_SUCCEEDED(rv)) {
 
                 // If we see the word "service," in the beginning
                 // of the contractId then we create it as a service
                 // if not we do a createInstance
 
                 nsCOMPtr<nsISupports> startupInstance;
--- a/uriloader/base/nsURILoader.cpp
+++ b/uriloader/base/nsURILoader.cpp
@@ -460,18 +460,17 @@ nsresult nsDocumentOpenInfo::DispatchCon
       // the chance to register, as it is contained in a not-yet-loaded
       // module, but which has registered a contract ID.
       //
       nsCOMPtr<nsICategoryManager> catman =
         do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
       if (catman) {
         nsCString contractidString;
         rv = catman->GetCategoryEntry(NS_CONTENT_LISTENER_CATEGORYMANAGER_ENTRY,
-                                      mContentType.get(),
-                                      getter_Copies(contractidString));
+                                      mContentType, contractidString);
         if (NS_SUCCEEDED(rv) && !contractidString.IsEmpty()) {
           LOG(("  Listener contractid for '%s' is '%s'",
                mContentType.get(), contractidString.get()));
 
           listener = do_CreateInstance(contractidString.get());
           LOG(("  Listener from category manager: 0x%p", listener.get()));
 
           if (listener && TryContentListener(listener, aChannel)) {
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -2693,18 +2693,17 @@ nsExternalHelperAppService::GetTypeFromE
     do_GetService("@mozilla.org/categorymanager;1"));
   if (catMan) {
     // The extension in the category entry is always stored as lowercase
     nsAutoCString lowercaseFileExt(aFileExt);
     ToLowerCase(lowercaseFileExt);
     // Read the MIME type from the category entry, if available
     nsCString type;
     nsresult rv = catMan->GetCategoryEntry("ext-to-type-mapping",
-                                           lowercaseFileExt.get(),
-                                           getter_Copies(type));
+                                           lowercaseFileExt, type);
     if (NS_SUCCEEDED(rv)) {
       aContentType = type;
       return NS_OK;
     }
   }
 
   return NS_ERROR_NOT_AVAILABLE;
 }
--- a/xpcom/components/nsCategoryCache.cpp
+++ b/xpcom/components/nsCategoryCache.cpp
@@ -8,17 +8,17 @@
 #include "mozilla/Services.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIStringEnumerator.h"
 
 #include "nsXPCOMCID.h"
 
 #include "nsCategoryCache.h"
 
-nsCategoryObserver::nsCategoryObserver(const char* aCategory)
+nsCategoryObserver::nsCategoryObserver(const nsACString& aCategory)
   : mCategory(aCategory)
   , mCallback(nullptr)
   , mClosure(nullptr)
   , mObserversRemoved(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
   // First, enumerate the currently existing entries
   nsCOMPtr<nsICategoryManager> catMan =
@@ -38,19 +38,17 @@ nsCategoryObserver::nsCategoryObserver(c
   MOZ_ASSERT(strings);
 
   bool more;
   while (NS_SUCCEEDED(strings->HasMore(&more)) && more) {
     nsAutoCString entryName;
     strings->GetNext(entryName);
 
     nsCString entryValue;
-    rv = catMan->GetCategoryEntry(aCategory,
-                                  entryName.get(),
-                                  getter_Copies(entryValue));
+    rv = catMan->GetCategoryEntry(aCategory, entryName, entryValue);
     if (NS_SUCCEEDED(rv)) {
       nsCOMPtr<nsISupports> service = do_GetService(entryValue.get());
       if (service) {
         mHash.Put(entryName, service);
       }
     }
   }
 
@@ -146,19 +144,17 @@ nsCategoryObserver::Observe(nsISupports*
 
     nsCOMPtr<nsICategoryManager> catMan =
       do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
     if (!catMan) {
       return NS_OK;
     }
 
     nsCString entryValue;
-    catMan->GetCategoryEntry(mCategory.get(),
-                             str.get(),
-                             getter_Copies(entryValue));
+    catMan->GetCategoryEntry(mCategory, str, entryValue);
 
     nsCOMPtr<nsISupports> service = do_GetService(entryValue.get());
 
     if (service) {
       mHash.Put(str, service);
     }
     if (mCallback) {
       mCallback(mClosure);
--- a/xpcom/components/nsCategoryCache.h
+++ b/xpcom/components/nsCategoryCache.h
@@ -23,17 +23,17 @@
 #include "nsXPCOM.h"
 #include "MainThreadUtils.h"
 
 class nsCategoryObserver final : public nsIObserver
 {
   ~nsCategoryObserver();
 
 public:
-  explicit nsCategoryObserver(const char* aCategory);
+  explicit nsCategoryObserver(const nsACString& aCategory);
 
   void ListenerDied();
   void SetListener(void(aCallback)(void*), void* aClosure);
   nsInterfaceHashtable<nsCStringHashKey, nsISupports>& GetHash()
   {
     return mHash;
   }
 
@@ -99,17 +99,17 @@ public:
   }
 
 private:
   void LazyInit()
   {
     // Lazy initialization, so that services in this category can't
     // cause reentrant getService (bug 386376)
     if (!mObserver) {
-      mObserver = new nsCategoryObserver(mCategoryName.get());
+      mObserver = new nsCategoryObserver(mCategoryName);
       mObserver->SetListener(nsCategoryCache<T>::OnCategoryChanged, this);
     }
   }
 
   void AddEntries(nsCOMArray<T>& aResult)
   {
     for (auto iter = mObserver->GetHash().Iter(); !iter.Done(); iter.Next()) {
       nsISupports* entry = iter.UserData();
--- a/xpcom/components/nsCategoryManager.cpp
+++ b/xpcom/components/nsCategoryManager.cpp
@@ -201,89 +201,84 @@ CategoryNode::Create(CategoryAllocator* 
 CategoryNode::~CategoryNode() = default;
 
 void*
 CategoryNode::operator new(size_t aSize, CategoryAllocator* aArena)
 {
   return aArena->Allocate(aSize, mozilla::fallible);
 }
 
+static inline const char*
+MaybeStrdup(const nsACString& aStr, CategoryAllocator* aArena)
+{
+  if (aStr.IsLiteral()) {
+    return aStr.BeginReading();
+  }
+  return ArenaStrdup(PromiseFlatCString(aStr).get(), *aArena);
+}
+
 nsresult
-CategoryNode::GetLeaf(const char* aEntryName,
-                      char** aResult)
+CategoryNode::GetLeaf(const nsACString& aEntryName,
+                      nsACString& aResult)
 {
   MutexAutoLock lock(mLock);
   nsresult rv = NS_ERROR_NOT_AVAILABLE;
-  CategoryLeaf* ent = mTable.GetEntry(aEntryName);
+  CategoryLeaf* ent = mTable.GetEntry(PromiseFlatCString(aEntryName).get());
 
   if (ent && ent->value) {
-    *aResult = NS_strdup(ent->value);
-    if (*aResult) {
-      rv = NS_OK;
-    }
+    aResult.Assign(ent->value);
+    return NS_OK;
   }
 
   return rv;
 }
 
 nsresult
-CategoryNode::AddLeaf(const char* aEntryName,
-                      const char* aValue,
+CategoryNode::AddLeaf(const nsACString& aEntryName,
+                      const nsACString& aValue,
                       bool aReplace,
-                      char** aResult,
+                      nsACString& aResult,
                       CategoryAllocator* aArena)
 {
-  if (aResult) {
-    *aResult = nullptr;
-  }
+  aResult.SetIsVoid(true);
+
+  auto entryName = PromiseFlatCString(aEntryName);
 
   MutexAutoLock lock(mLock);
-  CategoryLeaf* leaf = mTable.GetEntry(aEntryName);
+  CategoryLeaf* leaf = mTable.GetEntry(entryName.get());
 
   if (!leaf) {
-    const char* arenaEntryName = ArenaStrdup(aEntryName, *aArena);
-    if (!arenaEntryName) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    leaf = mTable.PutEntry(arenaEntryName);
+    leaf = mTable.PutEntry(MaybeStrdup(aEntryName, aArena));
     if (!leaf) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
   if (leaf->value && !aReplace) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  const char* arenaValue = ArenaStrdup(aValue, *aArena);
-  if (!arenaValue) {
-    return NS_ERROR_OUT_OF_MEMORY;
+  if (leaf->value) {
+    aResult.AssignLiteral(leaf->value, strlen(leaf->value));
+  } else {
+    aResult.SetIsVoid(true);
   }
-
-  if (aResult && leaf->value) {
-    *aResult = ToNewCString(nsDependentCString(leaf->value));
-    if (!*aResult) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-  }
-
-  leaf->value = arenaValue;
+  leaf->value = MaybeStrdup(aValue, aArena);
   return NS_OK;
 }
 
 void
-CategoryNode::DeleteLeaf(const char* aEntryName)
+CategoryNode::DeleteLeaf(const nsACString& aEntryName)
 {
   // we don't throw any errors, because it normally doesn't matter
   // and it makes JS a lot cleaner
   MutexAutoLock lock(mLock);
 
   // we can just remove the entire hash entry without introspection
-  mTable.RemoveEntry(aEntryName);
+  mTable.RemoveEntry(PromiseFlatCString(aEntryName).get());
 }
 
 nsresult
 CategoryNode::Enumerate(nsISimpleEnumerator** aResult)
 {
   if (NS_WARN_IF(!aResult)) {
     return NS_ERROR_INVALID_ARG;
   }
@@ -334,18 +329,17 @@ CategoryEnumerator::Create(nsClassHashta
     delete enumObj;
     return nullptr;
   }
 
   for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) {
     // if a category has no entries, we pretend it doesn't exist
     CategoryNode* aNode = iter.UserData();
     if (aNode->Count()) {
-      const char* str = iter.Key();
-      enumObj->mArray[enumObj->mCount++] = str;
+      enumObj->mArray[enumObj->mCount++] = iter.Key();
     }
   }
 
   return enumObj;
 }
 
 
 //
@@ -418,20 +412,20 @@ nsCategoryManager::~nsCategoryManager()
 {
   // the hashtable contains entries that must be deleted before the arena is
   // destroyed, or else you will have PRLocks undestroyed and other Really
   // Bad Stuff (TM)
   mTable.Clear();
 }
 
 inline CategoryNode*
-nsCategoryManager::get_category(const char* aName)
+nsCategoryManager::get_category(const nsACString& aName)
 {
   CategoryNode* node;
-  if (!mTable.Get(aName, &node)) {
+  if (!mTable.Get(PromiseFlatCString(aName).get(), &node)) {
     return nullptr;
   }
   return node;
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(CategoryManagerMallocSizeOf)
 
 NS_IMETHODIMP
@@ -464,17 +458,17 @@ nsCategoryManager::SizeOfIncludingThis(m
 
 namespace {
 
 class CategoryNotificationRunnable : public Runnable
 {
 public:
   CategoryNotificationRunnable(nsISupports* aSubject,
                                const char* aTopic,
-                               const char* aData)
+                               const nsACString& aData)
     : Runnable("CategoryNotificationRunnable")
     , mSubject(aSubject)
     , mTopic(aTopic)
     , mData(aData)
   {
   }
 
   NS_DECL_NSIRUNNABLE
@@ -497,156 +491,135 @@ CategoryNotificationRunnable::Run()
   return NS_OK;
 }
 
 } // namespace
 
 
 void
 nsCategoryManager::NotifyObservers(const char* aTopic,
-                                   const char* aCategoryName,
-                                   const char* aEntryName)
+                                   const nsACString& aCategoryName,
+                                   const nsACString& aEntryName)
 {
   if (mSuppressNotifications) {
     return;
   }
 
   RefPtr<CategoryNotificationRunnable> r;
 
-  if (aEntryName) {
+  if (aEntryName.Length()) {
     nsCOMPtr<nsISupportsCString> entry =
       do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
     if (!entry) {
       return;
     }
 
-    nsresult rv = entry->SetData(nsDependentCString(aEntryName));
+    nsresult rv = entry->SetData(aEntryName);
     if (NS_FAILED(rv)) {
       return;
     }
 
     r = new CategoryNotificationRunnable(entry, aTopic, aCategoryName);
   } else {
     r = new CategoryNotificationRunnable(NS_ISUPPORTS_CAST(nsICategoryManager*,
                                                            this),
                                          aTopic, aCategoryName);
   }
 
   NS_DispatchToMainThread(r);
 }
 
 NS_IMETHODIMP
-nsCategoryManager::GetCategoryEntry(const char* aCategoryName,
-                                    const char* aEntryName,
-                                    char** aResult)
+nsCategoryManager::GetCategoryEntry(const nsACString& aCategoryName,
+                                    const nsACString& aEntryName,
+                                    nsACString& aResult)
 {
-  if (NS_WARN_IF(!aCategoryName) ||
-      NS_WARN_IF(!aEntryName) ||
-      NS_WARN_IF(!aResult)) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
   nsresult status = NS_ERROR_NOT_AVAILABLE;
 
   CategoryNode* category;
   {
     MutexAutoLock lock(mLock);
     category = get_category(aCategoryName);
   }
 
   if (category) {
     status = category->GetLeaf(aEntryName, aResult);
   }
 
   return status;
 }
 
 NS_IMETHODIMP
-nsCategoryManager::AddCategoryEntry(const char* aCategoryName,
-                                    const char* aEntryName,
-                                    const char* aValue,
+nsCategoryManager::AddCategoryEntry(const nsACString& aCategoryName,
+                                    const nsACString& aEntryName,
+                                    const nsACString& aValue,
                                     bool aPersist,
                                     bool aReplace,
-                                    char** aResult)
+                                    nsACString& aResult)
 {
   if (aPersist) {
     NS_ERROR("Category manager doesn't support persistence.");
     return NS_ERROR_INVALID_ARG;
   }
 
   AddCategoryEntry(aCategoryName, aEntryName, aValue, aReplace, aResult);
   return NS_OK;
 }
 
 void
-nsCategoryManager::AddCategoryEntry(const char* aCategoryName,
-                                    const char* aEntryName,
-                                    const char* aValue,
+nsCategoryManager::AddCategoryEntry(const nsACString& aCategoryName,
+                                    const nsACString& aEntryName,
+                                    const nsACString& aValue,
                                     bool aReplace,
-                                    char** aOldValue)
+                                    nsACString& aOldValue)
 {
-  if (aOldValue) {
-    *aOldValue = nullptr;
-  }
+  aOldValue.SetIsVoid(true);
 
   // Before we can insert a new entry, we'll need to
   //  find the |CategoryNode| to put it in...
   CategoryNode* category;
   {
     MutexAutoLock lock(mLock);
     category = get_category(aCategoryName);
 
     if (!category) {
       // That category doesn't exist yet; let's make it.
       category = CategoryNode::Create(&mArena);
 
-      char* categoryName = ArenaStrdup(aCategoryName, mArena);
-      mTable.Put(categoryName, category);
+      mTable.Put(MaybeStrdup(aCategoryName, &mArena),
+                 category);
     }
   }
 
   if (!category) {
     return;
   }
 
-  // We will need the return value of AddLeaf even if the called doesn't want it
-  char* oldEntry = nullptr;
-
   nsresult rv = category->AddLeaf(aEntryName,
                                   aValue,
                                   aReplace,
-                                  &oldEntry,
+                                  aOldValue,
                                   &mArena);
 
   if (NS_SUCCEEDED(rv)) {
-    if (oldEntry) {
+    if (!aOldValue.IsEmpty()) {
       NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID,
                       aCategoryName, aEntryName);
     }
     NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID,
                     aCategoryName, aEntryName);
 
-    if (aOldValue) {
-      *aOldValue = oldEntry;
-    } else {
-      free(oldEntry);
-    }
   }
 }
 
 NS_IMETHODIMP
-nsCategoryManager::DeleteCategoryEntry(const char* aCategoryName,
-                                       const char* aEntryName,
+nsCategoryManager::DeleteCategoryEntry(const nsACString& aCategoryName,
+                                       const nsACString& aEntryName,
                                        bool aDontPersist)
 {
-  if (NS_WARN_IF(!aCategoryName) ||
-      NS_WARN_IF(!aEntryName)) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
   /*
     Note: no errors are reported since failure to delete
     probably won't hurt you, and returning errors seriously
     inconveniences JS clients
   */
 
   CategoryNode* category;
   {
@@ -660,50 +633,41 @@ nsCategoryManager::DeleteCategoryEntry(c
     NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID,
                     aCategoryName, aEntryName);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsCategoryManager::DeleteCategory(const char* aCategoryName)
+nsCategoryManager::DeleteCategory(const nsACString& aCategoryName)
 {
-  if (NS_WARN_IF(!aCategoryName)) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
   // the categories are arena-allocated, so we don't
   // actually delete them. We just remove all of the
   // leaf nodes.
 
   CategoryNode* category;
   {
     MutexAutoLock lock(mLock);
     category = get_category(aCategoryName);
   }
 
   if (category) {
     category->Clear();
     NotifyObservers(NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID,
-                    aCategoryName, nullptr);
+                    aCategoryName, VoidCString());
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsCategoryManager::EnumerateCategory(const char* aCategoryName,
+nsCategoryManager::EnumerateCategory(const nsACString& aCategoryName,
                                      nsISimpleEnumerator** aResult)
 {
-  if (NS_WARN_IF(!aCategoryName) ||
-      NS_WARN_IF(!aResult)) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
   CategoryNode* category;
   {
     MutexAutoLock lock(mLock);
     category = get_category(aCategoryName);
   }
 
   if (!category) {
     return NS_NewEmptyEnumerator(aResult);
@@ -762,18 +726,20 @@ NS_CreateServicesFromCategory(const char
   nsresult rv;
 
   nsCOMPtr<nsICategoryManager> categoryManager =
     do_GetService("@mozilla.org/categorymanager;1");
   if (!categoryManager) {
     return;
   }
 
+  nsDependentCString category(aCategory);
+
   nsCOMPtr<nsISimpleEnumerator> enumerator;
-  rv = categoryManager->EnumerateCategory(aCategory,
+  rv = categoryManager->EnumerateCategory(category,
                                           getter_AddRefs(enumerator));
   if (NS_FAILED(rv)) {
     return;
   }
 
   nsCOMPtr<nsIUTF8StringEnumerator> senumerator =
     do_QueryInterface(enumerator);
   if (!senumerator) {
@@ -785,18 +751,17 @@ NS_CreateServicesFromCategory(const char
   while (NS_SUCCEEDED(senumerator->HasMore(&hasMore)) && hasMore) {
     // From here on just skip any error we get.
     nsAutoCString entryString;
     if (NS_FAILED(senumerator->GetNext(entryString))) {
       continue;
     }
 
     nsCString contractID;
-    rv = categoryManager->GetCategoryEntry(aCategory, entryString.get(),
-                                           getter_Copies(contractID));
+    rv = categoryManager->GetCategoryEntry(category, entryString, contractID);
     if (NS_FAILED(rv)) {
       continue;
     }
 
     nsCOMPtr<nsISupports> instance = do_GetService(contractID.get());
     if (!instance) {
       LogMessage("While creating services from category '%s', could not create service for entry '%s', contract ID '%s'",
                  aCategory, entryString.get(), contractID.get());
--- a/xpcom/components/nsCategoryManager.h
+++ b/xpcom/components/nsCategoryManager.h
@@ -45,26 +45,26 @@ public:
 /**
  * CategoryNode keeps a hashtable of its entries.
  * the CategoryNode itself is permanently allocated in
  * the arena.
  */
 class CategoryNode
 {
 public:
-  nsresult GetLeaf(const char* aEntryName,
-                   char** aResult);
+  nsresult GetLeaf(const nsACString& aEntryName,
+                   nsACString& aResult);
 
-  nsresult AddLeaf(const char* aEntryName,
-                   const char* aValue,
+  nsresult AddLeaf(const nsACString& aEntryName,
+                   const nsACString& aValue,
                    bool aReplace,
-                   char** aResult,
+                   nsACString& aResult,
                    CategoryAllocator* aArena);
 
-  void DeleteLeaf(const char* aEntryName);
+  void DeleteLeaf(const nsACString& aEntryName);
 
   void Clear()
   {
     mozilla::MutexAutoLock lock(mLock);
     mTable.Clear();
   }
 
   uint32_t Count()
@@ -109,40 +109,49 @@ public:
 
   /**
    * Suppress or unsuppress notifications of category changes to the
    * observer service. This is to be used by nsComponentManagerImpl
    * on startup while reading the stored category list.
    */
   nsresult SuppressNotifications(bool aSuppress);
 
-  void AddCategoryEntry(const char* aCategory,
-                        const char* aKey,
-                        const char* aValue,
-                        bool aReplace = true,
-                        char** aOldValue = nullptr);
+  void AddCategoryEntry(const nsACString& aCategory,
+                        const nsACString& aKey,
+                        const nsACString& aValue,
+                        bool aReplace,
+                        nsACString& aOldValue);
+
+  void AddCategoryEntry(const nsACString& aCategory,
+                        const nsACString& aKey,
+                        const nsACString& aValue,
+                        bool aReplace = true)
+  {
+    nsCString oldValue;
+    return AddCategoryEntry(aCategory, aKey, aValue, aReplace, oldValue);
+  }
 
   static nsresult Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
   void InitMemoryReporter();
 
   static nsCategoryManager* GetSingleton();
   static void Destroy();
 
 private:
   static nsCategoryManager* gCategoryManager;
 
   nsCategoryManager();
   ~nsCategoryManager();
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
 
-  CategoryNode* get_category(const char* aName);
+  CategoryNode* get_category(const nsACString& aName);
   void NotifyObservers(const char* aTopic,
-                       const char* aCategoryName, // must be a static string
-                       const char* aEntryName);
+                       const nsACString& aCategoryName, // must be a static string
+                       const nsACString& aEntryName);
 
   CategoryAllocator mArena;
   nsClassHashtable<nsDepCharHashKey, CategoryNode> mTable;
   mozilla::Mutex mLock;
   bool mSuppressNotifications;
 };
 
 #endif
--- a/xpcom/components/nsComponentManager.cpp
+++ b/xpcom/components/nsComponentManager.cpp
@@ -97,32 +97,25 @@ nsGetServiceFromCategory::operator()(con
   nsCString value;
   nsCOMPtr<nsICategoryManager> catman;
   nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
   if (!compMgr) {
     rv = NS_ERROR_NOT_INITIALIZED;
     goto error;
   }
 
-  if (!mCategory || !mEntry) {
-    // when categories have defaults, use that for null mEntry
-    rv = NS_ERROR_NULL_POINTER;
-    goto error;
-  }
-
   rv = compMgr->nsComponentManagerImpl::GetService(kCategoryManagerCID,
                                                    NS_GET_IID(nsICategoryManager),
                                                    getter_AddRefs(catman));
   if (NS_FAILED(rv)) {
     goto error;
   }
 
   /* find the contractID for category.entry */
-  rv = catman->GetCategoryEntry(mCategory, mEntry,
-                                getter_Copies(value));
+  rv = catman->GetCategoryEntry(mCategory, mEntry, value);
   if (NS_FAILED(rv)) {
     goto error;
   }
   if (value.IsVoid()) {
     rv = NS_ERROR_SERVICE_NOT_AVAILABLE;
     goto error;
   }
 
@@ -550,19 +543,20 @@ nsComponentManagerImpl::RegisterModule(c
       }
       MOZ_ASSERT(!entry->cid, "Incorrectly terminated contract list");
     }
   }
 
   if (aModule->mCategoryEntries) {
     const mozilla::Module::CategoryEntry* entry;
     for (entry = aModule->mCategoryEntries; entry->category; ++entry)
-      nsCategoryManager::GetSingleton()->AddCategoryEntry(entry->category,
-                                                          entry->entry,
-                                                          entry->value);
+      nsCategoryManager::GetSingleton()->AddCategoryEntry(
+          AsLiteralCString(entry->category),
+          AsLiteralCString(entry->entry),
+          AsLiteralCString(entry->value));
   }
 }
 
 void
 nsComponentManagerImpl::RegisterCIDEntryLocked(
     const mozilla::Module::CIDEntry* aEntry,
     KnownModule* aModule)
 {
@@ -772,17 +766,18 @@ void
 nsComponentManagerImpl::ManifestCategory(ManifestProcessingContext& aCx,
                                          int aLineNo, char* const* aArgv)
 {
   char* category = aArgv[0];
   char* key = aArgv[1];
   char* value = aArgv[2];
 
   nsCategoryManager::GetSingleton()->
-  AddCategoryEntry(category, key, value);
+  AddCategoryEntry(nsDependentCString(category), nsDependentCString(key),
+                   nsDependentCString(value));
 }
 
 void
 nsComponentManagerImpl::RereadChromeManifests(bool aChromeOnly)
 {
   for (uint32_t i = 0; i < sModuleLocations->Length(); ++i) {
     ComponentLocation& l = sModuleLocations->ElementAt(i);
     RegisterManifest(l.type, l.location, aChromeOnly);
@@ -1542,18 +1537,18 @@ nsComponentManagerImpl::GetServiceByCont
   return NS_OK;
 }
 
 already_AddRefed<mozilla::ModuleLoader>
 nsComponentManagerImpl::LoaderForExtension(const nsACString& aExt)
 {
   nsCOMPtr<mozilla::ModuleLoader> loader = mLoaderMap.Get(aExt);
   if (!loader) {
-    loader = do_GetServiceFromCategory("module-loader",
-                                       PromiseFlatCString(aExt).get());
+    loader = do_GetServiceFromCategory(NS_LITERAL_CSTRING("module-loader"),
+                                       aExt);
     if (!loader) {
       return nullptr;
     }
 
     mLoaderMap.Put(aExt, loader);
   }
 
   return loader.forget();
--- a/xpcom/components/nsICategoryManager.idl
+++ b/xpcom/components/nsICategoryManager.idl
@@ -2,68 +2,127 @@
 /* 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 "nsISupports.idl"
 
 interface nsISimpleEnumerator;
 
+%{C++
+#include "nsString.h"
+%}
+
 /*
  * nsICategoryManager
  */
 
 [scriptable, uuid(3275b2cd-af6d-429a-80d7-f0c5120342ac)]
 interface nsICategoryManager : nsISupports
 {
     /**
      * Get the value for the given category's entry.
      * @param aCategory The name of the category ("protocol")
      * @param aEntry The entry you're looking for ("http")
      * @return The value.
      */
-    string getCategoryEntry(in string aCategory, in string aEntry);
+    ACString getCategoryEntry(in ACString aCategory, in ACString aEntry);
     
     /**
      * Add an entry to a category.
      * @param aCategory The name of the category ("protocol")
      * @param aEntry The entry to be added ("http")
      * @param aValue The value for the entry ("moz.httprulez.1")
      * @param aPersist Should this data persist between invocations?
      * @param aReplace Should we replace an existing entry?
      * @return Previous entry, if any
      */
-    string addCategoryEntry(in string aCategory, in string aEntry,
-			    in string aValue, in boolean aPersist,
-			    in boolean aReplace);
+    ACString addCategoryEntry(in ACString aCategory, in ACString aEntry,
+                              in ACString aValue, in boolean aPersist,
+                              in boolean aReplace);
 
     /**
      * Delete an entry from the category.
      * @param aCategory The name of the category ("protocol")
      * @param aEntry The entry to be added ("http")
      * @param aPersist Delete persistent data from registry, if present?
      */
-    void deleteCategoryEntry(in string aCategory, in string aEntry,
-                               in boolean aPersist);
+    void deleteCategoryEntry(in ACString aCategory, in ACString aEntry,
+                             in boolean aPersist);
 
     /**
      * Delete a category and all entries.
      * @param aCategory The category to be deleted.
      */
-    void deleteCategory(in string aCategory);
+    void deleteCategory(in ACString aCategory);
 
     /**
      * Enumerate the entries in a category.
      * @param aCategory The category to be enumerated.
      * @return a simple enumerator, each result QIs to
      *         nsISupportsCString.
      */
-    nsISimpleEnumerator enumerateCategory(in string aCategory);
+    nsISimpleEnumerator enumerateCategory(in ACString aCategory);
 
     /**
      * Enumerate all existing categories
      * @param aCategory The category to be enumerated.
      * @return a simple enumerator, each result QIs to
      *         nsISupportsCString.
      */
     nsISimpleEnumerator enumerateCategories();
+
+    %{C++
+    template<int N>
+    nsresult
+    GetCategoryEntry(const char (&aCategory)[N], const nsACString& aEntry,
+                     nsACString& aResult)
+    {
+        return GetCategoryEntry(nsLiteralCString(aCategory),
+                                aEntry, aResult);
+    }
+
+    template<int N, int M>
+    nsresult
+    GetCategoryEntry(const char (&aCategory)[N], const char (&aEntry)[M],
+                     nsACString& aResult)
+    {
+        return GetCategoryEntry(nsLiteralCString(aCategory),
+                                nsLiteralCString(aEntry),
+                                aResult);
+    }
+
+    nsresult
+    AddCategoryEntry(const nsACString& aCategory, const nsACString& aEntry,
+                     const nsACString& aValue, bool aPersist, bool aReplace)
+    {
+        nsCString oldValue;
+        return AddCategoryEntry(aCategory, aEntry, aValue, aPersist, aReplace,
+                                oldValue);
+    }
+
+    template<int N>
+    nsresult
+    AddCategoryEntry(const char (&aCategory)[N], const nsACString& aEntry,
+                     const nsACString& aValue, bool aPersist, bool aReplace)
+    {
+        nsCString oldValue;
+        return AddCategoryEntry(nsLiteralCString(aCategory), aEntry, aValue,
+                                aPersist, aReplace, oldValue);
+    }
+
+    template<int N>
+    nsresult
+    DeleteCategoryEntry(const char (&aCategory)[N], const nsACString& aEntry, bool aPersist)
+    {
+        return DeleteCategoryEntry(nsLiteralCString(aCategory), aEntry, aPersist);
+    }
+
+
+    template<int N>
+    nsresult
+    EnumerateCategory(const char (&aCategory)[N], nsISimpleEnumerator** aResult)
+    {
+        return EnumerateCategory(nsLiteralCString(aCategory), aResult);
+    }
+    %}
 };
 
--- a/xpcom/components/nsServiceManagerUtils.h
+++ b/xpcom/components/nsServiceManagerUtils.h
@@ -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/. */
 
 #ifndef nsServiceManagerUtils_h__
 #define nsServiceManagerUtils_h__
 
 #include "nsIServiceManager.h"
 #include "nsCOMPtr.h"
+#include "nsString.h"
 
 inline const nsGetServiceByCID
 do_GetService(const nsCID& aCID)
 {
   return nsGetServiceByCID(aCID);
 }
 
 inline const nsGetServiceByCIDWithError
@@ -32,34 +33,34 @@ inline const nsGetServiceByContractIDWit
 do_GetService(const char* aContractID, nsresult* aError)
 {
   return nsGetServiceByContractIDWithError(aContractID, aError);
 }
 
 class MOZ_STACK_CLASS nsGetServiceFromCategory final : public nsCOMPtr_helper
 {
 public:
-  nsGetServiceFromCategory(const char* aCategory, const char* aEntry,
+  nsGetServiceFromCategory(const nsACString& aCategory, const nsACString& aEntry,
                            nsresult* aErrorPtr)
     : mCategory(aCategory)
     , mEntry(aEntry)
     , mErrorPtr(aErrorPtr)
   {
   }
 
   virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const
     override;
 protected:
-  const char*                 mCategory;
-  const char*                 mEntry;
+  const nsCString             mCategory;
+  const nsCString             mEntry;
   nsresult*                   mErrorPtr;
 };
 
 inline const nsGetServiceFromCategory
-do_GetServiceFromCategory(const char* aCategory, const char* aEntry,
+do_GetServiceFromCategory(const nsACString& aCategory, const nsACString& aEntry,
                           nsresult* aError = 0)
 {
   return nsGetServiceFromCategory(aCategory, aEntry, aError);
 }
 
 nsresult CallGetService(const nsCID& aClass, const nsIID& aIID, void** aResult);
 
 nsresult CallGetService(const char* aContractID, const nsIID& aIID,
--- a/xpcom/io/nsDirectoryService.cpp
+++ b/xpcom/io/nsDirectoryService.cpp
@@ -353,18 +353,18 @@ nsDirectoryService::RegisterCategoryProv
   }
 
   bool more;
   while (NS_SUCCEEDED(strings->HasMore(&more)) && more) {
     nsAutoCString entry;
     strings->GetNext(entry);
 
     nsCString contractID;
-    catman->GetCategoryEntry(XPCOM_DIRECTORY_PROVIDER_CATEGORY, entry.get(),
-                             getter_Copies(contractID));
+    catman->GetCategoryEntry(XPCOM_DIRECTORY_PROVIDER_CATEGORY, entry,
+                             contractID);
 
     if (!contractID.IsVoid()) {
       nsCOMPtr<nsIDirectoryServiceProvider> provider = do_GetService(contractID.get());
       if (provider) {
         RegisterProvider(provider);
       }
     }
   }