/src/mozilla-central/xpcom/components/nsCategoryManager.cpp
Line | Count | Source (jump to first uncovered line) |
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 "nsICategoryManager.h" |
8 | | #include "nsCategoryManager.h" |
9 | | |
10 | | #include "prio.h" |
11 | | #include "prlock.h" |
12 | | #include "nsArrayEnumerator.h" |
13 | | #include "nsCOMPtr.h" |
14 | | #include "nsTHashtable.h" |
15 | | #include "nsClassHashtable.h" |
16 | | #include "nsIFactory.h" |
17 | | #include "nsStringEnumerator.h" |
18 | | #include "nsSupportsPrimitives.h" |
19 | | #include "nsComponentManagerUtils.h" |
20 | | #include "nsServiceManagerUtils.h" |
21 | | #include "nsIObserver.h" |
22 | | #include "nsIObserverService.h" |
23 | | #include "nsReadableUtils.h" |
24 | | #include "nsCRT.h" |
25 | | #include "nsQuickSort.h" |
26 | | #include "nsEnumeratorUtils.h" |
27 | | #include "nsThreadUtils.h" |
28 | | #include "mozilla/ArenaAllocatorExtensions.h" |
29 | | #include "mozilla/MemoryReporting.h" |
30 | | #include "mozilla/Services.h" |
31 | | #include "mozilla/SimpleEnumerator.h" |
32 | | |
33 | | #include "ManifestParser.h" |
34 | | #include "nsSimpleEnumerator.h" |
35 | | |
36 | | using namespace mozilla; |
37 | | class nsIComponentLoaderManager; |
38 | | |
39 | | /* |
40 | | CategoryDatabase |
41 | | contains 0 or more 1-1 mappings of string to Category |
42 | | each Category contains 0 or more 1-1 mappings of string keys to string values |
43 | | |
44 | | In other words, the CategoryDatabase is a tree, whose root is a hashtable. |
45 | | Internal nodes (or Categories) are hashtables. Leaf nodes are strings. |
46 | | |
47 | | The leaf strings are allocated in an arena, because we assume they're not |
48 | | going to change much ;) |
49 | | */ |
50 | | |
51 | | // |
52 | | // CategoryEnumerator class |
53 | | // |
54 | | |
55 | | class CategoryEnumerator |
56 | | : public nsSimpleEnumerator |
57 | | , private nsStringEnumeratorBase |
58 | | { |
59 | | public: |
60 | | NS_DECL_ISUPPORTS_INHERITED |
61 | | NS_DECL_NSISIMPLEENUMERATOR |
62 | | NS_DECL_NSIUTF8STRINGENUMERATOR |
63 | | |
64 | | using nsStringEnumeratorBase::GetNext; |
65 | | |
66 | | const nsID& DefaultInterface() override |
67 | 0 | { |
68 | 0 | return NS_GET_IID(nsISupportsCString); |
69 | 0 | } |
70 | | |
71 | | static CategoryEnumerator* Create(nsClassHashtable<nsDepCharHashKey, |
72 | | CategoryNode>& aTable); |
73 | | |
74 | | protected: |
75 | | CategoryEnumerator() |
76 | | : mArray(nullptr) |
77 | | , mCount(0) |
78 | | , mSimpleCurItem(0) |
79 | | , mStringCurItem(0) |
80 | 0 | { |
81 | 0 | } |
82 | | |
83 | | ~CategoryEnumerator() override |
84 | 0 | { |
85 | 0 | delete [] mArray; |
86 | 0 | } |
87 | | |
88 | | const char** mArray; |
89 | | uint32_t mCount; |
90 | | uint32_t mSimpleCurItem; |
91 | | uint32_t mStringCurItem; |
92 | | }; |
93 | | |
94 | | NS_IMPL_ISUPPORTS_INHERITED(CategoryEnumerator, nsSimpleEnumerator, |
95 | | nsIUTF8StringEnumerator, |
96 | | nsIStringEnumerator) |
97 | | |
98 | | NS_IMETHODIMP |
99 | | CategoryEnumerator::HasMoreElements(bool* aResult) |
100 | 0 | { |
101 | 0 | *aResult = (mSimpleCurItem < mCount); |
102 | 0 |
|
103 | 0 | return NS_OK; |
104 | 0 | } |
105 | | |
106 | | NS_IMETHODIMP |
107 | | CategoryEnumerator::GetNext(nsISupports** aResult) |
108 | 0 | { |
109 | 0 | if (mSimpleCurItem >= mCount) { |
110 | 0 | return NS_ERROR_FAILURE; |
111 | 0 | } |
112 | 0 | |
113 | 0 | auto* str = new nsSupportsDependentCString(mArray[mSimpleCurItem++]); |
114 | 0 | if (!str) { |
115 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
116 | 0 | } |
117 | 0 | |
118 | 0 | *aResult = str; |
119 | 0 | NS_ADDREF(*aResult); |
120 | 0 | return NS_OK; |
121 | 0 | } |
122 | | |
123 | | NS_IMETHODIMP |
124 | | CategoryEnumerator::HasMore(bool* aResult) |
125 | 0 | { |
126 | 0 | *aResult = (mStringCurItem < mCount); |
127 | 0 |
|
128 | 0 | return NS_OK; |
129 | 0 | } |
130 | | |
131 | | NS_IMETHODIMP |
132 | | CategoryEnumerator::GetNext(nsACString& aResult) |
133 | 0 | { |
134 | 0 | if (mStringCurItem >= mCount) { |
135 | 0 | return NS_ERROR_FAILURE; |
136 | 0 | } |
137 | 0 | |
138 | 0 | aResult = nsDependentCString(mArray[mStringCurItem++]); |
139 | 0 | return NS_OK; |
140 | 0 | } |
141 | | |
142 | | CategoryEnumerator* |
143 | | CategoryEnumerator::Create(nsClassHashtable<nsDepCharHashKey, CategoryNode>& |
144 | | aTable) |
145 | 0 | { |
146 | 0 | auto* enumObj = new CategoryEnumerator(); |
147 | 0 | if (!enumObj) { |
148 | 0 | return nullptr; |
149 | 0 | } |
150 | 0 | |
151 | 0 | enumObj->mArray = new const char* [aTable.Count()]; |
152 | 0 | if (!enumObj->mArray) { |
153 | 0 | delete enumObj; |
154 | 0 | return nullptr; |
155 | 0 | } |
156 | 0 | |
157 | 0 | for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) { |
158 | 0 | // if a category has no entries, we pretend it doesn't exist |
159 | 0 | CategoryNode* aNode = iter.UserData(); |
160 | 0 | if (aNode->Count()) { |
161 | 0 | enumObj->mArray[enumObj->mCount++] = iter.Key(); |
162 | 0 | } |
163 | 0 | } |
164 | 0 |
|
165 | 0 | return enumObj; |
166 | 0 | } |
167 | | |
168 | | class CategoryEntry final : public nsICategoryEntry |
169 | | { |
170 | | NS_DECL_ISUPPORTS |
171 | | NS_DECL_NSICATEGORYENTRY |
172 | | NS_DECL_NSISUPPORTSCSTRING |
173 | | NS_DECL_NSISUPPORTSPRIMITIVE |
174 | | |
175 | | CategoryEntry(const char* aKey, const char* aValue) |
176 | | : mKey(aKey) |
177 | | , mValue(aValue) |
178 | 3 | {} |
179 | | |
180 | 0 | const char* Key() const { return mKey; } |
181 | | |
182 | | static CategoryEntry* Cast(nsICategoryEntry* aEntry) |
183 | 0 | { |
184 | 0 | return static_cast<CategoryEntry*>(aEntry); |
185 | 0 | } |
186 | | |
187 | | private: |
188 | | ~CategoryEntry() = default; |
189 | | |
190 | | const char* mKey; |
191 | | const char* mValue; |
192 | | }; |
193 | | |
194 | | NS_IMPL_ISUPPORTS(CategoryEntry, nsICategoryEntry, nsISupportsCString) |
195 | | |
196 | | nsresult |
197 | | CategoryEntry::ToString(char** aResult) |
198 | 0 | { |
199 | 0 | *aResult = moz_xstrdup(mKey); |
200 | 0 | return NS_OK; |
201 | 0 | } |
202 | | |
203 | | nsresult |
204 | | CategoryEntry::GetType(uint16_t* aType) |
205 | 0 | { |
206 | 0 | *aType = TYPE_CSTRING; |
207 | 0 | return NS_OK; |
208 | 0 | } |
209 | | |
210 | | nsresult |
211 | | CategoryEntry::GetData(nsACString& aData) |
212 | 0 | { |
213 | 0 | aData = mKey; |
214 | 0 | return NS_OK; |
215 | 0 | } |
216 | | |
217 | | nsresult |
218 | | CategoryEntry::SetData(const nsACString& aData) |
219 | 0 | { |
220 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
221 | 0 | } |
222 | | |
223 | | nsresult |
224 | | CategoryEntry::GetEntry(nsACString& aEntry) |
225 | 0 | { |
226 | 0 | aEntry = mKey; |
227 | 0 | return NS_OK; |
228 | 0 | } |
229 | | |
230 | | nsresult |
231 | | CategoryEntry::GetValue(nsACString& aValue) |
232 | 3 | { |
233 | 3 | aValue = mValue; |
234 | 3 | return NS_OK; |
235 | 3 | } |
236 | | |
237 | | static nsresult |
238 | | CreateEntryEnumerator(nsTHashtable<CategoryLeaf>& aTable, |
239 | | nsISimpleEnumerator** aResult) |
240 | 3 | { |
241 | 3 | nsCOMArray<nsICategoryEntry> entries(aTable.Count()); |
242 | 3 | |
243 | 6 | for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) { |
244 | 3 | CategoryLeaf* leaf = iter.Get(); |
245 | 3 | if (leaf->value) { |
246 | 3 | entries.AppendElement(new CategoryEntry(leaf->GetKey(), leaf->value)); |
247 | 3 | } |
248 | 3 | } |
249 | 3 | |
250 | 3 | entries.Sort([](nsICategoryEntry* aA, nsICategoryEntry* aB, void*) { |
251 | 0 | return strcmp(CategoryEntry::Cast(aA)->Key(), CategoryEntry::Cast(aB)->Key()); |
252 | 0 | }, nullptr); |
253 | 3 | |
254 | 3 | return NS_NewArrayEnumerator(aResult, entries, NS_GET_IID(nsICategoryEntry)); |
255 | 3 | } |
256 | | |
257 | | // |
258 | | // CategoryNode implementations |
259 | | // |
260 | | |
261 | | CategoryNode* |
262 | | CategoryNode::Create(CategoryAllocator* aArena) |
263 | 90 | { |
264 | 90 | return new (aArena) CategoryNode(); |
265 | 90 | } |
266 | | |
267 | 0 | CategoryNode::~CategoryNode() = default; |
268 | | |
269 | | void* |
270 | | CategoryNode::operator new(size_t aSize, CategoryAllocator* aArena) |
271 | 90 | { |
272 | 90 | return aArena->Allocate(aSize, mozilla::fallible); |
273 | 90 | } |
274 | | |
275 | | static inline const char* |
276 | | MaybeStrdup(const nsACString& aStr, CategoryAllocator* aArena) |
277 | 828 | { |
278 | 828 | if (aStr.IsLiteral()) { |
279 | 573 | return aStr.BeginReading(); |
280 | 573 | } |
281 | 255 | return ArenaStrdup(PromiseFlatCString(aStr).get(), *aArena); |
282 | 255 | } |
283 | | |
284 | | nsresult |
285 | | CategoryNode::GetLeaf(const nsACString& aEntryName, |
286 | | nsACString& aResult) |
287 | 1 | { |
288 | 1 | MutexAutoLock lock(mLock); |
289 | 1 | nsresult rv = NS_ERROR_NOT_AVAILABLE; |
290 | 1 | CategoryLeaf* ent = mTable.GetEntry(PromiseFlatCString(aEntryName).get()); |
291 | 1 | |
292 | 1 | if (ent && ent->value) { |
293 | 1 | aResult.Assign(ent->value); |
294 | 1 | return NS_OK; |
295 | 1 | } |
296 | 0 | |
297 | 0 | return rv; |
298 | 0 | } |
299 | | |
300 | | nsresult |
301 | | CategoryNode::AddLeaf(const nsACString& aEntryName, |
302 | | const nsACString& aValue, |
303 | | bool aReplace, |
304 | | nsACString& aResult, |
305 | | CategoryAllocator* aArena) |
306 | 369 | { |
307 | 369 | aResult.SetIsVoid(true); |
308 | 369 | |
309 | 369 | auto entryName = PromiseFlatCString(aEntryName); |
310 | 369 | |
311 | 369 | MutexAutoLock lock(mLock); |
312 | 369 | CategoryLeaf* leaf = mTable.GetEntry(entryName.get()); |
313 | 369 | |
314 | 369 | if (!leaf) { |
315 | 369 | leaf = mTable.PutEntry(MaybeStrdup(aEntryName, aArena)); |
316 | 369 | if (!leaf) { |
317 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
318 | 0 | } |
319 | 369 | } |
320 | 369 | |
321 | 369 | if (leaf->value && !aReplace) { |
322 | 0 | return NS_ERROR_INVALID_ARG; |
323 | 0 | } |
324 | 369 | |
325 | 369 | if (leaf->value) { |
326 | 0 | aResult.AssignLiteral(leaf->value, strlen(leaf->value)); |
327 | 369 | } else { |
328 | 369 | aResult.SetIsVoid(true); |
329 | 369 | } |
330 | 369 | leaf->value = MaybeStrdup(aValue, aArena); |
331 | 369 | return NS_OK; |
332 | 369 | } |
333 | | |
334 | | void |
335 | | CategoryNode::DeleteLeaf(const nsACString& aEntryName) |
336 | 0 | { |
337 | 0 | // we don't throw any errors, because it normally doesn't matter |
338 | 0 | // and it makes JS a lot cleaner |
339 | 0 | MutexAutoLock lock(mLock); |
340 | 0 |
|
341 | 0 | // we can just remove the entire hash entry without introspection |
342 | 0 | mTable.RemoveEntry(PromiseFlatCString(aEntryName).get()); |
343 | 0 | } |
344 | | |
345 | | nsresult |
346 | | CategoryNode::Enumerate(nsISimpleEnumerator** aResult) |
347 | 3 | { |
348 | 3 | MutexAutoLock lock(mLock); |
349 | 3 | return CreateEntryEnumerator(mTable, aResult); |
350 | 3 | } |
351 | | |
352 | | size_t |
353 | | CategoryNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) |
354 | 0 | { |
355 | 0 | // We don't measure the strings pointed to by the entries because the |
356 | 0 | // pointers are non-owning. |
357 | 0 | return mTable.ShallowSizeOfExcludingThis(aMallocSizeOf); |
358 | 0 | } |
359 | | |
360 | | // |
361 | | // nsCategoryManager implementations |
362 | | // |
363 | | |
364 | | NS_IMPL_QUERY_INTERFACE(nsCategoryManager, nsICategoryManager, nsIMemoryReporter) |
365 | | |
366 | | NS_IMETHODIMP_(MozExternalRefCountType) |
367 | | nsCategoryManager::AddRef() |
368 | 17 | { |
369 | 17 | return 2; |
370 | 17 | } |
371 | | |
372 | | NS_IMETHODIMP_(MozExternalRefCountType) |
373 | | nsCategoryManager::Release() |
374 | 14 | { |
375 | 14 | return 1; |
376 | 14 | } |
377 | | |
378 | | nsCategoryManager* nsCategoryManager::gCategoryManager; |
379 | | |
380 | | /* static */ nsCategoryManager* |
381 | | nsCategoryManager::GetSingleton() |
382 | 381 | { |
383 | 381 | if (!gCategoryManager) { |
384 | 3 | gCategoryManager = new nsCategoryManager(); |
385 | 3 | } |
386 | 381 | return gCategoryManager; |
387 | 381 | } |
388 | | |
389 | | /* static */ void |
390 | | nsCategoryManager::Destroy() |
391 | 0 | { |
392 | 0 | // The nsMemoryReporterManager gets destroyed before the nsCategoryManager, |
393 | 0 | // so we don't need to unregister the nsCategoryManager as a memory reporter. |
394 | 0 | // In debug builds we assert that unregistering fails, as a way (imperfect |
395 | 0 | // but better than nothing) of testing the "destroyed before" part. |
396 | 0 | MOZ_ASSERT(NS_FAILED(UnregisterWeakMemoryReporter(gCategoryManager))); |
397 | 0 |
|
398 | 0 | delete gCategoryManager; |
399 | 0 | gCategoryManager = nullptr; |
400 | 0 | } |
401 | | |
402 | | nsresult |
403 | | nsCategoryManager::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult) |
404 | 3 | { |
405 | 3 | if (aOuter) { |
406 | 0 | return NS_ERROR_NO_AGGREGATION; |
407 | 0 | } |
408 | 3 | |
409 | 3 | return GetSingleton()->QueryInterface(aIID, aResult); |
410 | 3 | } |
411 | | |
412 | | nsCategoryManager::nsCategoryManager() |
413 | | : mArena() |
414 | | , mTable() |
415 | | , mLock("nsCategoryManager") |
416 | | , mSuppressNotifications(false) |
417 | 3 | { |
418 | 3 | } |
419 | | |
420 | | void |
421 | | nsCategoryManager::InitMemoryReporter() |
422 | 3 | { |
423 | 3 | RegisterWeakMemoryReporter(this); |
424 | 3 | } |
425 | | |
426 | | nsCategoryManager::~nsCategoryManager() |
427 | 0 | { |
428 | 0 | // the hashtable contains entries that must be deleted before the arena is |
429 | 0 | // destroyed, or else you will have PRLocks undestroyed and other Really |
430 | 0 | // Bad Stuff (TM) |
431 | 0 | mTable.Clear(); |
432 | 0 | } |
433 | | |
434 | | inline CategoryNode* |
435 | | nsCategoryManager::get_category(const nsACString& aName) |
436 | 380 | { |
437 | 380 | CategoryNode* node; |
438 | 380 | if (!mTable.Get(PromiseFlatCString(aName).get(), &node)) { |
439 | 97 | return nullptr; |
440 | 97 | } |
441 | 283 | return node; |
442 | 283 | } |
443 | | |
444 | | MOZ_DEFINE_MALLOC_SIZE_OF(CategoryManagerMallocSizeOf) |
445 | | |
446 | | NS_IMETHODIMP |
447 | | nsCategoryManager::CollectReports(nsIHandleReportCallback* aHandleReport, |
448 | | nsISupports* aData, bool aAnonymize) |
449 | 0 | { |
450 | 0 | MOZ_COLLECT_REPORT( |
451 | 0 | "explicit/xpcom/category-manager", KIND_HEAP, UNITS_BYTES, |
452 | 0 | SizeOfIncludingThis(CategoryManagerMallocSizeOf), |
453 | 0 | "Memory used for the XPCOM category manager."); |
454 | 0 |
|
455 | 0 | return NS_OK; |
456 | 0 | } |
457 | | |
458 | | size_t |
459 | | nsCategoryManager::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) |
460 | 0 | { |
461 | 0 | size_t n = aMallocSizeOf(this); |
462 | 0 |
|
463 | 0 | n += mArena.SizeOfExcludingThis(aMallocSizeOf); |
464 | 0 |
|
465 | 0 | n += mTable.ShallowSizeOfExcludingThis(aMallocSizeOf); |
466 | 0 | for (auto iter = mTable.ConstIter(); !iter.Done(); iter.Next()) { |
467 | 0 | // We don't measure the key string because it's a non-owning pointer. |
468 | 0 | n += iter.Data()->SizeOfExcludingThis(aMallocSizeOf); |
469 | 0 | } |
470 | 0 |
|
471 | 0 | return n; |
472 | 0 | } |
473 | | |
474 | | namespace { |
475 | | |
476 | | class CategoryNotificationRunnable : public Runnable |
477 | | { |
478 | | public: |
479 | | CategoryNotificationRunnable(nsISupports* aSubject, |
480 | | const char* aTopic, |
481 | | const nsACString& aData) |
482 | | : Runnable("CategoryNotificationRunnable") |
483 | | , mSubject(aSubject) |
484 | | , mTopic(aTopic) |
485 | | , mData(aData) |
486 | 0 | { |
487 | 0 | } |
488 | | |
489 | | NS_DECL_NSIRUNNABLE |
490 | | |
491 | | private: |
492 | | nsCOMPtr<nsISupports> mSubject; |
493 | | const char* mTopic; |
494 | | NS_ConvertUTF8toUTF16 mData; |
495 | | }; |
496 | | |
497 | | NS_IMETHODIMP |
498 | | CategoryNotificationRunnable::Run() |
499 | 0 | { |
500 | 0 | nsCOMPtr<nsIObserverService> observerService = |
501 | 0 | mozilla::services::GetObserverService(); |
502 | 0 | if (observerService) { |
503 | 0 | observerService->NotifyObservers(mSubject, mTopic, mData.get()); |
504 | 0 | } |
505 | 0 |
|
506 | 0 | return NS_OK; |
507 | 0 | } |
508 | | |
509 | | } // namespace |
510 | | |
511 | | |
512 | | void |
513 | | nsCategoryManager::NotifyObservers(const char* aTopic, |
514 | | const nsACString& aCategoryName, |
515 | | const nsACString& aEntryName) |
516 | 369 | { |
517 | 369 | if (mSuppressNotifications) { |
518 | 369 | return; |
519 | 369 | } |
520 | 0 | |
521 | 0 | RefPtr<CategoryNotificationRunnable> r; |
522 | 0 |
|
523 | 0 | if (aEntryName.Length()) { |
524 | 0 | nsCOMPtr<nsISupportsCString> entry = |
525 | 0 | do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID); |
526 | 0 | if (!entry) { |
527 | 0 | return; |
528 | 0 | } |
529 | 0 | |
530 | 0 | nsresult rv = entry->SetData(aEntryName); |
531 | 0 | if (NS_FAILED(rv)) { |
532 | 0 | return; |
533 | 0 | } |
534 | 0 | |
535 | 0 | r = new CategoryNotificationRunnable(entry, aTopic, aCategoryName); |
536 | 0 | } else { |
537 | 0 | r = new CategoryNotificationRunnable(NS_ISUPPORTS_CAST(nsICategoryManager*, |
538 | 0 | this), |
539 | 0 | aTopic, aCategoryName); |
540 | 0 | } |
541 | 0 |
|
542 | 0 | NS_DispatchToMainThread(r); |
543 | 0 | } |
544 | | |
545 | | NS_IMETHODIMP |
546 | | nsCategoryManager::GetCategoryEntry(const nsACString& aCategoryName, |
547 | | const nsACString& aEntryName, |
548 | | nsACString& aResult) |
549 | 1 | { |
550 | 1 | nsresult status = NS_ERROR_NOT_AVAILABLE; |
551 | 1 | |
552 | 1 | CategoryNode* category; |
553 | 1 | { |
554 | 1 | MutexAutoLock lock(mLock); |
555 | 1 | category = get_category(aCategoryName); |
556 | 1 | } |
557 | 1 | |
558 | 1 | if (category) { |
559 | 1 | status = category->GetLeaf(aEntryName, aResult); |
560 | 1 | } |
561 | 1 | |
562 | 1 | return status; |
563 | 1 | } |
564 | | |
565 | | NS_IMETHODIMP |
566 | | nsCategoryManager::AddCategoryEntry(const nsACString& aCategoryName, |
567 | | const nsACString& aEntryName, |
568 | | const nsACString& aValue, |
569 | | bool aPersist, |
570 | | bool aReplace, |
571 | | nsACString& aResult) |
572 | 0 | { |
573 | 0 | if (aPersist) { |
574 | 0 | NS_ERROR("Category manager doesn't support persistence."); |
575 | 0 | return NS_ERROR_INVALID_ARG; |
576 | 0 | } |
577 | 0 |
|
578 | 0 | AddCategoryEntry(aCategoryName, aEntryName, aValue, aReplace, aResult); |
579 | 0 | return NS_OK; |
580 | 0 | } |
581 | | |
582 | | void |
583 | | nsCategoryManager::AddCategoryEntry(const nsACString& aCategoryName, |
584 | | const nsACString& aEntryName, |
585 | | const nsACString& aValue, |
586 | | bool aReplace, |
587 | | nsACString& aOldValue) |
588 | 369 | { |
589 | 369 | aOldValue.SetIsVoid(true); |
590 | 369 | |
591 | 369 | // Before we can insert a new entry, we'll need to |
592 | 369 | // find the |CategoryNode| to put it in... |
593 | 369 | CategoryNode* category; |
594 | 369 | { |
595 | 369 | MutexAutoLock lock(mLock); |
596 | 369 | category = get_category(aCategoryName); |
597 | 369 | |
598 | 369 | if (!category) { |
599 | 90 | // That category doesn't exist yet; let's make it. |
600 | 90 | category = CategoryNode::Create(&mArena); |
601 | 90 | |
602 | 90 | mTable.Put(MaybeStrdup(aCategoryName, &mArena), |
603 | 90 | category); |
604 | 90 | } |
605 | 369 | } |
606 | 369 | |
607 | 369 | if (!category) { |
608 | 0 | return; |
609 | 0 | } |
610 | 369 | |
611 | 369 | nsresult rv = category->AddLeaf(aEntryName, |
612 | 369 | aValue, |
613 | 369 | aReplace, |
614 | 369 | aOldValue, |
615 | 369 | &mArena); |
616 | 369 | |
617 | 369 | if (NS_SUCCEEDED(rv)) { |
618 | 369 | if (!aOldValue.IsEmpty()) { |
619 | 0 | NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID, |
620 | 0 | aCategoryName, aEntryName); |
621 | 0 | } |
622 | 369 | NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID, |
623 | 369 | aCategoryName, aEntryName); |
624 | 369 | |
625 | 369 | } |
626 | 369 | } |
627 | | |
628 | | NS_IMETHODIMP |
629 | | nsCategoryManager::DeleteCategoryEntry(const nsACString& aCategoryName, |
630 | | const nsACString& aEntryName, |
631 | | bool aDontPersist) |
632 | 0 | { |
633 | 0 | /* |
634 | 0 | Note: no errors are reported since failure to delete |
635 | 0 | probably won't hurt you, and returning errors seriously |
636 | 0 | inconveniences JS clients |
637 | 0 | */ |
638 | 0 |
|
639 | 0 | CategoryNode* category; |
640 | 0 | { |
641 | 0 | MutexAutoLock lock(mLock); |
642 | 0 | category = get_category(aCategoryName); |
643 | 0 | } |
644 | 0 |
|
645 | 0 | if (category) { |
646 | 0 | category->DeleteLeaf(aEntryName); |
647 | 0 |
|
648 | 0 | NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID, |
649 | 0 | aCategoryName, aEntryName); |
650 | 0 | } |
651 | 0 |
|
652 | 0 | return NS_OK; |
653 | 0 | } |
654 | | |
655 | | NS_IMETHODIMP |
656 | | nsCategoryManager::DeleteCategory(const nsACString& aCategoryName) |
657 | 0 | { |
658 | 0 | // the categories are arena-allocated, so we don't |
659 | 0 | // actually delete them. We just remove all of the |
660 | 0 | // leaf nodes. |
661 | 0 |
|
662 | 0 | CategoryNode* category; |
663 | 0 | { |
664 | 0 | MutexAutoLock lock(mLock); |
665 | 0 | category = get_category(aCategoryName); |
666 | 0 | } |
667 | 0 |
|
668 | 0 | if (category) { |
669 | 0 | category->Clear(); |
670 | 0 | NotifyObservers(NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID, |
671 | 0 | aCategoryName, VoidCString()); |
672 | 0 | } |
673 | 0 |
|
674 | 0 | return NS_OK; |
675 | 0 | } |
676 | | |
677 | | NS_IMETHODIMP |
678 | | nsCategoryManager::EnumerateCategory(const nsACString& aCategoryName, |
679 | | nsISimpleEnumerator** aResult) |
680 | 10 | { |
681 | 10 | CategoryNode* category; |
682 | 10 | { |
683 | 10 | MutexAutoLock lock(mLock); |
684 | 10 | category = get_category(aCategoryName); |
685 | 10 | } |
686 | 10 | |
687 | 10 | if (!category) { |
688 | 7 | return NS_NewEmptyEnumerator(aResult); |
689 | 7 | } |
690 | 3 | |
691 | 3 | return category->Enumerate(aResult); |
692 | 3 | } |
693 | | |
694 | | NS_IMETHODIMP |
695 | | nsCategoryManager::EnumerateCategories(nsISimpleEnumerator** aResult) |
696 | 0 | { |
697 | 0 | if (NS_WARN_IF(!aResult)) { |
698 | 0 | return NS_ERROR_INVALID_ARG; |
699 | 0 | } |
700 | 0 | |
701 | 0 | MutexAutoLock lock(mLock); |
702 | 0 | CategoryEnumerator* enumObj = CategoryEnumerator::Create(mTable); |
703 | 0 |
|
704 | 0 | if (!enumObj) { |
705 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
706 | 0 | } |
707 | 0 | |
708 | 0 | *aResult = enumObj; |
709 | 0 | NS_ADDREF(*aResult); |
710 | 0 | return NS_OK; |
711 | 0 | } |
712 | | |
713 | | struct writecat_struct |
714 | | { |
715 | | PRFileDesc* fd; |
716 | | bool success; |
717 | | }; |
718 | | |
719 | | nsresult |
720 | | nsCategoryManager::SuppressNotifications(bool aSuppress) |
721 | 6 | { |
722 | 6 | mSuppressNotifications = aSuppress; |
723 | 6 | return NS_OK; |
724 | 6 | } |
725 | | |
726 | | /* |
727 | | * CreateServicesFromCategory() |
728 | | * |
729 | | * Given a category, this convenience functions enumerates the category and |
730 | | * creates a service of every CID or ContractID registered under the category. |
731 | | * If observerTopic is non null and the service implements nsIObserver, |
732 | | * this will attempt to notify the observer with the origin, observerTopic string |
733 | | * as parameter. |
734 | | */ |
735 | | void |
736 | | NS_CreateServicesFromCategory(const char* aCategory, |
737 | | nsISupports* aOrigin, |
738 | | const char* aObserverTopic, |
739 | | const char16_t* aObserverData) |
740 | 7 | { |
741 | 7 | nsresult rv; |
742 | 7 | |
743 | 7 | nsCOMPtr<nsICategoryManager> categoryManager = |
744 | 7 | do_GetService("@mozilla.org/categorymanager;1"); |
745 | 7 | if (!categoryManager) { |
746 | 0 | return; |
747 | 0 | } |
748 | 7 | |
749 | 7 | nsDependentCString category(aCategory); |
750 | 7 | |
751 | 7 | nsCOMPtr<nsISimpleEnumerator> enumerator; |
752 | 7 | rv = categoryManager->EnumerateCategory(category, |
753 | 7 | getter_AddRefs(enumerator)); |
754 | 7 | if (NS_FAILED(rv)) { |
755 | 0 | return; |
756 | 0 | } |
757 | 7 | |
758 | 7 | for (auto& categoryEntry : SimpleEnumerator<nsICategoryEntry>(enumerator)) { |
759 | 0 | // From here on just skip any error we get. |
760 | 0 | nsAutoCString entryString; |
761 | 0 | categoryEntry->GetEntry(entryString); |
762 | 0 |
|
763 | 0 | nsAutoCString contractID; |
764 | 0 | categoryEntry->GetValue(contractID); |
765 | 0 |
|
766 | 0 | nsCOMPtr<nsISupports> instance = do_GetService(contractID.get()); |
767 | 0 | if (!instance) { |
768 | 0 | LogMessage("While creating services from category '%s', could not create service for entry '%s', contract ID '%s'", |
769 | 0 | aCategory, entryString.get(), contractID.get()); |
770 | 0 | continue; |
771 | 0 | } |
772 | 0 | |
773 | 0 | if (aObserverTopic) { |
774 | 0 | // try an observer, if it implements it. |
775 | 0 | nsCOMPtr<nsIObserver> observer = do_QueryInterface(instance); |
776 | 0 | if (observer) { |
777 | 0 | observer->Observe(aOrigin, aObserverTopic, |
778 | 0 | aObserverData ? aObserverData : u""); |
779 | 0 | } else { |
780 | 0 | LogMessage("While creating services from category '%s', service for entry '%s', contract ID '%s' does not implement nsIObserver.", |
781 | 0 | aCategory, entryString.get(), contractID.get()); |
782 | 0 | } |
783 | 0 | } |
784 | 0 | } |
785 | 7 | } |