Bug 464947 - Clear Recent History dialog should clear form data by timespan. r=mconnor
[wine-gecko.git] / xpcom / components / nsCategoryManager.cpp
blob0ed15bfa27eecfeb6ed9bbd0f986077364b56b4a
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2000
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Scott Collins <scc@netscape.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #define PL_ARENA_CONST_ALIGN_MASK 7
41 #include "nsICategoryManager.h"
42 #include "nsCategoryManager.h"
44 #include "plarena.h"
45 #include "prio.h"
46 #include "prprf.h"
47 #include "prlock.h"
48 #include "nsCOMPtr.h"
49 #include "nsTHashtable.h"
50 #include "nsClassHashtable.h"
51 #include "nsIFactory.h"
52 #include "nsIStringEnumerator.h"
53 #include "nsSupportsPrimitives.h"
54 #include "nsServiceManagerUtils.h"
55 #include "nsIObserver.h"
56 #include "nsIObserverService.h"
57 #include "nsReadableUtils.h"
58 #include "nsCRT.h"
59 #include "nsQuickSort.h"
60 #include "nsEnumeratorUtils.h"
61 #include "nsIProxyObjectManager.h"
62 #include "nsThreadUtils.h"
64 class nsIComponentLoaderManager;
67 CategoryDatabase
68 contains 0 or more 1-1 mappings of string to Category
69 each Category contains 0 or more 1-1 mappings of string keys to string values
71 In other words, the CategoryDatabase is a tree, whose root is a hashtable.
72 Internal nodes (or Categories) are hashtables. Leaf nodes are strings.
74 The leaf strings are allocated in an arena, because we assume they're not
75 going to change much ;)
78 #define NS_CATEGORYMANAGER_ARENA_SIZE (1024 * 8)
80 // pulled in from nsComponentManager.cpp
81 char* ArenaStrdup(const char* s, PLArenaPool* aArena);
84 // BaseStringEnumerator is subclassed by EntryEnumerator and
85 // CategoryEnumerator
87 class BaseStringEnumerator
88 : public nsISimpleEnumerator,
89 private nsIUTF8StringEnumerator
91 public:
92 NS_DECL_ISUPPORTS
93 NS_DECL_NSISIMPLEENUMERATOR
94 NS_DECL_NSIUTF8STRINGENUMERATOR
96 protected:
97 // Callback function for NS_QuickSort to sort mArray
98 static int SortCallback(const void *, const void *, void *);
100 BaseStringEnumerator()
101 : mArray(nsnull),
102 mCount(0),
103 mSimpleCurItem(0),
104 mStringCurItem(0) { }
106 // A virtual destructor is needed here because subclasses of
107 // BaseStringEnumerator do not implement their own Release() method.
109 virtual ~BaseStringEnumerator()
111 if (mArray)
112 delete[] mArray;
115 void Sort();
117 const char** mArray;
118 PRUint32 mCount;
119 PRUint32 mSimpleCurItem;
120 PRUint32 mStringCurItem;
123 NS_IMPL_ISUPPORTS2(BaseStringEnumerator, nsISimpleEnumerator, nsIUTF8StringEnumerator)
125 NS_IMETHODIMP
126 BaseStringEnumerator::HasMoreElements(PRBool *_retval)
128 *_retval = (mSimpleCurItem < mCount);
130 return NS_OK;
133 NS_IMETHODIMP
134 BaseStringEnumerator::GetNext(nsISupports **_retval)
136 if (mSimpleCurItem >= mCount)
137 return NS_ERROR_FAILURE;
139 nsSupportsDependentCString* str =
140 new nsSupportsDependentCString(mArray[mSimpleCurItem++]);
141 if (!str)
142 return NS_ERROR_OUT_OF_MEMORY;
144 *_retval = str;
145 NS_ADDREF(*_retval);
146 return NS_OK;
149 NS_IMETHODIMP
150 BaseStringEnumerator::HasMore(PRBool *_retval)
152 *_retval = (mStringCurItem < mCount);
154 return NS_OK;
157 NS_IMETHODIMP
158 BaseStringEnumerator::GetNext(nsACString& _retval)
160 if (mStringCurItem >= mCount)
161 return NS_ERROR_FAILURE;
163 _retval = nsDependentCString(mArray[mStringCurItem++]);
164 return NS_OK;
168 BaseStringEnumerator::SortCallback(const void *e1, const void *e2,
169 void * /*unused*/)
171 char const *const *s1 = reinterpret_cast<char const *const *>(e1);
172 char const *const *s2 = reinterpret_cast<char const *const *>(e2);
174 return strcmp(*s1, *s2);
177 void
178 BaseStringEnumerator::Sort()
180 NS_QuickSort(mArray, mCount, sizeof(mArray[0]), SortCallback, nsnull);
184 // EntryEnumerator is the wrapper that allows nsICategoryManager::EnumerateCategory
186 class EntryEnumerator
187 : public BaseStringEnumerator
189 public:
190 static EntryEnumerator* Create(nsTHashtable<CategoryLeaf>& aTable);
192 private:
193 static PLDHashOperator
194 enumfunc_createenumerator(CategoryLeaf* aLeaf, void* userArg);
198 PLDHashOperator
199 EntryEnumerator::enumfunc_createenumerator(CategoryLeaf* aLeaf, void* userArg)
201 EntryEnumerator* mythis = static_cast<EntryEnumerator*>(userArg);
202 if (aLeaf->nonpValue)
203 mythis->mArray[mythis->mCount++] = aLeaf->GetKey();
205 return PL_DHASH_NEXT;
208 EntryEnumerator*
209 EntryEnumerator::Create(nsTHashtable<CategoryLeaf>& aTable)
211 EntryEnumerator* enumObj = new EntryEnumerator();
212 if (!enumObj)
213 return nsnull;
215 enumObj->mArray = new char const* [aTable.Count()];
216 if (!enumObj->mArray) {
217 delete enumObj;
218 return nsnull;
221 aTable.EnumerateEntries(enumfunc_createenumerator, enumObj);
223 enumObj->Sort();
225 return enumObj;
230 // CategoryNode implementations
233 CategoryNode*
234 CategoryNode::Create(PLArenaPool* aArena)
236 CategoryNode* node = new(aArena) CategoryNode();
237 if (!node)
238 return nsnull;
240 if (!node->mTable.Init()) {
241 delete node;
242 return nsnull;
245 node->mLock = PR_NewLock();
246 if (!node->mLock) {
247 delete node;
248 return nsnull;
251 return node;
254 CategoryNode::~CategoryNode()
256 if (mLock)
257 PR_DestroyLock(mLock);
260 void*
261 CategoryNode::operator new(size_t aSize, PLArenaPool* aArena)
263 void* p;
264 PL_ARENA_ALLOCATE(p, aArena, aSize);
265 return p;
268 NS_METHOD
269 CategoryNode::GetLeaf(const char* aEntryName,
270 char** _retval)
272 PR_Lock(mLock);
273 nsresult rv = NS_ERROR_NOT_AVAILABLE;
274 CategoryLeaf* ent =
275 mTable.GetEntry(aEntryName);
277 // we only want the non-persistent value
278 if (ent && ent->nonpValue) {
279 *_retval = nsCRT::strdup(ent->nonpValue);
280 if (*_retval)
281 rv = NS_OK;
283 PR_Unlock(mLock);
285 return rv;
288 NS_METHOD
289 CategoryNode::AddLeaf(const char* aEntryName,
290 const char* aValue,
291 PRBool aPersist,
292 PRBool aReplace,
293 char** _retval,
294 PLArenaPool* aArena)
296 PR_Lock(mLock);
297 CategoryLeaf* leaf =
298 mTable.GetEntry(aEntryName);
300 nsresult rv = NS_OK;
301 if (leaf) {
302 //if the entry was found, aReplace must be specified
303 if (!aReplace && (leaf->nonpValue || (aPersist && leaf->pValue )))
304 rv = NS_ERROR_INVALID_ARG;
305 } else {
306 const char* arenaEntryName = ArenaStrdup(aEntryName, aArena);
307 if (!arenaEntryName) {
308 rv = NS_ERROR_OUT_OF_MEMORY;
309 } else {
310 leaf = mTable.PutEntry(arenaEntryName);
311 if (!leaf)
312 rv = NS_ERROR_OUT_OF_MEMORY;
316 if (NS_SUCCEEDED(rv)) {
317 const char* arenaValue = ArenaStrdup(aValue, aArena);
318 if (!arenaValue) {
319 rv = NS_ERROR_OUT_OF_MEMORY;
320 } else {
321 if (_retval) {
322 const char *toDup = leaf->nonpValue ? leaf->nonpValue : leaf->pValue;
323 if (toDup) {
324 *_retval = ToNewCString(nsDependentCString(toDup));
325 if (!*_retval)
326 return NS_ERROR_OUT_OF_MEMORY;
328 else {
329 *_retval = nsnull;
333 leaf->nonpValue = arenaValue;
334 if (aPersist)
335 leaf->pValue = arenaValue;
339 PR_Unlock(mLock);
340 return rv;
343 NS_METHOD
344 CategoryNode::DeleteLeaf(const char* aEntryName,
345 PRBool aDontPersist)
347 // we don't throw any errors, because it normally doesn't matter
348 // and it makes JS a lot cleaner
349 PR_Lock(mLock);
351 if (aDontPersist) {
352 // we can just remove the entire hash entry without introspection
353 mTable.RemoveEntry(aEntryName);
354 } else {
355 // if we are keeping the persistent value, we need to look at
356 // the contents of the current entry
357 CategoryLeaf* leaf = mTable.GetEntry(aEntryName);
358 if (leaf) {
359 if (leaf->pValue) {
360 leaf->nonpValue = nsnull;
361 } else {
362 // if there is no persistent value, just remove the entry
363 mTable.RawRemoveEntry(leaf);
367 PR_Unlock(mLock);
369 return NS_OK;
372 NS_METHOD
373 CategoryNode::Enumerate(nsISimpleEnumerator **_retval)
375 NS_ENSURE_ARG_POINTER(_retval);
377 PR_Lock(mLock);
378 EntryEnumerator* enumObj = EntryEnumerator::Create(mTable);
379 PR_Unlock(mLock);
381 if (!enumObj)
382 return NS_ERROR_OUT_OF_MEMORY;
384 *_retval = enumObj;
385 NS_ADDREF(*_retval);
386 return NS_OK;
389 struct persistent_userstruct {
390 PRFileDesc* fd;
391 const char* categoryName;
392 PRBool success;
395 PLDHashOperator
396 enumfunc_pentries(CategoryLeaf* aLeaf, void* userArg)
398 persistent_userstruct* args =
399 static_cast<persistent_userstruct*>(userArg);
401 PLDHashOperator status = PL_DHASH_NEXT;
403 if (aLeaf->pValue) {
404 if (PR_fprintf(args->fd,
405 "%s,%s,%s\n",
406 args->categoryName,
407 aLeaf->GetKey(),
408 aLeaf->pValue) == (PRUint32) -1) {
409 args->success = PR_FALSE;
410 status = PL_DHASH_STOP;
414 return status;
417 PRBool
418 CategoryNode::WritePersistentEntries(PRFileDesc* fd, const char* aCategoryName)
420 persistent_userstruct args = {
422 aCategoryName,
423 PR_TRUE
426 PR_Lock(mLock);
427 mTable.EnumerateEntries(enumfunc_pentries, &args);
428 PR_Unlock(mLock);
430 return args.success;
435 // CategoryEnumerator class
438 class CategoryEnumerator
439 : public BaseStringEnumerator
441 public:
442 static CategoryEnumerator* Create(nsClassHashtable<nsDepCharHashKey, CategoryNode>& aTable);
444 private:
445 static PLDHashOperator
446 enumfunc_createenumerator(const char* aStr,
447 CategoryNode* aNode,
448 void* userArg);
451 CategoryEnumerator*
452 CategoryEnumerator::Create(nsClassHashtable<nsDepCharHashKey, CategoryNode>& aTable)
454 CategoryEnumerator* enumObj = new CategoryEnumerator();
455 if (!enumObj)
456 return nsnull;
458 enumObj->mArray = new const char* [aTable.Count()];
459 if (!enumObj->mArray) {
460 delete enumObj;
461 return nsnull;
464 aTable.EnumerateRead(enumfunc_createenumerator, enumObj);
466 return enumObj;
469 PLDHashOperator
470 CategoryEnumerator::enumfunc_createenumerator(const char* aStr, CategoryNode* aNode, void* userArg)
472 CategoryEnumerator* mythis = static_cast<CategoryEnumerator*>(userArg);
474 // if a category has no entries, we pretend it doesn't exist
475 if (aNode->Count())
476 mythis->mArray[mythis->mCount++] = aStr;
478 return PL_DHASH_NEXT;
483 // nsCategoryManager implementations
486 NS_IMPL_THREADSAFE_ISUPPORTS1(nsCategoryManager, nsICategoryManager)
488 nsCategoryManager*
489 nsCategoryManager::Create()
491 nsCategoryManager* manager = new nsCategoryManager();
493 if (!manager)
494 return nsnull;
496 PL_INIT_ARENA_POOL(&(manager->mArena), "CategoryManagerArena",
497 NS_CATEGORYMANAGER_ARENA_SIZE); // this never fails
499 if (!manager->mTable.Init()) {
500 delete manager;
501 return nsnull;
504 manager->mLock = PR_NewLock();
506 if (!manager->mLock) {
507 delete manager;
508 return nsnull;
511 return manager;
514 nsCategoryManager::~nsCategoryManager()
516 if (mLock)
517 PR_DestroyLock(mLock);
519 // the hashtable contains entries that must be deleted before the arena is
520 // destroyed, or else you will have PRLocks undestroyed and other Really
521 // Bad Stuff (TM)
522 mTable.Clear();
524 PL_FinishArenaPool(&mArena);
527 inline CategoryNode*
528 nsCategoryManager::get_category(const char* aName) {
529 CategoryNode* node;
530 if (!mTable.Get(aName, &node)) {
531 return nsnull;
533 return node;
536 void
537 nsCategoryManager::NotifyObservers( const char *aTopic,
538 const char *aCategoryName,
539 const char *aEntryName )
541 if (mSuppressNotifications)
542 return;
544 nsCOMPtr<nsIObserverService> observerService
545 (do_GetService("@mozilla.org/observer-service;1"));
546 if (!observerService)
547 return;
549 nsCOMPtr<nsIObserverService> obsProxy;
550 NS_GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
551 NS_GET_IID(nsIObserverService),
552 observerService,
553 NS_PROXY_ASYNC,
554 getter_AddRefs(obsProxy));
555 if (!obsProxy)
556 return;
558 if (aEntryName) {
559 nsCOMPtr<nsISupportsCString> entry
560 (do_CreateInstance (NS_SUPPORTS_CSTRING_CONTRACTID));
561 if (!entry)
562 return;
564 nsresult rv = entry->SetData(nsDependentCString(aEntryName));
565 if (NS_FAILED(rv))
566 return;
568 obsProxy->NotifyObservers(entry, aTopic,
569 NS_ConvertUTF8toUTF16(aCategoryName).get());
570 } else {
571 obsProxy->NotifyObservers(this, aTopic,
572 NS_ConvertUTF8toUTF16(aCategoryName).get());
576 NS_IMETHODIMP
577 nsCategoryManager::GetCategoryEntry( const char *aCategoryName,
578 const char *aEntryName,
579 char **_retval )
581 NS_ENSURE_ARG_POINTER(aCategoryName);
582 NS_ENSURE_ARG_POINTER(aEntryName);
583 NS_ENSURE_ARG_POINTER(_retval);
585 nsresult status = NS_ERROR_NOT_AVAILABLE;
587 PR_Lock(mLock);
588 CategoryNode* category = get_category(aCategoryName);
589 PR_Unlock(mLock);
591 if (category) {
592 status = category->GetLeaf(aEntryName, _retval);
595 return status;
598 NS_IMETHODIMP
599 nsCategoryManager::AddCategoryEntry( const char *aCategoryName,
600 const char *aEntryName,
601 const char *aValue,
602 PRBool aPersist,
603 PRBool aReplace,
604 char **_retval )
606 NS_ENSURE_ARG_POINTER(aCategoryName);
607 NS_ENSURE_ARG_POINTER(aEntryName);
608 NS_ENSURE_ARG_POINTER(aValue);
610 // Before we can insert a new entry, we'll need to
611 // find the |CategoryNode| to put it in...
612 PR_Lock(mLock);
613 CategoryNode* category = get_category(aCategoryName);
615 if (!category) {
616 // That category doesn't exist yet; let's make it.
617 category = CategoryNode::Create(&mArena);
619 char* categoryName = ArenaStrdup(aCategoryName, &mArena);
620 mTable.Put(categoryName, category);
622 PR_Unlock(mLock);
624 if (!category)
625 return NS_ERROR_OUT_OF_MEMORY;
627 // We will need the return value of AddLeaf even if the called doesn't want it
628 char *oldEntry = nsnull;
630 nsresult rv = category->AddLeaf(aEntryName,
631 aValue,
632 aPersist,
633 aReplace,
634 &oldEntry,
635 &mArena);
637 if (NS_SUCCEEDED(rv)) {
638 if (oldEntry) {
639 NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID,
640 aCategoryName, oldEntry);
642 NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID,
643 aCategoryName, aEntryName);
645 if (_retval)
646 *_retval = oldEntry;
647 else if (oldEntry)
648 nsMemory::Free(oldEntry);
651 return rv;
654 NS_IMETHODIMP
655 nsCategoryManager::DeleteCategoryEntry( const char *aCategoryName,
656 const char *aEntryName,
657 PRBool aDontPersist)
659 NS_ENSURE_ARG_POINTER(aCategoryName);
660 NS_ENSURE_ARG_POINTER(aEntryName);
663 Note: no errors are reported since failure to delete
664 probably won't hurt you, and returning errors seriously
665 inconveniences JS clients
668 PR_Lock(mLock);
669 CategoryNode* category = get_category(aCategoryName);
670 PR_Unlock(mLock);
672 if (!category)
673 return NS_OK;
675 nsresult rv = category->DeleteLeaf(aEntryName,
676 aDontPersist);
678 if (NS_SUCCEEDED(rv)) {
679 NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID,
680 aCategoryName, aEntryName);
683 return rv;
686 NS_IMETHODIMP
687 nsCategoryManager::DeleteCategory( const char *aCategoryName )
689 NS_ENSURE_ARG_POINTER(aCategoryName);
691 // the categories are arena-allocated, so we don't
692 // actually delete them. We just remove all of the
693 // leaf nodes.
695 PR_Lock(mLock);
696 CategoryNode* category = get_category(aCategoryName);
697 PR_Unlock(mLock);
699 if (category) {
700 category->Clear();
701 NotifyObservers(NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID,
702 aCategoryName, nsnull);
705 return NS_OK;
708 NS_IMETHODIMP
709 nsCategoryManager::EnumerateCategory( const char *aCategoryName,
710 nsISimpleEnumerator **_retval )
712 NS_ENSURE_ARG_POINTER(aCategoryName);
713 NS_ENSURE_ARG_POINTER(_retval);
715 PR_Lock(mLock);
716 CategoryNode* category = get_category(aCategoryName);
717 PR_Unlock(mLock);
719 if (!category) {
720 return NS_NewEmptyEnumerator(_retval);
723 return category->Enumerate(_retval);
726 NS_IMETHODIMP
727 nsCategoryManager::EnumerateCategories(nsISimpleEnumerator **_retval)
729 NS_ENSURE_ARG_POINTER(_retval);
731 PR_Lock(mLock);
732 CategoryEnumerator* enumObj = CategoryEnumerator::Create(mTable);
733 PR_Unlock(mLock);
735 if (!enumObj)
736 return NS_ERROR_OUT_OF_MEMORY;
738 *_retval = enumObj;
739 NS_ADDREF(*_retval);
740 return NS_OK;
743 struct writecat_struct {
744 PRFileDesc* fd;
745 PRBool success;
748 PLDHashOperator
749 enumfunc_categories(const char* aKey, CategoryNode* aCategory, void* userArg)
751 writecat_struct* args = static_cast<writecat_struct*>(userArg);
753 PLDHashOperator result = PL_DHASH_NEXT;
755 if (!aCategory->WritePersistentEntries(args->fd, aKey)) {
756 args->success = PR_FALSE;
757 result = PL_DHASH_STOP;
760 return result;
763 NS_METHOD
764 nsCategoryManager::WriteCategoryManagerToRegistry(PRFileDesc* fd)
766 writecat_struct args = {
768 PR_TRUE
771 PR_Lock(mLock);
772 mTable.EnumerateRead(enumfunc_categories, &args);
773 PR_Unlock(mLock);
775 if (!args.success) {
776 return NS_ERROR_UNEXPECTED;
779 return NS_OK;
782 NS_METHOD
783 nsCategoryManager::SuppressNotifications(PRBool aSuppress)
785 mSuppressNotifications = aSuppress;
786 return NS_OK;
789 class nsCategoryManagerFactory : public nsIFactory
791 public:
792 nsCategoryManagerFactory() { }
794 NS_DECL_ISUPPORTS
795 NS_DECL_NSIFACTORY
798 NS_IMPL_ISUPPORTS1(nsCategoryManagerFactory, nsIFactory)
800 NS_IMETHODIMP
801 nsCategoryManagerFactory::CreateInstance( nsISupports* aOuter, const nsIID& aIID, void** aResult )
803 NS_ENSURE_ARG_POINTER(aResult);
805 *aResult = 0;
807 nsresult status = NS_OK;
808 if ( aOuter )
809 status = NS_ERROR_NO_AGGREGATION;
810 else
812 nsCategoryManager* raw_category_manager = nsCategoryManager::Create();
813 nsCOMPtr<nsICategoryManager> new_category_manager = raw_category_manager;
814 if ( new_category_manager )
815 status = new_category_manager->QueryInterface(aIID, aResult);
816 else
817 status = NS_ERROR_OUT_OF_MEMORY;
820 return status;
823 NS_IMETHODIMP
824 nsCategoryManagerFactory::LockFactory( PRBool )
826 // Not implemented...
827 return NS_OK;
830 nsresult
831 NS_CategoryManagerGetFactory( nsIFactory** aFactory )
833 // assert(aFactory);
835 nsresult status;
837 *aFactory = 0;
838 nsIFactory* new_factory = static_cast<nsIFactory*>(new nsCategoryManagerFactory);
839 if (new_factory)
841 *aFactory = new_factory;
842 NS_ADDREF(*aFactory);
843 status = NS_OK;
845 else
846 status = NS_ERROR_OUT_OF_MEMORY;
848 return status;
854 * CreateServicesFromCategory()
856 * Given a category, this convenience functions enumerates the category and
857 * creates a service of every CID or ContractID registered under the category.
858 * If observerTopic is non null and the service implements nsIObserver,
859 * this will attempt to notify the observer with the origin, observerTopic string
860 * as parameter.
862 NS_COM nsresult
863 NS_CreateServicesFromCategory(const char *category,
864 nsISupports *origin,
865 const char *observerTopic)
867 nsresult rv = NS_OK;
869 int nFailed = 0;
870 nsCOMPtr<nsICategoryManager> categoryManager =
871 do_GetService("@mozilla.org/categorymanager;1", &rv);
872 if (!categoryManager) return rv;
874 nsCOMPtr<nsISimpleEnumerator> enumerator;
875 rv = categoryManager->EnumerateCategory(category,
876 getter_AddRefs(enumerator));
877 if (NS_FAILED(rv)) return rv;
879 nsCOMPtr<nsISupports> entry;
880 while (NS_SUCCEEDED(enumerator->GetNext(getter_AddRefs(entry)))) {
881 // From here on just skip any error we get.
882 nsCOMPtr<nsISupportsCString> catEntry = do_QueryInterface(entry, &rv);
883 if (NS_FAILED(rv)) {
884 nFailed++;
885 continue;
887 nsCAutoString entryString;
888 rv = catEntry->GetData(entryString);
889 if (NS_FAILED(rv)) {
890 nFailed++;
891 continue;
893 nsXPIDLCString contractID;
894 rv = categoryManager->GetCategoryEntry(category,entryString.get(), getter_Copies(contractID));
895 if (NS_FAILED(rv)) {
896 nFailed++;
897 continue;
900 nsCOMPtr<nsISupports> instance = do_GetService(contractID, &rv);
901 if (NS_FAILED(rv)) {
902 nFailed++;
903 continue;
906 if (observerTopic) {
907 // try an observer, if it implements it.
908 nsCOMPtr<nsIObserver> observer = do_QueryInterface(instance, &rv);
909 if (NS_SUCCEEDED(rv) && observer)
910 observer->Observe(origin, observerTopic, EmptyString().get());
913 return (nFailed ? NS_ERROR_FAILURE : NS_OK);