xpcom/components/nsCategoryManager.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/xpcom/components/nsCategoryManager.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,844 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#define PL_ARENA_CONST_ALIGN_MASK 7
    1.10 +
    1.11 +#include "nsICategoryManager.h"
    1.12 +#include "nsCategoryManager.h"
    1.13 +
    1.14 +#include "plarena.h"
    1.15 +#include "prio.h"
    1.16 +#include "prprf.h"
    1.17 +#include "prlock.h"
    1.18 +#include "nsCOMPtr.h"
    1.19 +#include "nsTHashtable.h"
    1.20 +#include "nsClassHashtable.h"
    1.21 +#include "nsIFactory.h"
    1.22 +#include "nsIStringEnumerator.h"
    1.23 +#include "nsSupportsPrimitives.h"
    1.24 +#include "nsComponentManagerUtils.h"
    1.25 +#include "nsServiceManagerUtils.h"
    1.26 +#include "nsIObserver.h"
    1.27 +#include "nsIObserverService.h"
    1.28 +#include "nsReadableUtils.h"
    1.29 +#include "nsCRT.h"
    1.30 +#include "nsQuickSort.h"
    1.31 +#include "nsEnumeratorUtils.h"
    1.32 +#include "nsThreadUtils.h"
    1.33 +#include "mozilla/MemoryReporting.h"
    1.34 +#include "mozilla/Services.h"
    1.35 +
    1.36 +#include "ManifestParser.h"
    1.37 +#include "nsISimpleEnumerator.h"
    1.38 +
    1.39 +using namespace mozilla;
    1.40 +class nsIComponentLoaderManager;
    1.41 +
    1.42 +/*
    1.43 +  CategoryDatabase
    1.44 +  contains 0 or more 1-1 mappings of string to Category
    1.45 +  each Category contains 0 or more 1-1 mappings of string keys to string values
    1.46 +
    1.47 +  In other words, the CategoryDatabase is a tree, whose root is a hashtable.
    1.48 +  Internal nodes (or Categories) are hashtables. Leaf nodes are strings.
    1.49 +
    1.50 +  The leaf strings are allocated in an arena, because we assume they're not
    1.51 +  going to change much ;)
    1.52 +*/
    1.53 +
    1.54 +#define NS_CATEGORYMANAGER_ARENA_SIZE (1024 * 8)
    1.55 +
    1.56 +// pulled in from nsComponentManager.cpp
    1.57 +char* ArenaStrdup(const char* s, PLArenaPool* aArena);
    1.58 +
    1.59 +//
    1.60 +// BaseStringEnumerator is subclassed by EntryEnumerator and
    1.61 +// CategoryEnumerator
    1.62 +//
    1.63 +class BaseStringEnumerator
    1.64 +  : public nsISimpleEnumerator,
    1.65 +    private nsIUTF8StringEnumerator
    1.66 +{
    1.67 +public:
    1.68 +  NS_DECL_ISUPPORTS
    1.69 +  NS_DECL_NSISIMPLEENUMERATOR
    1.70 +  NS_DECL_NSIUTF8STRINGENUMERATOR
    1.71 +
    1.72 +protected:
    1.73 +  // Callback function for NS_QuickSort to sort mArray
    1.74 +  static int SortCallback(const void *, const void *, void *);
    1.75 +
    1.76 +  BaseStringEnumerator()
    1.77 +    : mArray(nullptr),
    1.78 +      mCount(0),
    1.79 +      mSimpleCurItem(0),
    1.80 +      mStringCurItem(0) { }
    1.81 +
    1.82 +  // A virtual destructor is needed here because subclasses of
    1.83 +  // BaseStringEnumerator do not implement their own Release() method.
    1.84 +
    1.85 +  virtual ~BaseStringEnumerator()
    1.86 +  {
    1.87 +    if (mArray)
    1.88 +      delete[] mArray;
    1.89 +  }
    1.90 +
    1.91 +  void Sort();
    1.92 +
    1.93 +  const char** mArray;
    1.94 +  uint32_t mCount;
    1.95 +  uint32_t mSimpleCurItem;
    1.96 +  uint32_t mStringCurItem;
    1.97 +};
    1.98 +
    1.99 +NS_IMPL_ISUPPORTS(BaseStringEnumerator, nsISimpleEnumerator, nsIUTF8StringEnumerator)
   1.100 +
   1.101 +NS_IMETHODIMP
   1.102 +BaseStringEnumerator::HasMoreElements(bool *_retval)
   1.103 +{
   1.104 +  *_retval = (mSimpleCurItem < mCount);
   1.105 +
   1.106 +  return NS_OK;
   1.107 +}
   1.108 +
   1.109 +NS_IMETHODIMP
   1.110 +BaseStringEnumerator::GetNext(nsISupports **_retval)
   1.111 +{
   1.112 +  if (mSimpleCurItem >= mCount)
   1.113 +    return NS_ERROR_FAILURE;
   1.114 +
   1.115 +  nsSupportsDependentCString* str =
   1.116 +    new nsSupportsDependentCString(mArray[mSimpleCurItem++]);
   1.117 +  if (!str)
   1.118 +    return NS_ERROR_OUT_OF_MEMORY;
   1.119 +
   1.120 +  *_retval = str;
   1.121 +  NS_ADDREF(*_retval);
   1.122 +  return NS_OK;
   1.123 +}
   1.124 +
   1.125 +NS_IMETHODIMP
   1.126 +BaseStringEnumerator::HasMore(bool *_retval)
   1.127 +{
   1.128 +  *_retval = (mStringCurItem < mCount);
   1.129 +
   1.130 +  return NS_OK;
   1.131 +}
   1.132 +
   1.133 +NS_IMETHODIMP
   1.134 +BaseStringEnumerator::GetNext(nsACString& _retval)
   1.135 +{
   1.136 +  if (mStringCurItem >= mCount)
   1.137 +    return NS_ERROR_FAILURE;
   1.138 +
   1.139 +  _retval = nsDependentCString(mArray[mStringCurItem++]);
   1.140 +  return NS_OK;
   1.141 +}
   1.142 +
   1.143 +int
   1.144 +BaseStringEnumerator::SortCallback(const void *e1, const void *e2,
   1.145 +                                   void * /*unused*/)
   1.146 +{
   1.147 +  char const *const *s1 = reinterpret_cast<char const *const *>(e1);
   1.148 +  char const *const *s2 = reinterpret_cast<char const *const *>(e2);
   1.149 +
   1.150 +  return strcmp(*s1, *s2);
   1.151 +}
   1.152 +
   1.153 +void
   1.154 +BaseStringEnumerator::Sort()
   1.155 +{
   1.156 +  NS_QuickSort(mArray, mCount, sizeof(mArray[0]), SortCallback, nullptr);
   1.157 +}
   1.158 +
   1.159 +//
   1.160 +// EntryEnumerator is the wrapper that allows nsICategoryManager::EnumerateCategory
   1.161 +//
   1.162 +class EntryEnumerator
   1.163 +  : public BaseStringEnumerator
   1.164 +{
   1.165 +public:
   1.166 +  static EntryEnumerator* Create(nsTHashtable<CategoryLeaf>& aTable);
   1.167 +
   1.168 +private:
   1.169 +  static PLDHashOperator
   1.170 +    enumfunc_createenumerator(CategoryLeaf* aLeaf, void* userArg);
   1.171 +};
   1.172 +
   1.173 +
   1.174 +PLDHashOperator
   1.175 +EntryEnumerator::enumfunc_createenumerator(CategoryLeaf* aLeaf, void* userArg)
   1.176 +{
   1.177 +  EntryEnumerator* mythis = static_cast<EntryEnumerator*>(userArg);
   1.178 +  if (aLeaf->value)
   1.179 +    mythis->mArray[mythis->mCount++] = aLeaf->GetKey();
   1.180 +
   1.181 +  return PL_DHASH_NEXT;
   1.182 +}
   1.183 +
   1.184 +EntryEnumerator*
   1.185 +EntryEnumerator::Create(nsTHashtable<CategoryLeaf>& aTable)
   1.186 +{
   1.187 +  EntryEnumerator* enumObj = new EntryEnumerator();
   1.188 +  if (!enumObj)
   1.189 +    return nullptr;
   1.190 +
   1.191 +  enumObj->mArray = new char const* [aTable.Count()];
   1.192 +  if (!enumObj->mArray) {
   1.193 +    delete enumObj;
   1.194 +    return nullptr;
   1.195 +  }
   1.196 +
   1.197 +  aTable.EnumerateEntries(enumfunc_createenumerator, enumObj);
   1.198 +
   1.199 +  enumObj->Sort();
   1.200 +
   1.201 +  return enumObj;
   1.202 +}
   1.203 +
   1.204 +
   1.205 +//
   1.206 +// CategoryNode implementations
   1.207 +//
   1.208 +
   1.209 +CategoryNode*
   1.210 +CategoryNode::Create(PLArenaPool* aArena)
   1.211 +{
   1.212 +  CategoryNode* node = new(aArena) CategoryNode();
   1.213 +  if (!node)
   1.214 +    return nullptr;
   1.215 +
   1.216 +  return node;
   1.217 +}
   1.218 +
   1.219 +CategoryNode::~CategoryNode()
   1.220 +{
   1.221 +}
   1.222 +
   1.223 +void*
   1.224 +CategoryNode::operator new(size_t aSize, PLArenaPool* aArena)
   1.225 +{
   1.226 +  void* p;
   1.227 +  PL_ARENA_ALLOCATE(p, aArena, aSize);
   1.228 +  return p;
   1.229 +}
   1.230 +
   1.231 +NS_METHOD
   1.232 +CategoryNode::GetLeaf(const char* aEntryName,
   1.233 +                      char** _retval)
   1.234 +{
   1.235 +  MutexAutoLock lock(mLock);
   1.236 +  nsresult rv = NS_ERROR_NOT_AVAILABLE;
   1.237 +  CategoryLeaf* ent =
   1.238 +    mTable.GetEntry(aEntryName);
   1.239 +
   1.240 +  if (ent && ent->value) {
   1.241 +    *_retval = NS_strdup(ent->value);
   1.242 +    if (*_retval)
   1.243 +      rv = NS_OK;
   1.244 +  }
   1.245 +
   1.246 +  return rv;
   1.247 +}
   1.248 +
   1.249 +NS_METHOD
   1.250 +CategoryNode::AddLeaf(const char* aEntryName,
   1.251 +                      const char* aValue,
   1.252 +                      bool aReplace,
   1.253 +                      char** _retval,
   1.254 +                      PLArenaPool* aArena)
   1.255 +{
   1.256 +  if (_retval)
   1.257 +    *_retval = nullptr;
   1.258 +
   1.259 +  MutexAutoLock lock(mLock);
   1.260 +  CategoryLeaf* leaf = 
   1.261 +    mTable.GetEntry(aEntryName);
   1.262 +
   1.263 +  if (!leaf) {
   1.264 +    const char* arenaEntryName = ArenaStrdup(aEntryName, aArena);
   1.265 +    if (!arenaEntryName)
   1.266 +      return NS_ERROR_OUT_OF_MEMORY;
   1.267 +
   1.268 +    leaf = mTable.PutEntry(arenaEntryName);
   1.269 +    if (!leaf)
   1.270 +      return NS_ERROR_OUT_OF_MEMORY;
   1.271 +  }
   1.272 +
   1.273 +  if (leaf->value && !aReplace)
   1.274 +    return NS_ERROR_INVALID_ARG;
   1.275 +
   1.276 +  const char* arenaValue = ArenaStrdup(aValue, aArena);
   1.277 +  if (!arenaValue)
   1.278 +    return NS_ERROR_OUT_OF_MEMORY;
   1.279 +
   1.280 +  if (_retval && leaf->value) {
   1.281 +    *_retval = ToNewCString(nsDependentCString(leaf->value));
   1.282 +    if (!*_retval)
   1.283 +      return NS_ERROR_OUT_OF_MEMORY;
   1.284 +  }
   1.285 +
   1.286 +  leaf->value = arenaValue;
   1.287 +  return NS_OK;
   1.288 +}
   1.289 +
   1.290 +void
   1.291 +CategoryNode::DeleteLeaf(const char* aEntryName)
   1.292 +{
   1.293 +  // we don't throw any errors, because it normally doesn't matter
   1.294 +  // and it makes JS a lot cleaner
   1.295 +  MutexAutoLock lock(mLock);
   1.296 +
   1.297 +  // we can just remove the entire hash entry without introspection
   1.298 +  mTable.RemoveEntry(aEntryName);
   1.299 +}
   1.300 +
   1.301 +NS_METHOD 
   1.302 +CategoryNode::Enumerate(nsISimpleEnumerator **_retval)
   1.303 +{
   1.304 +  if (NS_WARN_IF(!_retval))
   1.305 +    return NS_ERROR_INVALID_ARG;
   1.306 +
   1.307 +  MutexAutoLock lock(mLock);
   1.308 +  EntryEnumerator* enumObj = EntryEnumerator::Create(mTable);
   1.309 +
   1.310 +  if (!enumObj)
   1.311 +    return NS_ERROR_OUT_OF_MEMORY;
   1.312 +
   1.313 +  *_retval = enumObj;
   1.314 +  NS_ADDREF(*_retval);
   1.315 +  return NS_OK;
   1.316 +}
   1.317 +
   1.318 +size_t
   1.319 +CategoryNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
   1.320 +{
   1.321 +    // We don't measure the strings pointed to by the entries because the
   1.322 +    // pointers are non-owning.
   1.323 +    return mTable.SizeOfExcludingThis(nullptr, aMallocSizeOf);
   1.324 +}
   1.325 +
   1.326 +struct persistent_userstruct {
   1.327 +  PRFileDesc* fd;
   1.328 +  const char* categoryName;
   1.329 +  bool        success;
   1.330 +};
   1.331 +
   1.332 +PLDHashOperator
   1.333 +enumfunc_pentries(CategoryLeaf* aLeaf, void* userArg)
   1.334 +{
   1.335 +  persistent_userstruct* args =
   1.336 +    static_cast<persistent_userstruct*>(userArg);
   1.337 +
   1.338 +  PLDHashOperator status = PL_DHASH_NEXT;
   1.339 +
   1.340 +  if (aLeaf->value) {
   1.341 +    if (PR_fprintf(args->fd,
   1.342 +                   "%s,%s,%s\n",
   1.343 +                   args->categoryName,
   1.344 +                   aLeaf->GetKey(),
   1.345 +                   aLeaf->value) == (uint32_t) -1) {
   1.346 +      args->success = false;
   1.347 +      status = PL_DHASH_STOP;
   1.348 +    }
   1.349 +  }
   1.350 +
   1.351 +  return status;
   1.352 +}
   1.353 +
   1.354 +//
   1.355 +// CategoryEnumerator class
   1.356 +//
   1.357 +
   1.358 +class CategoryEnumerator
   1.359 +  : public BaseStringEnumerator
   1.360 +{
   1.361 +public:
   1.362 +  static CategoryEnumerator* Create(nsClassHashtable<nsDepCharHashKey, CategoryNode>& aTable);
   1.363 +
   1.364 +private:
   1.365 +  static PLDHashOperator
   1.366 +  enumfunc_createenumerator(const char* aStr,
   1.367 +                            CategoryNode* aNode,
   1.368 +                            void* userArg);
   1.369 +};
   1.370 +
   1.371 +CategoryEnumerator*
   1.372 +CategoryEnumerator::Create(nsClassHashtable<nsDepCharHashKey, CategoryNode>& aTable)
   1.373 +{
   1.374 +  CategoryEnumerator* enumObj = new CategoryEnumerator();
   1.375 +  if (!enumObj)
   1.376 +    return nullptr;
   1.377 +
   1.378 +  enumObj->mArray = new const char* [aTable.Count()];
   1.379 +  if (!enumObj->mArray) {
   1.380 +    delete enumObj;
   1.381 +    return nullptr;
   1.382 +  }
   1.383 +
   1.384 +  aTable.EnumerateRead(enumfunc_createenumerator, enumObj);
   1.385 +
   1.386 +  return enumObj;
   1.387 +}
   1.388 +
   1.389 +PLDHashOperator
   1.390 +CategoryEnumerator::enumfunc_createenumerator(const char* aStr, CategoryNode* aNode, void* userArg)
   1.391 +{
   1.392 +  CategoryEnumerator* mythis = static_cast<CategoryEnumerator*>(userArg);
   1.393 +
   1.394 +  // if a category has no entries, we pretend it doesn't exist
   1.395 +  if (aNode->Count())
   1.396 +    mythis->mArray[mythis->mCount++] = aStr;
   1.397 +
   1.398 +  return PL_DHASH_NEXT;
   1.399 +}
   1.400 +
   1.401 +
   1.402 +//
   1.403 +// nsCategoryManager implementations
   1.404 +//
   1.405 +
   1.406 +NS_IMPL_QUERY_INTERFACE(nsCategoryManager, nsICategoryManager, nsIMemoryReporter)
   1.407 +
   1.408 +NS_IMETHODIMP_(MozExternalRefCountType)
   1.409 +nsCategoryManager::AddRef()
   1.410 +{
   1.411 +  return 2;
   1.412 +}
   1.413 +
   1.414 +NS_IMETHODIMP_(MozExternalRefCountType)
   1.415 +nsCategoryManager::Release()
   1.416 +{
   1.417 +  return 1;
   1.418 +}
   1.419 +
   1.420 +nsCategoryManager* nsCategoryManager::gCategoryManager;
   1.421 +
   1.422 +/* static */ nsCategoryManager*
   1.423 +nsCategoryManager::GetSingleton()
   1.424 +{
   1.425 +  if (!gCategoryManager)
   1.426 +    gCategoryManager = new nsCategoryManager();
   1.427 +  return gCategoryManager;
   1.428 +}
   1.429 +
   1.430 +/* static */ void
   1.431 +nsCategoryManager::Destroy()
   1.432 +{
   1.433 +  delete gCategoryManager;
   1.434 +  gCategoryManager = nullptr;
   1.435 +}
   1.436 +
   1.437 +nsresult
   1.438 +nsCategoryManager::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
   1.439 +{
   1.440 +  if (aOuter)
   1.441 +    return NS_ERROR_NO_AGGREGATION;
   1.442 +
   1.443 +  return GetSingleton()->QueryInterface(aIID, aResult);
   1.444 +}
   1.445 +
   1.446 +nsCategoryManager::nsCategoryManager()
   1.447 +  : mLock("nsCategoryManager")
   1.448 +  , mSuppressNotifications(false)
   1.449 +{
   1.450 +  PL_INIT_ARENA_POOL(&mArena, "CategoryManagerArena",
   1.451 +                     NS_CATEGORYMANAGER_ARENA_SIZE);
   1.452 +}
   1.453 +
   1.454 +void
   1.455 +nsCategoryManager::InitMemoryReporter()
   1.456 +{
   1.457 +  RegisterWeakMemoryReporter(this);
   1.458 +}
   1.459 +
   1.460 +nsCategoryManager::~nsCategoryManager()
   1.461 +{
   1.462 +  UnregisterWeakMemoryReporter(this);
   1.463 +
   1.464 +  // the hashtable contains entries that must be deleted before the arena is
   1.465 +  // destroyed, or else you will have PRLocks undestroyed and other Really
   1.466 +  // Bad Stuff (TM)
   1.467 +  mTable.Clear();
   1.468 +
   1.469 +  PL_FinishArenaPool(&mArena);
   1.470 +}
   1.471 +
   1.472 +inline CategoryNode*
   1.473 +nsCategoryManager::get_category(const char* aName) {
   1.474 +  CategoryNode* node;
   1.475 +  if (!mTable.Get(aName, &node)) {
   1.476 +    return nullptr;
   1.477 +  }
   1.478 +  return node;
   1.479 +}
   1.480 +
   1.481 +MOZ_DEFINE_MALLOC_SIZE_OF(CategoryManagerMallocSizeOf)
   1.482 +
   1.483 +NS_IMETHODIMP
   1.484 +nsCategoryManager::CollectReports(nsIHandleReportCallback* aHandleReport,
   1.485 +                                  nsISupports* aData)
   1.486 +{
   1.487 +  return MOZ_COLLECT_REPORT(
   1.488 +    "explicit/xpcom/category-manager", KIND_HEAP, UNITS_BYTES,
   1.489 +    SizeOfIncludingThis(CategoryManagerMallocSizeOf),
   1.490 +    "Memory used for the XPCOM category manager.");
   1.491 +}
   1.492 +
   1.493 +static size_t
   1.494 +SizeOfCategoryManagerTableEntryExcludingThis(nsDepCharHashKey::KeyType aKey,
   1.495 +                                             const nsAutoPtr<CategoryNode> &aData,
   1.496 +                                             MallocSizeOf aMallocSizeOf,
   1.497 +                                             void* aUserArg)
   1.498 +{
   1.499 +    // We don't measure the string pointed to by aKey because it's a non-owning
   1.500 +    // pointer.
   1.501 +    return aData.get()->SizeOfExcludingThis(aMallocSizeOf);
   1.502 +}
   1.503 +
   1.504 +size_t
   1.505 +nsCategoryManager::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
   1.506 +{
   1.507 +  size_t n = aMallocSizeOf(this);
   1.508 +
   1.509 +  n += PL_SizeOfArenaPoolExcludingPool(&mArena, aMallocSizeOf);
   1.510 +
   1.511 +  n += mTable.SizeOfExcludingThis(SizeOfCategoryManagerTableEntryExcludingThis,
   1.512 +                                  aMallocSizeOf);
   1.513 +
   1.514 +  return n;
   1.515 +}
   1.516 +
   1.517 +namespace {
   1.518 +
   1.519 +class CategoryNotificationRunnable : public nsRunnable
   1.520 +{
   1.521 +public:
   1.522 +  CategoryNotificationRunnable(nsISupports* aSubject,
   1.523 +                               const char* aTopic,
   1.524 +                               const char* aData)
   1.525 +    : mSubject(aSubject)
   1.526 +    , mTopic(aTopic)
   1.527 +    , mData(aData)
   1.528 +  { }
   1.529 +
   1.530 +  NS_DECL_NSIRUNNABLE
   1.531 +
   1.532 +private:
   1.533 +  nsCOMPtr<nsISupports> mSubject;
   1.534 +  const char* mTopic;
   1.535 +  NS_ConvertUTF8toUTF16 mData;
   1.536 +};
   1.537 +
   1.538 +NS_IMETHODIMP
   1.539 +CategoryNotificationRunnable::Run()
   1.540 +{
   1.541 +  nsCOMPtr<nsIObserverService> observerService =
   1.542 +    mozilla::services::GetObserverService();
   1.543 +  if (observerService)
   1.544 +    observerService->NotifyObservers(mSubject, mTopic, mData.get());
   1.545 +
   1.546 +  return NS_OK;
   1.547 +}
   1.548 +  
   1.549 +} // anonymous namespace
   1.550 +
   1.551 +
   1.552 +void
   1.553 +nsCategoryManager::NotifyObservers( const char *aTopic,
   1.554 +                                    const char *aCategoryName,
   1.555 +                                    const char *aEntryName )
   1.556 +{
   1.557 +  if (mSuppressNotifications)
   1.558 +    return;
   1.559 +
   1.560 +  nsRefPtr<CategoryNotificationRunnable> r;
   1.561 +
   1.562 +  if (aEntryName) {
   1.563 +    nsCOMPtr<nsISupportsCString> entry
   1.564 +      (do_CreateInstance (NS_SUPPORTS_CSTRING_CONTRACTID));
   1.565 +    if (!entry)
   1.566 +      return;
   1.567 +
   1.568 +    nsresult rv = entry->SetData(nsDependentCString(aEntryName));
   1.569 +    if (NS_FAILED(rv))
   1.570 +      return;
   1.571 +
   1.572 +    r = new CategoryNotificationRunnable(entry, aTopic, aCategoryName);
   1.573 +  } else {
   1.574 +    r = new CategoryNotificationRunnable(
   1.575 +              NS_ISUPPORTS_CAST(nsICategoryManager*, this),
   1.576 +              aTopic, aCategoryName);
   1.577 +  }
   1.578 +
   1.579 +  NS_DispatchToMainThread(r);
   1.580 +}
   1.581 +
   1.582 +NS_IMETHODIMP
   1.583 +nsCategoryManager::GetCategoryEntry( const char *aCategoryName,
   1.584 +                                     const char *aEntryName,
   1.585 +                                     char **_retval )
   1.586 +{
   1.587 +  if (NS_WARN_IF(!aCategoryName) ||
   1.588 +      NS_WARN_IF(!aEntryName) ||
   1.589 +      NS_WARN_IF(!_retval))
   1.590 +    return NS_ERROR_INVALID_ARG;;
   1.591 +
   1.592 +  nsresult status = NS_ERROR_NOT_AVAILABLE;
   1.593 +
   1.594 +  CategoryNode* category;
   1.595 +  {
   1.596 +    MutexAutoLock lock(mLock);
   1.597 +    category = get_category(aCategoryName);
   1.598 +  }
   1.599 +
   1.600 +  if (category) {
   1.601 +    status = category->GetLeaf(aEntryName, _retval);
   1.602 +  }
   1.603 +
   1.604 +  return status;
   1.605 +}
   1.606 +
   1.607 +NS_IMETHODIMP
   1.608 +nsCategoryManager::AddCategoryEntry( const char *aCategoryName,
   1.609 +                                     const char *aEntryName,
   1.610 +                                     const char *aValue,
   1.611 +                                     bool aPersist,
   1.612 +                                     bool aReplace,
   1.613 +                                     char **_retval )
   1.614 +{
   1.615 +  if (aPersist) {
   1.616 +    NS_ERROR("Category manager doesn't support persistence.");
   1.617 +    return NS_ERROR_INVALID_ARG;
   1.618 +  }
   1.619 +
   1.620 +  AddCategoryEntry(aCategoryName, aEntryName, aValue, aReplace, _retval);
   1.621 +  return NS_OK;
   1.622 +}
   1.623 +
   1.624 +void
   1.625 +nsCategoryManager::AddCategoryEntry(const char *aCategoryName,
   1.626 +                                    const char *aEntryName,
   1.627 +                                    const char *aValue,
   1.628 +                                    bool aReplace,
   1.629 +                                    char** aOldValue)
   1.630 +{
   1.631 +  if (aOldValue)
   1.632 +    *aOldValue = nullptr;
   1.633 +
   1.634 +  // Before we can insert a new entry, we'll need to
   1.635 +  //  find the |CategoryNode| to put it in...
   1.636 +  CategoryNode* category;
   1.637 +  {
   1.638 +    MutexAutoLock lock(mLock);
   1.639 +    category = get_category(aCategoryName);
   1.640 +
   1.641 +    if (!category) {
   1.642 +      // That category doesn't exist yet; let's make it.
   1.643 +      category = CategoryNode::Create(&mArena);
   1.644 +        
   1.645 +      char* categoryName = ArenaStrdup(aCategoryName, &mArena);
   1.646 +      mTable.Put(categoryName, category);
   1.647 +    }
   1.648 +  }
   1.649 +
   1.650 +  if (!category)
   1.651 +    return;
   1.652 +
   1.653 +  // We will need the return value of AddLeaf even if the called doesn't want it
   1.654 +  char *oldEntry = nullptr;
   1.655 +
   1.656 +  nsresult rv = category->AddLeaf(aEntryName,
   1.657 +                                  aValue,
   1.658 +                                  aReplace,
   1.659 +                                  &oldEntry,
   1.660 +                                  &mArena);
   1.661 +
   1.662 +  if (NS_SUCCEEDED(rv)) {
   1.663 +    if (oldEntry) {
   1.664 +      NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID,
   1.665 +                      aCategoryName, aEntryName);
   1.666 +    }
   1.667 +    NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID,
   1.668 +                    aCategoryName, aEntryName);
   1.669 +
   1.670 +    if (aOldValue)
   1.671 +      *aOldValue = oldEntry;
   1.672 +    else
   1.673 +      NS_Free(oldEntry);
   1.674 +  }
   1.675 +}
   1.676 +
   1.677 +NS_IMETHODIMP
   1.678 +nsCategoryManager::DeleteCategoryEntry( const char *aCategoryName,
   1.679 +                                        const char *aEntryName,
   1.680 +                                        bool aDontPersist)
   1.681 +{
   1.682 +  if (NS_WARN_IF(!aCategoryName) ||
   1.683 +      NS_WARN_IF(!aEntryName))
   1.684 +    return NS_ERROR_INVALID_ARG;
   1.685 +
   1.686 +  /*
   1.687 +    Note: no errors are reported since failure to delete
   1.688 +    probably won't hurt you, and returning errors seriously
   1.689 +    inconveniences JS clients
   1.690 +  */
   1.691 +
   1.692 +  CategoryNode* category;
   1.693 +  {
   1.694 +    MutexAutoLock lock(mLock);
   1.695 +    category = get_category(aCategoryName);
   1.696 +  }
   1.697 +
   1.698 +  if (category) {
   1.699 +    category->DeleteLeaf(aEntryName);
   1.700 +
   1.701 +    NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID,
   1.702 +                    aCategoryName, aEntryName);
   1.703 +  }
   1.704 +
   1.705 +  return NS_OK;
   1.706 +}
   1.707 +
   1.708 +NS_IMETHODIMP
   1.709 +nsCategoryManager::DeleteCategory( const char *aCategoryName )
   1.710 +{
   1.711 +  if (NS_WARN_IF(!aCategoryName))
   1.712 +    return NS_ERROR_INVALID_ARG;
   1.713 +
   1.714 +  // the categories are arena-allocated, so we don't
   1.715 +  // actually delete them. We just remove all of the
   1.716 +  // leaf nodes.
   1.717 +
   1.718 +  CategoryNode* category;
   1.719 +  {
   1.720 +    MutexAutoLock lock(mLock);
   1.721 +    category = get_category(aCategoryName);
   1.722 +  }
   1.723 +
   1.724 +  if (category) {
   1.725 +    category->Clear();
   1.726 +    NotifyObservers(NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID,
   1.727 +                    aCategoryName, nullptr);
   1.728 +  }
   1.729 +
   1.730 +  return NS_OK;
   1.731 +}
   1.732 +
   1.733 +NS_IMETHODIMP
   1.734 +nsCategoryManager::EnumerateCategory( const char *aCategoryName,
   1.735 +                                      nsISimpleEnumerator **_retval )
   1.736 +{
   1.737 +  if (NS_WARN_IF(!aCategoryName) ||
   1.738 +      NS_WARN_IF(!_retval))
   1.739 +    return NS_ERROR_INVALID_ARG;
   1.740 +
   1.741 +  CategoryNode* category;
   1.742 +  {
   1.743 +    MutexAutoLock lock(mLock);
   1.744 +    category = get_category(aCategoryName);
   1.745 +  }
   1.746 +  
   1.747 +  if (!category) {
   1.748 +    return NS_NewEmptyEnumerator(_retval);
   1.749 +  }
   1.750 +
   1.751 +  return category->Enumerate(_retval);
   1.752 +}
   1.753 +
   1.754 +NS_IMETHODIMP 
   1.755 +nsCategoryManager::EnumerateCategories(nsISimpleEnumerator **_retval)
   1.756 +{
   1.757 +  if (NS_WARN_IF(!_retval))
   1.758 +    return NS_ERROR_INVALID_ARG;
   1.759 +
   1.760 +  MutexAutoLock lock(mLock);
   1.761 +  CategoryEnumerator* enumObj = CategoryEnumerator::Create(mTable);
   1.762 +
   1.763 +  if (!enumObj)
   1.764 +    return NS_ERROR_OUT_OF_MEMORY;
   1.765 +
   1.766 +  *_retval = enumObj;
   1.767 +  NS_ADDREF(*_retval);
   1.768 +  return NS_OK;
   1.769 +}
   1.770 +
   1.771 +struct writecat_struct {
   1.772 +  PRFileDesc* fd;
   1.773 +  bool        success;
   1.774 +};
   1.775 +
   1.776 +NS_METHOD
   1.777 +nsCategoryManager::SuppressNotifications(bool aSuppress)
   1.778 +{
   1.779 +  mSuppressNotifications = aSuppress;
   1.780 +  return NS_OK;
   1.781 +}
   1.782 +
   1.783 +/*
   1.784 + * CreateServicesFromCategory()
   1.785 + *
   1.786 + * Given a category, this convenience functions enumerates the category and 
   1.787 + * creates a service of every CID or ContractID registered under the category.
   1.788 + * If observerTopic is non null and the service implements nsIObserver,
   1.789 + * this will attempt to notify the observer with the origin, observerTopic string
   1.790 + * as parameter.
   1.791 + */
   1.792 +void
   1.793 +NS_CreateServicesFromCategory(const char *category,
   1.794 +                              nsISupports *origin,
   1.795 +                              const char *observerTopic)
   1.796 +{
   1.797 +  nsresult rv;
   1.798 +
   1.799 +  nsCOMPtr<nsICategoryManager> categoryManager = 
   1.800 +    do_GetService("@mozilla.org/categorymanager;1");
   1.801 +  if (!categoryManager)
   1.802 +    return;
   1.803 +
   1.804 +  nsCOMPtr<nsISimpleEnumerator> enumerator;
   1.805 +  rv = categoryManager->EnumerateCategory(category, 
   1.806 +                                          getter_AddRefs(enumerator));
   1.807 +  if (NS_FAILED(rv))
   1.808 +    return;
   1.809 +
   1.810 +  nsCOMPtr<nsIUTF8StringEnumerator> senumerator =
   1.811 +    do_QueryInterface(enumerator);
   1.812 +  if (!senumerator) {
   1.813 +    NS_WARNING("Category enumerator doesn't support nsIUTF8StringEnumerator.");
   1.814 +    return;
   1.815 +  }
   1.816 +
   1.817 +  bool hasMore;
   1.818 +  while (NS_SUCCEEDED(senumerator->HasMore(&hasMore)) && hasMore) {
   1.819 +    // From here on just skip any error we get.
   1.820 +    nsAutoCString entryString;
   1.821 +    if (NS_FAILED(senumerator->GetNext(entryString)))
   1.822 +      continue;
   1.823 +      
   1.824 +    nsXPIDLCString contractID;
   1.825 +    rv = categoryManager->GetCategoryEntry(category,entryString.get(),
   1.826 +                                           getter_Copies(contractID));
   1.827 +    if (NS_FAILED(rv))
   1.828 +      continue;
   1.829 +
   1.830 +    nsCOMPtr<nsISupports> instance = do_GetService(contractID);
   1.831 +    if (!instance) {
   1.832 +      LogMessage("While creating services from category '%s', could not create service for entry '%s', contract ID '%s'",
   1.833 +                 category, entryString.get(), contractID.get());
   1.834 +      continue;
   1.835 +    }
   1.836 +
   1.837 +    if (observerTopic) {
   1.838 +      // try an observer, if it implements it.
   1.839 +      nsCOMPtr<nsIObserver> observer = do_QueryInterface(instance);
   1.840 +      if (observer)
   1.841 +        observer->Observe(origin, observerTopic, EmptyString().get());
   1.842 +      else
   1.843 +        LogMessage("While creating services from category '%s', service for entry '%s', contract ID '%s' does not implement nsIObserver.",
   1.844 +                   category, entryString.get(), contractID.get());
   1.845 +    }
   1.846 +  }
   1.847 +}

mercurial