1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #define PL_ARENA_CONST_ALIGN_MASK 7
8 #include "nsICategoryManager.h"
9 #include "nsCategoryManager.h"
16 #include "nsTHashtable.h"
17 #include "nsClassHashtable.h"
18 #include "nsIFactory.h"
19 #include "nsIStringEnumerator.h"
20 #include "nsIMemoryReporter.h"
21 #include "nsSupportsPrimitives.h"
22 #include "nsComponentManagerUtils.h"
23 #include "nsServiceManagerUtils.h"
24 #include "nsIObserver.h"
25 #include "nsIObserverService.h"
26 #include "nsReadableUtils.h"
28 #include "nsQuickSort.h"
29 #include "nsEnumeratorUtils.h"
30 #include "nsThreadUtils.h"
31 #include "mozilla/Services.h"
33 #include "ManifestParser.h"
35 using namespace mozilla
;
36 class nsIComponentLoaderManager
;
40 contains 0 or more 1-1 mappings of string to Category
41 each Category contains 0 or more 1-1 mappings of string keys to string values
43 In other words, the CategoryDatabase is a tree, whose root is a hashtable.
44 Internal nodes (or Categories) are hashtables. Leaf nodes are strings.
46 The leaf strings are allocated in an arena, because we assume they're not
47 going to change much ;)
50 #define NS_CATEGORYMANAGER_ARENA_SIZE (1024 * 8)
52 // pulled in from nsComponentManager.cpp
53 char* ArenaStrdup(const char* s
, PLArenaPool
* aArena
);
56 // BaseStringEnumerator is subclassed by EntryEnumerator and
59 class BaseStringEnumerator
60 : public nsISimpleEnumerator
,
61 private nsIUTF8StringEnumerator
65 NS_DECL_NSISIMPLEENUMERATOR
66 NS_DECL_NSIUTF8STRINGENUMERATOR
69 // Callback function for NS_QuickSort to sort mArray
70 static int SortCallback(const void *, const void *, void *);
72 BaseStringEnumerator()
78 // A virtual destructor is needed here because subclasses of
79 // BaseStringEnumerator do not implement their own Release() method.
81 virtual ~BaseStringEnumerator()
91 uint32_t mSimpleCurItem
;
92 uint32_t mStringCurItem
;
95 NS_IMPL_ISUPPORTS2(BaseStringEnumerator
, nsISimpleEnumerator
, nsIUTF8StringEnumerator
)
98 BaseStringEnumerator::HasMoreElements(bool *_retval
)
100 *_retval
= (mSimpleCurItem
< mCount
);
106 BaseStringEnumerator::GetNext(nsISupports
**_retval
)
108 if (mSimpleCurItem
>= mCount
)
109 return NS_ERROR_FAILURE
;
111 nsSupportsDependentCString
* str
=
112 new nsSupportsDependentCString(mArray
[mSimpleCurItem
++]);
114 return NS_ERROR_OUT_OF_MEMORY
;
122 BaseStringEnumerator::HasMore(bool *_retval
)
124 *_retval
= (mStringCurItem
< mCount
);
130 BaseStringEnumerator::GetNext(nsACString
& _retval
)
132 if (mStringCurItem
>= mCount
)
133 return NS_ERROR_FAILURE
;
135 _retval
= nsDependentCString(mArray
[mStringCurItem
++]);
140 BaseStringEnumerator::SortCallback(const void *e1
, const void *e2
,
143 char const *const *s1
= reinterpret_cast<char const *const *>(e1
);
144 char const *const *s2
= reinterpret_cast<char const *const *>(e2
);
146 return strcmp(*s1
, *s2
);
150 BaseStringEnumerator::Sort()
152 NS_QuickSort(mArray
, mCount
, sizeof(mArray
[0]), SortCallback
, nullptr);
156 // EntryEnumerator is the wrapper that allows nsICategoryManager::EnumerateCategory
158 class EntryEnumerator
159 : public BaseStringEnumerator
162 static EntryEnumerator
* Create(nsTHashtable
<CategoryLeaf
>& aTable
);
165 static PLDHashOperator
166 enumfunc_createenumerator(CategoryLeaf
* aLeaf
, void* userArg
);
171 EntryEnumerator::enumfunc_createenumerator(CategoryLeaf
* aLeaf
, void* userArg
)
173 EntryEnumerator
* mythis
= static_cast<EntryEnumerator
*>(userArg
);
175 mythis
->mArray
[mythis
->mCount
++] = aLeaf
->GetKey();
177 return PL_DHASH_NEXT
;
181 EntryEnumerator::Create(nsTHashtable
<CategoryLeaf
>& aTable
)
183 EntryEnumerator
* enumObj
= new EntryEnumerator();
187 enumObj
->mArray
= new char const* [aTable
.Count()];
188 if (!enumObj
->mArray
) {
193 aTable
.EnumerateEntries(enumfunc_createenumerator
, enumObj
);
202 // CategoryNode implementations
206 CategoryNode::Create(PLArenaPool
* aArena
)
208 CategoryNode
* node
= new(aArena
) CategoryNode();
216 CategoryNode::~CategoryNode()
221 CategoryNode::operator new(size_t aSize
, PLArenaPool
* aArena
)
224 PL_ARENA_ALLOCATE(p
, aArena
, aSize
);
229 CategoryNode::GetLeaf(const char* aEntryName
,
232 MutexAutoLock
lock(mLock
);
233 nsresult rv
= NS_ERROR_NOT_AVAILABLE
;
235 mTable
.GetEntry(aEntryName
);
237 if (ent
&& ent
->value
) {
238 *_retval
= NS_strdup(ent
->value
);
247 CategoryNode::AddLeaf(const char* aEntryName
,
256 MutexAutoLock
lock(mLock
);
258 mTable
.GetEntry(aEntryName
);
261 const char* arenaEntryName
= ArenaStrdup(aEntryName
, aArena
);
263 return NS_ERROR_OUT_OF_MEMORY
;
265 leaf
= mTable
.PutEntry(arenaEntryName
);
267 return NS_ERROR_OUT_OF_MEMORY
;
270 if (leaf
->value
&& !aReplace
)
271 return NS_ERROR_INVALID_ARG
;
273 const char* arenaValue
= ArenaStrdup(aValue
, aArena
);
275 return NS_ERROR_OUT_OF_MEMORY
;
277 if (_retval
&& leaf
->value
) {
278 *_retval
= ToNewCString(nsDependentCString(leaf
->value
));
280 return NS_ERROR_OUT_OF_MEMORY
;
283 leaf
->value
= arenaValue
;
288 CategoryNode::DeleteLeaf(const char* aEntryName
)
290 // we don't throw any errors, because it normally doesn't matter
291 // and it makes JS a lot cleaner
292 MutexAutoLock
lock(mLock
);
294 // we can just remove the entire hash entry without introspection
295 mTable
.RemoveEntry(aEntryName
);
299 CategoryNode::Enumerate(nsISimpleEnumerator
**_retval
)
301 NS_ENSURE_ARG_POINTER(_retval
);
303 MutexAutoLock
lock(mLock
);
304 EntryEnumerator
* enumObj
= EntryEnumerator::Create(mTable
);
307 return NS_ERROR_OUT_OF_MEMORY
;
315 CategoryNode::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf
)
317 // We don't measure the strings pointed to by the entries because the
318 // pointers are non-owning.
319 return mTable
.SizeOfExcludingThis(nullptr, aMallocSizeOf
);
322 struct persistent_userstruct
{
324 const char* categoryName
;
329 enumfunc_pentries(CategoryLeaf
* aLeaf
, void* userArg
)
331 persistent_userstruct
* args
=
332 static_cast<persistent_userstruct
*>(userArg
);
334 PLDHashOperator status
= PL_DHASH_NEXT
;
337 if (PR_fprintf(args
->fd
,
341 aLeaf
->value
) == (uint32_t) -1) {
342 args
->success
= false;
343 status
= PL_DHASH_STOP
;
351 // CategoryEnumerator class
354 class CategoryEnumerator
355 : public BaseStringEnumerator
358 static CategoryEnumerator
* Create(nsClassHashtable
<nsDepCharHashKey
, CategoryNode
>& aTable
);
361 static PLDHashOperator
362 enumfunc_createenumerator(const char* aStr
,
368 CategoryEnumerator::Create(nsClassHashtable
<nsDepCharHashKey
, CategoryNode
>& aTable
)
370 CategoryEnumerator
* enumObj
= new CategoryEnumerator();
374 enumObj
->mArray
= new const char* [aTable
.Count()];
375 if (!enumObj
->mArray
) {
380 aTable
.EnumerateRead(enumfunc_createenumerator
, enumObj
);
386 CategoryEnumerator::enumfunc_createenumerator(const char* aStr
, CategoryNode
* aNode
, void* userArg
)
388 CategoryEnumerator
* mythis
= static_cast<CategoryEnumerator
*>(userArg
);
390 // if a category has no entries, we pretend it doesn't exist
392 mythis
->mArray
[mythis
->mCount
++] = aStr
;
394 return PL_DHASH_NEXT
;
399 // nsCategoryManager implementations
402 NS_IMPL_QUERY_INTERFACE1(nsCategoryManager
, nsICategoryManager
)
404 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(CategoryManagerMallocSizeOf
)
406 NS_MEMORY_REPORTER_IMPLEMENT(CategoryManager
,
407 "explicit/xpcom/category-manager",
409 nsIMemoryReporter::UNITS_BYTES
,
410 nsCategoryManager::GetCategoryManagerSize
,
411 "Memory used for the XPCOM category manager.")
413 NS_IMETHODIMP_(nsrefcnt
)
414 nsCategoryManager::AddRef()
419 NS_IMETHODIMP_(nsrefcnt
)
420 nsCategoryManager::Release()
425 nsCategoryManager
* nsCategoryManager::gCategoryManager
;
427 /* static */ nsCategoryManager
*
428 nsCategoryManager::GetSingleton()
430 if (!gCategoryManager
)
431 gCategoryManager
= new nsCategoryManager();
432 return gCategoryManager
;
436 nsCategoryManager::Destroy()
438 delete gCategoryManager
;
439 gCategoryManager
= nullptr;
443 nsCategoryManager::Create(nsISupports
* aOuter
, REFNSIID aIID
, void** aResult
)
446 return NS_ERROR_NO_AGGREGATION
;
448 return GetSingleton()->QueryInterface(aIID
, aResult
);
451 nsCategoryManager::nsCategoryManager()
452 : mLock("nsCategoryManager")
453 , mSuppressNotifications(false)
456 PL_INIT_ARENA_POOL(&mArena
, "CategoryManagerArena",
457 NS_CATEGORYMANAGER_ARENA_SIZE
);
463 nsCategoryManager::InitMemoryReporter()
465 mReporter
= new NS_MEMORY_REPORTER_NAME(CategoryManager
);
466 NS_RegisterMemoryReporter(mReporter
);
469 nsCategoryManager::~nsCategoryManager()
471 (void)::NS_UnregisterMemoryReporter(mReporter
);
474 // the hashtable contains entries that must be deleted before the arena is
475 // destroyed, or else you will have PRLocks undestroyed and other Really
479 PL_FinishArenaPool(&mArena
);
483 nsCategoryManager::get_category(const char* aName
) {
485 if (!mTable
.Get(aName
, &node
)) {
492 nsCategoryManager::GetCategoryManagerSize()
494 MOZ_ASSERT(nsCategoryManager::gCategoryManager
);
495 return nsCategoryManager::gCategoryManager
->SizeOfIncludingThis(
496 CategoryManagerMallocSizeOf
);
500 SizeOfCategoryManagerTableEntryExcludingThis(nsDepCharHashKey::KeyType aKey
,
501 const nsAutoPtr
<CategoryNode
> &aData
,
502 nsMallocSizeOfFun aMallocSizeOf
,
505 // We don't measure the string pointed to by aKey because it's a non-owning
507 return aData
.get()->SizeOfExcludingThis(aMallocSizeOf
);
511 nsCategoryManager::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf
)
513 size_t n
= aMallocSizeOf(this);
515 n
+= PL_SizeOfArenaPoolExcludingPool(&mArena
, aMallocSizeOf
);
517 n
+= mTable
.SizeOfExcludingThis(SizeOfCategoryManagerTableEntryExcludingThis
,
525 class CategoryNotificationRunnable
: public nsRunnable
528 CategoryNotificationRunnable(nsISupports
* aSubject
,
539 nsCOMPtr
<nsISupports
> mSubject
;
541 NS_ConvertUTF8toUTF16 mData
;
545 CategoryNotificationRunnable::Run()
547 nsCOMPtr
<nsIObserverService
> observerService
=
548 mozilla::services::GetObserverService();
550 observerService
->NotifyObservers(mSubject
, mTopic
, mData
.get());
555 } // anonymous namespace
559 nsCategoryManager::NotifyObservers( const char *aTopic
,
560 const char *aCategoryName
,
561 const char *aEntryName
)
563 if (mSuppressNotifications
)
566 nsRefPtr
<CategoryNotificationRunnable
> r
;
569 nsCOMPtr
<nsISupportsCString
> entry
570 (do_CreateInstance (NS_SUPPORTS_CSTRING_CONTRACTID
));
574 nsresult rv
= entry
->SetData(nsDependentCString(aEntryName
));
578 r
= new CategoryNotificationRunnable(entry
, aTopic
, aCategoryName
);
580 r
= new CategoryNotificationRunnable(this, aTopic
, aCategoryName
);
583 NS_DispatchToMainThread(r
);
587 nsCategoryManager::GetCategoryEntry( const char *aCategoryName
,
588 const char *aEntryName
,
591 NS_ENSURE_ARG_POINTER(aCategoryName
);
592 NS_ENSURE_ARG_POINTER(aEntryName
);
593 NS_ENSURE_ARG_POINTER(_retval
);
595 nsresult status
= NS_ERROR_NOT_AVAILABLE
;
597 CategoryNode
* category
;
599 MutexAutoLock
lock(mLock
);
600 category
= get_category(aCategoryName
);
604 status
= category
->GetLeaf(aEntryName
, _retval
);
611 nsCategoryManager::AddCategoryEntry( const char *aCategoryName
,
612 const char *aEntryName
,
619 NS_ERROR("Category manager doesn't support persistence.");
620 return NS_ERROR_INVALID_ARG
;
623 AddCategoryEntry(aCategoryName
, aEntryName
, aValue
, aReplace
, _retval
);
628 nsCategoryManager::AddCategoryEntry(const char *aCategoryName
,
629 const char *aEntryName
,
637 // Before we can insert a new entry, we'll need to
638 // find the |CategoryNode| to put it in...
639 CategoryNode
* category
;
641 MutexAutoLock
lock(mLock
);
642 category
= get_category(aCategoryName
);
645 // That category doesn't exist yet; let's make it.
646 category
= CategoryNode::Create(&mArena
);
648 char* categoryName
= ArenaStrdup(aCategoryName
, &mArena
);
649 mTable
.Put(categoryName
, category
);
656 // We will need the return value of AddLeaf even if the called doesn't want it
657 char *oldEntry
= nullptr;
659 nsresult rv
= category
->AddLeaf(aEntryName
,
665 if (NS_SUCCEEDED(rv
)) {
667 NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID
,
668 aCategoryName
, oldEntry
);
670 NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID
,
671 aCategoryName
, aEntryName
);
674 *aOldValue
= oldEntry
;
681 nsCategoryManager::DeleteCategoryEntry( const char *aCategoryName
,
682 const char *aEntryName
,
685 NS_ENSURE_ARG_POINTER(aCategoryName
);
686 NS_ENSURE_ARG_POINTER(aEntryName
);
689 Note: no errors are reported since failure to delete
690 probably won't hurt you, and returning errors seriously
691 inconveniences JS clients
694 CategoryNode
* category
;
696 MutexAutoLock
lock(mLock
);
697 category
= get_category(aCategoryName
);
701 category
->DeleteLeaf(aEntryName
);
703 NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID
,
704 aCategoryName
, aEntryName
);
711 nsCategoryManager::DeleteCategory( const char *aCategoryName
)
713 NS_ENSURE_ARG_POINTER(aCategoryName
);
715 // the categories are arena-allocated, so we don't
716 // actually delete them. We just remove all of the
719 CategoryNode
* category
;
721 MutexAutoLock
lock(mLock
);
722 category
= get_category(aCategoryName
);
727 NotifyObservers(NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID
,
728 aCategoryName
, nullptr);
735 nsCategoryManager::EnumerateCategory( const char *aCategoryName
,
736 nsISimpleEnumerator
**_retval
)
738 NS_ENSURE_ARG_POINTER(aCategoryName
);
739 NS_ENSURE_ARG_POINTER(_retval
);
741 CategoryNode
* category
;
743 MutexAutoLock
lock(mLock
);
744 category
= get_category(aCategoryName
);
748 return NS_NewEmptyEnumerator(_retval
);
751 return category
->Enumerate(_retval
);
755 nsCategoryManager::EnumerateCategories(nsISimpleEnumerator
**_retval
)
757 NS_ENSURE_ARG_POINTER(_retval
);
759 MutexAutoLock
lock(mLock
);
760 CategoryEnumerator
* enumObj
= CategoryEnumerator::Create(mTable
);
763 return NS_ERROR_OUT_OF_MEMORY
;
770 struct writecat_struct
{
776 nsCategoryManager::SuppressNotifications(bool aSuppress
)
778 mSuppressNotifications
= aSuppress
;
783 * CreateServicesFromCategory()
785 * Given a category, this convenience functions enumerates the category and
786 * creates a service of every CID or ContractID registered under the category.
787 * If observerTopic is non null and the service implements nsIObserver,
788 * this will attempt to notify the observer with the origin, observerTopic string
792 NS_CreateServicesFromCategory(const char *category
,
794 const char *observerTopic
)
798 nsCOMPtr
<nsICategoryManager
> categoryManager
=
799 do_GetService("@mozilla.org/categorymanager;1");
800 if (!categoryManager
)
803 nsCOMPtr
<nsISimpleEnumerator
> enumerator
;
804 rv
= categoryManager
->EnumerateCategory(category
,
805 getter_AddRefs(enumerator
));
809 nsCOMPtr
<nsIUTF8StringEnumerator
> senumerator
=
810 do_QueryInterface(enumerator
);
812 NS_WARNING("Category enumerator doesn't support nsIUTF8StringEnumerator.");
817 while (NS_SUCCEEDED(senumerator
->HasMore(&hasMore
)) && hasMore
) {
818 // From here on just skip any error we get.
819 nsAutoCString entryString
;
820 if (NS_FAILED(senumerator
->GetNext(entryString
)))
823 nsXPIDLCString contractID
;
824 rv
= categoryManager
->GetCategoryEntry(category
,entryString
.get(),
825 getter_Copies(contractID
));
829 nsCOMPtr
<nsISupports
> instance
= do_GetService(contractID
);
831 LogMessage("While creating services from category '%s', could not create service for entry '%s', contract ID '%s'",
832 category
, entryString
.get(), contractID
.get());
837 // try an observer, if it implements it.
838 nsCOMPtr
<nsIObserver
> observer
= do_QueryInterface(instance
);
840 observer
->Observe(origin
, observerTopic
, EmptyString().get());
842 LogMessage("While creating services from category '%s', service for entry '%s', contract ID '%s' does not implement nsIObserver.",
843 category
, entryString
.get(), contractID
.get());