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