xpcom/components/nsCategoryManager.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #define PL_ARENA_CONST_ALIGN_MASK 7
     8 #include "nsICategoryManager.h"
     9 #include "nsCategoryManager.h"
    11 #include "plarena.h"
    12 #include "prio.h"
    13 #include "prprf.h"
    14 #include "prlock.h"
    15 #include "nsCOMPtr.h"
    16 #include "nsTHashtable.h"
    17 #include "nsClassHashtable.h"
    18 #include "nsIFactory.h"
    19 #include "nsIStringEnumerator.h"
    20 #include "nsSupportsPrimitives.h"
    21 #include "nsComponentManagerUtils.h"
    22 #include "nsServiceManagerUtils.h"
    23 #include "nsIObserver.h"
    24 #include "nsIObserverService.h"
    25 #include "nsReadableUtils.h"
    26 #include "nsCRT.h"
    27 #include "nsQuickSort.h"
    28 #include "nsEnumeratorUtils.h"
    29 #include "nsThreadUtils.h"
    30 #include "mozilla/MemoryReporting.h"
    31 #include "mozilla/Services.h"
    33 #include "ManifestParser.h"
    34 #include "nsISimpleEnumerator.h"
    36 using namespace mozilla;
    37 class nsIComponentLoaderManager;
    39 /*
    40   CategoryDatabase
    41   contains 0 or more 1-1 mappings of string to Category
    42   each Category contains 0 or more 1-1 mappings of string keys to string values
    44   In other words, the CategoryDatabase is a tree, whose root is a hashtable.
    45   Internal nodes (or Categories) are hashtables. Leaf nodes are strings.
    47   The leaf strings are allocated in an arena, because we assume they're not
    48   going to change much ;)
    49 */
    51 #define NS_CATEGORYMANAGER_ARENA_SIZE (1024 * 8)
    53 // pulled in from nsComponentManager.cpp
    54 char* ArenaStrdup(const char* s, PLArenaPool* aArena);
    56 //
    57 // BaseStringEnumerator is subclassed by EntryEnumerator and
    58 // CategoryEnumerator
    59 //
    60 class BaseStringEnumerator
    61   : public nsISimpleEnumerator,
    62     private nsIUTF8StringEnumerator
    63 {
    64 public:
    65   NS_DECL_ISUPPORTS
    66   NS_DECL_NSISIMPLEENUMERATOR
    67   NS_DECL_NSIUTF8STRINGENUMERATOR
    69 protected:
    70   // Callback function for NS_QuickSort to sort mArray
    71   static int SortCallback(const void *, const void *, void *);
    73   BaseStringEnumerator()
    74     : mArray(nullptr),
    75       mCount(0),
    76       mSimpleCurItem(0),
    77       mStringCurItem(0) { }
    79   // A virtual destructor is needed here because subclasses of
    80   // BaseStringEnumerator do not implement their own Release() method.
    82   virtual ~BaseStringEnumerator()
    83   {
    84     if (mArray)
    85       delete[] mArray;
    86   }
    88   void Sort();
    90   const char** mArray;
    91   uint32_t mCount;
    92   uint32_t mSimpleCurItem;
    93   uint32_t mStringCurItem;
    94 };
    96 NS_IMPL_ISUPPORTS(BaseStringEnumerator, nsISimpleEnumerator, nsIUTF8StringEnumerator)
    98 NS_IMETHODIMP
    99 BaseStringEnumerator::HasMoreElements(bool *_retval)
   100 {
   101   *_retval = (mSimpleCurItem < mCount);
   103   return NS_OK;
   104 }
   106 NS_IMETHODIMP
   107 BaseStringEnumerator::GetNext(nsISupports **_retval)
   108 {
   109   if (mSimpleCurItem >= mCount)
   110     return NS_ERROR_FAILURE;
   112   nsSupportsDependentCString* str =
   113     new nsSupportsDependentCString(mArray[mSimpleCurItem++]);
   114   if (!str)
   115     return NS_ERROR_OUT_OF_MEMORY;
   117   *_retval = str;
   118   NS_ADDREF(*_retval);
   119   return NS_OK;
   120 }
   122 NS_IMETHODIMP
   123 BaseStringEnumerator::HasMore(bool *_retval)
   124 {
   125   *_retval = (mStringCurItem < mCount);
   127   return NS_OK;
   128 }
   130 NS_IMETHODIMP
   131 BaseStringEnumerator::GetNext(nsACString& _retval)
   132 {
   133   if (mStringCurItem >= mCount)
   134     return NS_ERROR_FAILURE;
   136   _retval = nsDependentCString(mArray[mStringCurItem++]);
   137   return NS_OK;
   138 }
   140 int
   141 BaseStringEnumerator::SortCallback(const void *e1, const void *e2,
   142                                    void * /*unused*/)
   143 {
   144   char const *const *s1 = reinterpret_cast<char const *const *>(e1);
   145   char const *const *s2 = reinterpret_cast<char const *const *>(e2);
   147   return strcmp(*s1, *s2);
   148 }
   150 void
   151 BaseStringEnumerator::Sort()
   152 {
   153   NS_QuickSort(mArray, mCount, sizeof(mArray[0]), SortCallback, nullptr);
   154 }
   156 //
   157 // EntryEnumerator is the wrapper that allows nsICategoryManager::EnumerateCategory
   158 //
   159 class EntryEnumerator
   160   : public BaseStringEnumerator
   161 {
   162 public:
   163   static EntryEnumerator* Create(nsTHashtable<CategoryLeaf>& aTable);
   165 private:
   166   static PLDHashOperator
   167     enumfunc_createenumerator(CategoryLeaf* aLeaf, void* userArg);
   168 };
   171 PLDHashOperator
   172 EntryEnumerator::enumfunc_createenumerator(CategoryLeaf* aLeaf, void* userArg)
   173 {
   174   EntryEnumerator* mythis = static_cast<EntryEnumerator*>(userArg);
   175   if (aLeaf->value)
   176     mythis->mArray[mythis->mCount++] = aLeaf->GetKey();
   178   return PL_DHASH_NEXT;
   179 }
   181 EntryEnumerator*
   182 EntryEnumerator::Create(nsTHashtable<CategoryLeaf>& aTable)
   183 {
   184   EntryEnumerator* enumObj = new EntryEnumerator();
   185   if (!enumObj)
   186     return nullptr;
   188   enumObj->mArray = new char const* [aTable.Count()];
   189   if (!enumObj->mArray) {
   190     delete enumObj;
   191     return nullptr;
   192   }
   194   aTable.EnumerateEntries(enumfunc_createenumerator, enumObj);
   196   enumObj->Sort();
   198   return enumObj;
   199 }
   202 //
   203 // CategoryNode implementations
   204 //
   206 CategoryNode*
   207 CategoryNode::Create(PLArenaPool* aArena)
   208 {
   209   CategoryNode* node = new(aArena) CategoryNode();
   210   if (!node)
   211     return nullptr;
   213   return node;
   214 }
   216 CategoryNode::~CategoryNode()
   217 {
   218 }
   220 void*
   221 CategoryNode::operator new(size_t aSize, PLArenaPool* aArena)
   222 {
   223   void* p;
   224   PL_ARENA_ALLOCATE(p, aArena, aSize);
   225   return p;
   226 }
   228 NS_METHOD
   229 CategoryNode::GetLeaf(const char* aEntryName,
   230                       char** _retval)
   231 {
   232   MutexAutoLock lock(mLock);
   233   nsresult rv = NS_ERROR_NOT_AVAILABLE;
   234   CategoryLeaf* ent =
   235     mTable.GetEntry(aEntryName);
   237   if (ent && ent->value) {
   238     *_retval = NS_strdup(ent->value);
   239     if (*_retval)
   240       rv = NS_OK;
   241   }
   243   return rv;
   244 }
   246 NS_METHOD
   247 CategoryNode::AddLeaf(const char* aEntryName,
   248                       const char* aValue,
   249                       bool aReplace,
   250                       char** _retval,
   251                       PLArenaPool* aArena)
   252 {
   253   if (_retval)
   254     *_retval = nullptr;
   256   MutexAutoLock lock(mLock);
   257   CategoryLeaf* leaf = 
   258     mTable.GetEntry(aEntryName);
   260   if (!leaf) {
   261     const char* arenaEntryName = ArenaStrdup(aEntryName, aArena);
   262     if (!arenaEntryName)
   263       return NS_ERROR_OUT_OF_MEMORY;
   265     leaf = mTable.PutEntry(arenaEntryName);
   266     if (!leaf)
   267       return NS_ERROR_OUT_OF_MEMORY;
   268   }
   270   if (leaf->value && !aReplace)
   271     return NS_ERROR_INVALID_ARG;
   273   const char* arenaValue = ArenaStrdup(aValue, aArena);
   274   if (!arenaValue)
   275     return NS_ERROR_OUT_OF_MEMORY;
   277   if (_retval && leaf->value) {
   278     *_retval = ToNewCString(nsDependentCString(leaf->value));
   279     if (!*_retval)
   280       return NS_ERROR_OUT_OF_MEMORY;
   281   }
   283   leaf->value = arenaValue;
   284   return NS_OK;
   285 }
   287 void
   288 CategoryNode::DeleteLeaf(const char* aEntryName)
   289 {
   290   // we don't throw any errors, because it normally doesn't matter
   291   // and it makes JS a lot cleaner
   292   MutexAutoLock lock(mLock);
   294   // we can just remove the entire hash entry without introspection
   295   mTable.RemoveEntry(aEntryName);
   296 }
   298 NS_METHOD 
   299 CategoryNode::Enumerate(nsISimpleEnumerator **_retval)
   300 {
   301   if (NS_WARN_IF(!_retval))
   302     return NS_ERROR_INVALID_ARG;
   304   MutexAutoLock lock(mLock);
   305   EntryEnumerator* enumObj = EntryEnumerator::Create(mTable);
   307   if (!enumObj)
   308     return NS_ERROR_OUT_OF_MEMORY;
   310   *_retval = enumObj;
   311   NS_ADDREF(*_retval);
   312   return NS_OK;
   313 }
   315 size_t
   316 CategoryNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
   317 {
   318     // We don't measure the strings pointed to by the entries because the
   319     // pointers are non-owning.
   320     return mTable.SizeOfExcludingThis(nullptr, aMallocSizeOf);
   321 }
   323 struct persistent_userstruct {
   324   PRFileDesc* fd;
   325   const char* categoryName;
   326   bool        success;
   327 };
   329 PLDHashOperator
   330 enumfunc_pentries(CategoryLeaf* aLeaf, void* userArg)
   331 {
   332   persistent_userstruct* args =
   333     static_cast<persistent_userstruct*>(userArg);
   335   PLDHashOperator status = PL_DHASH_NEXT;
   337   if (aLeaf->value) {
   338     if (PR_fprintf(args->fd,
   339                    "%s,%s,%s\n",
   340                    args->categoryName,
   341                    aLeaf->GetKey(),
   342                    aLeaf->value) == (uint32_t) -1) {
   343       args->success = false;
   344       status = PL_DHASH_STOP;
   345     }
   346   }
   348   return status;
   349 }
   351 //
   352 // CategoryEnumerator class
   353 //
   355 class CategoryEnumerator
   356   : public BaseStringEnumerator
   357 {
   358 public:
   359   static CategoryEnumerator* Create(nsClassHashtable<nsDepCharHashKey, CategoryNode>& aTable);
   361 private:
   362   static PLDHashOperator
   363   enumfunc_createenumerator(const char* aStr,
   364                             CategoryNode* aNode,
   365                             void* userArg);
   366 };
   368 CategoryEnumerator*
   369 CategoryEnumerator::Create(nsClassHashtable<nsDepCharHashKey, CategoryNode>& aTable)
   370 {
   371   CategoryEnumerator* enumObj = new CategoryEnumerator();
   372   if (!enumObj)
   373     return nullptr;
   375   enumObj->mArray = new const char* [aTable.Count()];
   376   if (!enumObj->mArray) {
   377     delete enumObj;
   378     return nullptr;
   379   }
   381   aTable.EnumerateRead(enumfunc_createenumerator, enumObj);
   383   return enumObj;
   384 }
   386 PLDHashOperator
   387 CategoryEnumerator::enumfunc_createenumerator(const char* aStr, CategoryNode* aNode, void* userArg)
   388 {
   389   CategoryEnumerator* mythis = static_cast<CategoryEnumerator*>(userArg);
   391   // if a category has no entries, we pretend it doesn't exist
   392   if (aNode->Count())
   393     mythis->mArray[mythis->mCount++] = aStr;
   395   return PL_DHASH_NEXT;
   396 }
   399 //
   400 // nsCategoryManager implementations
   401 //
   403 NS_IMPL_QUERY_INTERFACE(nsCategoryManager, nsICategoryManager, nsIMemoryReporter)
   405 NS_IMETHODIMP_(MozExternalRefCountType)
   406 nsCategoryManager::AddRef()
   407 {
   408   return 2;
   409 }
   411 NS_IMETHODIMP_(MozExternalRefCountType)
   412 nsCategoryManager::Release()
   413 {
   414   return 1;
   415 }
   417 nsCategoryManager* nsCategoryManager::gCategoryManager;
   419 /* static */ nsCategoryManager*
   420 nsCategoryManager::GetSingleton()
   421 {
   422   if (!gCategoryManager)
   423     gCategoryManager = new nsCategoryManager();
   424   return gCategoryManager;
   425 }
   427 /* static */ void
   428 nsCategoryManager::Destroy()
   429 {
   430   delete gCategoryManager;
   431   gCategoryManager = nullptr;
   432 }
   434 nsresult
   435 nsCategoryManager::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
   436 {
   437   if (aOuter)
   438     return NS_ERROR_NO_AGGREGATION;
   440   return GetSingleton()->QueryInterface(aIID, aResult);
   441 }
   443 nsCategoryManager::nsCategoryManager()
   444   : mLock("nsCategoryManager")
   445   , mSuppressNotifications(false)
   446 {
   447   PL_INIT_ARENA_POOL(&mArena, "CategoryManagerArena",
   448                      NS_CATEGORYMANAGER_ARENA_SIZE);
   449 }
   451 void
   452 nsCategoryManager::InitMemoryReporter()
   453 {
   454   RegisterWeakMemoryReporter(this);
   455 }
   457 nsCategoryManager::~nsCategoryManager()
   458 {
   459   UnregisterWeakMemoryReporter(this);
   461   // the hashtable contains entries that must be deleted before the arena is
   462   // destroyed, or else you will have PRLocks undestroyed and other Really
   463   // Bad Stuff (TM)
   464   mTable.Clear();
   466   PL_FinishArenaPool(&mArena);
   467 }
   469 inline CategoryNode*
   470 nsCategoryManager::get_category(const char* aName) {
   471   CategoryNode* node;
   472   if (!mTable.Get(aName, &node)) {
   473     return nullptr;
   474   }
   475   return node;
   476 }
   478 MOZ_DEFINE_MALLOC_SIZE_OF(CategoryManagerMallocSizeOf)
   480 NS_IMETHODIMP
   481 nsCategoryManager::CollectReports(nsIHandleReportCallback* aHandleReport,
   482                                   nsISupports* aData)
   483 {
   484   return MOZ_COLLECT_REPORT(
   485     "explicit/xpcom/category-manager", KIND_HEAP, UNITS_BYTES,
   486     SizeOfIncludingThis(CategoryManagerMallocSizeOf),
   487     "Memory used for the XPCOM category manager.");
   488 }
   490 static size_t
   491 SizeOfCategoryManagerTableEntryExcludingThis(nsDepCharHashKey::KeyType aKey,
   492                                              const nsAutoPtr<CategoryNode> &aData,
   493                                              MallocSizeOf aMallocSizeOf,
   494                                              void* aUserArg)
   495 {
   496     // We don't measure the string pointed to by aKey because it's a non-owning
   497     // pointer.
   498     return aData.get()->SizeOfExcludingThis(aMallocSizeOf);
   499 }
   501 size_t
   502 nsCategoryManager::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
   503 {
   504   size_t n = aMallocSizeOf(this);
   506   n += PL_SizeOfArenaPoolExcludingPool(&mArena, aMallocSizeOf);
   508   n += mTable.SizeOfExcludingThis(SizeOfCategoryManagerTableEntryExcludingThis,
   509                                   aMallocSizeOf);
   511   return n;
   512 }
   514 namespace {
   516 class CategoryNotificationRunnable : public nsRunnable
   517 {
   518 public:
   519   CategoryNotificationRunnable(nsISupports* aSubject,
   520                                const char* aTopic,
   521                                const char* aData)
   522     : mSubject(aSubject)
   523     , mTopic(aTopic)
   524     , mData(aData)
   525   { }
   527   NS_DECL_NSIRUNNABLE
   529 private:
   530   nsCOMPtr<nsISupports> mSubject;
   531   const char* mTopic;
   532   NS_ConvertUTF8toUTF16 mData;
   533 };
   535 NS_IMETHODIMP
   536 CategoryNotificationRunnable::Run()
   537 {
   538   nsCOMPtr<nsIObserverService> observerService =
   539     mozilla::services::GetObserverService();
   540   if (observerService)
   541     observerService->NotifyObservers(mSubject, mTopic, mData.get());
   543   return NS_OK;
   544 }
   546 } // anonymous namespace
   549 void
   550 nsCategoryManager::NotifyObservers( const char *aTopic,
   551                                     const char *aCategoryName,
   552                                     const char *aEntryName )
   553 {
   554   if (mSuppressNotifications)
   555     return;
   557   nsRefPtr<CategoryNotificationRunnable> r;
   559   if (aEntryName) {
   560     nsCOMPtr<nsISupportsCString> entry
   561       (do_CreateInstance (NS_SUPPORTS_CSTRING_CONTRACTID));
   562     if (!entry)
   563       return;
   565     nsresult rv = entry->SetData(nsDependentCString(aEntryName));
   566     if (NS_FAILED(rv))
   567       return;
   569     r = new CategoryNotificationRunnable(entry, aTopic, aCategoryName);
   570   } else {
   571     r = new CategoryNotificationRunnable(
   572               NS_ISUPPORTS_CAST(nsICategoryManager*, this),
   573               aTopic, aCategoryName);
   574   }
   576   NS_DispatchToMainThread(r);
   577 }
   579 NS_IMETHODIMP
   580 nsCategoryManager::GetCategoryEntry( const char *aCategoryName,
   581                                      const char *aEntryName,
   582                                      char **_retval )
   583 {
   584   if (NS_WARN_IF(!aCategoryName) ||
   585       NS_WARN_IF(!aEntryName) ||
   586       NS_WARN_IF(!_retval))
   587     return NS_ERROR_INVALID_ARG;;
   589   nsresult status = NS_ERROR_NOT_AVAILABLE;
   591   CategoryNode* category;
   592   {
   593     MutexAutoLock lock(mLock);
   594     category = get_category(aCategoryName);
   595   }
   597   if (category) {
   598     status = category->GetLeaf(aEntryName, _retval);
   599   }
   601   return status;
   602 }
   604 NS_IMETHODIMP
   605 nsCategoryManager::AddCategoryEntry( const char *aCategoryName,
   606                                      const char *aEntryName,
   607                                      const char *aValue,
   608                                      bool aPersist,
   609                                      bool aReplace,
   610                                      char **_retval )
   611 {
   612   if (aPersist) {
   613     NS_ERROR("Category manager doesn't support persistence.");
   614     return NS_ERROR_INVALID_ARG;
   615   }
   617   AddCategoryEntry(aCategoryName, aEntryName, aValue, aReplace, _retval);
   618   return NS_OK;
   619 }
   621 void
   622 nsCategoryManager::AddCategoryEntry(const char *aCategoryName,
   623                                     const char *aEntryName,
   624                                     const char *aValue,
   625                                     bool aReplace,
   626                                     char** aOldValue)
   627 {
   628   if (aOldValue)
   629     *aOldValue = nullptr;
   631   // Before we can insert a new entry, we'll need to
   632   //  find the |CategoryNode| to put it in...
   633   CategoryNode* category;
   634   {
   635     MutexAutoLock lock(mLock);
   636     category = get_category(aCategoryName);
   638     if (!category) {
   639       // That category doesn't exist yet; let's make it.
   640       category = CategoryNode::Create(&mArena);
   642       char* categoryName = ArenaStrdup(aCategoryName, &mArena);
   643       mTable.Put(categoryName, category);
   644     }
   645   }
   647   if (!category)
   648     return;
   650   // We will need the return value of AddLeaf even if the called doesn't want it
   651   char *oldEntry = nullptr;
   653   nsresult rv = category->AddLeaf(aEntryName,
   654                                   aValue,
   655                                   aReplace,
   656                                   &oldEntry,
   657                                   &mArena);
   659   if (NS_SUCCEEDED(rv)) {
   660     if (oldEntry) {
   661       NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID,
   662                       aCategoryName, aEntryName);
   663     }
   664     NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID,
   665                     aCategoryName, aEntryName);
   667     if (aOldValue)
   668       *aOldValue = oldEntry;
   669     else
   670       NS_Free(oldEntry);
   671   }
   672 }
   674 NS_IMETHODIMP
   675 nsCategoryManager::DeleteCategoryEntry( const char *aCategoryName,
   676                                         const char *aEntryName,
   677                                         bool aDontPersist)
   678 {
   679   if (NS_WARN_IF(!aCategoryName) ||
   680       NS_WARN_IF(!aEntryName))
   681     return NS_ERROR_INVALID_ARG;
   683   /*
   684     Note: no errors are reported since failure to delete
   685     probably won't hurt you, and returning errors seriously
   686     inconveniences JS clients
   687   */
   689   CategoryNode* category;
   690   {
   691     MutexAutoLock lock(mLock);
   692     category = get_category(aCategoryName);
   693   }
   695   if (category) {
   696     category->DeleteLeaf(aEntryName);
   698     NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID,
   699                     aCategoryName, aEntryName);
   700   }
   702   return NS_OK;
   703 }
   705 NS_IMETHODIMP
   706 nsCategoryManager::DeleteCategory( const char *aCategoryName )
   707 {
   708   if (NS_WARN_IF(!aCategoryName))
   709     return NS_ERROR_INVALID_ARG;
   711   // the categories are arena-allocated, so we don't
   712   // actually delete them. We just remove all of the
   713   // leaf nodes.
   715   CategoryNode* category;
   716   {
   717     MutexAutoLock lock(mLock);
   718     category = get_category(aCategoryName);
   719   }
   721   if (category) {
   722     category->Clear();
   723     NotifyObservers(NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID,
   724                     aCategoryName, nullptr);
   725   }
   727   return NS_OK;
   728 }
   730 NS_IMETHODIMP
   731 nsCategoryManager::EnumerateCategory( const char *aCategoryName,
   732                                       nsISimpleEnumerator **_retval )
   733 {
   734   if (NS_WARN_IF(!aCategoryName) ||
   735       NS_WARN_IF(!_retval))
   736     return NS_ERROR_INVALID_ARG;
   738   CategoryNode* category;
   739   {
   740     MutexAutoLock lock(mLock);
   741     category = get_category(aCategoryName);
   742   }
   744   if (!category) {
   745     return NS_NewEmptyEnumerator(_retval);
   746   }
   748   return category->Enumerate(_retval);
   749 }
   751 NS_IMETHODIMP 
   752 nsCategoryManager::EnumerateCategories(nsISimpleEnumerator **_retval)
   753 {
   754   if (NS_WARN_IF(!_retval))
   755     return NS_ERROR_INVALID_ARG;
   757   MutexAutoLock lock(mLock);
   758   CategoryEnumerator* enumObj = CategoryEnumerator::Create(mTable);
   760   if (!enumObj)
   761     return NS_ERROR_OUT_OF_MEMORY;
   763   *_retval = enumObj;
   764   NS_ADDREF(*_retval);
   765   return NS_OK;
   766 }
   768 struct writecat_struct {
   769   PRFileDesc* fd;
   770   bool        success;
   771 };
   773 NS_METHOD
   774 nsCategoryManager::SuppressNotifications(bool aSuppress)
   775 {
   776   mSuppressNotifications = aSuppress;
   777   return NS_OK;
   778 }
   780 /*
   781  * CreateServicesFromCategory()
   782  *
   783  * Given a category, this convenience functions enumerates the category and 
   784  * creates a service of every CID or ContractID registered under the category.
   785  * If observerTopic is non null and the service implements nsIObserver,
   786  * this will attempt to notify the observer with the origin, observerTopic string
   787  * as parameter.
   788  */
   789 void
   790 NS_CreateServicesFromCategory(const char *category,
   791                               nsISupports *origin,
   792                               const char *observerTopic)
   793 {
   794   nsresult rv;
   796   nsCOMPtr<nsICategoryManager> categoryManager = 
   797     do_GetService("@mozilla.org/categorymanager;1");
   798   if (!categoryManager)
   799     return;
   801   nsCOMPtr<nsISimpleEnumerator> enumerator;
   802   rv = categoryManager->EnumerateCategory(category, 
   803                                           getter_AddRefs(enumerator));
   804   if (NS_FAILED(rv))
   805     return;
   807   nsCOMPtr<nsIUTF8StringEnumerator> senumerator =
   808     do_QueryInterface(enumerator);
   809   if (!senumerator) {
   810     NS_WARNING("Category enumerator doesn't support nsIUTF8StringEnumerator.");
   811     return;
   812   }
   814   bool hasMore;
   815   while (NS_SUCCEEDED(senumerator->HasMore(&hasMore)) && hasMore) {
   816     // From here on just skip any error we get.
   817     nsAutoCString entryString;
   818     if (NS_FAILED(senumerator->GetNext(entryString)))
   819       continue;
   821     nsXPIDLCString contractID;
   822     rv = categoryManager->GetCategoryEntry(category,entryString.get(),
   823                                            getter_Copies(contractID));
   824     if (NS_FAILED(rv))
   825       continue;
   827     nsCOMPtr<nsISupports> instance = do_GetService(contractID);
   828     if (!instance) {
   829       LogMessage("While creating services from category '%s', could not create service for entry '%s', contract ID '%s'",
   830                  category, entryString.get(), contractID.get());
   831       continue;
   832     }
   834     if (observerTopic) {
   835       // try an observer, if it implements it.
   836       nsCOMPtr<nsIObserver> observer = do_QueryInterface(instance);
   837       if (observer)
   838         observer->Observe(origin, observerTopic, EmptyString().get());
   839       else
   840         LogMessage("While creating services from category '%s', service for entry '%s', contract ID '%s' does not implement nsIObserver.",
   841                    category, entryString.get(), contractID.get());
   842     }
   843   }
   844 }

mercurial