LCOV - code coverage report
Current view: top level - xpcom/components - nsCategoryManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 195 298 65.4 %
Date: 2017-07-14 16:53:18 Functions: 39 54 72.2 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "nsICategoryManager.h"
       8             : #include "nsCategoryManager.h"
       9             : 
      10             : #include "prio.h"
      11             : #include "prlock.h"
      12             : #include "nsCOMPtr.h"
      13             : #include "nsTHashtable.h"
      14             : #include "nsClassHashtable.h"
      15             : #include "nsIFactory.h"
      16             : #include "nsIStringEnumerator.h"
      17             : #include "nsSupportsPrimitives.h"
      18             : #include "nsComponentManagerUtils.h"
      19             : #include "nsServiceManagerUtils.h"
      20             : #include "nsIObserver.h"
      21             : #include "nsIObserverService.h"
      22             : #include "nsReadableUtils.h"
      23             : #include "nsCRT.h"
      24             : #include "nsQuickSort.h"
      25             : #include "nsEnumeratorUtils.h"
      26             : #include "nsThreadUtils.h"
      27             : #include "mozilla/ArenaAllocatorExtensions.h"
      28             : #include "mozilla/MemoryReporting.h"
      29             : #include "mozilla/Services.h"
      30             : 
      31             : #include "ManifestParser.h"
      32             : #include "nsISimpleEnumerator.h"
      33             : 
      34             : using namespace mozilla;
      35             : class nsIComponentLoaderManager;
      36             : 
      37             : /*
      38             :   CategoryDatabase
      39             :   contains 0 or more 1-1 mappings of string to Category
      40             :   each Category contains 0 or more 1-1 mappings of string keys to string values
      41             : 
      42             :   In other words, the CategoryDatabase is a tree, whose root is a hashtable.
      43             :   Internal nodes (or Categories) are hashtables. Leaf nodes are strings.
      44             : 
      45             :   The leaf strings are allocated in an arena, because we assume they're not
      46             :   going to change much ;)
      47             : */
      48             : 
      49             : //
      50             : // BaseStringEnumerator is subclassed by EntryEnumerator and
      51             : // CategoryEnumerator
      52             : //
      53             : class BaseStringEnumerator
      54             :   : public nsISimpleEnumerator
      55             :   , private nsIUTF8StringEnumerator
      56             : {
      57             : public:
      58             :   NS_DECL_ISUPPORTS
      59             :   NS_DECL_NSISIMPLEENUMERATOR
      60             :   NS_DECL_NSIUTF8STRINGENUMERATOR
      61             : 
      62             : protected:
      63             :   // Callback function for NS_QuickSort to sort mArray
      64             :   static int SortCallback(const void*, const void*, void*);
      65             : 
      66          22 :   BaseStringEnumerator()
      67          22 :     : mArray(nullptr)
      68             :     , mCount(0)
      69             :     , mSimpleCurItem(0)
      70          22 :     , mStringCurItem(0)
      71             :   {
      72          22 :   }
      73             : 
      74             :   // A virtual destructor is needed here because subclasses of
      75             :   // BaseStringEnumerator do not implement their own Release() method.
      76             : 
      77          21 :   virtual ~BaseStringEnumerator()
      78          42 :   {
      79          21 :     delete [] mArray;
      80          21 :   }
      81             : 
      82             :   void Sort();
      83             : 
      84             :   const char** mArray;
      85             :   uint32_t mCount;
      86             :   uint32_t mSimpleCurItem;
      87             :   uint32_t mStringCurItem;
      88             : };
      89             : 
      90         155 : NS_IMPL_ISUPPORTS(BaseStringEnumerator, nsISimpleEnumerator,
      91             :                   nsIUTF8StringEnumerator)
      92             : 
      93             : NS_IMETHODIMP
      94           9 : BaseStringEnumerator::HasMoreElements(bool* aResult)
      95             : {
      96           9 :   *aResult = (mSimpleCurItem < mCount);
      97             : 
      98           9 :   return NS_OK;
      99             : }
     100             : 
     101             : NS_IMETHODIMP
     102          31 : BaseStringEnumerator::GetNext(nsISupports** aResult)
     103             : {
     104          31 :   if (mSimpleCurItem >= mCount) {
     105           6 :     return NS_ERROR_FAILURE;
     106             :   }
     107             : 
     108          50 :   auto* str = new nsSupportsDependentCString(mArray[mSimpleCurItem++]);
     109          25 :   if (!str) {
     110           0 :     return NS_ERROR_OUT_OF_MEMORY;
     111             :   }
     112             : 
     113          25 :   *aResult = str;
     114          25 :   NS_ADDREF(*aResult);
     115          25 :   return NS_OK;
     116             : }
     117             : 
     118             : NS_IMETHODIMP
     119          64 : BaseStringEnumerator::HasMore(bool* aResult)
     120             : {
     121          64 :   *aResult = (mStringCurItem < mCount);
     122             : 
     123          64 :   return NS_OK;
     124             : }
     125             : 
     126             : NS_IMETHODIMP
     127          52 : BaseStringEnumerator::GetNext(nsACString& aResult)
     128             : {
     129          52 :   if (mStringCurItem >= mCount) {
     130           0 :     return NS_ERROR_FAILURE;
     131             :   }
     132             : 
     133          52 :   aResult = nsDependentCString(mArray[mStringCurItem++]);
     134          52 :   return NS_OK;
     135             : }
     136             : 
     137             : int
     138         133 : BaseStringEnumerator::SortCallback(const void* aE1, const void* aE2,
     139             :                                    void* /*unused*/)
     140             : {
     141         133 :   char const* const* s1 = reinterpret_cast<char const* const*>(aE1);
     142         133 :   char const* const* s2 = reinterpret_cast<char const* const*>(aE2);
     143             : 
     144         133 :   return strcmp(*s1, *s2);
     145             : }
     146             : 
     147             : void
     148          22 : BaseStringEnumerator::Sort()
     149             : {
     150          22 :   NS_QuickSort(mArray, mCount, sizeof(mArray[0]), SortCallback, nullptr);
     151          22 : }
     152             : 
     153             : //
     154             : // EntryEnumerator is the wrapper that allows nsICategoryManager::EnumerateCategory
     155             : //
     156          85 : class EntryEnumerator
     157             :   : public BaseStringEnumerator
     158             : {
     159             : public:
     160             :   static EntryEnumerator* Create(nsTHashtable<CategoryLeaf>& aTable);
     161             : };
     162             : 
     163             : 
     164             : EntryEnumerator*
     165          22 : EntryEnumerator::Create(nsTHashtable<CategoryLeaf>& aTable)
     166             : {
     167          22 :   auto* enumObj = new EntryEnumerator();
     168          22 :   if (!enumObj) {
     169           0 :     return nullptr;
     170             :   }
     171             : 
     172          44 :   enumObj->mArray = new char const* [aTable.Count()];
     173          22 :   if (!enumObj->mArray) {
     174           0 :     delete enumObj;
     175           0 :     return nullptr;
     176             :   }
     177             : 
     178          99 :   for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) {
     179          77 :     CategoryLeaf* leaf = iter.Get();
     180          77 :     if (leaf->value) {
     181          77 :       enumObj->mArray[enumObj->mCount++] = leaf->GetKey();
     182             :     }
     183             :   }
     184             : 
     185          22 :   enumObj->Sort();
     186             : 
     187          22 :   return enumObj;
     188             : }
     189             : 
     190             : 
     191             : //
     192             : // CategoryNode implementations
     193             : //
     194             : 
     195             : CategoryNode*
     196          98 : CategoryNode::Create(CategoryAllocator* aArena)
     197             : {
     198          98 :   return new (aArena) CategoryNode();
     199             : }
     200             : 
     201             : CategoryNode::~CategoryNode() = default;
     202             : 
     203             : void*
     204          98 : CategoryNode::operator new(size_t aSize, CategoryAllocator* aArena)
     205             : {
     206          98 :   return aArena->Allocate(aSize, mozilla::fallible);
     207             : }
     208             : 
     209             : nsresult
     210         114 : CategoryNode::GetLeaf(const char* aEntryName,
     211             :                       char** aResult)
     212             : {
     213         228 :   MutexAutoLock lock(mLock);
     214         114 :   nsresult rv = NS_ERROR_NOT_AVAILABLE;
     215         114 :   CategoryLeaf* ent = mTable.GetEntry(aEntryName);
     216             : 
     217         114 :   if (ent && ent->value) {
     218         114 :     *aResult = NS_strdup(ent->value);
     219         114 :     if (*aResult) {
     220         114 :       rv = NS_OK;
     221             :     }
     222             :   }
     223             : 
     224         228 :   return rv;
     225             : }
     226             : 
     227             : nsresult
     228         442 : CategoryNode::AddLeaf(const char* aEntryName,
     229             :                       const char* aValue,
     230             :                       bool aReplace,
     231             :                       char** aResult,
     232             :                       CategoryAllocator* aArena)
     233             : {
     234         442 :   if (aResult) {
     235         442 :     *aResult = nullptr;
     236             :   }
     237             : 
     238         884 :   MutexAutoLock lock(mLock);
     239         442 :   CategoryLeaf* leaf = mTable.GetEntry(aEntryName);
     240             : 
     241         442 :   if (!leaf) {
     242         442 :     const char* arenaEntryName = ArenaStrdup(aEntryName, *aArena);
     243         442 :     if (!arenaEntryName) {
     244           0 :       return NS_ERROR_OUT_OF_MEMORY;
     245             :     }
     246             : 
     247         442 :     leaf = mTable.PutEntry(arenaEntryName);
     248         442 :     if (!leaf) {
     249           0 :       return NS_ERROR_OUT_OF_MEMORY;
     250             :     }
     251             :   }
     252             : 
     253         442 :   if (leaf->value && !aReplace) {
     254           0 :     return NS_ERROR_INVALID_ARG;
     255             :   }
     256             : 
     257         442 :   const char* arenaValue = ArenaStrdup(aValue, *aArena);
     258         442 :   if (!arenaValue) {
     259           0 :     return NS_ERROR_OUT_OF_MEMORY;
     260             :   }
     261             : 
     262         442 :   if (aResult && leaf->value) {
     263           0 :     *aResult = ToNewCString(nsDependentCString(leaf->value));
     264           0 :     if (!*aResult) {
     265           0 :       return NS_ERROR_OUT_OF_MEMORY;
     266             :     }
     267             :   }
     268             : 
     269         442 :   leaf->value = arenaValue;
     270         442 :   return NS_OK;
     271             : }
     272             : 
     273             : void
     274           0 : CategoryNode::DeleteLeaf(const char* aEntryName)
     275             : {
     276             :   // we don't throw any errors, because it normally doesn't matter
     277             :   // and it makes JS a lot cleaner
     278           0 :   MutexAutoLock lock(mLock);
     279             : 
     280             :   // we can just remove the entire hash entry without introspection
     281           0 :   mTable.RemoveEntry(aEntryName);
     282           0 : }
     283             : 
     284             : nsresult
     285          22 : CategoryNode::Enumerate(nsISimpleEnumerator** aResult)
     286             : {
     287          22 :   if (NS_WARN_IF(!aResult)) {
     288           0 :     return NS_ERROR_INVALID_ARG;
     289             :   }
     290             : 
     291          44 :   MutexAutoLock lock(mLock);
     292          22 :   EntryEnumerator* enumObj = EntryEnumerator::Create(mTable);
     293             : 
     294          22 :   if (!enumObj) {
     295           0 :     return NS_ERROR_OUT_OF_MEMORY;
     296             :   }
     297             : 
     298          22 :   *aResult = enumObj;
     299          22 :   NS_ADDREF(*aResult);
     300          22 :   return NS_OK;
     301             : }
     302             : 
     303             : size_t
     304           0 : CategoryNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
     305             : {
     306             :   // We don't measure the strings pointed to by the entries because the
     307             :   // pointers are non-owning.
     308           0 :   return mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
     309             : }
     310             : 
     311             : //
     312             : // CategoryEnumerator class
     313             : //
     314             : 
     315           0 : class CategoryEnumerator
     316             :   : public BaseStringEnumerator
     317             : {
     318             : public:
     319             :   static CategoryEnumerator* Create(nsClassHashtable<nsDepCharHashKey,
     320             :                                                      CategoryNode>& aTable);
     321             : };
     322             : 
     323             : CategoryEnumerator*
     324           0 : CategoryEnumerator::Create(nsClassHashtable<nsDepCharHashKey, CategoryNode>&
     325             :                            aTable)
     326             : {
     327           0 :   auto* enumObj = new CategoryEnumerator();
     328           0 :   if (!enumObj) {
     329           0 :     return nullptr;
     330             :   }
     331             : 
     332           0 :   enumObj->mArray = new const char* [aTable.Count()];
     333           0 :   if (!enumObj->mArray) {
     334           0 :     delete enumObj;
     335           0 :     return nullptr;
     336             :   }
     337             : 
     338           0 :   for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) {
     339             :     // if a category has no entries, we pretend it doesn't exist
     340           0 :     CategoryNode* aNode = iter.UserData();
     341           0 :     if (aNode->Count()) {
     342           0 :       const char* str = iter.Key();
     343           0 :       enumObj->mArray[enumObj->mCount++] = str;
     344             :     }
     345             :   }
     346             : 
     347           0 :   return enumObj;
     348             : }
     349             : 
     350             : 
     351             : //
     352             : // nsCategoryManager implementations
     353             : //
     354             : 
     355         131 : NS_IMPL_QUERY_INTERFACE(nsCategoryManager, nsICategoryManager, nsIMemoryReporter)
     356             : 
     357             : NS_IMETHODIMP_(MozExternalRefCountType)
     358         109 : nsCategoryManager::AddRef()
     359             : {
     360         109 :   return 2;
     361             : }
     362             : 
     363             : NS_IMETHODIMP_(MozExternalRefCountType)
     364          94 : nsCategoryManager::Release()
     365             : {
     366          94 :   return 1;
     367             : }
     368             : 
     369             : nsCategoryManager* nsCategoryManager::gCategoryManager;
     370             : 
     371             : /* static */ nsCategoryManager*
     372         451 : nsCategoryManager::GetSingleton()
     373             : {
     374         451 :   if (!gCategoryManager) {
     375           3 :     gCategoryManager = new nsCategoryManager();
     376             :   }
     377         451 :   return gCategoryManager;
     378             : }
     379             : 
     380             : /* static */ void
     381           0 : nsCategoryManager::Destroy()
     382             : {
     383             :   // The nsMemoryReporterManager gets destroyed before the nsCategoryManager,
     384             :   // so we don't need to unregister the nsCategoryManager as a memory reporter.
     385             :   // In debug builds we assert that unregistering fails, as a way (imperfect
     386             :   // but better than nothing) of testing the "destroyed before" part.
     387           0 :   MOZ_ASSERT(NS_FAILED(UnregisterWeakMemoryReporter(gCategoryManager)));
     388             : 
     389           0 :   delete gCategoryManager;
     390           0 :   gCategoryManager = nullptr;
     391           0 : }
     392             : 
     393             : nsresult
     394           3 : nsCategoryManager::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
     395             : {
     396           3 :   if (aOuter) {
     397           0 :     return NS_ERROR_NO_AGGREGATION;
     398             :   }
     399             : 
     400           3 :   return GetSingleton()->QueryInterface(aIID, aResult);
     401             : }
     402             : 
     403           3 : nsCategoryManager::nsCategoryManager()
     404             :   : mArena()
     405             :   , mTable()
     406             :   , mLock("nsCategoryManager")
     407           3 :   , mSuppressNotifications(false)
     408             : {
     409           3 : }
     410             : 
     411             : void
     412           3 : nsCategoryManager::InitMemoryReporter()
     413             : {
     414           3 :   RegisterWeakMemoryReporter(this);
     415           3 : }
     416             : 
     417           0 : nsCategoryManager::~nsCategoryManager()
     418             : {
     419             :   // the hashtable contains entries that must be deleted before the arena is
     420             :   // destroyed, or else you will have PRLocks undestroyed and other Really
     421             :   // Bad Stuff (TM)
     422           0 :   mTable.Clear();
     423           0 : }
     424             : 
     425             : inline CategoryNode*
     426         600 : nsCategoryManager::get_category(const char* aName)
     427             : {
     428             :   CategoryNode* node;
     429         600 :   if (!mTable.Get(aName, &node)) {
     430         120 :     return nullptr;
     431             :   }
     432         480 :   return node;
     433             : }
     434             : 
     435           0 : MOZ_DEFINE_MALLOC_SIZE_OF(CategoryManagerMallocSizeOf)
     436             : 
     437             : NS_IMETHODIMP
     438           0 : nsCategoryManager::CollectReports(nsIHandleReportCallback* aHandleReport,
     439             :                                   nsISupports* aData, bool aAnonymize)
     440             : {
     441           0 :   MOZ_COLLECT_REPORT(
     442             :     "explicit/xpcom/category-manager", KIND_HEAP, UNITS_BYTES,
     443             :     SizeOfIncludingThis(CategoryManagerMallocSizeOf),
     444           0 :     "Memory used for the XPCOM category manager.");
     445             : 
     446           0 :   return NS_OK;
     447             : }
     448             : 
     449             : size_t
     450           0 : nsCategoryManager::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
     451             : {
     452           0 :   size_t n = aMallocSizeOf(this);
     453             : 
     454           0 :   n += mArena.SizeOfExcludingThis(aMallocSizeOf);
     455             : 
     456           0 :   n += mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
     457           0 :   for (auto iter = mTable.ConstIter(); !iter.Done(); iter.Next()) {
     458             :     // We don't measure the key string because it's a non-owning pointer.
     459           0 :     n += iter.Data()->SizeOfExcludingThis(aMallocSizeOf);
     460             :   }
     461             : 
     462           0 :   return n;
     463             : }
     464             : 
     465             : namespace {
     466             : 
     467          12 : class CategoryNotificationRunnable : public Runnable
     468             : {
     469             : public:
     470           5 :   CategoryNotificationRunnable(nsISupports* aSubject,
     471             :                                const char* aTopic,
     472             :                                const char* aData)
     473           5 :     : Runnable("CategoryNotificationRunnable")
     474             :     , mSubject(aSubject)
     475             :     , mTopic(aTopic)
     476           5 :     , mData(aData)
     477             :   {
     478           5 :   }
     479             : 
     480             :   NS_DECL_NSIRUNNABLE
     481             : 
     482             : private:
     483             :   nsCOMPtr<nsISupports> mSubject;
     484             :   const char* mTopic;
     485             :   NS_ConvertUTF8toUTF16 mData;
     486             : };
     487             : 
     488             : NS_IMETHODIMP
     489           4 : CategoryNotificationRunnable::Run()
     490             : {
     491             :   nsCOMPtr<nsIObserverService> observerService =
     492           8 :     mozilla::services::GetObserverService();
     493           4 :   if (observerService) {
     494           4 :     observerService->NotifyObservers(mSubject, mTopic, mData.get());
     495             :   }
     496             : 
     497           8 :   return NS_OK;
     498             : }
     499             : 
     500             : } // namespace
     501             : 
     502             : 
     503             : void
     504         442 : nsCategoryManager::NotifyObservers(const char* aTopic,
     505             :                                    const char* aCategoryName,
     506             :                                    const char* aEntryName)
     507             : {
     508         442 :   if (mSuppressNotifications) {
     509         874 :     return;
     510             :   }
     511             : 
     512          10 :   RefPtr<CategoryNotificationRunnable> r;
     513             : 
     514           5 :   if (aEntryName) {
     515             :     nsCOMPtr<nsISupportsCString> entry =
     516          10 :       do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
     517           5 :     if (!entry) {
     518           0 :       return;
     519             :     }
     520             : 
     521           5 :     nsresult rv = entry->SetData(nsDependentCString(aEntryName));
     522           5 :     if (NS_FAILED(rv)) {
     523           0 :       return;
     524             :     }
     525             : 
     526          10 :     r = new CategoryNotificationRunnable(entry, aTopic, aCategoryName);
     527             :   } else {
     528             :     r = new CategoryNotificationRunnable(NS_ISUPPORTS_CAST(nsICategoryManager*,
     529             :                                                            this),
     530           0 :                                          aTopic, aCategoryName);
     531             :   }
     532             : 
     533           5 :   NS_DispatchToMainThread(r);
     534             : }
     535             : 
     536             : NS_IMETHODIMP
     537         115 : nsCategoryManager::GetCategoryEntry(const char* aCategoryName,
     538             :                                     const char* aEntryName,
     539             :                                     char** aResult)
     540             : {
     541         345 :   if (NS_WARN_IF(!aCategoryName) ||
     542         230 :       NS_WARN_IF(!aEntryName) ||
     543         115 :       NS_WARN_IF(!aResult)) {
     544           0 :     return NS_ERROR_INVALID_ARG;
     545             :   }
     546             : 
     547         115 :   nsresult status = NS_ERROR_NOT_AVAILABLE;
     548             : 
     549             :   CategoryNode* category;
     550             :   {
     551         230 :     MutexAutoLock lock(mLock);
     552         115 :     category = get_category(aCategoryName);
     553             :   }
     554             : 
     555         115 :   if (category) {
     556         114 :     status = category->GetLeaf(aEntryName, aResult);
     557             :   }
     558             : 
     559         115 :   return status;
     560             : }
     561             : 
     562             : NS_IMETHODIMP
     563           3 : nsCategoryManager::AddCategoryEntry(const char* aCategoryName,
     564             :                                     const char* aEntryName,
     565             :                                     const char* aValue,
     566             :                                     bool aPersist,
     567             :                                     bool aReplace,
     568             :                                     char** aResult)
     569             : {
     570           3 :   if (aPersist) {
     571           0 :     NS_ERROR("Category manager doesn't support persistence.");
     572           0 :     return NS_ERROR_INVALID_ARG;
     573             :   }
     574             : 
     575           3 :   AddCategoryEntry(aCategoryName, aEntryName, aValue, aReplace, aResult);
     576           3 :   return NS_OK;
     577             : }
     578             : 
     579             : void
     580         442 : nsCategoryManager::AddCategoryEntry(const char* aCategoryName,
     581             :                                     const char* aEntryName,
     582             :                                     const char* aValue,
     583             :                                     bool aReplace,
     584             :                                     char** aOldValue)
     585             : {
     586         442 :   if (aOldValue) {
     587           3 :     *aOldValue = nullptr;
     588             :   }
     589             : 
     590             :   // Before we can insert a new entry, we'll need to
     591             :   //  find the |CategoryNode| to put it in...
     592             :   CategoryNode* category;
     593             :   {
     594         884 :     MutexAutoLock lock(mLock);
     595         442 :     category = get_category(aCategoryName);
     596             : 
     597         442 :     if (!category) {
     598             :       // That category doesn't exist yet; let's make it.
     599          98 :       category = CategoryNode::Create(&mArena);
     600             : 
     601          98 :       char* categoryName = ArenaStrdup(aCategoryName, mArena);
     602          98 :       mTable.Put(categoryName, category);
     603             :     }
     604             :   }
     605             : 
     606         442 :   if (!category) {
     607           0 :     return;
     608             :   }
     609             : 
     610             :   // We will need the return value of AddLeaf even if the called doesn't want it
     611         442 :   char* oldEntry = nullptr;
     612             : 
     613         442 :   nsresult rv = category->AddLeaf(aEntryName,
     614             :                                   aValue,
     615             :                                   aReplace,
     616             :                                   &oldEntry,
     617         442 :                                   &mArena);
     618             : 
     619         442 :   if (NS_SUCCEEDED(rv)) {
     620         442 :     if (oldEntry) {
     621             :       NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID,
     622           0 :                       aCategoryName, aEntryName);
     623             :     }
     624             :     NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID,
     625         442 :                     aCategoryName, aEntryName);
     626             : 
     627         442 :     if (aOldValue) {
     628           3 :       *aOldValue = oldEntry;
     629             :     } else {
     630         439 :       free(oldEntry);
     631             :     }
     632             :   }
     633             : }
     634             : 
     635             : NS_IMETHODIMP
     636           0 : nsCategoryManager::DeleteCategoryEntry(const char* aCategoryName,
     637             :                                        const char* aEntryName,
     638             :                                        bool aDontPersist)
     639             : {
     640           0 :   if (NS_WARN_IF(!aCategoryName) ||
     641           0 :       NS_WARN_IF(!aEntryName)) {
     642           0 :     return NS_ERROR_INVALID_ARG;
     643             :   }
     644             : 
     645             :   /*
     646             :     Note: no errors are reported since failure to delete
     647             :     probably won't hurt you, and returning errors seriously
     648             :     inconveniences JS clients
     649             :   */
     650             : 
     651             :   CategoryNode* category;
     652             :   {
     653           0 :     MutexAutoLock lock(mLock);
     654           0 :     category = get_category(aCategoryName);
     655             :   }
     656             : 
     657           0 :   if (category) {
     658           0 :     category->DeleteLeaf(aEntryName);
     659             : 
     660             :     NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID,
     661           0 :                     aCategoryName, aEntryName);
     662             :   }
     663             : 
     664           0 :   return NS_OK;
     665             : }
     666             : 
     667             : NS_IMETHODIMP
     668           0 : nsCategoryManager::DeleteCategory(const char* aCategoryName)
     669             : {
     670           0 :   if (NS_WARN_IF(!aCategoryName)) {
     671           0 :     return NS_ERROR_INVALID_ARG;
     672             :   }
     673             : 
     674             :   // the categories are arena-allocated, so we don't
     675             :   // actually delete them. We just remove all of the
     676             :   // leaf nodes.
     677             : 
     678             :   CategoryNode* category;
     679             :   {
     680           0 :     MutexAutoLock lock(mLock);
     681           0 :     category = get_category(aCategoryName);
     682             :   }
     683             : 
     684           0 :   if (category) {
     685           0 :     category->Clear();
     686             :     NotifyObservers(NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID,
     687           0 :                     aCategoryName, nullptr);
     688             :   }
     689             : 
     690           0 :   return NS_OK;
     691             : }
     692             : 
     693             : NS_IMETHODIMP
     694          43 : nsCategoryManager::EnumerateCategory(const char* aCategoryName,
     695             :                                      nsISimpleEnumerator** aResult)
     696             : {
     697          86 :   if (NS_WARN_IF(!aCategoryName) ||
     698          43 :       NS_WARN_IF(!aResult)) {
     699           0 :     return NS_ERROR_INVALID_ARG;
     700             :   }
     701             : 
     702             :   CategoryNode* category;
     703             :   {
     704          86 :     MutexAutoLock lock(mLock);
     705          43 :     category = get_category(aCategoryName);
     706             :   }
     707             : 
     708          43 :   if (!category) {
     709          21 :     return NS_NewEmptyEnumerator(aResult);
     710             :   }
     711             : 
     712          22 :   return category->Enumerate(aResult);
     713             : }
     714             : 
     715             : NS_IMETHODIMP
     716           0 : nsCategoryManager::EnumerateCategories(nsISimpleEnumerator** aResult)
     717             : {
     718           0 :   if (NS_WARN_IF(!aResult)) {
     719           0 :     return NS_ERROR_INVALID_ARG;
     720             :   }
     721             : 
     722           0 :   MutexAutoLock lock(mLock);
     723           0 :   CategoryEnumerator* enumObj = CategoryEnumerator::Create(mTable);
     724             : 
     725           0 :   if (!enumObj) {
     726           0 :     return NS_ERROR_OUT_OF_MEMORY;
     727             :   }
     728             : 
     729           0 :   *aResult = enumObj;
     730           0 :   NS_ADDREF(*aResult);
     731           0 :   return NS_OK;
     732             : }
     733             : 
     734             : struct writecat_struct
     735             : {
     736             :   PRFileDesc* fd;
     737             :   bool        success;
     738             : };
     739             : 
     740             : nsresult
     741           6 : nsCategoryManager::SuppressNotifications(bool aSuppress)
     742             : {
     743           6 :   mSuppressNotifications = aSuppress;
     744           6 :   return NS_OK;
     745             : }
     746             : 
     747             : /*
     748             :  * CreateServicesFromCategory()
     749             :  *
     750             :  * Given a category, this convenience functions enumerates the category and
     751             :  * creates a service of every CID or ContractID registered under the category.
     752             :  * If observerTopic is non null and the service implements nsIObserver,
     753             :  * this will attempt to notify the observer with the origin, observerTopic string
     754             :  * as parameter.
     755             :  */
     756             : void
     757           9 : NS_CreateServicesFromCategory(const char* aCategory,
     758             :                               nsISupports* aOrigin,
     759             :                               const char* aObserverTopic,
     760             :                               const char16_t* aObserverData)
     761             : {
     762             :   nsresult rv;
     763             : 
     764             :   nsCOMPtr<nsICategoryManager> categoryManager =
     765          18 :     do_GetService("@mozilla.org/categorymanager;1");
     766           9 :   if (!categoryManager) {
     767           0 :     return;
     768             :   }
     769             : 
     770          18 :   nsCOMPtr<nsISimpleEnumerator> enumerator;
     771          18 :   rv = categoryManager->EnumerateCategory(aCategory,
     772          18 :                                           getter_AddRefs(enumerator));
     773           9 :   if (NS_FAILED(rv)) {
     774           0 :     return;
     775             :   }
     776             : 
     777             :   nsCOMPtr<nsIUTF8StringEnumerator> senumerator =
     778          18 :     do_QueryInterface(enumerator);
     779           9 :   if (!senumerator) {
     780           0 :     NS_WARNING("Category enumerator doesn't support nsIUTF8StringEnumerator.");
     781           0 :     return;
     782             :   }
     783             : 
     784             :   bool hasMore;
     785          39 :   while (NS_SUCCEEDED(senumerator->HasMore(&hasMore)) && hasMore) {
     786             :     // From here on just skip any error we get.
     787          30 :     nsAutoCString entryString;
     788          15 :     if (NS_FAILED(senumerator->GetNext(entryString))) {
     789           0 :       continue;
     790             :     }
     791             : 
     792          30 :     nsXPIDLCString contractID;
     793          30 :     rv = categoryManager->GetCategoryEntry(aCategory, entryString.get(),
     794          30 :                                            getter_Copies(contractID));
     795          15 :     if (NS_FAILED(rv)) {
     796           0 :       continue;
     797             :     }
     798             : 
     799          30 :     nsCOMPtr<nsISupports> instance = do_GetService(contractID);
     800          15 :     if (!instance) {
     801           0 :       LogMessage("While creating services from category '%s', could not create service for entry '%s', contract ID '%s'",
     802           0 :                  aCategory, entryString.get(), contractID.get());
     803           0 :       continue;
     804             :     }
     805             : 
     806          15 :     if (aObserverTopic) {
     807             :       // try an observer, if it implements it.
     808          30 :       nsCOMPtr<nsIObserver> observer = do_QueryInterface(instance);
     809          15 :       if (observer) {
     810          28 :         observer->Observe(aOrigin, aObserverTopic,
     811          28 :                           aObserverData ? aObserverData : u"");
     812             :       } else {
     813           1 :         LogMessage("While creating services from category '%s', service for entry '%s', contract ID '%s' does not implement nsIObserver.",
     814           1 :                    aCategory, entryString.get(), contractID.get());
     815             :       }
     816             :     }
     817             :   }
     818             : }

Generated by: LCOV version 1.13