Bug 37795 - "Move nsCategoryCache to the XPCOM Glue". nsCategoryCache was initially created as part of Bug 315598 by Christian Biesinger <cbiesinger@gmx.at>. r=bsmedberg.
authorbent.mozilla@gmail.com
Thu, 26 Apr 2007 06:53:48 -0700
changeset 836 a3f72faa922a3b3a115adeacaa67ce0b20f042ce
parent 835 dfaba0404aed38e5f0f676036da5e7568145b91f
child 837 dd7c630adaf802c1fd0f06f7f44c2e0430ed6078
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmedberg
bugs37795, 315598
milestone1.9a4pre
Bug 37795 - "Move nsCategoryCache to the XPCOM Glue". nsCategoryCache was initially created as part of Bug 315598 by Christian Biesinger <cbiesinger@gmx.at>. r=bsmedberg.
xpcom/components/Makefile.in
xpcom/components/nsCategoryCache.cpp
xpcom/components/nsCategoryCache.h
xpcom/ds/nsObserverService.h
xpcom/glue/Makefile.in
xpcom/glue/nsCategoryCache.cpp
xpcom/glue/nsCategoryCache.h
xpcom/glue/objs.mk
--- a/xpcom/components/Makefile.in
+++ b/xpcom/components/Makefile.in
@@ -48,26 +48,24 @@ LIBRARY_NAME	= xpcomcomponents_s
 GRE_MODULE	= 1
 MOZILLA_INTERNAL_API = 1
 
 REQUIRES	= string \
 		  $(NULL)
 
 CPPSRCS		= \
 		nsCategoryManager.cpp \
-		nsCategoryCache.cpp \
 		nsComponentManager.cpp \
 		nsNativeComponentLoader.cpp \
 		nsStaticComponentLoader.cpp \
 		nsServiceManagerObsolete.cpp \
 		$(NULL)
 
 EXPORTS		= \
 		nsCategoryManagerUtils.h \
-		nsCategoryCache.h \
 		nsIServiceManagerObsolete.h \
 		nsModule.h \
 		nsObsoleteModuleLoading.h \
 		$(NULL)
 
 XPIDLSRCS	= \
 		nsIModuleLoader.idl \
 		nsIComponentManagerObsolete.idl \
