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