Bug 796834 - B2G RIL: Setup data call returns fail during radio power changed. r...
[gecko.git] / xpcom / components / nsCategoryManager.cpp
blob43da2389861f88fc8ea09f80290a066953a44ef5
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"
11 #include "plarena.h"
12 #include "prio.h"
13 #include "prprf.h"
14 #include "prlock.h"
15 #include "nsCOMPtr.h"
16 #include "nsTHashtable.h"
17 #include "nsClassHashtable.h"
18 #include "nsIFactory.h"
19 #include "nsIStringEnumerator.h"
20 #include "nsSupportsPrimitives.h"
21 #include "nsComponentManagerUtils.h"
22 #include "nsServiceManagerUtils.h"
23 #include "nsIObserver.h"
24 #include "nsIObserverService.h"
25 #include "nsReadableUtils.h"
26 #include "nsCRT.h"
27 #include "nsQuickSort.h"
28 #include "nsEnumeratorUtils.h"
29 #include "nsThreadUtils.h"
30 #include "mozilla/Services.h"
32 #include "ManifestParser.h"
34 using namespace mozilla;
35 class nsIComponentLoaderManager;
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
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.
45 The leaf strings are allocated in an arena, because we assume they're not
46 going to change much ;)
49 #define NS_CATEGORYMANAGER_ARENA_SIZE (1024 * 8)
51 // pulled in from nsComponentManager.cpp
52 char* ArenaStrdup(const char* s, PLArenaPool* aArena);
55 // BaseStringEnumerator is subclassed by EntryEnumerator and
56 // CategoryEnumerator
58 class BaseStringEnumerator
59 : public nsISimpleEnumerator,
60 private nsIUTF8StringEnumerator
62 public:
63 NS_DECL_ISUPPORTS
64 NS_DECL_NSISIMPLEENUMERATOR
65 NS_DECL_NSIUTF8STRINGENUMERATOR
67 protected:
68 // Callback function for NS_QuickSort to sort mArray
69 static int SortCallback(const void *, const void *, void *);
71 BaseStringEnumerator()
72 : mArray(nullptr),
73 mCount(0),
74 mSimpleCurItem(0),
75 mStringCurItem(0) { }
77 // A virtual destructor is needed here because subclasses of
78 // BaseStringEnumerator do not implement their own Release() method.
80 virtual ~BaseStringEnumerator()
82 if (mArray)
83 delete[] mArray;
86 void Sort();
88 const char** mArray;
89 uint32_t mCount;
90 uint32_t mSimpleCurItem;
91 uint32_t mStringCurItem;
94 NS_IMPL_ISUPPORTS2(BaseStringEnumerator, nsISimpleEnumerator, nsIUTF8StringEnumerator)
96 NS_IMETHODIMP
97 BaseStringEnumerator::HasMoreElements(bool *_retval)
99 *_retval = (mSimpleCurItem < mCount);
101 return NS_OK;
104 NS_IMETHODIMP
105 BaseStringEnumerator::GetNext(nsISupports **_retval)
107 if (mSimpleCurItem >= mCount)
108 return NS_ERROR_FAILURE;
110 nsSupportsDependentCString* str =
111 new nsSupportsDependentCString(mArray[mSimpleCurItem++]);
112 if (!str)
113 return NS_ERROR_OUT_OF_MEMORY;
115 *_retval = str;
116 NS_ADDREF(*_retval);
117 return NS_OK;
120 NS_IMETHODIMP
121 BaseStringEnumerator::HasMore(bool *_retval)
123 *_retval = (mStringCurItem < mCount);
125 return NS_OK;
128 NS_IMETHODIMP
129 BaseStringEnumerator::GetNext(nsACString& _retval)
131 if (mStringCurItem >= mCount)
132 return NS_ERROR_FAILURE;
134 _retval = nsDependentCString(mArray[mStringCurItem++]);
135 return NS_OK;
139 BaseStringEnumerator::SortCallback(const void *e1, const void *e2,
140 void * /*unused*/)
142 char const *const *s1 = reinterpret_cast<char const *const *>(e1);
143 char const *const *s2 = reinterpret_cast<char const *const *>(e2);
145 return strcmp(*s1, *s2);
148 void
149 BaseStringEnumerator::Sort()
151 NS_QuickSort(mArray, mCount, sizeof(mArray[0]), SortCallback, nullptr);
155 // EntryEnumerator is the wrapper that allows nsICategoryManager::EnumerateCategory
157 class EntryEnumerator
158 : public BaseStringEnumerator
160 public:
161 static EntryEnumerator* Create(nsTHashtable<CategoryLeaf>& aTable);
163 private:
164 static PLDHashOperator
165 enumfunc_createenumerator(CategoryLeaf* aLeaf, void* userArg);
169 PLDHashOperator
170 EntryEnumerator::enumfunc_createenumerator(CategoryLeaf* aLeaf, void* userArg)
172 EntryEnumerator* mythis = static_cast<EntryEnumerator*>(userArg);
173 if (aLeaf->value)
174 mythis->mArray[mythis->mCount++] = aLeaf->GetKey();
176 return PL_DHASH_NEXT;
179 EntryEnumerator*
180 EntryEnumerator::Create(nsTHashtable<CategoryLeaf>& aTable)
182 EntryEnumerator* enumObj = new EntryEnumerator();
183 if (!enumObj)
184 return nullptr;
186 enumObj->mArray = new char const* [aTable.Count()];
187 if (!enumObj->mArray) {
188 delete enumObj;
189 return nullptr;
192 aTable.EnumerateEntries(enumfunc_createenumerator, enumObj);
194 enumObj->Sort();
196 return enumObj;
201 // CategoryNode implementations
204 CategoryNode*
205 CategoryNode::Create(PLArenaPool* aArena)
207 CategoryNode* node = new(aArena) CategoryNode();
208 if (!node)
209 return nullptr;
211 node->mTable.Init();
212 return node;
215 CategoryNode::~CategoryNode()
219 void*
220 CategoryNode::operator new(size_t aSize, PLArenaPool* aArena)
222 void* p;
223 PL_ARENA_ALLOCATE(p, aArena, aSize);
224 return p;
227 NS_METHOD
228 CategoryNode::GetLeaf(const char* aEntryName,
229 char** _retval)
231 MutexAutoLock lock(mLock);
232 nsresult rv = NS_ERROR_NOT_AVAILABLE;
233 CategoryLeaf* ent =
234 mTable.GetEntry(aEntryName);
236 if (ent && ent->value) {
237 *_retval = NS_strdup(ent->value);
238 if (*_retval)
239 rv = NS_OK;
242 return rv;
245 NS_METHOD
246 CategoryNode::AddLeaf(const char* aEntryName,
247 const char* aValue,
248 bool aReplace,
249 char** _retval,
250 PLArenaPool* aArena)
252 if (_retval)
253 *_retval = NULL;
255 MutexAutoLock lock(mLock);
256 CategoryLeaf* leaf =
257 mTable.GetEntry(aEntryName);
259 if (!leaf) {
260 const char* arenaEntryName = ArenaStrdup(aEntryName, aArena);
261 if (!arenaEntryName)
262 return NS_ERROR_OUT_OF_MEMORY;
264 leaf = mTable.PutEntry(arenaEntryName);
265 if (!leaf)
266 return NS_ERROR_OUT_OF_MEMORY;
269 if (leaf->value && !aReplace)
270 return NS_ERROR_INVALID_ARG;
272 const char* arenaValue = ArenaStrdup(aValue, aArena);
273 if (!arenaValue)
274 return NS_ERROR_OUT_OF_MEMORY;
276 if (_retval && leaf->value) {
277 *_retval = ToNewCString(nsDependentCString(leaf->value));
278 if (!*_retval)
279 return NS_ERROR_OUT_OF_MEMORY;
282 leaf->value = arenaValue;
283 return NS_OK;
286 void
287 CategoryNode::DeleteLeaf(const char* aEntryName)
289 // we don't throw any errors, because it normally doesn't matter
290 // and it makes JS a lot cleaner
291 MutexAutoLock lock(mLock);
293 // we can just remove the entire hash entry without introspection
294 mTable.RemoveEntry(aEntryName);
297 NS_METHOD
298 CategoryNode::Enumerate(nsISimpleEnumerator **_retval)
300 NS_ENSURE_ARG_POINTER(_retval);
302 MutexAutoLock lock(mLock);
303 EntryEnumerator* enumObj = EntryEnumerator::Create(mTable);
305 if (!enumObj)
306 return NS_ERROR_OUT_OF_MEMORY;
308 *_retval = enumObj;
309 NS_ADDREF(*_retval);
310 return NS_OK;
313 struct persistent_userstruct {
314 PRFileDesc* fd;
315 const char* categoryName;
316 bool success;
319 PLDHashOperator
320 enumfunc_pentries(CategoryLeaf* aLeaf, void* userArg)
322 persistent_userstruct* args =
323 static_cast<persistent_userstruct*>(userArg);
325 PLDHashOperator status = PL_DHASH_NEXT;
327 if (aLeaf->value) {
328 if (PR_fprintf(args->fd,
329 "%s,%s,%s\n",
330 args->categoryName,
331 aLeaf->GetKey(),
332 aLeaf->value) == (uint32_t) -1) {
333 args->success = false;
334 status = PL_DHASH_STOP;
338 return status;
342 // CategoryEnumerator class
345 class CategoryEnumerator
346 : public BaseStringEnumerator
348 public:
349 static CategoryEnumerator* Create(nsClassHashtable<nsDepCharHashKey, CategoryNode>& aTable);
351 private:
352 static PLDHashOperator
353 enumfunc_createenumerator(const char* aStr,
354 CategoryNode* aNode,
355 void* userArg);
358 CategoryEnumerator*
359 CategoryEnumerator::Create(nsClassHashtable<nsDepCharHashKey, CategoryNode>& aTable)
361 CategoryEnumerator* enumObj = new CategoryEnumerator();
362 if (!enumObj)
363 return nullptr;
365 enumObj->mArray = new const char* [aTable.Count()];
366 if (!enumObj->mArray) {
367 delete enumObj;
368 return nullptr;
371 aTable.EnumerateRead(enumfunc_createenumerator, enumObj);
373 return enumObj;
376 PLDHashOperator
377 CategoryEnumerator::enumfunc_createenumerator(const char* aStr, CategoryNode* aNode, void* userArg)
379 CategoryEnumerator* mythis = static_cast<CategoryEnumerator*>(userArg);
381 // if a category has no entries, we pretend it doesn't exist
382 if (aNode->Count())
383 mythis->mArray[mythis->mCount++] = aStr;
385 return PL_DHASH_NEXT;
390 // nsCategoryManager implementations
393 NS_IMPL_QUERY_INTERFACE1(nsCategoryManager, nsICategoryManager)
395 NS_IMETHODIMP_(nsrefcnt)
396 nsCategoryManager::AddRef()
398 return 2;
401 NS_IMETHODIMP_(nsrefcnt)
402 nsCategoryManager::Release()
404 return 1;
407 nsCategoryManager* nsCategoryManager::gCategoryManager;
409 /* static */ nsCategoryManager*
410 nsCategoryManager::GetSingleton()
412 if (!gCategoryManager)
413 gCategoryManager = new nsCategoryManager();
414 return gCategoryManager;
417 /* static */ void
418 nsCategoryManager::Destroy()
420 delete gCategoryManager;
423 nsresult
424 nsCategoryManager::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
426 if (aOuter)
427 return NS_ERROR_NO_AGGREGATION;
429 return GetSingleton()->QueryInterface(aIID, aResult);
432 nsCategoryManager::nsCategoryManager()
433 : mLock("nsCategoryManager")
434 , mSuppressNotifications(false)
436 PL_INIT_ARENA_POOL(&mArena, "CategoryManagerArena",
437 NS_CATEGORYMANAGER_ARENA_SIZE);
439 mTable.Init();
442 nsCategoryManager::~nsCategoryManager()
444 // the hashtable contains entries that must be deleted before the arena is
445 // destroyed, or else you will have PRLocks undestroyed and other Really
446 // Bad Stuff (TM)
447 mTable.Clear();
449 PL_FinishArenaPool(&mArena);
452 inline CategoryNode*
453 nsCategoryManager::get_category(const char* aName) {
454 CategoryNode* node;
455 if (!mTable.Get(aName, &node)) {
456 return nullptr;
458 return node;
461 namespace {
463 class CategoryNotificationRunnable : public nsRunnable
465 public:
466 CategoryNotificationRunnable(nsISupports* aSubject,
467 const char* aTopic,
468 const char* aData)
469 : mSubject(aSubject)
470 , mTopic(aTopic)
471 , mData(aData)
474 NS_DECL_NSIRUNNABLE
476 private:
477 nsCOMPtr<nsISupports> mSubject;
478 const char* mTopic;
479 NS_ConvertUTF8toUTF16 mData;
482 NS_IMETHODIMP
483 CategoryNotificationRunnable::Run()
485 nsCOMPtr<nsIObserverService> observerService =
486 mozilla::services::GetObserverService();
487 if (observerService)
488 observerService->NotifyObservers(mSubject, mTopic, mData.get());
490 return NS_OK;
493 } // anonymous namespace
496 void
497 nsCategoryManager::NotifyObservers( const char *aTopic,
498 const char *aCategoryName,
499 const char *aEntryName )
501 if (mSuppressNotifications)
502 return;
504 nsRefPtr<CategoryNotificationRunnable> r;
506 if (aEntryName) {
507 nsCOMPtr<nsISupportsCString> entry
508 (do_CreateInstance (NS_SUPPORTS_CSTRING_CONTRACTID));
509 if (!entry)
510 return;
512 nsresult rv = entry->SetData(nsDependentCString(aEntryName));
513 if (NS_FAILED(rv))
514 return;
516 r = new CategoryNotificationRunnable(entry, aTopic, aCategoryName);
517 } else {
518 r = new CategoryNotificationRunnable(this, aTopic, aCategoryName);
521 NS_DispatchToMainThread(r);
524 NS_IMETHODIMP
525 nsCategoryManager::GetCategoryEntry( const char *aCategoryName,
526 const char *aEntryName,
527 char **_retval )
529 NS_ENSURE_ARG_POINTER(aCategoryName);
530 NS_ENSURE_ARG_POINTER(aEntryName);
531 NS_ENSURE_ARG_POINTER(_retval);
533 nsresult status = NS_ERROR_NOT_AVAILABLE;
535 CategoryNode* category;
537 MutexAutoLock lock(mLock);
538 category = get_category(aCategoryName);
541 if (category) {
542 status = category->GetLeaf(aEntryName, _retval);
545 return status;
548 NS_IMETHODIMP
549 nsCategoryManager::AddCategoryEntry( const char *aCategoryName,
550 const char *aEntryName,
551 const char *aValue,
552 bool aPersist,
553 bool aReplace,
554 char **_retval )
556 if (aPersist) {
557 NS_ERROR("Category manager doesn't support persistence.");
558 return NS_ERROR_INVALID_ARG;
561 AddCategoryEntry(aCategoryName, aEntryName, aValue, aReplace, _retval);
562 return NS_OK;
565 void
566 nsCategoryManager::AddCategoryEntry(const char *aCategoryName,
567 const char *aEntryName,
568 const char *aValue,
569 bool aReplace,
570 char** aOldValue)
572 if (aOldValue)
573 *aOldValue = NULL;
575 // Before we can insert a new entry, we'll need to
576 // find the |CategoryNode| to put it in...
577 CategoryNode* category;
579 MutexAutoLock lock(mLock);
580 category = get_category(aCategoryName);
582 if (!category) {
583 // That category doesn't exist yet; let's make it.
584 category = CategoryNode::Create(&mArena);
586 char* categoryName = ArenaStrdup(aCategoryName, &mArena);
587 mTable.Put(categoryName, category);
591 if (!category)
592 return;
594 // We will need the return value of AddLeaf even if the called doesn't want it
595 char *oldEntry = nullptr;
597 nsresult rv = category->AddLeaf(aEntryName,
598 aValue,
599 aReplace,
600 &oldEntry,
601 &mArena);
603 if (NS_SUCCEEDED(rv)) {
604 if (oldEntry) {
605 NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID,
606 aCategoryName, oldEntry);
608 NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID,
609 aCategoryName, aEntryName);
611 if (aOldValue)
612 *aOldValue = oldEntry;
613 else
614 NS_Free(oldEntry);
618 NS_IMETHODIMP
619 nsCategoryManager::DeleteCategoryEntry( const char *aCategoryName,
620 const char *aEntryName,
621 bool aDontPersist)
623 NS_ENSURE_ARG_POINTER(aCategoryName);
624 NS_ENSURE_ARG_POINTER(aEntryName);
627 Note: no errors are reported since failure to delete
628 probably won't hurt you, and returning errors seriously
629 inconveniences JS clients
632 CategoryNode* category;
634 MutexAutoLock lock(mLock);
635 category = get_category(aCategoryName);
638 if (category) {
639 category->DeleteLeaf(aEntryName);
641 NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID,
642 aCategoryName, aEntryName);
645 return NS_OK;
648 NS_IMETHODIMP
649 nsCategoryManager::DeleteCategory( const char *aCategoryName )
651 NS_ENSURE_ARG_POINTER(aCategoryName);
653 // the categories are arena-allocated, so we don't
654 // actually delete them. We just remove all of the
655 // leaf nodes.
657 CategoryNode* category;
659 MutexAutoLock lock(mLock);
660 category = get_category(aCategoryName);
663 if (category) {
664 category->Clear();
665 NotifyObservers(NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID,
666 aCategoryName, nullptr);
669 return NS_OK;
672 NS_IMETHODIMP
673 nsCategoryManager::EnumerateCategory( const char *aCategoryName,
674 nsISimpleEnumerator **_retval )
676 NS_ENSURE_ARG_POINTER(aCategoryName);
677 NS_ENSURE_ARG_POINTER(_retval);
679 CategoryNode* category;
681 MutexAutoLock lock(mLock);
682 category = get_category(aCategoryName);
685 if (!category) {
686 return NS_NewEmptyEnumerator(_retval);
689 return category->Enumerate(_retval);
692 NS_IMETHODIMP
693 nsCategoryManager::EnumerateCategories(nsISimpleEnumerator **_retval)
695 NS_ENSURE_ARG_POINTER(_retval);
697 MutexAutoLock lock(mLock);
698 CategoryEnumerator* enumObj = CategoryEnumerator::Create(mTable);
700 if (!enumObj)
701 return NS_ERROR_OUT_OF_MEMORY;
703 *_retval = enumObj;
704 NS_ADDREF(*_retval);
705 return NS_OK;
708 struct writecat_struct {
709 PRFileDesc* fd;
710 bool success;
713 NS_METHOD
714 nsCategoryManager::SuppressNotifications(bool aSuppress)
716 mSuppressNotifications = aSuppress;
717 return NS_OK;
721 * CreateServicesFromCategory()
723 * Given a category, this convenience functions enumerates the category and
724 * creates a service of every CID or ContractID registered under the category.
725 * If observerTopic is non null and the service implements nsIObserver,
726 * this will attempt to notify the observer with the origin, observerTopic string
727 * as parameter.
729 void
730 NS_CreateServicesFromCategory(const char *category,
731 nsISupports *origin,
732 const char *observerTopic)
734 nsresult rv;
736 nsCOMPtr<nsICategoryManager> categoryManager =
737 do_GetService("@mozilla.org/categorymanager;1");
738 if (!categoryManager)
739 return;
741 nsCOMPtr<nsISimpleEnumerator> enumerator;
742 rv = categoryManager->EnumerateCategory(category,
743 getter_AddRefs(enumerator));
744 if (NS_FAILED(rv))
745 return;
747 nsCOMPtr<nsIUTF8StringEnumerator> senumerator =
748 do_QueryInterface(enumerator);
749 if (!senumerator) {
750 NS_WARNING("Category enumerator doesn't support nsIUTF8StringEnumerator.");
751 return;
754 bool hasMore;
755 while (NS_SUCCEEDED(senumerator->HasMore(&hasMore)) && hasMore) {
756 // From here on just skip any error we get.
757 nsAutoCString entryString;
758 if (NS_FAILED(senumerator->GetNext(entryString)))
759 continue;
761 nsXPIDLCString contractID;
762 rv = categoryManager->GetCategoryEntry(category,entryString.get(),
763 getter_Copies(contractID));
764 if (NS_FAILED(rv))
765 continue;
767 nsCOMPtr<nsISupports> instance = do_GetService(contractID);
768 if (!instance) {
769 LogMessage("While creating services from category '%s', could not create service for entry '%s', contract ID '%s'",
770 category, entryString.get(), contractID.get());
771 continue;
774 if (observerTopic) {
775 // try an observer, if it implements it.
776 nsCOMPtr<nsIObserver> observer = do_QueryInterface(instance);
777 if (observer)
778 observer->Observe(origin, observerTopic, EmptyString().get());
779 else
780 LogMessage("While creating services from category '%s', service for entry '%s', contract ID '%s' does not implement nsIObserver.",
781 category, entryString.get(), contractID.get());