1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/components/nsCategoryManager.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,844 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#define PL_ARENA_CONST_ALIGN_MASK 7 1.10 + 1.11 +#include "nsICategoryManager.h" 1.12 +#include "nsCategoryManager.h" 1.13 + 1.14 +#include "plarena.h" 1.15 +#include "prio.h" 1.16 +#include "prprf.h" 1.17 +#include "prlock.h" 1.18 +#include "nsCOMPtr.h" 1.19 +#include "nsTHashtable.h" 1.20 +#include "nsClassHashtable.h" 1.21 +#include "nsIFactory.h" 1.22 +#include "nsIStringEnumerator.h" 1.23 +#include "nsSupportsPrimitives.h" 1.24 +#include "nsComponentManagerUtils.h" 1.25 +#include "nsServiceManagerUtils.h" 1.26 +#include "nsIObserver.h" 1.27 +#include "nsIObserverService.h" 1.28 +#include "nsReadableUtils.h" 1.29 +#include "nsCRT.h" 1.30 +#include "nsQuickSort.h" 1.31 +#include "nsEnumeratorUtils.h" 1.32 +#include "nsThreadUtils.h" 1.33 +#include "mozilla/MemoryReporting.h" 1.34 +#include "mozilla/Services.h" 1.35 + 1.36 +#include "ManifestParser.h" 1.37 +#include "nsISimpleEnumerator.h" 1.38 + 1.39 +using namespace mozilla; 1.40 +class nsIComponentLoaderManager; 1.41 + 1.42 +/* 1.43 + CategoryDatabase 1.44 + contains 0 or more 1-1 mappings of string to Category 1.45 + each Category contains 0 or more 1-1 mappings of string keys to string values 1.46 + 1.47 + In other words, the CategoryDatabase is a tree, whose root is a hashtable. 1.48 + Internal nodes (or Categories) are hashtables. Leaf nodes are strings. 1.49 + 1.50 + The leaf strings are allocated in an arena, because we assume they're not 1.51 + going to change much ;) 1.52 +*/ 1.53 + 1.54 +#define NS_CATEGORYMANAGER_ARENA_SIZE (1024 * 8) 1.55 + 1.56 +// pulled in from nsComponentManager.cpp 1.57 +char* ArenaStrdup(const char* s, PLArenaPool* aArena); 1.58 + 1.59 +// 1.60 +// BaseStringEnumerator is subclassed by EntryEnumerator and 1.61 +// CategoryEnumerator 1.62 +// 1.63 +class BaseStringEnumerator 1.64 + : public nsISimpleEnumerator, 1.65 + private nsIUTF8StringEnumerator 1.66 +{ 1.67 +public: 1.68 + NS_DECL_ISUPPORTS 1.69 + NS_DECL_NSISIMPLEENUMERATOR 1.70 + NS_DECL_NSIUTF8STRINGENUMERATOR 1.71 + 1.72 +protected: 1.73 + // Callback function for NS_QuickSort to sort mArray 1.74 + static int SortCallback(const void *, const void *, void *); 1.75 + 1.76 + BaseStringEnumerator() 1.77 + : mArray(nullptr), 1.78 + mCount(0), 1.79 + mSimpleCurItem(0), 1.80 + mStringCurItem(0) { } 1.81 + 1.82 + // A virtual destructor is needed here because subclasses of 1.83 + // BaseStringEnumerator do not implement their own Release() method. 1.84 + 1.85 + virtual ~BaseStringEnumerator() 1.86 + { 1.87 + if (mArray) 1.88 + delete[] mArray; 1.89 + } 1.90 + 1.91 + void Sort(); 1.92 + 1.93 + const char** mArray; 1.94 + uint32_t mCount; 1.95 + uint32_t mSimpleCurItem; 1.96 + uint32_t mStringCurItem; 1.97 +}; 1.98 + 1.99 +NS_IMPL_ISUPPORTS(BaseStringEnumerator, nsISimpleEnumerator, nsIUTF8StringEnumerator) 1.100 + 1.101 +NS_IMETHODIMP 1.102 +BaseStringEnumerator::HasMoreElements(bool *_retval) 1.103 +{ 1.104 + *_retval = (mSimpleCurItem < mCount); 1.105 + 1.106 + return NS_OK; 1.107 +} 1.108 + 1.109 +NS_IMETHODIMP 1.110 +BaseStringEnumerator::GetNext(nsISupports **_retval) 1.111 +{ 1.112 + if (mSimpleCurItem >= mCount) 1.113 + return NS_ERROR_FAILURE; 1.114 + 1.115 + nsSupportsDependentCString* str = 1.116 + new nsSupportsDependentCString(mArray[mSimpleCurItem++]); 1.117 + if (!str) 1.118 + return NS_ERROR_OUT_OF_MEMORY; 1.119 + 1.120 + *_retval = str; 1.121 + NS_ADDREF(*_retval); 1.122 + return NS_OK; 1.123 +} 1.124 + 1.125 +NS_IMETHODIMP 1.126 +BaseStringEnumerator::HasMore(bool *_retval) 1.127 +{ 1.128 + *_retval = (mStringCurItem < mCount); 1.129 + 1.130 + return NS_OK; 1.131 +} 1.132 + 1.133 +NS_IMETHODIMP 1.134 +BaseStringEnumerator::GetNext(nsACString& _retval) 1.135 +{ 1.136 + if (mStringCurItem >= mCount) 1.137 + return NS_ERROR_FAILURE; 1.138 + 1.139 + _retval = nsDependentCString(mArray[mStringCurItem++]); 1.140 + return NS_OK; 1.141 +} 1.142 + 1.143 +int 1.144 +BaseStringEnumerator::SortCallback(const void *e1, const void *e2, 1.145 + void * /*unused*/) 1.146 +{ 1.147 + char const *const *s1 = reinterpret_cast<char const *const *>(e1); 1.148 + char const *const *s2 = reinterpret_cast<char const *const *>(e2); 1.149 + 1.150 + return strcmp(*s1, *s2); 1.151 +} 1.152 + 1.153 +void 1.154 +BaseStringEnumerator::Sort() 1.155 +{ 1.156 + NS_QuickSort(mArray, mCount, sizeof(mArray[0]), SortCallback, nullptr); 1.157 +} 1.158 + 1.159 +// 1.160 +// EntryEnumerator is the wrapper that allows nsICategoryManager::EnumerateCategory 1.161 +// 1.162 +class EntryEnumerator 1.163 + : public BaseStringEnumerator 1.164 +{ 1.165 +public: 1.166 + static EntryEnumerator* Create(nsTHashtable<CategoryLeaf>& aTable); 1.167 + 1.168 +private: 1.169 + static PLDHashOperator 1.170 + enumfunc_createenumerator(CategoryLeaf* aLeaf, void* userArg); 1.171 +}; 1.172 + 1.173 + 1.174 +PLDHashOperator 1.175 +EntryEnumerator::enumfunc_createenumerator(CategoryLeaf* aLeaf, void* userArg) 1.176 +{ 1.177 + EntryEnumerator* mythis = static_cast<EntryEnumerator*>(userArg); 1.178 + if (aLeaf->value) 1.179 + mythis->mArray[mythis->mCount++] = aLeaf->GetKey(); 1.180 + 1.181 + return PL_DHASH_NEXT; 1.182 +} 1.183 + 1.184 +EntryEnumerator* 1.185 +EntryEnumerator::Create(nsTHashtable<CategoryLeaf>& aTable) 1.186 +{ 1.187 + EntryEnumerator* enumObj = new EntryEnumerator(); 1.188 + if (!enumObj) 1.189 + return nullptr; 1.190 + 1.191 + enumObj->mArray = new char const* [aTable.Count()]; 1.192 + if (!enumObj->mArray) { 1.193 + delete enumObj; 1.194 + return nullptr; 1.195 + } 1.196 + 1.197 + aTable.EnumerateEntries(enumfunc_createenumerator, enumObj); 1.198 + 1.199 + enumObj->Sort(); 1.200 + 1.201 + return enumObj; 1.202 +} 1.203 + 1.204 + 1.205 +// 1.206 +// CategoryNode implementations 1.207 +// 1.208 + 1.209 +CategoryNode* 1.210 +CategoryNode::Create(PLArenaPool* aArena) 1.211 +{ 1.212 + CategoryNode* node = new(aArena) CategoryNode(); 1.213 + if (!node) 1.214 + return nullptr; 1.215 + 1.216 + return node; 1.217 +} 1.218 + 1.219 +CategoryNode::~CategoryNode() 1.220 +{ 1.221 +} 1.222 + 1.223 +void* 1.224 +CategoryNode::operator new(size_t aSize, PLArenaPool* aArena) 1.225 +{ 1.226 + void* p; 1.227 + PL_ARENA_ALLOCATE(p, aArena, aSize); 1.228 + return p; 1.229 +} 1.230 + 1.231 +NS_METHOD 1.232 +CategoryNode::GetLeaf(const char* aEntryName, 1.233 + char** _retval) 1.234 +{ 1.235 + MutexAutoLock lock(mLock); 1.236 + nsresult rv = NS_ERROR_NOT_AVAILABLE; 1.237 + CategoryLeaf* ent = 1.238 + mTable.GetEntry(aEntryName); 1.239 + 1.240 + if (ent && ent->value) { 1.241 + *_retval = NS_strdup(ent->value); 1.242 + if (*_retval) 1.243 + rv = NS_OK; 1.244 + } 1.245 + 1.246 + return rv; 1.247 +} 1.248 + 1.249 +NS_METHOD 1.250 +CategoryNode::AddLeaf(const char* aEntryName, 1.251 + const char* aValue, 1.252 + bool aReplace, 1.253 + char** _retval, 1.254 + PLArenaPool* aArena) 1.255 +{ 1.256 + if (_retval) 1.257 + *_retval = nullptr; 1.258 + 1.259 + MutexAutoLock lock(mLock); 1.260 + CategoryLeaf* leaf = 1.261 + mTable.GetEntry(aEntryName); 1.262 + 1.263 + if (!leaf) { 1.264 + const char* arenaEntryName = ArenaStrdup(aEntryName, aArena); 1.265 + if (!arenaEntryName) 1.266 + return NS_ERROR_OUT_OF_MEMORY; 1.267 + 1.268 + leaf = mTable.PutEntry(arenaEntryName); 1.269 + if (!leaf) 1.270 + return NS_ERROR_OUT_OF_MEMORY; 1.271 + } 1.272 + 1.273 + if (leaf->value && !aReplace) 1.274 + return NS_ERROR_INVALID_ARG; 1.275 + 1.276 + const char* arenaValue = ArenaStrdup(aValue, aArena); 1.277 + if (!arenaValue) 1.278 + return NS_ERROR_OUT_OF_MEMORY; 1.279 + 1.280 + if (_retval && leaf->value) { 1.281 + *_retval = ToNewCString(nsDependentCString(leaf->value)); 1.282 + if (!*_retval) 1.283 + return NS_ERROR_OUT_OF_MEMORY; 1.284 + } 1.285 + 1.286 + leaf->value = arenaValue; 1.287 + return NS_OK; 1.288 +} 1.289 + 1.290 +void 1.291 +CategoryNode::DeleteLeaf(const char* aEntryName) 1.292 +{ 1.293 + // we don't throw any errors, because it normally doesn't matter 1.294 + // and it makes JS a lot cleaner 1.295 + MutexAutoLock lock(mLock); 1.296 + 1.297 + // we can just remove the entire hash entry without introspection 1.298 + mTable.RemoveEntry(aEntryName); 1.299 +} 1.300 + 1.301 +NS_METHOD 1.302 +CategoryNode::Enumerate(nsISimpleEnumerator **_retval) 1.303 +{ 1.304 + if (NS_WARN_IF(!_retval)) 1.305 + return NS_ERROR_INVALID_ARG; 1.306 + 1.307 + MutexAutoLock lock(mLock); 1.308 + EntryEnumerator* enumObj = EntryEnumerator::Create(mTable); 1.309 + 1.310 + if (!enumObj) 1.311 + return NS_ERROR_OUT_OF_MEMORY; 1.312 + 1.313 + *_retval = enumObj; 1.314 + NS_ADDREF(*_retval); 1.315 + return NS_OK; 1.316 +} 1.317 + 1.318 +size_t 1.319 +CategoryNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) 1.320 +{ 1.321 + // We don't measure the strings pointed to by the entries because the 1.322 + // pointers are non-owning. 1.323 + return mTable.SizeOfExcludingThis(nullptr, aMallocSizeOf); 1.324 +} 1.325 + 1.326 +struct persistent_userstruct { 1.327 + PRFileDesc* fd; 1.328 + const char* categoryName; 1.329 + bool success; 1.330 +}; 1.331 + 1.332 +PLDHashOperator 1.333 +enumfunc_pentries(CategoryLeaf* aLeaf, void* userArg) 1.334 +{ 1.335 + persistent_userstruct* args = 1.336 + static_cast<persistent_userstruct*>(userArg); 1.337 + 1.338 + PLDHashOperator status = PL_DHASH_NEXT; 1.339 + 1.340 + if (aLeaf->value) { 1.341 + if (PR_fprintf(args->fd, 1.342 + "%s,%s,%s\n", 1.343 + args->categoryName, 1.344 + aLeaf->GetKey(), 1.345 + aLeaf->value) == (uint32_t) -1) { 1.346 + args->success = false; 1.347 + status = PL_DHASH_STOP; 1.348 + } 1.349 + } 1.350 + 1.351 + return status; 1.352 +} 1.353 + 1.354 +// 1.355 +// CategoryEnumerator class 1.356 +// 1.357 + 1.358 +class CategoryEnumerator 1.359 + : public BaseStringEnumerator 1.360 +{ 1.361 +public: 1.362 + static CategoryEnumerator* Create(nsClassHashtable<nsDepCharHashKey, CategoryNode>& aTable); 1.363 + 1.364 +private: 1.365 + static PLDHashOperator 1.366 + enumfunc_createenumerator(const char* aStr, 1.367 + CategoryNode* aNode, 1.368 + void* userArg); 1.369 +}; 1.370 + 1.371 +CategoryEnumerator* 1.372 +CategoryEnumerator::Create(nsClassHashtable<nsDepCharHashKey, CategoryNode>& aTable) 1.373 +{ 1.374 + CategoryEnumerator* enumObj = new CategoryEnumerator(); 1.375 + if (!enumObj) 1.376 + return nullptr; 1.377 + 1.378 + enumObj->mArray = new const char* [aTable.Count()]; 1.379 + if (!enumObj->mArray) { 1.380 + delete enumObj; 1.381 + return nullptr; 1.382 + } 1.383 + 1.384 + aTable.EnumerateRead(enumfunc_createenumerator, enumObj); 1.385 + 1.386 + return enumObj; 1.387 +} 1.388 + 1.389 +PLDHashOperator 1.390 +CategoryEnumerator::enumfunc_createenumerator(const char* aStr, CategoryNode* aNode, void* userArg) 1.391 +{ 1.392 + CategoryEnumerator* mythis = static_cast<CategoryEnumerator*>(userArg); 1.393 + 1.394 + // if a category has no entries, we pretend it doesn't exist 1.395 + if (aNode->Count()) 1.396 + mythis->mArray[mythis->mCount++] = aStr; 1.397 + 1.398 + return PL_DHASH_NEXT; 1.399 +} 1.400 + 1.401 + 1.402 +// 1.403 +// nsCategoryManager implementations 1.404 +// 1.405 + 1.406 +NS_IMPL_QUERY_INTERFACE(nsCategoryManager, nsICategoryManager, nsIMemoryReporter) 1.407 + 1.408 +NS_IMETHODIMP_(MozExternalRefCountType) 1.409 +nsCategoryManager::AddRef() 1.410 +{ 1.411 + return 2; 1.412 +} 1.413 + 1.414 +NS_IMETHODIMP_(MozExternalRefCountType) 1.415 +nsCategoryManager::Release() 1.416 +{ 1.417 + return 1; 1.418 +} 1.419 + 1.420 +nsCategoryManager* nsCategoryManager::gCategoryManager; 1.421 + 1.422 +/* static */ nsCategoryManager* 1.423 +nsCategoryManager::GetSingleton() 1.424 +{ 1.425 + if (!gCategoryManager) 1.426 + gCategoryManager = new nsCategoryManager(); 1.427 + return gCategoryManager; 1.428 +} 1.429 + 1.430 +/* static */ void 1.431 +nsCategoryManager::Destroy() 1.432 +{ 1.433 + delete gCategoryManager; 1.434 + gCategoryManager = nullptr; 1.435 +} 1.436 + 1.437 +nsresult 1.438 +nsCategoryManager::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult) 1.439 +{ 1.440 + if (aOuter) 1.441 + return NS_ERROR_NO_AGGREGATION; 1.442 + 1.443 + return GetSingleton()->QueryInterface(aIID, aResult); 1.444 +} 1.445 + 1.446 +nsCategoryManager::nsCategoryManager() 1.447 + : mLock("nsCategoryManager") 1.448 + , mSuppressNotifications(false) 1.449 +{ 1.450 + PL_INIT_ARENA_POOL(&mArena, "CategoryManagerArena", 1.451 + NS_CATEGORYMANAGER_ARENA_SIZE); 1.452 +} 1.453 + 1.454 +void 1.455 +nsCategoryManager::InitMemoryReporter() 1.456 +{ 1.457 + RegisterWeakMemoryReporter(this); 1.458 +} 1.459 + 1.460 +nsCategoryManager::~nsCategoryManager() 1.461 +{ 1.462 + UnregisterWeakMemoryReporter(this); 1.463 + 1.464 + // the hashtable contains entries that must be deleted before the arena is 1.465 + // destroyed, or else you will have PRLocks undestroyed and other Really 1.466 + // Bad Stuff (TM) 1.467 + mTable.Clear(); 1.468 + 1.469 + PL_FinishArenaPool(&mArena); 1.470 +} 1.471 + 1.472 +inline CategoryNode* 1.473 +nsCategoryManager::get_category(const char* aName) { 1.474 + CategoryNode* node; 1.475 + if (!mTable.Get(aName, &node)) { 1.476 + return nullptr; 1.477 + } 1.478 + return node; 1.479 +} 1.480 + 1.481 +MOZ_DEFINE_MALLOC_SIZE_OF(CategoryManagerMallocSizeOf) 1.482 + 1.483 +NS_IMETHODIMP 1.484 +nsCategoryManager::CollectReports(nsIHandleReportCallback* aHandleReport, 1.485 + nsISupports* aData) 1.486 +{ 1.487 + return MOZ_COLLECT_REPORT( 1.488 + "explicit/xpcom/category-manager", KIND_HEAP, UNITS_BYTES, 1.489 + SizeOfIncludingThis(CategoryManagerMallocSizeOf), 1.490 + "Memory used for the XPCOM category manager."); 1.491 +} 1.492 + 1.493 +static size_t 1.494 +SizeOfCategoryManagerTableEntryExcludingThis(nsDepCharHashKey::KeyType aKey, 1.495 + const nsAutoPtr<CategoryNode> &aData, 1.496 + MallocSizeOf aMallocSizeOf, 1.497 + void* aUserArg) 1.498 +{ 1.499 + // We don't measure the string pointed to by aKey because it's a non-owning 1.500 + // pointer. 1.501 + return aData.get()->SizeOfExcludingThis(aMallocSizeOf); 1.502 +} 1.503 + 1.504 +size_t 1.505 +nsCategoryManager::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) 1.506 +{ 1.507 + size_t n = aMallocSizeOf(this); 1.508 + 1.509 + n += PL_SizeOfArenaPoolExcludingPool(&mArena, aMallocSizeOf); 1.510 + 1.511 + n += mTable.SizeOfExcludingThis(SizeOfCategoryManagerTableEntryExcludingThis, 1.512 + aMallocSizeOf); 1.513 + 1.514 + return n; 1.515 +} 1.516 + 1.517 +namespace { 1.518 + 1.519 +class CategoryNotificationRunnable : public nsRunnable 1.520 +{ 1.521 +public: 1.522 + CategoryNotificationRunnable(nsISupports* aSubject, 1.523 + const char* aTopic, 1.524 + const char* aData) 1.525 + : mSubject(aSubject) 1.526 + , mTopic(aTopic) 1.527 + , mData(aData) 1.528 + { } 1.529 + 1.530 + NS_DECL_NSIRUNNABLE 1.531 + 1.532 +private: 1.533 + nsCOMPtr<nsISupports> mSubject; 1.534 + const char* mTopic; 1.535 + NS_ConvertUTF8toUTF16 mData; 1.536 +}; 1.537 + 1.538 +NS_IMETHODIMP 1.539 +CategoryNotificationRunnable::Run() 1.540 +{ 1.541 + nsCOMPtr<nsIObserverService> observerService = 1.542 + mozilla::services::GetObserverService(); 1.543 + if (observerService) 1.544 + observerService->NotifyObservers(mSubject, mTopic, mData.get()); 1.545 + 1.546 + return NS_OK; 1.547 +} 1.548 + 1.549 +} // anonymous namespace 1.550 + 1.551 + 1.552 +void 1.553 +nsCategoryManager::NotifyObservers( const char *aTopic, 1.554 + const char *aCategoryName, 1.555 + const char *aEntryName ) 1.556 +{ 1.557 + if (mSuppressNotifications) 1.558 + return; 1.559 + 1.560 + nsRefPtr<CategoryNotificationRunnable> r; 1.561 + 1.562 + if (aEntryName) { 1.563 + nsCOMPtr<nsISupportsCString> entry 1.564 + (do_CreateInstance (NS_SUPPORTS_CSTRING_CONTRACTID)); 1.565 + if (!entry) 1.566 + return; 1.567 + 1.568 + nsresult rv = entry->SetData(nsDependentCString(aEntryName)); 1.569 + if (NS_FAILED(rv)) 1.570 + return; 1.571 + 1.572 + r = new CategoryNotificationRunnable(entry, aTopic, aCategoryName); 1.573 + } else { 1.574 + r = new CategoryNotificationRunnable( 1.575 + NS_ISUPPORTS_CAST(nsICategoryManager*, this), 1.576 + aTopic, aCategoryName); 1.577 + } 1.578 + 1.579 + NS_DispatchToMainThread(r); 1.580 +} 1.581 + 1.582 +NS_IMETHODIMP 1.583 +nsCategoryManager::GetCategoryEntry( const char *aCategoryName, 1.584 + const char *aEntryName, 1.585 + char **_retval ) 1.586 +{ 1.587 + if (NS_WARN_IF(!aCategoryName) || 1.588 + NS_WARN_IF(!aEntryName) || 1.589 + NS_WARN_IF(!_retval)) 1.590 + return NS_ERROR_INVALID_ARG;; 1.591 + 1.592 + nsresult status = NS_ERROR_NOT_AVAILABLE; 1.593 + 1.594 + CategoryNode* category; 1.595 + { 1.596 + MutexAutoLock lock(mLock); 1.597 + category = get_category(aCategoryName); 1.598 + } 1.599 + 1.600 + if (category) { 1.601 + status = category->GetLeaf(aEntryName, _retval); 1.602 + } 1.603 + 1.604 + return status; 1.605 +} 1.606 + 1.607 +NS_IMETHODIMP 1.608 +nsCategoryManager::AddCategoryEntry( const char *aCategoryName, 1.609 + const char *aEntryName, 1.610 + const char *aValue, 1.611 + bool aPersist, 1.612 + bool aReplace, 1.613 + char **_retval ) 1.614 +{ 1.615 + if (aPersist) { 1.616 + NS_ERROR("Category manager doesn't support persistence."); 1.617 + return NS_ERROR_INVALID_ARG; 1.618 + } 1.619 + 1.620 + AddCategoryEntry(aCategoryName, aEntryName, aValue, aReplace, _retval); 1.621 + return NS_OK; 1.622 +} 1.623 + 1.624 +void 1.625 +nsCategoryManager::AddCategoryEntry(const char *aCategoryName, 1.626 + const char *aEntryName, 1.627 + const char *aValue, 1.628 + bool aReplace, 1.629 + char** aOldValue) 1.630 +{ 1.631 + if (aOldValue) 1.632 + *aOldValue = nullptr; 1.633 + 1.634 + // Before we can insert a new entry, we'll need to 1.635 + // find the |CategoryNode| to put it in... 1.636 + CategoryNode* category; 1.637 + { 1.638 + MutexAutoLock lock(mLock); 1.639 + category = get_category(aCategoryName); 1.640 + 1.641 + if (!category) { 1.642 + // That category doesn't exist yet; let's make it. 1.643 + category = CategoryNode::Create(&mArena); 1.644 + 1.645 + char* categoryName = ArenaStrdup(aCategoryName, &mArena); 1.646 + mTable.Put(categoryName, category); 1.647 + } 1.648 + } 1.649 + 1.650 + if (!category) 1.651 + return; 1.652 + 1.653 + // We will need the return value of AddLeaf even if the called doesn't want it 1.654 + char *oldEntry = nullptr; 1.655 + 1.656 + nsresult rv = category->AddLeaf(aEntryName, 1.657 + aValue, 1.658 + aReplace, 1.659 + &oldEntry, 1.660 + &mArena); 1.661 + 1.662 + if (NS_SUCCEEDED(rv)) { 1.663 + if (oldEntry) { 1.664 + NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID, 1.665 + aCategoryName, aEntryName); 1.666 + } 1.667 + NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID, 1.668 + aCategoryName, aEntryName); 1.669 + 1.670 + if (aOldValue) 1.671 + *aOldValue = oldEntry; 1.672 + else 1.673 + NS_Free(oldEntry); 1.674 + } 1.675 +} 1.676 + 1.677 +NS_IMETHODIMP 1.678 +nsCategoryManager::DeleteCategoryEntry( const char *aCategoryName, 1.679 + const char *aEntryName, 1.680 + bool aDontPersist) 1.681 +{ 1.682 + if (NS_WARN_IF(!aCategoryName) || 1.683 + NS_WARN_IF(!aEntryName)) 1.684 + return NS_ERROR_INVALID_ARG; 1.685 + 1.686 + /* 1.687 + Note: no errors are reported since failure to delete 1.688 + probably won't hurt you, and returning errors seriously 1.689 + inconveniences JS clients 1.690 + */ 1.691 + 1.692 + CategoryNode* category; 1.693 + { 1.694 + MutexAutoLock lock(mLock); 1.695 + category = get_category(aCategoryName); 1.696 + } 1.697 + 1.698 + if (category) { 1.699 + category->DeleteLeaf(aEntryName); 1.700 + 1.701 + NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID, 1.702 + aCategoryName, aEntryName); 1.703 + } 1.704 + 1.705 + return NS_OK; 1.706 +} 1.707 + 1.708 +NS_IMETHODIMP 1.709 +nsCategoryManager::DeleteCategory( const char *aCategoryName ) 1.710 +{ 1.711 + if (NS_WARN_IF(!aCategoryName)) 1.712 + return NS_ERROR_INVALID_ARG; 1.713 + 1.714 + // the categories are arena-allocated, so we don't 1.715 + // actually delete them. We just remove all of the 1.716 + // leaf nodes. 1.717 + 1.718 + CategoryNode* category; 1.719 + { 1.720 + MutexAutoLock lock(mLock); 1.721 + category = get_category(aCategoryName); 1.722 + } 1.723 + 1.724 + if (category) { 1.725 + category->Clear(); 1.726 + NotifyObservers(NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID, 1.727 + aCategoryName, nullptr); 1.728 + } 1.729 + 1.730 + return NS_OK; 1.731 +} 1.732 + 1.733 +NS_IMETHODIMP 1.734 +nsCategoryManager::EnumerateCategory( const char *aCategoryName, 1.735 + nsISimpleEnumerator **_retval ) 1.736 +{ 1.737 + if (NS_WARN_IF(!aCategoryName) || 1.738 + NS_WARN_IF(!_retval)) 1.739 + return NS_ERROR_INVALID_ARG; 1.740 + 1.741 + CategoryNode* category; 1.742 + { 1.743 + MutexAutoLock lock(mLock); 1.744 + category = get_category(aCategoryName); 1.745 + } 1.746 + 1.747 + if (!category) { 1.748 + return NS_NewEmptyEnumerator(_retval); 1.749 + } 1.750 + 1.751 + return category->Enumerate(_retval); 1.752 +} 1.753 + 1.754 +NS_IMETHODIMP 1.755 +nsCategoryManager::EnumerateCategories(nsISimpleEnumerator **_retval) 1.756 +{ 1.757 + if (NS_WARN_IF(!_retval)) 1.758 + return NS_ERROR_INVALID_ARG; 1.759 + 1.760 + MutexAutoLock lock(mLock); 1.761 + CategoryEnumerator* enumObj = CategoryEnumerator::Create(mTable); 1.762 + 1.763 + if (!enumObj) 1.764 + return NS_ERROR_OUT_OF_MEMORY; 1.765 + 1.766 + *_retval = enumObj; 1.767 + NS_ADDREF(*_retval); 1.768 + return NS_OK; 1.769 +} 1.770 + 1.771 +struct writecat_struct { 1.772 + PRFileDesc* fd; 1.773 + bool success; 1.774 +}; 1.775 + 1.776 +NS_METHOD 1.777 +nsCategoryManager::SuppressNotifications(bool aSuppress) 1.778 +{ 1.779 + mSuppressNotifications = aSuppress; 1.780 + return NS_OK; 1.781 +} 1.782 + 1.783 +/* 1.784 + * CreateServicesFromCategory() 1.785 + * 1.786 + * Given a category, this convenience functions enumerates the category and 1.787 + * creates a service of every CID or ContractID registered under the category. 1.788 + * If observerTopic is non null and the service implements nsIObserver, 1.789 + * this will attempt to notify the observer with the origin, observerTopic string 1.790 + * as parameter. 1.791 + */ 1.792 +void 1.793 +NS_CreateServicesFromCategory(const char *category, 1.794 + nsISupports *origin, 1.795 + const char *observerTopic) 1.796 +{ 1.797 + nsresult rv; 1.798 + 1.799 + nsCOMPtr<nsICategoryManager> categoryManager = 1.800 + do_GetService("@mozilla.org/categorymanager;1"); 1.801 + if (!categoryManager) 1.802 + return; 1.803 + 1.804 + nsCOMPtr<nsISimpleEnumerator> enumerator; 1.805 + rv = categoryManager->EnumerateCategory(category, 1.806 + getter_AddRefs(enumerator)); 1.807 + if (NS_FAILED(rv)) 1.808 + return; 1.809 + 1.810 + nsCOMPtr<nsIUTF8StringEnumerator> senumerator = 1.811 + do_QueryInterface(enumerator); 1.812 + if (!senumerator) { 1.813 + NS_WARNING("Category enumerator doesn't support nsIUTF8StringEnumerator."); 1.814 + return; 1.815 + } 1.816 + 1.817 + bool hasMore; 1.818 + while (NS_SUCCEEDED(senumerator->HasMore(&hasMore)) && hasMore) { 1.819 + // From here on just skip any error we get. 1.820 + nsAutoCString entryString; 1.821 + if (NS_FAILED(senumerator->GetNext(entryString))) 1.822 + continue; 1.823 + 1.824 + nsXPIDLCString contractID; 1.825 + rv = categoryManager->GetCategoryEntry(category,entryString.get(), 1.826 + getter_Copies(contractID)); 1.827 + if (NS_FAILED(rv)) 1.828 + continue; 1.829 + 1.830 + nsCOMPtr<nsISupports> instance = do_GetService(contractID); 1.831 + if (!instance) { 1.832 + LogMessage("While creating services from category '%s', could not create service for entry '%s', contract ID '%s'", 1.833 + category, entryString.get(), contractID.get()); 1.834 + continue; 1.835 + } 1.836 + 1.837 + if (observerTopic) { 1.838 + // try an observer, if it implements it. 1.839 + nsCOMPtr<nsIObserver> observer = do_QueryInterface(instance); 1.840 + if (observer) 1.841 + observer->Observe(origin, observerTopic, EmptyString().get()); 1.842 + else 1.843 + LogMessage("While creating services from category '%s', service for entry '%s', contract ID '%s' does not implement nsIObserver.", 1.844 + category, entryString.get(), contractID.get()); 1.845 + } 1.846 + } 1.847 +}