Line data Source code
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 : #include "nsIObserverService.h"
8 : #include "mozilla/Services.h"
9 : #include "nsISupportsPrimitives.h"
10 : #include "nsIStringEnumerator.h"
11 :
12 : #include "nsXPCOMCID.h"
13 :
14 : #include "nsCategoryCache.h"
15 :
16 9 : nsCategoryObserver::nsCategoryObserver(const char* aCategory)
17 : : mCategory(aCategory)
18 : , mCallback(nullptr)
19 : , mClosure(nullptr)
20 9 : , mObserversRemoved(false)
21 : {
22 9 : MOZ_ASSERT(NS_IsMainThread());
23 : // First, enumerate the currently existing entries
24 : nsCOMPtr<nsICategoryManager> catMan =
25 18 : do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
26 9 : if (!catMan) {
27 0 : return;
28 : }
29 :
30 18 : nsCOMPtr<nsISimpleEnumerator> enumerator;
31 18 : nsresult rv = catMan->EnumerateCategory(aCategory,
32 18 : getter_AddRefs(enumerator));
33 9 : if (NS_FAILED(rv)) {
34 0 : return;
35 : }
36 :
37 18 : nsCOMPtr<nsIUTF8StringEnumerator> strings = do_QueryInterface(enumerator);
38 9 : MOZ_ASSERT(strings);
39 :
40 : bool more;
41 51 : while (NS_SUCCEEDED(strings->HasMore(&more)) && more) {
42 42 : nsAutoCString entryName;
43 21 : strings->GetNext(entryName);
44 :
45 42 : nsCString entryValue;
46 42 : rv = catMan->GetCategoryEntry(aCategory,
47 : entryName.get(),
48 42 : getter_Copies(entryValue));
49 21 : if (NS_SUCCEEDED(rv)) {
50 42 : nsCOMPtr<nsISupports> service = do_GetService(entryValue.get());
51 21 : if (service) {
52 21 : mHash.Put(entryName, service);
53 : }
54 : }
55 : }
56 :
57 : // Now, listen for changes
58 : nsCOMPtr<nsIObserverService> serv =
59 18 : mozilla::services::GetObserverService();
60 9 : if (serv) {
61 9 : serv->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
62 9 : serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID, false);
63 9 : serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID, false);
64 9 : serv->AddObserver(this, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID, false);
65 : }
66 : }
67 :
68 : nsCategoryObserver::~nsCategoryObserver() = default;
69 :
70 107 : NS_IMPL_ISUPPORTS(nsCategoryObserver, nsIObserver)
71 :
72 : void
73 1 : nsCategoryObserver::ListenerDied()
74 : {
75 1 : MOZ_ASSERT(NS_IsMainThread());
76 1 : RemoveObservers();
77 1 : mCallback = nullptr;
78 1 : mClosure = nullptr;
79 1 : }
80 :
81 : void
82 9 : nsCategoryObserver::SetListener(void(aCallback)(void*), void* aClosure)
83 : {
84 9 : MOZ_ASSERT(NS_IsMainThread());
85 9 : mCallback = aCallback;
86 9 : mClosure = aClosure;
87 9 : }
88 :
89 : void
90 1 : nsCategoryObserver::RemoveObservers()
91 : {
92 1 : MOZ_ASSERT(NS_IsMainThread());
93 :
94 1 : if (mObserversRemoved) {
95 0 : return;
96 : }
97 :
98 1 : if (mCallback) {
99 1 : mCallback(mClosure);
100 : }
101 :
102 1 : mObserversRemoved = true;
103 : nsCOMPtr<nsIObserverService> obsSvc =
104 2 : mozilla::services::GetObserverService();
105 1 : if (obsSvc) {
106 1 : obsSvc->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
107 1 : obsSvc->RemoveObserver(this, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID);
108 1 : obsSvc->RemoveObserver(this, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID);
109 1 : obsSvc->RemoveObserver(this, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID);
110 : }
111 : }
112 :
113 : NS_IMETHODIMP
114 9 : nsCategoryObserver::Observe(nsISupports* aSubject, const char* aTopic,
115 : const char16_t* aData)
116 : {
117 9 : MOZ_ASSERT(NS_IsMainThread());
118 :
119 9 : if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
120 0 : mHash.Clear();
121 0 : RemoveObservers();
122 :
123 0 : return NS_OK;
124 : }
125 :
126 45 : if (!aData ||
127 45 : !nsDependentString(aData).Equals(NS_ConvertASCIItoUTF16(mCategory))) {
128 8 : return NS_OK;
129 : }
130 :
131 2 : nsAutoCString str;
132 2 : nsCOMPtr<nsISupportsCString> strWrapper(do_QueryInterface(aSubject));
133 1 : if (strWrapper) {
134 1 : strWrapper->GetData(str);
135 : }
136 :
137 1 : if (strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID) == 0) {
138 : // We may get an add notification even when we already have an entry. This
139 : // is due to the notification happening asynchronously, so if the entry gets
140 : // added and an nsCategoryObserver gets instantiated before events get
141 : // processed, we'd get the notification for an existing entry.
142 : // Do nothing in that case.
143 1 : if (mHash.GetWeak(str)) {
144 0 : return NS_OK;
145 : }
146 :
147 : nsCOMPtr<nsICategoryManager> catMan =
148 2 : do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
149 1 : if (!catMan) {
150 0 : return NS_OK;
151 : }
152 :
153 2 : nsCString entryValue;
154 2 : catMan->GetCategoryEntry(mCategory.get(),
155 : str.get(),
156 2 : getter_Copies(entryValue));
157 :
158 2 : nsCOMPtr<nsISupports> service = do_GetService(entryValue.get());
159 :
160 1 : if (service) {
161 1 : mHash.Put(str, service);
162 : }
163 1 : if (mCallback) {
164 1 : mCallback(mClosure);
165 : }
166 0 : } else if (strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID) == 0) {
167 0 : mHash.Remove(str);
168 0 : if (mCallback) {
169 0 : mCallback(mClosure);
170 : }
171 0 : } else if (strcmp(aTopic, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID) == 0) {
172 0 : mHash.Clear();
173 0 : if (mCallback) {
174 0 : mCallback(mClosure);
175 : }
176 : }
177 1 : return NS_OK;
178 : }
|