xpcom/components/nsCategoryManager.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial