Back to index

lightning-sunbird  0.9+nobinonly
nsCategoryManager.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Scott Collins <scc@netscape.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #define PL_ARENA_CONST_ALIGN_MASK 7
00040 
00041 #include "nsICategoryManager.h"
00042 #include "nsCategoryManager.h"
00043 
00044 #include "plarena.h"
00045 #include "prio.h"
00046 #include "prprf.h"
00047 #include "prlock.h"
00048 #include "nsCOMPtr.h"
00049 #include "nsTHashtable.h"
00050 #include "nsClassHashtable.h"
00051 #include "nsIFactory.h"
00052 #include "nsIStringEnumerator.h"
00053 #include "nsSupportsPrimitives.h"
00054 #include "nsServiceManagerUtils.h"
00055 #include "nsIObserver.h"
00056 #include "nsIObserverService.h"
00057 #include "nsReadableUtils.h"
00058 #include "nsCRT.h"
00059 #include "nsQuickSort.h"
00060 #include "nsEnumeratorUtils.h"
00061 #include "nsIProxyObjectManager.h"
00062 
00063 class nsIComponentLoaderManager;
00064 
00065 /*
00066   CategoryDatabase
00067   contains 0 or more 1-1 mappings of string to Category
00068   each Category contains 0 or more 1-1 mappings of string keys to string values
00069 
00070   In other words, the CategoryDatabase is a tree, whose root is a hashtable.
00071   Internal nodes (or Categories) are hashtables. Leaf nodes are strings.
00072 
00073   The leaf strings are allocated in an arena, because we assume they're not
00074   going to change much ;)
00075 */
00076 
00077 #define NS_CATEGORYMANAGER_ARENA_SIZE (1024 * 8)
00078 
00079 // pulled in from nsComponentManager.cpp
00080 char* ArenaStrdup(const char* s, PLArenaPool* aArena);
00081 
00082 //
00083 // BaseStringEnumerator is subclassed by EntryEnumerator and
00084 // CategoryEnumerator
00085 //
00086 class BaseStringEnumerator
00087   : public nsISimpleEnumerator,
00088            nsIUTF8StringEnumerator
00089 {
00090 public:
00091   NS_DECL_ISUPPORTS
00092   NS_DECL_NSISIMPLEENUMERATOR
00093   NS_DECL_NSIUTF8STRINGENUMERATOR
00094 
00095 protected:
00096   // Callback function for NS_QuickSort to sort mArray
00097   static int SortCallback(const void *, const void *, void *);
00098 
00099   BaseStringEnumerator()
00100     : mArray(nsnull),
00101       mCount(0),
00102       mSimpleCurItem(0),
00103       mStringCurItem(0) { }
00104 
00105   // A virtual destructor is needed here because subclasses of
00106   // BaseStringEnumerator do not implement their own Release() method.
00107 
00108   virtual ~BaseStringEnumerator()
00109   {
00110     if (mArray)
00111       delete[] mArray;
00112   }
00113 
00114   void Sort();
00115 
00116   const char** mArray;
00117   PRUint32 mCount;
00118   PRUint32 mSimpleCurItem;
00119   PRUint32 mStringCurItem;
00120 };
00121 
00122 NS_IMPL_ISUPPORTS2(BaseStringEnumerator, nsISimpleEnumerator, nsIUTF8StringEnumerator)
00123 
00124 NS_IMETHODIMP
00125 BaseStringEnumerator::HasMoreElements(PRBool *_retval)
00126 {
00127   *_retval = (mSimpleCurItem < mCount);
00128 
00129   return NS_OK;
00130 }
00131 
00132 NS_IMETHODIMP
00133 BaseStringEnumerator::GetNext(nsISupports **_retval)
00134 {
00135   if (mSimpleCurItem >= mCount)
00136     return NS_ERROR_FAILURE;
00137 
00138   nsSupportsDependentCString* str =
00139     new nsSupportsDependentCString(mArray[mSimpleCurItem++]);
00140   if (!str)
00141     return NS_ERROR_OUT_OF_MEMORY;
00142 
00143   *_retval = str;
00144   NS_ADDREF(*_retval);
00145   return NS_OK;
00146 }
00147 
00148 NS_IMETHODIMP
00149 BaseStringEnumerator::HasMore(PRBool *_retval)
00150 {
00151   *_retval = (mStringCurItem < mCount);
00152 
00153   return NS_OK;
00154 }
00155 
00156 NS_IMETHODIMP
00157 BaseStringEnumerator::GetNext(nsACString& _retval)
00158 {
00159   if (mStringCurItem >= mCount)
00160     return NS_ERROR_FAILURE;
00161 
00162   _retval = nsDependentCString(mArray[mStringCurItem++]);
00163   return NS_OK;
00164 }
00165 
00166 int
00167 BaseStringEnumerator::SortCallback(const void *e1, const void *e2,
00168                                    void * /*unused*/)
00169 {
00170   char const *const *s1 = NS_REINTERPRET_CAST(char const *const *, e1);
00171   char const *const *s2 = NS_REINTERPRET_CAST(char const *const *, e2);
00172 
00173   return strcmp(*s1, *s2);
00174 }
00175 
00176 void
00177 BaseStringEnumerator::Sort()
00178 {
00179   NS_QuickSort(mArray, mCount, sizeof(mArray[0]), SortCallback, nsnull);
00180 }
00181 
00182 //
00183 // EntryEnumerator is the wrapper that allows nsICategoryManager::EnumerateCategory
00184 //
00185 class EntryEnumerator
00186   : public BaseStringEnumerator
00187 {
00188 public:
00189   static EntryEnumerator* Create(nsTHashtable<CategoryLeaf>& aTable);
00190 
00191 private:
00192   static PLDHashOperator PR_CALLBACK
00193     enumfunc_createenumerator(CategoryLeaf* aLeaf, void* userArg);
00194 };
00195 
00196 
00197 PLDHashOperator PR_CALLBACK
00198 EntryEnumerator::enumfunc_createenumerator(CategoryLeaf* aLeaf, void* userArg)
00199 {
00200   EntryEnumerator* mythis = NS_STATIC_CAST(EntryEnumerator*, userArg);
00201   mythis->mArray[mythis->mCount++] = aLeaf->GetKey();
00202 
00203   return PL_DHASH_NEXT;
00204 }
00205 
00206 EntryEnumerator*
00207 EntryEnumerator::Create(nsTHashtable<CategoryLeaf>& aTable)
00208 {
00209   EntryEnumerator* enumObj = new EntryEnumerator();
00210   if (!enumObj)
00211     return nsnull;
00212 
00213   enumObj->mArray = new char const* [aTable.Count()];
00214   if (!enumObj->mArray) {
00215     delete enumObj;
00216     return nsnull;
00217   }
00218 
00219   aTable.EnumerateEntries(enumfunc_createenumerator, enumObj);
00220 
00221   enumObj->Sort();
00222 
00223   return enumObj;
00224 }
00225 
00226 
00227 //
00228 // CategoryNode implementations
00229 //
00230 
00231 CategoryNode*
00232 CategoryNode::Create(PLArenaPool* aArena)
00233 {
00234   CategoryNode* node = new(aArena) CategoryNode();
00235   if (!node)
00236     return nsnull;
00237 
00238   if (!node->mTable.Init()) {
00239     delete node;
00240     return nsnull;
00241   }
00242 
00243   node->mLock = PR_NewLock();
00244   if (!node->mLock) {
00245     delete node;
00246     return nsnull;
00247   }
00248 
00249   return node;
00250 }
00251 
00252 CategoryNode::~CategoryNode()
00253 {
00254   if (mLock)
00255     PR_DestroyLock(mLock);
00256 }
00257 
00258 void*
00259 CategoryNode::operator new(size_t aSize, PLArenaPool* aArena)
00260 {
00261   void* p;
00262   PL_ARENA_ALLOCATE(p, aArena, aSize);
00263   return p;
00264 }
00265 
00266 NS_METHOD
00267 CategoryNode::GetLeaf(const char* aEntryName,
00268                       char** _retval)
00269 {
00270   PR_Lock(mLock);
00271   nsresult rv = NS_ERROR_NOT_AVAILABLE;
00272   CategoryLeaf* ent =
00273     mTable.GetEntry(aEntryName);
00274 
00275   // we only want the non-persistent value
00276   if (ent && ent->nonpValue) {
00277     *_retval = nsCRT::strdup(ent->nonpValue);
00278     if (*_retval)
00279       rv = NS_OK;
00280   }
00281   PR_Unlock(mLock);
00282 
00283   return rv;
00284 }
00285 
00286 NS_METHOD
00287 CategoryNode::AddLeaf(const char* aEntryName,
00288                       const char* aValue,
00289                       PRBool aPersist,
00290                       PRBool aReplace,
00291                       char** _retval,
00292                       PLArenaPool* aArena)
00293 {
00294   PR_Lock(mLock);
00295   CategoryLeaf* leaf = 
00296     mTable.GetEntry(aEntryName);
00297 
00298   nsresult rv = NS_OK;
00299   if (leaf) {
00300     //if the entry was found, aReplace must be specified
00301     if (!aReplace && (leaf->nonpValue || (aPersist && leaf->pValue )))
00302       rv = NS_ERROR_INVALID_ARG;
00303   } else {
00304     const char* arenaEntryName = ArenaStrdup(aEntryName, aArena);
00305     if (!arenaEntryName) {
00306       rv = NS_ERROR_OUT_OF_MEMORY;
00307     } else {
00308       leaf = mTable.PutEntry(arenaEntryName);
00309       if (!leaf)
00310         rv = NS_ERROR_OUT_OF_MEMORY;
00311     }
00312   }
00313 
00314   if (NS_SUCCEEDED(rv)) {
00315     const char* arenaValue = ArenaStrdup(aValue, aArena);
00316     if (!arenaValue) {
00317       rv = NS_ERROR_OUT_OF_MEMORY;
00318     } else {
00319       leaf->nonpValue = arenaValue;
00320       if (aPersist)
00321         leaf->pValue = arenaValue;
00322     }
00323   }
00324     
00325   PR_Unlock(mLock);
00326   return rv;
00327 }
00328 
00329 NS_METHOD
00330 CategoryNode::DeleteLeaf(const char* aEntryName,
00331                          PRBool aDontPersist)
00332 {
00333   // we don't throw any errors, because it normally doesn't matter
00334   // and it makes JS a lot cleaner
00335   PR_Lock(mLock);
00336 
00337   if (aDontPersist) {
00338     // we can just remove the entire hash entry without introspection
00339     mTable.RemoveEntry(aEntryName);
00340   } else {
00341     // if we are keeping the persistent value, we need to look at
00342     // the contents of the current entry
00343     CategoryLeaf* leaf = mTable.GetEntry(aEntryName);
00344     if (leaf) {
00345       if (leaf->pValue) {
00346         leaf->nonpValue = nsnull;
00347       } else {
00348         // if there is no persistent value, just remove the entry
00349         mTable.RawRemoveEntry(leaf);
00350       }
00351     }
00352   }
00353   PR_Unlock(mLock);
00354 
00355   return NS_OK;
00356 }
00357 
00358 NS_METHOD 
00359 CategoryNode::Enumerate(nsISimpleEnumerator **_retval)
00360 {
00361   NS_ENSURE_ARG_POINTER(_retval);
00362 
00363   PR_Lock(mLock);
00364   EntryEnumerator* enumObj = EntryEnumerator::Create(mTable);
00365   PR_Unlock(mLock);
00366 
00367   if (!enumObj)
00368     return NS_ERROR_OUT_OF_MEMORY;
00369 
00370   *_retval = enumObj;
00371   NS_ADDREF(*_retval);
00372   return NS_OK;
00373 }
00374 
00375 struct persistent_userstruct {
00376   PRFileDesc* fd;
00377   const char* categoryName;
00378   PRBool      success;
00379 };
00380 
00381 PLDHashOperator PR_CALLBACK
00382 enumfunc_pentries(CategoryLeaf* aLeaf, void* userArg)
00383 {
00384   persistent_userstruct* args =
00385     NS_STATIC_CAST(persistent_userstruct*, userArg);
00386 
00387   PLDHashOperator status = PL_DHASH_NEXT;
00388 
00389   if (aLeaf->pValue) {
00390     if (PR_fprintf(args->fd,
00391                    "%s,%s,%s\n",
00392                    args->categoryName,
00393                    aLeaf->GetKey(),
00394                    aLeaf->pValue) == (PRUint32) -1) {
00395       args->success = PR_FALSE;
00396       status = PL_DHASH_STOP;
00397     }
00398   }
00399 
00400   return status;
00401 }
00402 
00403 PRBool
00404 CategoryNode::WritePersistentEntries(PRFileDesc* fd, const char* aCategoryName)
00405 {
00406   persistent_userstruct args = {
00407     fd,
00408     aCategoryName,
00409     PR_TRUE
00410   };
00411 
00412   PR_Lock(mLock);
00413   mTable.EnumerateEntries(enumfunc_pentries, &args);
00414   PR_Unlock(mLock);
00415 
00416   return args.success;
00417 }
00418 
00419 
00420 //
00421 // CategoryEnumerator class
00422 //
00423 
00424 class CategoryEnumerator
00425   : public BaseStringEnumerator
00426 {
00427 public:
00428   static CategoryEnumerator* Create(nsClassHashtable<nsDepCharHashKey, CategoryNode>& aTable);
00429 
00430 private:
00431   static PLDHashOperator PR_CALLBACK
00432   enumfunc_createenumerator(const char* aStr,
00433                             CategoryNode* aNode,
00434                             void* userArg);
00435 };
00436 
00437 CategoryEnumerator*
00438 CategoryEnumerator::Create(nsClassHashtable<nsDepCharHashKey, CategoryNode>& aTable)
00439 {
00440   CategoryEnumerator* enumObj = new CategoryEnumerator();
00441   if (!enumObj)
00442     return nsnull;
00443 
00444   enumObj->mArray = new const char* [aTable.Count()];
00445   if (!enumObj->mArray) {
00446     delete enumObj;
00447     return nsnull;
00448   }
00449 
00450   aTable.EnumerateRead(enumfunc_createenumerator, enumObj);
00451 
00452   return enumObj;
00453 }
00454 
00455 PLDHashOperator PR_CALLBACK
00456 CategoryEnumerator::enumfunc_createenumerator(const char* aStr, CategoryNode* aNode, void* userArg)
00457 {
00458   CategoryEnumerator* mythis = NS_STATIC_CAST(CategoryEnumerator*, userArg);
00459 
00460   // if a category has no entries, we pretend it doesn't exist
00461   if (aNode->Count())
00462     mythis->mArray[mythis->mCount++] = aStr;
00463 
00464   return PL_DHASH_NEXT;
00465 }
00466 
00467 
00468 //
00469 // nsCategoryManager implementations
00470 //
00471 
00472 NS_IMPL_THREADSAFE_ISUPPORTS1(nsCategoryManager, nsICategoryManager)
00473 
00474 nsCategoryManager*
00475 nsCategoryManager::Create()
00476 {
00477   nsCategoryManager* manager = new nsCategoryManager();
00478   
00479   if (!manager)
00480     return nsnull;
00481 
00482   PL_INIT_ARENA_POOL(&(manager->mArena), "CategoryManagerArena",
00483                      NS_CATEGORYMANAGER_ARENA_SIZE); // this never fails
00484 
00485   if (!manager->mTable.Init()) {
00486     delete manager;
00487     return nsnull;
00488   }
00489 
00490   manager->mLock = PR_NewLock();
00491 
00492   if (!manager->mLock) {
00493     delete manager;
00494     return nsnull;
00495   }
00496 
00497   return manager;
00498 }
00499 
00500 nsCategoryManager::~nsCategoryManager()
00501 {
00502   if (mLock)
00503     PR_DestroyLock(mLock);
00504 
00505   // the hashtable contains entries that must be deleted before the arena is
00506   // destroyed, or else you will have PRLocks undestroyed and other Really
00507   // Bad Stuff (TM)
00508   mTable.Clear();
00509 
00510   PL_FinishArenaPool(&mArena);
00511 }
00512 
00513 inline CategoryNode*
00514 nsCategoryManager::get_category(const char* aName) {
00515   CategoryNode* node;
00516   if (!mTable.Get(aName, &node)) {
00517     return nsnull;
00518   }
00519   return node;
00520 }
00521 
00522 void
00523 nsCategoryManager::NotifyObservers( const char *aTopic,
00524                                     const char *aCategoryName,
00525                                     const char *aEntryName )
00526 {
00527   if (mSuppressNotifications)
00528     return;
00529 
00530   nsCOMPtr<nsIObserverService> observerService
00531     (do_GetService("@mozilla.org/observer-service;1"));
00532   if (!observerService)
00533     return;
00534 
00535   nsCOMPtr<nsIObserverService> obsProxy;
00536   NS_GetProxyForObject(NS_UI_THREAD_EVENTQ,
00537                        NS_GET_IID(nsIObserverService),
00538                        observerService,
00539                        PROXY_ASYNC,
00540                        getter_AddRefs(obsProxy));
00541   if (!obsProxy) return;
00542 
00543   if (aEntryName) {
00544     nsCOMPtr<nsISupportsCString> entry
00545       (do_CreateInstance (NS_SUPPORTS_CSTRING_CONTRACTID));
00546     if (!entry)
00547       return;
00548 
00549     nsresult rv = entry->SetData(nsDependentCString(aEntryName));
00550     if (NS_FAILED(rv))
00551       return;
00552 
00553     obsProxy->NotifyObservers(entry, aTopic,
00554                               NS_ConvertUTF8toUTF16(aCategoryName).get());
00555   } else {
00556     obsProxy->NotifyObservers(this, aTopic,
00557                               NS_ConvertUTF8toUTF16(aCategoryName).get());
00558   }
00559 }
00560 
00561 NS_IMETHODIMP
00562 nsCategoryManager::GetCategoryEntry( const char *aCategoryName,
00563                                      const char *aEntryName,
00564                                      char **_retval )
00565 {
00566   NS_ENSURE_ARG_POINTER(aCategoryName);
00567   NS_ENSURE_ARG_POINTER(aEntryName);
00568   NS_ENSURE_ARG_POINTER(_retval);
00569 
00570   nsresult status = NS_ERROR_NOT_AVAILABLE;
00571 
00572   PR_Lock(mLock);
00573   CategoryNode* category = get_category(aCategoryName);
00574   PR_Unlock(mLock);
00575 
00576   if (category) {
00577     status = category->GetLeaf(aEntryName, _retval);
00578   }
00579 
00580   return status;
00581 }
00582 
00583 NS_IMETHODIMP
00584 nsCategoryManager::AddCategoryEntry( const char *aCategoryName,
00585                                      const char *aEntryName,
00586                                      const char *aValue,
00587                                      PRBool aPersist,
00588                                      PRBool aReplace,
00589                                      char **_retval )
00590 {
00591   NS_ENSURE_ARG_POINTER(aCategoryName);
00592   NS_ENSURE_ARG_POINTER(aEntryName);
00593   NS_ENSURE_ARG_POINTER(aValue);
00594 
00595   // Before we can insert a new entry, we'll need to
00596   //  find the |CategoryNode| to put it in...
00597   PR_Lock(mLock);
00598   CategoryNode* category = get_category(aCategoryName);
00599 
00600   if (!category) {
00601     // That category doesn't exist yet; let's make it.
00602     category = CategoryNode::Create(&mArena);
00603         
00604     char* categoryName = ArenaStrdup(aCategoryName, &mArena);
00605     mTable.Put(categoryName, category);
00606   }
00607   PR_Unlock(mLock);
00608 
00609   if (!category)
00610     return NS_ERROR_OUT_OF_MEMORY;
00611 
00612   nsresult rv = category->AddLeaf(aEntryName,
00613                                   aValue,
00614                                   aPersist,
00615                                   aReplace,
00616                                   _retval,
00617                                   &mArena);
00618 
00619   if (NS_SUCCEEDED(rv)) {
00620     NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID,
00621                     aCategoryName, aEntryName);
00622   }
00623 
00624   return rv;
00625 }
00626 
00627 NS_IMETHODIMP
00628 nsCategoryManager::DeleteCategoryEntry( const char *aCategoryName,
00629                                         const char *aEntryName,
00630                                         PRBool aDontPersist)
00631 {
00632   NS_ENSURE_ARG_POINTER(aCategoryName);
00633   NS_ENSURE_ARG_POINTER(aEntryName);
00634 
00635   /*
00636     Note: no errors are reported since failure to delete
00637     probably won't hurt you, and returning errors seriously
00638     inconveniences JS clients
00639   */
00640 
00641   PR_Lock(mLock);
00642   CategoryNode* category = get_category(aCategoryName);
00643   PR_Unlock(mLock);
00644 
00645   if (!category)
00646     return NS_OK;
00647 
00648   nsresult rv = category->DeleteLeaf(aEntryName,
00649                                      aDontPersist);
00650 
00651   if (NS_SUCCEEDED(rv)) {
00652     NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID,
00653                     aCategoryName, aEntryName);
00654   }
00655 
00656   return rv;
00657 }
00658 
00659 NS_IMETHODIMP
00660 nsCategoryManager::DeleteCategory( const char *aCategoryName )
00661 {
00662   NS_ENSURE_ARG_POINTER(aCategoryName);
00663 
00664   // the categories are arena-allocated, so we don't
00665   // actually delete them. We just remove all of the
00666   // leaf nodes.
00667 
00668   PR_Lock(mLock);
00669   CategoryNode* category = get_category(aCategoryName);
00670   PR_Unlock(mLock);
00671 
00672   if (category) {
00673     category->Clear();
00674     NotifyObservers(NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID,
00675                     aCategoryName, nsnull);
00676   }
00677 
00678   return NS_OK;
00679 }
00680 
00681 NS_IMETHODIMP
00682 nsCategoryManager::EnumerateCategory( const char *aCategoryName,
00683                                       nsISimpleEnumerator **_retval )
00684 {
00685   NS_ENSURE_ARG_POINTER(aCategoryName);
00686   NS_ENSURE_ARG_POINTER(_retval);
00687 
00688   PR_Lock(mLock);
00689   CategoryNode* category = get_category(aCategoryName);
00690   PR_Unlock(mLock);
00691   
00692   if (!category) {
00693     return NS_NewEmptyEnumerator(_retval);
00694   }
00695 
00696   return category->Enumerate(_retval);
00697 }
00698 
00699 NS_IMETHODIMP 
00700 nsCategoryManager::EnumerateCategories(nsISimpleEnumerator **_retval)
00701 {
00702   NS_ENSURE_ARG_POINTER(_retval);
00703 
00704   PR_Lock(mLock);
00705   CategoryEnumerator* enumObj = CategoryEnumerator::Create(mTable);
00706   PR_Unlock(mLock);
00707 
00708   if (!enumObj)
00709     return NS_ERROR_OUT_OF_MEMORY;
00710 
00711   *_retval = enumObj;
00712   NS_ADDREF(*_retval);
00713   return NS_OK;
00714 }
00715 
00716 struct writecat_struct {
00717   PRFileDesc* fd;
00718   PRBool      success;
00719 };
00720 
00721 PLDHashOperator PR_CALLBACK
00722 enumfunc_categories(const char* aKey, CategoryNode* aCategory, void* userArg)
00723 {
00724   writecat_struct* args = NS_STATIC_CAST(writecat_struct*, userArg);
00725 
00726   PLDHashOperator result = PL_DHASH_NEXT;
00727 
00728   if (!aCategory->WritePersistentEntries(args->fd, aKey)) {
00729     args->success = PR_FALSE;
00730     result = PL_DHASH_STOP;
00731   }
00732 
00733   return result;
00734 }
00735 
00736 NS_METHOD
00737 nsCategoryManager::WriteCategoryManagerToRegistry(PRFileDesc* fd)
00738 {
00739   writecat_struct args = {
00740     fd,
00741     PR_TRUE
00742   };
00743 
00744   PR_Lock(mLock);
00745   mTable.EnumerateRead(enumfunc_categories, &args);
00746   PR_Unlock(mLock);
00747 
00748   if (!args.success) {
00749     return NS_ERROR_UNEXPECTED;
00750   }
00751 
00752   return NS_OK;
00753 }
00754 
00755 NS_METHOD
00756 nsCategoryManager::SuppressNotifications(PRBool aSuppress)
00757 {
00758   mSuppressNotifications = aSuppress;
00759   return NS_OK;
00760 }
00761 
00762 class nsCategoryManagerFactory : public nsIFactory
00763    {
00764      public:
00765        nsCategoryManagerFactory() { }
00766 
00767        NS_DECL_ISUPPORTS
00768        NS_DECL_NSIFACTORY
00769    };
00770 
00771 NS_IMPL_ISUPPORTS1(nsCategoryManagerFactory, nsIFactory)
00772 
00773 NS_IMETHODIMP
00774 nsCategoryManagerFactory::CreateInstance( nsISupports* aOuter, const nsIID& aIID, void** aResult )
00775   {
00776     NS_ENSURE_ARG_POINTER(aResult);
00777 
00778     *aResult = 0;
00779 
00780     nsresult status = NS_OK;
00781     if ( aOuter )
00782       status = NS_ERROR_NO_AGGREGATION;
00783     else
00784       {
00785         nsCategoryManager* raw_category_manager = nsCategoryManager::Create();
00786         nsCOMPtr<nsICategoryManager> new_category_manager = raw_category_manager;
00787         if ( new_category_manager )
00788               status = new_category_manager->QueryInterface(aIID, aResult);
00789         else
00790           status = NS_ERROR_OUT_OF_MEMORY;
00791       }
00792 
00793     return status;
00794   }
00795 
00796 NS_IMETHODIMP
00797 nsCategoryManagerFactory::LockFactory( PRBool )
00798   {
00799       // Not implemented...
00800     return NS_OK;
00801   }
00802 
00803 nsresult
00804 NS_CategoryManagerGetFactory( nsIFactory** aFactory )
00805   {
00806     // assert(aFactory);
00807 
00808     nsresult status;
00809 
00810     *aFactory = 0;
00811     nsIFactory* new_factory = NS_STATIC_CAST(nsIFactory*, new nsCategoryManagerFactory);
00812     if (new_factory)
00813       {
00814         *aFactory = new_factory;
00815         NS_ADDREF(*aFactory);
00816         status = NS_OK;
00817       }
00818     else
00819       status = NS_ERROR_OUT_OF_MEMORY;
00820 
00821     return status;
00822   }
00823 
00824 
00825 
00826 /*
00827  * CreateServicesFromCategory()
00828  *
00829  * Given a category, this convenience functions enumerates the category and 
00830  * creates a service of every CID or ContractID registered under the category.
00831  * If observerTopic is non null and the service implements nsIObserver,
00832  * this will attempt to notify the observer with the origin, observerTopic string
00833  * as parameter.
00834  */
00835 NS_COM nsresult
00836 NS_CreateServicesFromCategory(const char *category,
00837                               nsISupports *origin,
00838                               const char *observerTopic)
00839 {
00840     nsresult rv = NS_OK;
00841     
00842     int nFailed = 0; 
00843     nsCOMPtr<nsICategoryManager> categoryManager = 
00844         do_GetService("@mozilla.org/categorymanager;1", &rv);
00845     if (!categoryManager) return rv;
00846 
00847     nsCOMPtr<nsISimpleEnumerator> enumerator;
00848     rv = categoryManager->EnumerateCategory(category, 
00849             getter_AddRefs(enumerator));
00850     if (NS_FAILED(rv)) return rv;
00851 
00852     nsCOMPtr<nsISupports> entry;
00853     while (NS_SUCCEEDED(enumerator->GetNext(getter_AddRefs(entry)))) {
00854         // From here on just skip any error we get.
00855         nsCOMPtr<nsISupportsCString> catEntry = do_QueryInterface(entry, &rv);
00856         if (NS_FAILED(rv)) {
00857             nFailed++;
00858             continue;
00859         }
00860         nsCAutoString entryString;
00861         rv = catEntry->GetData(entryString);
00862         if (NS_FAILED(rv)) {
00863             nFailed++;
00864             continue;
00865         }
00866         nsXPIDLCString contractID;
00867         rv = categoryManager->GetCategoryEntry(category,entryString.get(), getter_Copies(contractID));
00868         if (NS_FAILED(rv)) {
00869             nFailed++;
00870             continue;
00871         }
00872         
00873         nsCOMPtr<nsISupports> instance = do_GetService(contractID, &rv);
00874         if (NS_FAILED(rv)) {
00875             nFailed++;
00876             continue;
00877         }
00878 
00879         if (observerTopic) {
00880             // try an observer, if it implements it.
00881             nsCOMPtr<nsIObserver> observer = do_QueryInterface(instance, &rv);
00882             if (NS_SUCCEEDED(rv) && observer)
00883                 observer->Observe(origin, observerTopic, EmptyString().get());
00884         }
00885     }
00886     return (nFailed ? NS_ERROR_FAILURE : NS_OK);
00887 }