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