Coverage Report

Created: 2018-09-25 14:53

/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
}