michael@0: /* vim:set st=2 sts=2 ts=2 et cin: */ 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: #include "nsIObserverService.h" michael@0: #include "mozilla/Services.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsIStringEnumerator.h" michael@0: michael@0: #include "nsXPCOMCID.h" michael@0: michael@0: #include "nsCategoryCache.h" michael@0: michael@0: nsCategoryObserver::nsCategoryObserver(const char* aCategory) michael@0: : mCategory(aCategory) michael@0: , mObserversRemoved(false) michael@0: { michael@0: // First, enumerate the currently existing entries michael@0: nsCOMPtr catMan = michael@0: do_GetService(NS_CATEGORYMANAGER_CONTRACTID); michael@0: if (!catMan) michael@0: return; michael@0: michael@0: nsCOMPtr enumerator; michael@0: nsresult rv = catMan->EnumerateCategory(aCategory, michael@0: getter_AddRefs(enumerator)); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: nsCOMPtr strings = do_QueryInterface(enumerator); michael@0: MOZ_ASSERT(strings); michael@0: michael@0: bool more; michael@0: while (NS_SUCCEEDED(strings->HasMore(&more)) && more) { michael@0: nsAutoCString entryName; michael@0: strings->GetNext(entryName); michael@0: michael@0: nsCString entryValue; michael@0: rv = catMan->GetCategoryEntry(aCategory, michael@0: entryName.get(), michael@0: getter_Copies(entryValue)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsCOMPtr service = do_GetService(entryValue.get()); michael@0: if (service) { michael@0: mHash.Put(entryName, service); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Now, listen for changes michael@0: nsCOMPtr serv = michael@0: mozilla::services::GetObserverService(); michael@0: if (serv) { michael@0: serv->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); michael@0: serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID, false); michael@0: serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID, false); michael@0: serv->AddObserver(this, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID, false); michael@0: } michael@0: } michael@0: michael@0: nsCategoryObserver::~nsCategoryObserver() { michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsCategoryObserver, nsIObserver) michael@0: michael@0: void michael@0: nsCategoryObserver::ListenerDied() { michael@0: RemoveObservers(); michael@0: } michael@0: michael@0: void michael@0: nsCategoryObserver::RemoveObservers() { michael@0: if (mObserversRemoved) michael@0: return; michael@0: michael@0: mObserversRemoved = true; michael@0: nsCOMPtr obsSvc = michael@0: mozilla::services::GetObserverService(); michael@0: if (obsSvc) { michael@0: obsSvc->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); michael@0: obsSvc->RemoveObserver(this, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID); michael@0: obsSvc->RemoveObserver(this, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID); michael@0: obsSvc->RemoveObserver(this, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsCategoryObserver::Observe(nsISupports* aSubject, const char* aTopic, michael@0: const char16_t* aData) { michael@0: if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { michael@0: mHash.Clear(); michael@0: RemoveObservers(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!aData || michael@0: !nsDependentString(aData).Equals(NS_ConvertASCIItoUTF16(mCategory))) michael@0: return NS_OK; michael@0: michael@0: nsAutoCString str; michael@0: nsCOMPtr strWrapper(do_QueryInterface(aSubject)); michael@0: if (strWrapper) michael@0: strWrapper->GetData(str); michael@0: michael@0: if (strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID) == 0) { michael@0: // We may get an add notification even when we already have an entry. This michael@0: // is due to the notification happening asynchronously, so if the entry gets michael@0: // added and an nsCategoryObserver gets instantiated before events get michael@0: // processed, we'd get the notification for an existing entry. michael@0: // Do nothing in that case. michael@0: if (mHash.GetWeak(str)) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr catMan = michael@0: do_GetService(NS_CATEGORYMANAGER_CONTRACTID); michael@0: if (!catMan) michael@0: return NS_OK; michael@0: michael@0: nsCString entryValue; michael@0: catMan->GetCategoryEntry(mCategory.get(), michael@0: str.get(), michael@0: getter_Copies(entryValue)); michael@0: michael@0: nsCOMPtr service = do_GetService(entryValue.get()); michael@0: michael@0: if (service) { michael@0: mHash.Put(str, service); michael@0: } michael@0: } else if (strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID) == 0) { michael@0: mHash.Remove(str); michael@0: } else if (strcmp(aTopic, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID) == 0) { michael@0: mHash.Clear(); michael@0: } michael@0: return NS_OK; michael@0: }