1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef nsCategoryCache_h_
8 #define nsCategoryCache_h_
9 
10 #include "mozilla/Attributes.h"
11 
12 #include "nsICategoryManager.h"
13 #include "nsIObserver.h"
14 #include "nsISimpleEnumerator.h"
15 #include "nsISupportsPrimitives.h"
16 
17 #include "nsServiceManagerUtils.h"
18 
19 #include "nsAutoPtr.h"
20 #include "nsCOMArray.h"
21 #include "nsInterfaceHashtable.h"
22 
23 #include "nsXPCOM.h"
24 
25 class nsCategoryObserver final : public nsIObserver
26 {
27   ~nsCategoryObserver();
28 
29 public:
30   explicit nsCategoryObserver(const char* aCategory);
31 
32   void ListenerDied();
GetHash()33   nsInterfaceHashtable<nsCStringHashKey, nsISupports>& GetHash()
34   {
35     return mHash;
36   }
37 
38   NS_DECL_ISUPPORTS
39   NS_DECL_NSIOBSERVER
40 private:
41   void RemoveObservers();
42 
43   nsInterfaceHashtable<nsCStringHashKey, nsISupports> mHash;
44   nsCString mCategory;
45   bool mObserversRemoved;
46 };
47 
48 /**
49  * This is a helper class that caches services that are registered in a certain
50  * category. The intended usage is that a service stores a variable of type
51  * nsCategoryCache<nsIFoo> in a member variable, where nsIFoo is the interface
52  * that these services should implement. The constructor of this class should
53  * then get the name of the category.
54  */
55 template<class T>
56 class nsCategoryCache final
57 {
58 public:
nsCategoryCache(const char * aCategory)59   explicit nsCategoryCache(const char* aCategory)
60     : mCategoryName(aCategory)
61   {
62   }
~nsCategoryCache()63   ~nsCategoryCache()
64   {
65     if (mObserver) {
66       mObserver->ListenerDied();
67     }
68   }
69 
GetEntries(nsCOMArray<T> & aResult)70   void GetEntries(nsCOMArray<T>& aResult)
71   {
72     // Lazy initialization, so that services in this category can't
73     // cause reentrant getService (bug 386376)
74     if (!mObserver) {
75       mObserver = new nsCategoryObserver(mCategoryName.get());
76     }
77 
78     for (auto iter = mObserver->GetHash().Iter(); !iter.Done(); iter.Next()) {
79       nsISupports* entry = iter.UserData();
80       nsCOMPtr<T> service = do_QueryInterface(entry);
81       if (service) {
82         aResult.AppendElement(service.forget());
83       }
84     }
85   }
86 
87 private:
88   // Not to be implemented
89   nsCategoryCache(const nsCategoryCache<T>&);
90 
91   nsCString mCategoryName;
92   RefPtr<nsCategoryObserver> mObserver;
93 };
94 
95 #endif
96