michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #define PL_ARENA_CONST_ALIGN_MASK 7 michael@0: michael@0: #include "nsICategoryManager.h" michael@0: #include "nsCategoryManager.h" michael@0: michael@0: #include "plarena.h" michael@0: #include "prio.h" michael@0: #include "prprf.h" michael@0: #include "prlock.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsTHashtable.h" michael@0: #include "nsClassHashtable.h" michael@0: #include "nsIFactory.h" michael@0: #include "nsIStringEnumerator.h" michael@0: #include "nsSupportsPrimitives.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsIObserver.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsCRT.h" michael@0: #include "nsQuickSort.h" michael@0: #include "nsEnumeratorUtils.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/Services.h" michael@0: michael@0: #include "ManifestParser.h" michael@0: #include "nsISimpleEnumerator.h" michael@0: michael@0: using namespace mozilla; michael@0: class nsIComponentLoaderManager; michael@0: michael@0: /* michael@0: CategoryDatabase michael@0: contains 0 or more 1-1 mappings of string to Category michael@0: each Category contains 0 or more 1-1 mappings of string keys to string values michael@0: michael@0: In other words, the CategoryDatabase is a tree, whose root is a hashtable. michael@0: Internal nodes (or Categories) are hashtables. Leaf nodes are strings. michael@0: michael@0: The leaf strings are allocated in an arena, because we assume they're not michael@0: going to change much ;) michael@0: */ michael@0: michael@0: #define NS_CATEGORYMANAGER_ARENA_SIZE (1024 * 8) michael@0: michael@0: // pulled in from nsComponentManager.cpp michael@0: char* ArenaStrdup(const char* s, PLArenaPool* aArena); michael@0: michael@0: // michael@0: // BaseStringEnumerator is subclassed by EntryEnumerator and michael@0: // CategoryEnumerator michael@0: // michael@0: class BaseStringEnumerator michael@0: : public nsISimpleEnumerator, michael@0: private nsIUTF8StringEnumerator michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSISIMPLEENUMERATOR michael@0: NS_DECL_NSIUTF8STRINGENUMERATOR michael@0: michael@0: protected: michael@0: // Callback function for NS_QuickSort to sort mArray michael@0: static int SortCallback(const void *, const void *, void *); michael@0: michael@0: BaseStringEnumerator() michael@0: : mArray(nullptr), michael@0: mCount(0), michael@0: mSimpleCurItem(0), michael@0: mStringCurItem(0) { } michael@0: michael@0: // A virtual destructor is needed here because subclasses of michael@0: // BaseStringEnumerator do not implement their own Release() method. michael@0: michael@0: virtual ~BaseStringEnumerator() michael@0: { michael@0: if (mArray) michael@0: delete[] mArray; michael@0: } michael@0: michael@0: void Sort(); michael@0: michael@0: const char** mArray; michael@0: uint32_t mCount; michael@0: uint32_t mSimpleCurItem; michael@0: uint32_t mStringCurItem; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(BaseStringEnumerator, nsISimpleEnumerator, nsIUTF8StringEnumerator) michael@0: michael@0: NS_IMETHODIMP michael@0: BaseStringEnumerator::HasMoreElements(bool *_retval) michael@0: { michael@0: *_retval = (mSimpleCurItem < mCount); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: BaseStringEnumerator::GetNext(nsISupports **_retval) michael@0: { michael@0: if (mSimpleCurItem >= mCount) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsSupportsDependentCString* str = michael@0: new nsSupportsDependentCString(mArray[mSimpleCurItem++]); michael@0: if (!str) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: *_retval = str; michael@0: NS_ADDREF(*_retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: BaseStringEnumerator::HasMore(bool *_retval) michael@0: { michael@0: *_retval = (mStringCurItem < mCount); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: BaseStringEnumerator::GetNext(nsACString& _retval) michael@0: { michael@0: if (mStringCurItem >= mCount) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: _retval = nsDependentCString(mArray[mStringCurItem++]); michael@0: return NS_OK; michael@0: } michael@0: michael@0: int michael@0: BaseStringEnumerator::SortCallback(const void *e1, const void *e2, michael@0: void * /*unused*/) michael@0: { michael@0: char const *const *s1 = reinterpret_cast(e1); michael@0: char const *const *s2 = reinterpret_cast(e2); michael@0: michael@0: return strcmp(*s1, *s2); michael@0: } michael@0: michael@0: void michael@0: BaseStringEnumerator::Sort() michael@0: { michael@0: NS_QuickSort(mArray, mCount, sizeof(mArray[0]), SortCallback, nullptr); michael@0: } michael@0: michael@0: // michael@0: // EntryEnumerator is the wrapper that allows nsICategoryManager::EnumerateCategory michael@0: // michael@0: class EntryEnumerator michael@0: : public BaseStringEnumerator michael@0: { michael@0: public: michael@0: static EntryEnumerator* Create(nsTHashtable& aTable); michael@0: michael@0: private: michael@0: static PLDHashOperator michael@0: enumfunc_createenumerator(CategoryLeaf* aLeaf, void* userArg); michael@0: }; michael@0: michael@0: michael@0: PLDHashOperator michael@0: EntryEnumerator::enumfunc_createenumerator(CategoryLeaf* aLeaf, void* userArg) michael@0: { michael@0: EntryEnumerator* mythis = static_cast(userArg); michael@0: if (aLeaf->value) michael@0: mythis->mArray[mythis->mCount++] = aLeaf->GetKey(); michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: EntryEnumerator* michael@0: EntryEnumerator::Create(nsTHashtable& aTable) michael@0: { michael@0: EntryEnumerator* enumObj = new EntryEnumerator(); michael@0: if (!enumObj) michael@0: return nullptr; michael@0: michael@0: enumObj->mArray = new char const* [aTable.Count()]; michael@0: if (!enumObj->mArray) { michael@0: delete enumObj; michael@0: return nullptr; michael@0: } michael@0: michael@0: aTable.EnumerateEntries(enumfunc_createenumerator, enumObj); michael@0: michael@0: enumObj->Sort(); michael@0: michael@0: return enumObj; michael@0: } michael@0: michael@0: michael@0: // michael@0: // CategoryNode implementations michael@0: // michael@0: michael@0: CategoryNode* michael@0: CategoryNode::Create(PLArenaPool* aArena) michael@0: { michael@0: CategoryNode* node = new(aArena) CategoryNode(); michael@0: if (!node) michael@0: return nullptr; michael@0: michael@0: return node; michael@0: } michael@0: michael@0: CategoryNode::~CategoryNode() michael@0: { michael@0: } michael@0: michael@0: void* michael@0: CategoryNode::operator new(size_t aSize, PLArenaPool* aArena) michael@0: { michael@0: void* p; michael@0: PL_ARENA_ALLOCATE(p, aArena, aSize); michael@0: return p; michael@0: } michael@0: michael@0: NS_METHOD michael@0: CategoryNode::GetLeaf(const char* aEntryName, michael@0: char** _retval) michael@0: { michael@0: MutexAutoLock lock(mLock); michael@0: nsresult rv = NS_ERROR_NOT_AVAILABLE; michael@0: CategoryLeaf* ent = michael@0: mTable.GetEntry(aEntryName); michael@0: michael@0: if (ent && ent->value) { michael@0: *_retval = NS_strdup(ent->value); michael@0: if (*_retval) michael@0: rv = NS_OK; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_METHOD michael@0: CategoryNode::AddLeaf(const char* aEntryName, michael@0: const char* aValue, michael@0: bool aReplace, michael@0: char** _retval, michael@0: PLArenaPool* aArena) michael@0: { michael@0: if (_retval) michael@0: *_retval = nullptr; michael@0: michael@0: MutexAutoLock lock(mLock); michael@0: CategoryLeaf* leaf = michael@0: mTable.GetEntry(aEntryName); michael@0: michael@0: if (!leaf) { michael@0: const char* arenaEntryName = ArenaStrdup(aEntryName, aArena); michael@0: if (!arenaEntryName) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: leaf = mTable.PutEntry(arenaEntryName); michael@0: if (!leaf) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: if (leaf->value && !aReplace) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: const char* arenaValue = ArenaStrdup(aValue, aArena); michael@0: if (!arenaValue) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: if (_retval && leaf->value) { michael@0: *_retval = ToNewCString(nsDependentCString(leaf->value)); michael@0: if (!*_retval) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: leaf->value = arenaValue; michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: CategoryNode::DeleteLeaf(const char* aEntryName) michael@0: { michael@0: // we don't throw any errors, because it normally doesn't matter michael@0: // and it makes JS a lot cleaner michael@0: MutexAutoLock lock(mLock); michael@0: michael@0: // we can just remove the entire hash entry without introspection michael@0: mTable.RemoveEntry(aEntryName); michael@0: } michael@0: michael@0: NS_METHOD michael@0: CategoryNode::Enumerate(nsISimpleEnumerator **_retval) michael@0: { michael@0: if (NS_WARN_IF(!_retval)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: MutexAutoLock lock(mLock); michael@0: EntryEnumerator* enumObj = EntryEnumerator::Create(mTable); michael@0: michael@0: if (!enumObj) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: *_retval = enumObj; michael@0: NS_ADDREF(*_retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: size_t michael@0: CategoryNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) michael@0: { michael@0: // We don't measure the strings pointed to by the entries because the michael@0: // pointers are non-owning. michael@0: return mTable.SizeOfExcludingThis(nullptr, aMallocSizeOf); michael@0: } michael@0: michael@0: struct persistent_userstruct { michael@0: PRFileDesc* fd; michael@0: const char* categoryName; michael@0: bool success; michael@0: }; michael@0: michael@0: PLDHashOperator michael@0: enumfunc_pentries(CategoryLeaf* aLeaf, void* userArg) michael@0: { michael@0: persistent_userstruct* args = michael@0: static_cast(userArg); michael@0: michael@0: PLDHashOperator status = PL_DHASH_NEXT; michael@0: michael@0: if (aLeaf->value) { michael@0: if (PR_fprintf(args->fd, michael@0: "%s,%s,%s\n", michael@0: args->categoryName, michael@0: aLeaf->GetKey(), michael@0: aLeaf->value) == (uint32_t) -1) { michael@0: args->success = false; michael@0: status = PL_DHASH_STOP; michael@0: } michael@0: } michael@0: michael@0: return status; michael@0: } michael@0: michael@0: // michael@0: // CategoryEnumerator class michael@0: // michael@0: michael@0: class CategoryEnumerator michael@0: : public BaseStringEnumerator michael@0: { michael@0: public: michael@0: static CategoryEnumerator* Create(nsClassHashtable& aTable); michael@0: michael@0: private: michael@0: static PLDHashOperator michael@0: enumfunc_createenumerator(const char* aStr, michael@0: CategoryNode* aNode, michael@0: void* userArg); michael@0: }; michael@0: michael@0: CategoryEnumerator* michael@0: CategoryEnumerator::Create(nsClassHashtable& aTable) michael@0: { michael@0: CategoryEnumerator* enumObj = new CategoryEnumerator(); michael@0: if (!enumObj) michael@0: return nullptr; michael@0: michael@0: enumObj->mArray = new const char* [aTable.Count()]; michael@0: if (!enumObj->mArray) { michael@0: delete enumObj; michael@0: return nullptr; michael@0: } michael@0: michael@0: aTable.EnumerateRead(enumfunc_createenumerator, enumObj); michael@0: michael@0: return enumObj; michael@0: } michael@0: michael@0: PLDHashOperator michael@0: CategoryEnumerator::enumfunc_createenumerator(const char* aStr, CategoryNode* aNode, void* userArg) michael@0: { michael@0: CategoryEnumerator* mythis = static_cast(userArg); michael@0: michael@0: // if a category has no entries, we pretend it doesn't exist michael@0: if (aNode->Count()) michael@0: mythis->mArray[mythis->mCount++] = aStr; michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: michael@0: // michael@0: // nsCategoryManager implementations michael@0: // michael@0: michael@0: NS_IMPL_QUERY_INTERFACE(nsCategoryManager, nsICategoryManager, nsIMemoryReporter) michael@0: michael@0: NS_IMETHODIMP_(MozExternalRefCountType) michael@0: nsCategoryManager::AddRef() michael@0: { michael@0: return 2; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(MozExternalRefCountType) michael@0: nsCategoryManager::Release() michael@0: { michael@0: return 1; michael@0: } michael@0: michael@0: nsCategoryManager* nsCategoryManager::gCategoryManager; michael@0: michael@0: /* static */ nsCategoryManager* michael@0: nsCategoryManager::GetSingleton() michael@0: { michael@0: if (!gCategoryManager) michael@0: gCategoryManager = new nsCategoryManager(); michael@0: return gCategoryManager; michael@0: } michael@0: michael@0: /* static */ void michael@0: nsCategoryManager::Destroy() michael@0: { michael@0: delete gCategoryManager; michael@0: gCategoryManager = nullptr; michael@0: } michael@0: michael@0: nsresult michael@0: nsCategoryManager::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult) michael@0: { michael@0: if (aOuter) michael@0: return NS_ERROR_NO_AGGREGATION; michael@0: michael@0: return GetSingleton()->QueryInterface(aIID, aResult); michael@0: } michael@0: michael@0: nsCategoryManager::nsCategoryManager() michael@0: : mLock("nsCategoryManager") michael@0: , mSuppressNotifications(false) michael@0: { michael@0: PL_INIT_ARENA_POOL(&mArena, "CategoryManagerArena", michael@0: NS_CATEGORYMANAGER_ARENA_SIZE); michael@0: } michael@0: michael@0: void michael@0: nsCategoryManager::InitMemoryReporter() michael@0: { michael@0: RegisterWeakMemoryReporter(this); michael@0: } michael@0: michael@0: nsCategoryManager::~nsCategoryManager() michael@0: { michael@0: UnregisterWeakMemoryReporter(this); michael@0: michael@0: // the hashtable contains entries that must be deleted before the arena is michael@0: // destroyed, or else you will have PRLocks undestroyed and other Really michael@0: // Bad Stuff (TM) michael@0: mTable.Clear(); michael@0: michael@0: PL_FinishArenaPool(&mArena); michael@0: } michael@0: michael@0: inline CategoryNode* michael@0: nsCategoryManager::get_category(const char* aName) { michael@0: CategoryNode* node; michael@0: if (!mTable.Get(aName, &node)) { michael@0: return nullptr; michael@0: } michael@0: return node; michael@0: } michael@0: michael@0: MOZ_DEFINE_MALLOC_SIZE_OF(CategoryManagerMallocSizeOf) michael@0: michael@0: NS_IMETHODIMP michael@0: nsCategoryManager::CollectReports(nsIHandleReportCallback* aHandleReport, michael@0: nsISupports* aData) michael@0: { michael@0: return MOZ_COLLECT_REPORT( michael@0: "explicit/xpcom/category-manager", KIND_HEAP, UNITS_BYTES, michael@0: SizeOfIncludingThis(CategoryManagerMallocSizeOf), michael@0: "Memory used for the XPCOM category manager."); michael@0: } michael@0: michael@0: static size_t michael@0: SizeOfCategoryManagerTableEntryExcludingThis(nsDepCharHashKey::KeyType aKey, michael@0: const nsAutoPtr &aData, michael@0: MallocSizeOf aMallocSizeOf, michael@0: void* aUserArg) michael@0: { michael@0: // We don't measure the string pointed to by aKey because it's a non-owning michael@0: // pointer. michael@0: return aData.get()->SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: size_t michael@0: nsCategoryManager::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) michael@0: { michael@0: size_t n = aMallocSizeOf(this); michael@0: michael@0: n += PL_SizeOfArenaPoolExcludingPool(&mArena, aMallocSizeOf); michael@0: michael@0: n += mTable.SizeOfExcludingThis(SizeOfCategoryManagerTableEntryExcludingThis, michael@0: aMallocSizeOf); michael@0: michael@0: return n; michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: class CategoryNotificationRunnable : public nsRunnable michael@0: { michael@0: public: michael@0: CategoryNotificationRunnable(nsISupports* aSubject, michael@0: const char* aTopic, michael@0: const char* aData) michael@0: : mSubject(aSubject) michael@0: , mTopic(aTopic) michael@0: , mData(aData) michael@0: { } michael@0: michael@0: NS_DECL_NSIRUNNABLE michael@0: michael@0: private: michael@0: nsCOMPtr mSubject; michael@0: const char* mTopic; michael@0: NS_ConvertUTF8toUTF16 mData; michael@0: }; michael@0: michael@0: NS_IMETHODIMP michael@0: CategoryNotificationRunnable::Run() michael@0: { michael@0: nsCOMPtr observerService = michael@0: mozilla::services::GetObserverService(); michael@0: if (observerService) michael@0: observerService->NotifyObservers(mSubject, mTopic, mData.get()); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // anonymous namespace michael@0: michael@0: michael@0: void michael@0: nsCategoryManager::NotifyObservers( const char *aTopic, michael@0: const char *aCategoryName, michael@0: const char *aEntryName ) michael@0: { michael@0: if (mSuppressNotifications) michael@0: return; michael@0: michael@0: nsRefPtr r; michael@0: michael@0: if (aEntryName) { michael@0: nsCOMPtr entry michael@0: (do_CreateInstance (NS_SUPPORTS_CSTRING_CONTRACTID)); michael@0: if (!entry) michael@0: return; michael@0: michael@0: nsresult rv = entry->SetData(nsDependentCString(aEntryName)); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: r = new CategoryNotificationRunnable(entry, aTopic, aCategoryName); michael@0: } else { michael@0: r = new CategoryNotificationRunnable( michael@0: NS_ISUPPORTS_CAST(nsICategoryManager*, this), michael@0: aTopic, aCategoryName); michael@0: } michael@0: michael@0: NS_DispatchToMainThread(r); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCategoryManager::GetCategoryEntry( const char *aCategoryName, michael@0: const char *aEntryName, michael@0: char **_retval ) michael@0: { michael@0: if (NS_WARN_IF(!aCategoryName) || michael@0: NS_WARN_IF(!aEntryName) || michael@0: NS_WARN_IF(!_retval)) michael@0: return NS_ERROR_INVALID_ARG;; michael@0: michael@0: nsresult status = NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: CategoryNode* category; michael@0: { michael@0: MutexAutoLock lock(mLock); michael@0: category = get_category(aCategoryName); michael@0: } michael@0: michael@0: if (category) { michael@0: status = category->GetLeaf(aEntryName, _retval); michael@0: } michael@0: michael@0: return status; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCategoryManager::AddCategoryEntry( const char *aCategoryName, michael@0: const char *aEntryName, michael@0: const char *aValue, michael@0: bool aPersist, michael@0: bool aReplace, michael@0: char **_retval ) michael@0: { michael@0: if (aPersist) { michael@0: NS_ERROR("Category manager doesn't support persistence."); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: AddCategoryEntry(aCategoryName, aEntryName, aValue, aReplace, _retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsCategoryManager::AddCategoryEntry(const char *aCategoryName, michael@0: const char *aEntryName, michael@0: const char *aValue, michael@0: bool aReplace, michael@0: char** aOldValue) michael@0: { michael@0: if (aOldValue) michael@0: *aOldValue = nullptr; michael@0: michael@0: // Before we can insert a new entry, we'll need to michael@0: // find the |CategoryNode| to put it in... michael@0: CategoryNode* category; michael@0: { michael@0: MutexAutoLock lock(mLock); michael@0: category = get_category(aCategoryName); michael@0: michael@0: if (!category) { michael@0: // That category doesn't exist yet; let's make it. michael@0: category = CategoryNode::Create(&mArena); michael@0: michael@0: char* categoryName = ArenaStrdup(aCategoryName, &mArena); michael@0: mTable.Put(categoryName, category); michael@0: } michael@0: } michael@0: michael@0: if (!category) michael@0: return; michael@0: michael@0: // We will need the return value of AddLeaf even if the called doesn't want it michael@0: char *oldEntry = nullptr; michael@0: michael@0: nsresult rv = category->AddLeaf(aEntryName, michael@0: aValue, michael@0: aReplace, michael@0: &oldEntry, michael@0: &mArena); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: if (oldEntry) { michael@0: NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID, michael@0: aCategoryName, aEntryName); michael@0: } michael@0: NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID, michael@0: aCategoryName, aEntryName); michael@0: michael@0: if (aOldValue) michael@0: *aOldValue = oldEntry; michael@0: else michael@0: NS_Free(oldEntry); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCategoryManager::DeleteCategoryEntry( const char *aCategoryName, michael@0: const char *aEntryName, michael@0: bool aDontPersist) michael@0: { michael@0: if (NS_WARN_IF(!aCategoryName) || michael@0: NS_WARN_IF(!aEntryName)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: /* michael@0: Note: no errors are reported since failure to delete michael@0: probably won't hurt you, and returning errors seriously michael@0: inconveniences JS clients michael@0: */ michael@0: michael@0: CategoryNode* category; michael@0: { michael@0: MutexAutoLock lock(mLock); michael@0: category = get_category(aCategoryName); michael@0: } michael@0: michael@0: if (category) { michael@0: category->DeleteLeaf(aEntryName); michael@0: michael@0: NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID, michael@0: aCategoryName, aEntryName); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCategoryManager::DeleteCategory( const char *aCategoryName ) michael@0: { michael@0: if (NS_WARN_IF(!aCategoryName)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: // the categories are arena-allocated, so we don't michael@0: // actually delete them. We just remove all of the michael@0: // leaf nodes. michael@0: michael@0: CategoryNode* category; michael@0: { michael@0: MutexAutoLock lock(mLock); michael@0: category = get_category(aCategoryName); michael@0: } michael@0: michael@0: if (category) { michael@0: category->Clear(); michael@0: NotifyObservers(NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID, michael@0: aCategoryName, nullptr); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCategoryManager::EnumerateCategory( const char *aCategoryName, michael@0: nsISimpleEnumerator **_retval ) michael@0: { michael@0: if (NS_WARN_IF(!aCategoryName) || michael@0: NS_WARN_IF(!_retval)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: CategoryNode* category; michael@0: { michael@0: MutexAutoLock lock(mLock); michael@0: category = get_category(aCategoryName); michael@0: } michael@0: michael@0: if (!category) { michael@0: return NS_NewEmptyEnumerator(_retval); michael@0: } michael@0: michael@0: return category->Enumerate(_retval); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCategoryManager::EnumerateCategories(nsISimpleEnumerator **_retval) michael@0: { michael@0: if (NS_WARN_IF(!_retval)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: MutexAutoLock lock(mLock); michael@0: CategoryEnumerator* enumObj = CategoryEnumerator::Create(mTable); michael@0: michael@0: if (!enumObj) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: *_retval = enumObj; michael@0: NS_ADDREF(*_retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: struct writecat_struct { michael@0: PRFileDesc* fd; michael@0: bool success; michael@0: }; michael@0: michael@0: NS_METHOD michael@0: nsCategoryManager::SuppressNotifications(bool aSuppress) michael@0: { michael@0: mSuppressNotifications = aSuppress; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* michael@0: * CreateServicesFromCategory() michael@0: * michael@0: * Given a category, this convenience functions enumerates the category and michael@0: * creates a service of every CID or ContractID registered under the category. michael@0: * If observerTopic is non null and the service implements nsIObserver, michael@0: * this will attempt to notify the observer with the origin, observerTopic string michael@0: * as parameter. michael@0: */ michael@0: void michael@0: NS_CreateServicesFromCategory(const char *category, michael@0: nsISupports *origin, michael@0: const char *observerTopic) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr categoryManager = michael@0: do_GetService("@mozilla.org/categorymanager;1"); michael@0: if (!categoryManager) michael@0: return; michael@0: michael@0: nsCOMPtr enumerator; michael@0: rv = categoryManager->EnumerateCategory(category, michael@0: getter_AddRefs(enumerator)); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: nsCOMPtr senumerator = michael@0: do_QueryInterface(enumerator); michael@0: if (!senumerator) { michael@0: NS_WARNING("Category enumerator doesn't support nsIUTF8StringEnumerator."); michael@0: return; michael@0: } michael@0: michael@0: bool hasMore; michael@0: while (NS_SUCCEEDED(senumerator->HasMore(&hasMore)) && hasMore) { michael@0: // From here on just skip any error we get. michael@0: nsAutoCString entryString; michael@0: if (NS_FAILED(senumerator->GetNext(entryString))) michael@0: continue; michael@0: michael@0: nsXPIDLCString contractID; michael@0: rv = categoryManager->GetCategoryEntry(category,entryString.get(), michael@0: getter_Copies(contractID)); michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: michael@0: nsCOMPtr instance = do_GetService(contractID); michael@0: if (!instance) { michael@0: LogMessage("While creating services from category '%s', could not create service for entry '%s', contract ID '%s'", michael@0: category, entryString.get(), contractID.get()); michael@0: continue; michael@0: } michael@0: michael@0: if (observerTopic) { michael@0: // try an observer, if it implements it. michael@0: nsCOMPtr observer = do_QueryInterface(instance); michael@0: if (observer) michael@0: observer->Observe(origin, observerTopic, EmptyString().get()); michael@0: else michael@0: LogMessage("While creating services from category '%s', service for entry '%s', contract ID '%s' does not implement nsIObserver.", michael@0: category, entryString.get(), contractID.get()); michael@0: } michael@0: } michael@0: }