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