deleted file mode 100644
--- a/xpcom/components/nsCategoryCache.cpp
+++ /dev/null
@@ -1,167 +0,0 @@
-/* vim:set st=2 sts=2 ts=2 et cin: */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is a cache for services in a category.
- *
- * The Initial Developer of the Original Code is
- * Christian Biesinger <cbiesinger@web.de>.
- * Portions created by the Initial Developer are Copyright (C) 2005
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#include "nsIObserverService.h"
-#include "nsISupportsPrimitives.h"
-
-#include "nsXPCOMCID.h"
-#include "nsObserverService.h"
-
-#include "nsCategoryCache.h"
-
-nsCategoryObserver::nsCategoryObserver(const char* aCategory,
-                                       nsCategoryListener* aListener)
-  : mListener(nsnull), mCategory(aCategory)
-{
-  if (!mHash.Init()) {
-    // OOM
-    return;
-  }
-
-  mListener = aListener;
-
-  // First, enumerate the currently existing entries
-  nsCOMPtr<nsICategoryManager> catMan =
-    do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
-  if (!catMan)
-    return;
-
-  nsCOMPtr<nsISimpleEnumerator> enumerator;
-  nsresult rv = catMan->EnumerateCategory(aCategory,
-                                          getter_AddRefs(enumerator));
-  if (NS_FAILED(rv))
-    return;
-
-  nsCOMPtr<nsISupports> entry;
-  while (NS_SUCCEEDED(enumerator->GetNext(getter_AddRefs(entry)))) {
-    nsCOMPtr<nsISupportsCString> entryName = do_QueryInterface(entry, &rv);
-
-    if (NS_SUCCEEDED(rv)) {
-      nsCAutoString categoryEntry;
-      rv = entryName->GetData(categoryEntry);
-
-      nsXPIDLCString entryValue;
-      catMan->GetCategoryEntry(aCategory,
-                               categoryEntry.get(),
-                               getter_Copies(entryValue));
-
-      if (NS_SUCCEEDED(rv)) {
-        mHash.Put(categoryEntry, entryValue);
-        mListener->EntryAdded(entryValue);
-      }
-    }
-  }
-
-  // Now, listen for changes
-  nsCOMPtr<nsIObserverService> serv =
-    do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
-  if (!serv)
-    return;
-
-  serv->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
-
-  serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID, PR_FALSE);
-  serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID, PR_FALSE);
-  serv->AddObserver(this, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID, PR_FALSE);
-}
-
-nsCategoryObserver::~nsCategoryObserver() {
-}
-
-NS_IMPL_ISUPPORTS1(nsCategoryObserver, nsIObserver)
-
-void
-nsCategoryObserver::ListenerDied() {
-  mListener = nsnull;
-
-  nsCOMPtr<nsIObserverService> serv =
-    do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
-  if (!serv)
-    return;
-
-  serv->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
-
-  serv->RemoveObserver(this, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID);
-  serv->RemoveObserver(this, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID);
-  serv->RemoveObserver(this, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID);
-}
-
-NS_IMETHODIMP
-nsCategoryObserver::Observe(nsISupports* aSubject, const char* aTopic,
-                            const PRUnichar* aData) {
-  if (!mListener)
-    return NS_OK;
-
-  if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
-    mHash.Clear();
-    mListener->CategoryCleared();
-    return NS_OK;
-  }
-
-  if (!aData || !nsDependentString(aData).EqualsASCII(mCategory.get()))
-    return NS_OK;
-
-  nsCAutoString str;
-  nsCOMPtr<nsISupportsCString> strWrapper(do_QueryInterface(aSubject));
-  if (strWrapper)
-    strWrapper->GetData(str);
-
-  if (strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID) == 0) {
-    nsCOMPtr<nsICategoryManager> catMan =
-      do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
-    if (!catMan)
-      return NS_OK;
-
-    nsXPIDLCString entryValue;
-    catMan->GetCategoryEntry(mCategory.get(),
-                             str.get(),
-                             getter_Copies(entryValue));
-
-    mHash.Put(str, entryValue);
-    mListener->EntryAdded(entryValue);
-  } else if (strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID) == 0) {
-    nsCAutoString val;
-    if (mHash.Get(str, &val)) {
-      mHash.Remove(str);
-      mListener->EntryRemoved(val);
-    }
-  } else if (strcmp(aTopic, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID) == 0) {
-    mHash.Clear();
-    mListener->CategoryCleared();
-  }
-  return NS_OK;
-}
deleted file mode 100644
--- a/xpcom/components/nsCategoryCache.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is a cache for services in a category.
- *
- * The Initial Developer of the Original Code is
- * Christian Biesinger <cbiesinger@web.de>.
- * Portions created by the Initial Developer are Copyright (C) 2005
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef nsCategoryCache_h_
-#define nsCategoryCache_h_
-
-#include "nsICategoryManager.h"
-#include "nsIObserver.h"
-#include "nsISimpleEnumerator.h"
-#include "nsISupportsPrimitives.h"
-
-#include "nsServiceManagerUtils.h"
-
-#include "nsAutoPtr.h"
-#include "nsCOMArray.h"
-#include "nsDataHashtable.h"
-
-#include "nsXPCOM.h"
-
-class NS_NO_VTABLE nsCategoryListener {
-  protected:
-    // no virtual destructor (people shouldn't delete through an
-    // nsCategoryListener pointer)
-    ~nsCategoryListener() {}
-
-  public:
-    virtual void EntryAdded(const nsCString& aValue) = 0;
-    virtual void EntryRemoved(const nsCString& aValue) = 0;
-    virtual void CategoryCleared() = 0;
-};
-
-class NS_COM nsCategoryObserver : public nsIObserver {
-  public:
-    nsCategoryObserver(const char* aCategory,
-                       nsCategoryListener* aCategoryListener);
-    ~nsCategoryObserver();
-
-    void ListenerDied();
-
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIOBSERVER
-  private:
-    nsDataHashtable<nsCStringHashKey, nsCString> mHash;
-    nsCategoryListener*                          mListener;
-    nsCString                                    mCategory;
-};
-
-/**
- * This is a helper class that caches services that are registered in a certain
- * category. The intended usage is that a service stores a variable of type
- * nsCategoryCache<nsIFoo> in a member variable, where nsIFoo is the interface
- * that these services should implement. The constructor of this class should
- * then get the name of the category.
- */
-template<class T>
-class nsCategoryCache : protected nsCategoryListener {
-  public:
-    explicit nsCategoryCache(const char* aCategory);
-    ~nsCategoryCache() { if (mObserver) mObserver->ListenerDied(); }
-
-    const nsCOMArray<T>& GetEntries() const { return mEntries; }
-  protected:
-    virtual void EntryAdded(const nsCString& aValue);
-    virtual void EntryRemoved(const nsCString& aValue);
-    virtual void CategoryCleared();
-  private:
-    friend class CategoryObserver;
-
-    // Not to be implemented
-    nsCategoryCache(const nsCategoryCache<T>&);
-
-    nsCOMArray<T> mEntries;
-    nsRefPtr<nsCategoryObserver> mObserver;
-};
-
-// -----------------------------------
-// Implementation
-
-template<class T>
-nsCategoryCache<T>::nsCategoryCache(const char* aCategory)
-{
-  mObserver = new nsCategoryObserver(aCategory, this);
-}
-
-template<class T>
-void nsCategoryCache<T>::EntryAdded(const nsCString& aValue) {
-  nsCOMPtr<T> catEntry = do_GetService(aValue.get());
-  if (catEntry)
-    mEntries.AppendObject(catEntry);
-}
-
-template<class T>
-void nsCategoryCache<T>::EntryRemoved(const nsCString& aValue) {
-  nsCOMPtr<T> catEntry = do_GetService(aValue.get());
-  if (catEntry)
-    mEntries.RemoveObject(catEntry);
-}
-
-template<class T>
-void nsCategoryCache<T>::CategoryCleared() {
-  mEntries.Clear();
-}
-
-#endif
--- a/xpcom/ds/nsObserverService.h
+++ b/xpcom/ds/nsObserverService.h
@@ -37,17 +37,16 @@
 
 #ifndef nsObserverService_h___
 #define nsObserverService_h___
 
 #include "nsIObserverService.h"
 #include "nsObserverList.h"
 #include "nsTHashtable.h"
 
