source: trunk/third/firefox/xpcom/components/nsCategoryManager.cpp @ 21695

Revision 21695, 19.2 KB checked in by rbasch, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21694, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2/* ***** BEGIN LICENSE BLOCK *****
3 * Version: NPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Netscape Public License
6 * Version 1.1 (the "License"); you may not use this file except in
7 * compliance with the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/NPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is mozilla.org code.
16 *
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2000
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 *   Scott Collins <scc@netscape.com>
24 *
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the NPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the NPL, the GPL or the LGPL.
36 *
37 * ***** END LICENSE BLOCK ***** */
38
39#define PL_ARENA_CONST_ALIGN_MASK 7
40
41#include "nsICategoryManager.h"
42#include "nsCategoryManager.h"
43
44#include "plarena.h"
45#include "prio.h"
46#include "prprf.h"
47#include "prlock.h"
48#include "nsCOMPtr.h"
49#include "nsTHashtable.h"
50#include "nsClassHashtable.h"
51#include "nsIFactory.h"
52#include "nsIStringEnumerator.h"
53#include "nsSupportsPrimitives.h"
54#include "nsIServiceManagerUtils.h"
55#include "nsIObserver.h"
56#include "nsReadableUtils.h"
57#include "nsCRT.h"
58#include "nsEnumeratorUtils.h"
59
60class nsIComponentLoaderManager;
61
62/*
63  CategoryDatabase
64  contains 0 or more 1-1 mappings of string to Category
65  each Category contains 0 or more 1-1 mappings of string keys to string values
66
67  In other words, the CategoryDatabase is a tree, whose root is a hashtable.
68  Internal nodes (or Categories) are hashtables. Leaf nodes are strings.
69
70  The leaf strings are allocated in an arena, because we assume they're not
71  going to change much ;)
72*/
73
74#define NS_CATEGORYMANAGER_ARENA_SIZE (1024 * 8)
75
76// pulled in from nsComponentManager.cpp
77char* ArenaStrdup(const char* s, PLArenaPool* aArena);
78
79//
80// BaseStringEnumerator is subclassed by EntryEnumerator and
81// CategoryEnumerator
82//
83class BaseStringEnumerator
84  : public nsISimpleEnumerator,
85           nsIUTF8StringEnumerator
86{
87public:
88  NS_DECL_ISUPPORTS
89  NS_DECL_NSISIMPLEENUMERATOR
90  NS_DECL_NSIUTF8STRINGENUMERATOR
91
92protected:
93  BaseStringEnumerator()
94    : mArray(nsnull),
95      mCount(0),
96      mSimpleCurItem(0),
97      mStringCurItem(0) { }
98
99  // A virtual destructor is needed here because subclasses of
100  // BaseStringEnumerator do not implement their own Release() method.
101
102  virtual ~BaseStringEnumerator()
103  {
104    if (mArray)
105      delete[] mArray;
106  }
107
108  const char** mArray;
109  PRUint32 mCount;
110  PRUint32 mSimpleCurItem;
111  PRUint32 mStringCurItem;
112};
113
114NS_IMPL_ISUPPORTS2(BaseStringEnumerator, nsISimpleEnumerator, nsIUTF8StringEnumerator)
115
116NS_IMETHODIMP
117BaseStringEnumerator::HasMoreElements(PRBool *_retval)
118{
119  *_retval = (mSimpleCurItem < mCount);
120
121  return NS_OK;
122}
123
124NS_IMETHODIMP
125BaseStringEnumerator::GetNext(nsISupports **_retval)
126{
127  if (mSimpleCurItem >= mCount)
128    return NS_ERROR_FAILURE;
129
130  nsSupportsDependentCString* str =
131    new nsSupportsDependentCString(mArray[mSimpleCurItem++]);
132  if (!str)
133    return NS_ERROR_OUT_OF_MEMORY;
134
135  *_retval = str;
136  NS_ADDREF(*_retval);
137  return NS_OK;
138}
139
140NS_IMETHODIMP
141BaseStringEnumerator::HasMore(PRBool *_retval)
142{
143  *_retval = (mStringCurItem < mCount);
144
145  return NS_OK;
146}
147
148NS_IMETHODIMP
149BaseStringEnumerator::GetNext(nsACString& _retval)
150{
151  if (mStringCurItem >= mCount)
152    return NS_ERROR_FAILURE;
153
154  _retval = nsDependentCString(mArray[mStringCurItem++]);
155  return NS_OK;
156}
157
158
159//
160// EntryEnumerator is the wrapper that allows nsICategoryManager::EnumerateCategory
161//
162class EntryEnumerator
163  : public BaseStringEnumerator
164{
165public:
166  static EntryEnumerator* Create(nsTHashtable<CategoryLeaf>& aTable);
167
168private:
169  static PLDHashOperator PR_CALLBACK
170    enumfunc_createenumerator(CategoryLeaf* aLeaf, void* userArg);
171};
172
173
174PLDHashOperator PR_CALLBACK
175EntryEnumerator::enumfunc_createenumerator(CategoryLeaf* aLeaf, void* userArg)
176{
177  EntryEnumerator* mythis = NS_STATIC_CAST(EntryEnumerator*, userArg);
178  mythis->mArray[mythis->mCount++] = aLeaf->GetKey();
179
180  return PL_DHASH_NEXT;
181}
182
183EntryEnumerator*
184EntryEnumerator::Create(nsTHashtable<CategoryLeaf>& aTable)
185{
186  EntryEnumerator* enumObj = new EntryEnumerator();
187  if (!enumObj)
188    return nsnull;
189
190  enumObj->mArray = new char const* [aTable.Count()];
191  if (!enumObj->mArray) {
192    delete enumObj;
193    return nsnull;
194  }
195
196  aTable.EnumerateEntries(enumfunc_createenumerator, enumObj);
197
198  return enumObj;
199}
200
201
202//
203// CategoryNode implementations
204//
205
206CategoryNode*
207CategoryNode::Create(PLArenaPool* aArena)
208{
209  CategoryNode* node = new(aArena) CategoryNode();
210  if (!node)
211    return nsnull;
212
213  if (!node->mTable.Init()) {
214    delete node;
215    return nsnull;
216  }
217
218  node->mLock = PR_NewLock();
219  if (!node->mLock) {
220    delete node;
221    return nsnull;
222  }
223
224  return node;
225}
226
227CategoryNode::~CategoryNode()
228{
229  if (mLock)
230    PR_DestroyLock(mLock);
231}
232
233void*
234CategoryNode::operator new(size_t aSize, PLArenaPool* aArena)
235{
236  void* p;
237  PL_ARENA_ALLOCATE(p, aArena, aSize);
238  return p;
239}
240
241NS_METHOD
242CategoryNode::GetLeaf(const char* aEntryName,
243                      char** _retval)
244{
245  PR_Lock(mLock);
246  nsresult rv = NS_ERROR_NOT_AVAILABLE;
247  CategoryLeaf* ent =
248    mTable.GetEntry(aEntryName);
249
250  // we only want the non-persistent value
251  if (ent && ent->nonpValue) {
252    *_retval = nsCRT::strdup(ent->nonpValue);
253    if (*_retval)
254      rv = NS_OK;
255  }
256  PR_Unlock(mLock);
257
258  return rv;
259}
260
261NS_METHOD
262CategoryNode::AddLeaf(const char* aEntryName,
263                      const char* aValue,
264                      PRBool aPersist,
265                      PRBool aReplace,
266                      char** _retval,
267                      PLArenaPool* aArena)
268{
269  PR_Lock(mLock);
270  CategoryLeaf* leaf =
271    mTable.GetEntry(aEntryName);
272
273  nsresult rv = NS_OK;
274  if (leaf) {
275    //if the entry was found, aReplace must be specified
276    if (!aReplace && (leaf->nonpValue || (aPersist && leaf->pValue )))
277      rv = NS_ERROR_INVALID_ARG;
278  } else {
279    const char* arenaEntryName = ArenaStrdup(aEntryName, aArena);
280    if (!arenaEntryName) {
281      rv = NS_ERROR_OUT_OF_MEMORY;
282    } else {
283      leaf = mTable.PutEntry(arenaEntryName);
284      if (!leaf)
285        rv = NS_ERROR_OUT_OF_MEMORY;
286    }
287  }
288
289  if (NS_SUCCEEDED(rv)) {
290    const char* arenaValue = ArenaStrdup(aValue, aArena);
291    if (!arenaValue) {
292      rv = NS_ERROR_OUT_OF_MEMORY;
293    } else {
294      leaf->nonpValue = arenaValue;
295      if (aPersist)
296        leaf->pValue = arenaValue;
297    }
298  }
299   
300  PR_Unlock(mLock);
301  return rv;
302}
303
304NS_METHOD
305CategoryNode::DeleteLeaf(const char* aEntryName,
306                         PRBool aDontPersist)
307{
308  // we don't throw any errors, because it normally doesn't matter
309  // and it makes JS a lot cleaner
310  PR_Lock(mLock);
311
312  if (aDontPersist) {
313    // we can just remove the entire hash entry without introspection
314    mTable.RemoveEntry(aEntryName);
315  } else {
316    // if we are keeping the persistent value, we need to look at
317    // the contents of the current entry
318    CategoryLeaf* leaf = mTable.GetEntry(aEntryName);
319    if (leaf) {
320      if (leaf->pValue) {
321        leaf->nonpValue = nsnull;
322      } else {
323        // if there is no persistent value, just remove the entry
324        mTable.RawRemoveEntry(leaf);
325      }
326    }
327  }
328  PR_Unlock(mLock);
329
330  return NS_OK;
331}
332
333NS_METHOD
334CategoryNode::Enumerate(nsISimpleEnumerator **_retval)
335{
336  NS_ENSURE_ARG_POINTER(_retval);
337
338  PR_Lock(mLock);
339  EntryEnumerator* enumObj = EntryEnumerator::Create(mTable);
340  PR_Unlock(mLock);
341
342  if (!enumObj)
343    return NS_ERROR_OUT_OF_MEMORY;
344
345  *_retval = enumObj;
346  NS_ADDREF(*_retval);
347  return NS_OK;
348}
349
350struct persistent_userstruct {
351  PRFileDesc* fd;
352  const char* categoryName;
353  PRBool      success;
354};
355
356PLDHashOperator PR_CALLBACK
357enumfunc_pentries(CategoryLeaf* aLeaf, void* userArg)
358{
359  persistent_userstruct* args =
360    NS_STATIC_CAST(persistent_userstruct*, userArg);
361
362  PLDHashOperator status = PL_DHASH_NEXT;
363
364  if (aLeaf->pValue) {
365    if (PR_fprintf(args->fd,
366                   "%s,%s,%s\n",
367                   args->categoryName,
368                   aLeaf->GetKey(),
369                   aLeaf->pValue) == (PRUint32) -1) {
370      args->success = PR_FALSE;
371      status = PL_DHASH_STOP;
372    }
373  }
374
375  return status;
376}
377
378PRBool
379CategoryNode::WritePersistentEntries(PRFileDesc* fd, const char* aCategoryName)
380{
381  persistent_userstruct args = {
382    fd,
383    aCategoryName,
384    PR_TRUE
385  };
386
387  PR_Lock(mLock);
388  mTable.EnumerateEntries(enumfunc_pentries, &args);
389  PR_Unlock(mLock);
390
391  return args.success;
392}
393
394
395//
396// CategoryEnumerator class
397//
398
399class CategoryEnumerator
400  : public BaseStringEnumerator
401{
402public:
403  static CategoryEnumerator* Create(nsClassHashtable<nsDepCharHashKey, CategoryNode>& aTable);
404
405private:
406  static PLDHashOperator PR_CALLBACK
407  enumfunc_createenumerator(const char* aStr,
408                            CategoryNode* aNode,
409                            void* userArg);
410};
411
412CategoryEnumerator*
413CategoryEnumerator::Create(nsClassHashtable<nsDepCharHashKey, CategoryNode>& aTable)
414{
415  CategoryEnumerator* enumObj = new CategoryEnumerator();
416  if (!enumObj)
417    return nsnull;
418
419  enumObj->mArray = new const char* [aTable.Count()];
420  if (!enumObj->mArray) {
421    delete enumObj;
422    return nsnull;
423  }
424
425  aTable.EnumerateRead(enumfunc_createenumerator, enumObj);
426
427  return enumObj;
428}
429
430PLDHashOperator PR_CALLBACK
431CategoryEnumerator::enumfunc_createenumerator(const char* aStr, CategoryNode* aNode, void* userArg)
432{
433  CategoryEnumerator* mythis = NS_STATIC_CAST(CategoryEnumerator*, userArg);
434
435  // if a category has no entries, we pretend it doesn't exist
436  if (aNode->Count())
437    mythis->mArray[mythis->mCount++] = aStr;
438
439  return PL_DHASH_NEXT;
440}
441
442
443//
444// nsCategoryManager implementations
445//
446
447NS_IMPL_THREADSAFE_ISUPPORTS1(nsCategoryManager, nsICategoryManager)
448
449nsCategoryManager*
450nsCategoryManager::Create()
451{
452  nsCategoryManager* manager = new nsCategoryManager();
453 
454  if (!manager)
455    return nsnull;
456
457  PL_INIT_ARENA_POOL(&(manager->mArena), "CategoryManagerArena",
458                     NS_CATEGORYMANAGER_ARENA_SIZE); // this never fails
459
460  if (!manager->mTable.Init()) {
461    delete manager;
462    return nsnull;
463  }
464
465  manager->mLock = PR_NewLock();
466
467  if (!manager->mLock) {
468    delete manager;
469    return nsnull;
470  }
471
472  return manager;
473}
474
475nsCategoryManager::~nsCategoryManager()
476{
477  if (mLock)
478    PR_DestroyLock(mLock);
479
480  // the hashtable contains entries that must be deleted before the arena is
481  // destroyed, or else you will have PRLocks undestroyed and other Really
482  // Bad Stuff (TM)
483  mTable.Clear();
484
485  PL_FinishArenaPool(&mArena);
486}
487
488inline CategoryNode*
489nsCategoryManager::get_category(const char* aName) {
490  CategoryNode* node;
491  if (!mTable.Get(aName, &node)) {
492    return nsnull;
493  }
494  return node;
495}
496
497NS_IMETHODIMP
498nsCategoryManager::GetCategoryEntry( const char *aCategoryName,
499                                     const char *aEntryName,
500                                     char **_retval )
501{
502  NS_ENSURE_ARG_POINTER(aCategoryName);
503  NS_ENSURE_ARG_POINTER(aEntryName);
504  NS_ENSURE_ARG_POINTER(_retval);
505
506  nsresult status = NS_ERROR_NOT_AVAILABLE;
507
508  PR_Lock(mLock);
509  CategoryNode* category = get_category(aCategoryName);
510  PR_Unlock(mLock);
511
512  if (category) {
513    status = category->GetLeaf(aEntryName, _retval);
514  }
515
516  return status;
517}
518
519NS_IMETHODIMP
520nsCategoryManager::AddCategoryEntry( const char *aCategoryName,
521                                     const char *aEntryName,
522                                     const char *aValue,
523                                     PRBool aPersist,
524                                     PRBool aReplace,
525                                     char **_retval )
526{
527  NS_ENSURE_ARG_POINTER(aCategoryName);
528  NS_ENSURE_ARG_POINTER(aEntryName);
529  NS_ENSURE_ARG_POINTER(aValue);
530
531  // Before we can insert a new entry, we'll need to
532  //  find the |CategoryNode| to put it in...
533  PR_Lock(mLock);
534  CategoryNode* category = get_category(aCategoryName);
535
536  if (!category) {
537    // That category doesn't exist yet; let's make it.
538    category = CategoryNode::Create(&mArena);
539       
540    char* categoryName = ArenaStrdup(aCategoryName, &mArena);
541    mTable.Put(categoryName, category);
542  }
543  PR_Unlock(mLock);
544
545  if (!category)
546    return NS_ERROR_OUT_OF_MEMORY;
547
548  return category->AddLeaf(aEntryName,
549                           aValue,
550                           aPersist,
551                           aReplace,
552                           _retval,
553                           &mArena);
554}
555
556NS_IMETHODIMP
557nsCategoryManager::DeleteCategoryEntry( const char *aCategoryName,
558                                        const char *aEntryName,
559                                        PRBool aDontPersist)
560{
561  NS_ENSURE_ARG_POINTER(aCategoryName);
562  NS_ENSURE_ARG_POINTER(aEntryName);
563
564  /*
565    Note: no errors are reported since failure to delete
566    probably won't hurt you, and returning errors seriously
567    inconveniences JS clients
568  */
569
570  PR_Lock(mLock);
571  CategoryNode* category = get_category(aCategoryName);
572  PR_Unlock(mLock);
573
574  if (!category)
575    return NS_OK;
576
577  return category->DeleteLeaf(aEntryName,
578                              aDontPersist);
579}
580
581NS_IMETHODIMP
582nsCategoryManager::DeleteCategory( const char *aCategoryName )
583{
584  NS_ENSURE_ARG_POINTER(aCategoryName);
585
586  // the categories are arena-allocated, so we don't
587  // actually delete them. We just remove all of the
588  // leaf nodes.
589
590  PR_Lock(mLock);
591  CategoryNode* category = get_category(aCategoryName);
592  PR_Unlock(mLock);
593
594  if (category)
595    category->Clear();
596
597  return NS_OK;
598}
599
600NS_IMETHODIMP
601nsCategoryManager::EnumerateCategory( const char *aCategoryName,
602                                      nsISimpleEnumerator **_retval )
603{
604  NS_ENSURE_ARG_POINTER(aCategoryName);
605  NS_ENSURE_ARG_POINTER(_retval);
606
607  PR_Lock(mLock);
608  CategoryNode* category = get_category(aCategoryName);
609  PR_Unlock(mLock);
610 
611  if (!category) {
612    return NS_NewEmptyEnumerator(_retval);
613  }
614
615  return category->Enumerate(_retval);
616}
617
618NS_IMETHODIMP
619nsCategoryManager::EnumerateCategories(nsISimpleEnumerator **_retval)
620{
621  NS_ENSURE_ARG_POINTER(_retval);
622
623  PR_Lock(mLock);
624  CategoryEnumerator* enumObj = CategoryEnumerator::Create(mTable);
625  PR_Unlock(mLock);
626
627  if (!enumObj)
628    return NS_ERROR_OUT_OF_MEMORY;
629
630  *_retval = enumObj;
631  NS_ADDREF(*_retval);
632  return NS_OK;
633}
634
635struct writecat_struct {
636  PRFileDesc* fd;
637  PRBool      success;
638};
639
640PLDHashOperator PR_CALLBACK
641enumfunc_categories(const char* aKey, CategoryNode* aCategory, void* userArg)
642{
643  writecat_struct* args = NS_STATIC_CAST(writecat_struct*, userArg);
644
645  PLDHashOperator result = PL_DHASH_NEXT;
646
647  if (!aCategory->WritePersistentEntries(args->fd, aKey)) {
648    args->success = PR_FALSE;
649    result = PL_DHASH_STOP;
650  }
651
652  return result;
653}
654
655NS_METHOD
656nsCategoryManager::WriteCategoryManagerToRegistry(PRFileDesc* fd)
657{
658  writecat_struct args = {
659    fd,
660    PR_TRUE
661  };
662
663  PR_Lock(mLock);
664  mTable.EnumerateRead(enumfunc_categories, &args);
665  PR_Unlock(mLock);
666
667  if (!args.success) {
668    return NS_ERROR_UNEXPECTED;
669  }
670
671  return NS_OK;
672}
673
674class nsCategoryManagerFactory : public nsIFactory
675   {
676     public:
677       nsCategoryManagerFactory() { }
678
679       NS_DECL_ISUPPORTS
680       NS_DECL_NSIFACTORY
681   };
682
683NS_IMPL_ISUPPORTS1(nsCategoryManagerFactory, nsIFactory)
684
685NS_IMETHODIMP
686nsCategoryManagerFactory::CreateInstance( nsISupports* aOuter, const nsIID& aIID, void** aResult )
687  {
688    NS_ENSURE_ARG_POINTER(aResult);
689
690    *aResult = 0;
691
692    nsresult status = NS_OK;
693    if ( aOuter )
694      status = NS_ERROR_NO_AGGREGATION;
695    else
696      {
697        nsCategoryManager* raw_category_manager = nsCategoryManager::Create();
698        nsCOMPtr<nsICategoryManager> new_category_manager = raw_category_manager;
699        if ( new_category_manager )
700              status = new_category_manager->QueryInterface(aIID, aResult);
701        else
702          status = NS_ERROR_OUT_OF_MEMORY;
703      }
704
705    return status;
706  }
707
708NS_IMETHODIMP
709nsCategoryManagerFactory::LockFactory( PRBool )
710  {
711      // Not implemented...
712    return NS_OK;
713  }
714
715nsresult
716NS_CategoryManagerGetFactory( nsIFactory** aFactory )
717  {
718    // assert(aFactory);
719
720    nsresult status;
721
722    *aFactory = 0;
723    nsIFactory* new_factory = NS_STATIC_CAST(nsIFactory*, new nsCategoryManagerFactory);
724    if (new_factory)
725      {
726        *aFactory = new_factory;
727        NS_ADDREF(*aFactory);
728        status = NS_OK;
729      }
730    else
731      status = NS_ERROR_OUT_OF_MEMORY;
732
733    return status;
734  }
735
736
737
738/*
739 * CreateServicesFromCategory()
740 *
741 * Given a category, this convenience functions enumerates the category and
742 * creates a service of every CID or ContractID registered under the category.
743 * If observerTopic is non null and the service implements nsIObserver,
744 * this will attempt to notify the observer with the origin, observerTopic string
745 * as parameter.
746 */
747NS_COM nsresult
748NS_CreateServicesFromCategory(const char *category,
749                              nsISupports *origin,
750                              const char *observerTopic)
751{
752    nsresult rv = NS_OK;
753   
754    int nFailed = 0;
755    nsCOMPtr<nsICategoryManager> categoryManager =
756        do_GetService("@mozilla.org/categorymanager;1", &rv);
757    if (!categoryManager) return rv;
758
759    nsCOMPtr<nsISimpleEnumerator> enumerator;
760    rv = categoryManager->EnumerateCategory(category,
761            getter_AddRefs(enumerator));
762    if (NS_FAILED(rv)) return rv;
763
764    nsCOMPtr<nsISupports> entry;
765    while (NS_SUCCEEDED(enumerator->GetNext(getter_AddRefs(entry)))) {
766        // From here on just skip any error we get.
767        nsCOMPtr<nsISupportsCString> catEntry = do_QueryInterface(entry, &rv);
768        if (NS_FAILED(rv)) {
769            nFailed++;
770            continue;
771        }
772        nsCAutoString entryString;
773        rv = catEntry->GetData(entryString);
774        if (NS_FAILED(rv)) {
775            nFailed++;
776            continue;
777        }
778        nsXPIDLCString contractID;
779        rv = categoryManager->GetCategoryEntry(category,entryString.get(), getter_Copies(contractID));
780        if (NS_FAILED(rv)) {
781            nFailed++;
782            continue;
783        }
784       
785        nsCOMPtr<nsISupports> instance = do_GetService(contractID, &rv);
786        if (NS_FAILED(rv)) {
787            nFailed++;
788            continue;
789        }
790
791        if (observerTopic) {
792            // try an observer, if it implements it.
793            nsCOMPtr<nsIObserver> observer = do_QueryInterface(instance, &rv);
794            if (NS_SUCCEEDED(rv) && observer)
795                observer->Observe(origin, observerTopic, EmptyString().get());
796        }
797    }
798    return (nFailed ? NS_ERROR_FAILURE : NS_OK);
799}
Note: See TracBrowser for help on using the repository browser.