-#define NS_OBSERVERSERVICE_CONTRACTID "@mozilla.org/observer-service;1"
 #define NS_OBSERVERSERVICE_CLASSNAME "Observer Service"
 
 // {D07F5195-E3D1-11d2-8ACD-00105A1B8860}
 #define NS_OBSERVERSERVICE_CID \
     { 0xd07f5195, 0xe3d1, 0x11d2, { 0x8a, 0xcd, 0x0, 0x10, 0x5a, 0x1b, 0x88, 0x60 } }
 
 class nsObserverService : public nsIObserverService {
 public:
--- a/xpcom/glue/Makefile.in
+++ b/xpcom/glue/Makefile.in
@@ -69,16 +69,17 @@ CPPSRCS		= \
 		$(NULL)
 
 SDK_HEADERS = \
 		pldhash.h \
 		nsArrayEnumerator.h \
 		nsArrayUtils.h \
 		nsAutoLock.h \
 		nsBaseHashtable.h \
+		nsCategoryCache.h \
 		nsCOMArray.h \
 		nsCRTGlue.h \
 		nsClassHashtable.h \
 		nsDataHashtable.h \
 		nsEnumeratorUtils.h \
 		nsHashKeys.h \
 		nsINIParser.h \
 		nsInterfaceHashtable.h \
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/nsCategoryCache.cpp
@@ -0,0 +1,167 @@
+/* vim:set st=2 sts=2 ts=2 et cin: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is a cache for services in a category.
+ *
+ * The Initial Developer of the Original Code is
+ * Christian Biesinger <cbiesinger@web.de>.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsIObserverService.h"
+#include "nsISupportsPrimitives.h"
+
+#include "nsXPCOMCID.h"
+
+#include "nsCategoryCache.h"
+
+nsCategoryObserver::nsCategoryObserver(const char* aCategory,
+                                       nsCategoryListener* aListener)
+  : mListener(nsnull), mCategory(aCategory)
+{
+  if (!mHash.Init()) {
+    // OOM
+    return;
+  }
+
+  mListener = aListener;
+
+  // First, enumerate the currently existing entries
+  nsCOMPtr<nsICategoryManager> catMan =
+    do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
+  if (!catMan)
+    return;
+
+  nsCOMPtr<nsISimpleEnumerator> enumerator;
+  nsresult rv = catMan->EnumerateCategory(aCategory,
+                                          getter_AddRefs(enumerator));
+  if (NS_FAILED(rv))
+    return;
+
+  nsCOMPtr<nsISupports> entry;
+  while (NS_SUCCEEDED(enumerator->GetNext(getter_AddRefs(entry)))) {
+    nsCOMPtr<nsISupportsCString> entryName = do_QueryInterface(entry, &rv);
+
+    if (NS_SUCCEEDED(rv)) {
+      nsCAutoString categoryEntry;
+      rv = entryName->GetData(categoryEntry);
+
+      nsCString entryValue;
+      catMan->GetCategoryEntry(aCategory,
+                               categoryEntry.get(),
+                               getter_Copies(entryValue));
+
+      if (NS_SUCCEEDED(rv)) {
+        mHash.Put(categoryEntry, entryValue);
+        mListener->EntryAdded(entryValue);
+      }
+    }
+  }
+
+  // Now, listen for changes
+  nsCOMPtr<nsIObserverService> serv =
+    do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
+  if (!serv)
+    return;
+
+  serv->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
+
+  serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID, PR_FALSE);
+  serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID, PR_FALSE);
+  serv->AddObserver(this, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID, PR_FALSE);
+}
+
+nsCategoryObserver::~nsCategoryObserver() {
+}
+
+NS_IMPL_ISUPPORTS1(nsCategoryObserver, nsIObserver)
+
+void
+nsCategoryObserver::ListenerDied() {
+  mListener = nsnull;
+
+  nsCOMPtr<nsIObserverService> serv =
+    do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
+  if (!serv)
+    return;
+
+  serv->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+
+  serv->RemoveObserver(this, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID);
+  serv->RemoveObserver(this, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID);
+  serv->RemoveObserver(this, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID);
+}
+
+NS_IMETHODIMP
+nsCategoryObserver::Observe(nsISupports* aSubject, const char* aTopic,
+                            const PRUnichar* aData) {
+  if (!mListener)
+    return NS_OK;
+
+  if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
+    mHash.Clear();
+    mListener->CategoryCleared();
+    return NS_OK;
+  }
+
+  if (!aData ||
+      !nsDependentString(aData).Equals(NS_ConvertASCIItoUTF16(mCategory)))
+    return NS_OK;
+
+  nsCAutoString str;
+  nsCOMPtr<nsISupportsCString> strWrapper(do_QueryInterface(aSubject));
+  if (strWrapper)
+    strWrapper->GetData(str);
+
+  if (strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID) == 0) {
+    nsCOMPtr<nsICategoryManager> catMan =
+      do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
+    if (!catMan)
+      return NS_OK;
+
+    nsCString entryValue;
+    catMan->GetCategoryEntry(mCategory.get(),
+                             str.get(),
+                             getter_Copies(entryValue));
+
+    mHash.Put(str, entryValue);
+    mListener->EntryAdded(entryValue);
+  } else if (strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID) == 0) {
+    nsCAutoString val;
+    if (mHash.Get(str, &val)) {
+      mHash.Remove(str);
+      mListener->EntryRemoved(val);
+    }
+  } else if (strcmp(aTopic, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID) == 0) {
+    mHash.Clear();
+    mListener->CategoryCleared();
+  }
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/nsCategoryCache.h
@@ -0,0 +1,137 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is a cache for services in a category.
+ *
+ * The Initial Developer of the Original Code is
+ * Christian Biesinger <cbiesinger@web.de>.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsCategoryCache_h_
+#define nsCategoryCache_h_
+
+#include "nsICategoryManager.h"
+#include "nsIObserver.h"
+#include "nsISimpleEnumerator.h"
+#include "nsISupportsPrimitives.h"
+
+#include "nsServiceManagerUtils.h"
+
+#include "nsAutoPtr.h"
+#include "nsCOMArray.h"
+#include "nsDataHashtable.h"
+
+#include "nsXPCOM.h"
+
+class NS_NO_VTABLE nsCategoryListener {
+  protected:
+    // no virtual destructor (people shouldn't delete through an
+    // nsCategoryListener pointer)
+    ~nsCategoryListener() {}
+
+  public:
+    virtual void EntryAdded(const nsCString& aValue) = 0;
+    virtual void EntryRemoved(const nsCString& aValue) = 0;
+    virtual void CategoryCleared() = 0;
+};
+
+class NS_COM_GLUE nsCategoryObserver : public nsIObserver {
+  public:
+    nsCategoryObserver(const char* aCategory,
+                       nsCategoryListener* aCategoryListener);
+    ~nsCategoryObserver();
+
+    void ListenerDied();
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIOBSERVER
+  private:
+    nsDataHashtable<nsCStringHashKey, nsCString> mHash;
+    nsCategoryListener*                          mListener;
+    nsCString                                    mCategory;
+};
+
+/**
+ * This is a helper class that caches services that are registered in a certain
+ * category. The intended usage is that a service stores a variable of type
+ * nsCategoryCache<nsIFoo> in a member variable, where nsIFoo is the interface
+ * that these services should implement. The constructor of this class should
+ * then get the name of the category.
+ */
+template<class T>
+class nsCategoryCache : protected nsCategoryListener {
+  public:
+    explicit nsCategoryCache(const char* aCategory);
+    ~nsCategoryCache() { if (mObserver) mObserver->ListenerDied(); }
+
+    const nsCOMArray<T>& GetEntries() const { return mEntries; }
+  protected:
+    virtual void EntryAdded(const nsCString& aValue);
+    virtual void EntryRemoved(const nsCString& aValue);
+    virtual void CategoryCleared();
+  private:
+    friend class CategoryObserver;
+
+    // Not to be implemented
+    nsCategoryCache(const nsCategoryCache<T>&);
+
+    nsCOMArray<T> mEntries;
+    nsRefPtr<nsCategoryObserver> mObserver;
+};
+
+// -----------------------------------
+// Implementation
+
+template<class T>
+nsCategoryCache<T>::nsCategoryCache(const char* aCategory)
+{
+  mObserver = new nsCategoryObserver(aCategory, this);
+}
+
+template<class T>
+void nsCategoryCache<T>::EntryAdded(const nsCString& aValue) {
+  nsCOMPtr<T> catEntry = do_GetService(aValue.get());
+  if (catEntry)
+    mEntries.AppendObject(catEntry);
+}
+
+template<class T>
+void nsCategoryCache<T>::EntryRemoved(const nsCString& aValue) {
+  nsCOMPtr<T> catEntry = do_GetService(aValue.get());
+  if (catEntry)
+    mEntries.RemoveObject(catEntry);
+}
+
+template<class T>
+void nsCategoryCache<T>::CategoryCleared() {
+  mEntries.Clear();
+}
+
+#endif
--- a/xpcom/glue/objs.mk
+++ b/xpcom/glue/objs.mk
@@ -38,16 +38,17 @@ XPCOM_GLUE_SRC_LCSRCS =          \
   pldhash.c                      \
   $(NULL)
 
 XPCOM_GLUE_SRC_CSRCS = $(addprefix $(topsrcdir)/xpcom/glue/, $(XPCOM_GLUE_SRC_LCSRCS))
 
 XPCOM_GLUE_SRC_LCPPSRCS =        \
   nsArrayEnumerator.cpp          \
   nsArrayUtils.cpp               \
+  nsCategoryCache.cpp            \
   nsCOMPtr.cpp                   \
   nsCOMArray.cpp                 \
   nsCRTGlue.cpp                  \
   nsComponentManagerUtils.cpp    \
   nsEnumeratorUtils.cpp          \
   nsID.cpp                       \
   nsIInterfaceRequestorUtils.cpp \
   nsINIParser.cpp                \