diff options
Diffstat (limited to '')
-rw-r--r-- | src/libs/xpcom18a4/xpcom/components/nsComponentManager.cpp | 3794 |
1 files changed, 3794 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/xpcom/components/nsComponentManager.cpp b/src/libs/xpcom18a4/xpcom/components/nsComponentManager.cpp new file mode 100644 index 00000000..980b0565 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/components/nsComponentManager.cpp @@ -0,0 +1,3794 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** + * + * This Original Code has been modified by IBM Corporation. + * Modifications made by IBM described herein are + * Copyright (c) International Business Machines + * Corporation, 2000 + * + * Modifications to Mozilla code or documentation + * identified per MPL Section 3.3 + * + * Date Modified by Description of modification + * 04/20/2000 IBM Corp. Added PR_CALLBACK for Optlink use in OS2 + */ +#include <stdlib.h> +#include "nscore.h" +#include "nsISupports.h" +#include "nspr.h" +#include "nsCRT.h" // for atoll +// Arena used by component manager for storing contractid string, dll +// location strings and small objects +// CAUTION: Arena align mask needs to be defined before including plarena.h +// currently from nsComponentManager.h +#define PL_ARENA_CONST_ALIGN_MASK 7 +#define NS_CM_BLOCK_SIZE (1024 * 8) + +#include "nsAutoLock.h" +#include "nsCOMPtr.h" +#include "nsComponentManager.h" +#include "nsComponentManagerObsolete.h" +#include "nsDirectoryService.h" +#include "nsDirectoryServiceDefs.h" +#include "nsCategoryManager.h" +#include "nsCategoryManagerUtils.h" +#include "nsIComponentLoader.h" +#include "nsIEnumerator.h" +#include "nsIInterfaceInfoManager.h" +#include "nsIModule.h" +#include "nsIObserverService.h" +#include "nsISimpleEnumerator.h" +#include "nsXPCOM.h" +#include "nsISupportsPrimitives.h" +#include "nsLocalFile.h" +#include "nsNativeComponentLoader.h" +#include "nsReadableUtils.h" +#include "nsString.h" +#include "nsXPIDLString.h" +#include "prcmon.h" +#include "xptinfo.h" // this after nsISupports, to pick up IID so that xpt stuff doesn't try to define it itself... + +#include "nsInt64.h" +#include "nsManifestLineReader.h" + +#include NEW_H // for placement new + + +#ifdef XP_BEOS +#include <FindDirectory.h> +#include <Path.h> +#endif + +#include "prlog.h" + +PRLogModuleInfo* nsComponentManagerLog = nsnull; + +#if 0 || defined (DEBUG_timeless) + #define SHOW_DENIED_ON_SHUTDOWN + #define SHOW_CI_ON_EXISTING_SERVICE + #define XPCOM_CHECK_PENDING_CIDS +#endif + +// Loader Types +#define NS_LOADER_DATA_ALLOC_STEP 6 + +// Bloated registry buffer size to improve startup performance -- needs to +// be big enough to fit the entire file into memory or it'll thrash. +// 512K is big enough to allow for some future growth in the registry. +#define BIG_REGISTRY_BUFLEN (512*1024) + +// Common Key Names +const char classIDKeyName[]="classID"; +const char classesKeyName[]="contractID"; +const char componentLoadersKeyName[]="componentLoaders"; +const char componentsKeyName[]="components"; +const char xpcomComponentsKeyName[]="software/mozilla/XPCOM/components"; +const char xpcomKeyName[]="software/mozilla/XPCOM"; + +// Common Value Names +const char classIDValueName[]="ClassID"; +const char classNameValueName[]="ClassName"; +const char componentCountValueName[]="ComponentsCount"; +const char componentTypeValueName[]="ComponentType"; +const char contractIDValueName[]="ContractID"; +const char fileSizeValueName[]="FileSize"; +const char inprocServerValueName[]="InprocServer"; +const char lastModValueName[]="LastModTimeStamp"; +const char nativeComponentType[]="application/x-mozilla-native"; +const char staticComponentType[]="application/x-mozilla-static"; +const char versionValueName[]="VersionString"; + +const static char XPCOM_ABSCOMPONENT_PREFIX[] = "abs:"; +const static char XPCOM_RELCOMPONENT_PREFIX[] = "rel:"; +const static char XPCOM_GRECOMPONENT_PREFIX[] = "gre:"; + +static const char gIDFormat[] = + "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}"; + + +#define NS_EMPTY_IID \ +{ \ + 0x00000000, \ + 0x0000, \ + 0x0000, \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} \ +} + +NS_DEFINE_CID(kEmptyCID, NS_EMPTY_IID); +NS_DEFINE_CID(kCategoryManagerCID, NS_CATEGORYMANAGER_CID); + +#define UID_STRING_LENGTH 39 + +// Set to true from NS_ShutdownXPCOM. +extern PRBool gXPCOMShuttingDown; + +static void GetIDString(const nsID& aCID, char buf[UID_STRING_LENGTH]) +{ + PR_snprintf(buf, UID_STRING_LENGTH, gIDFormat, + aCID.m0, (PRUint32) aCID.m1, (PRUint32) aCID.m2, + (PRUint32) aCID.m3[0], (PRUint32) aCID.m3[1], + (PRUint32) aCID.m3[2], (PRUint32) aCID.m3[3], + (PRUint32) aCID.m3[4], (PRUint32) aCID.m3[5], + (PRUint32) aCID.m3[6], (PRUint32) aCID.m3[7]); +} + +nsresult +nsCreateInstanceFromCategory::operator()(const nsIID& aIID, void** aInstancePtr) const +{ + /* + * If I were a real man, I would consolidate this with + * nsGetServiceFromContractID::operator(). + */ + nsresult rv; + nsXPIDLCString value; + nsCOMPtr<nsIComponentManager> compMgr; + nsCOMPtr<nsICategoryManager> catman = + do_GetService(kCategoryManagerCID, &rv); + + if (NS_FAILED(rv)) goto error; + + if (!mCategory || !mEntry) { + // when categories have defaults, use that for null mEntry + rv = NS_ERROR_NULL_POINTER; + goto error; + } + + /* find the contractID for category.entry */ + rv = catman->GetCategoryEntry(mCategory, mEntry, + getter_Copies(value)); + if (NS_FAILED(rv)) goto error; + if (!value) { + rv = NS_ERROR_SERVICE_NOT_AVAILABLE; + goto error; + } + NS_GetComponentManager(getter_AddRefs(compMgr)); + if (!compMgr) + return NS_ERROR_FAILURE; + compMgr->CreateInstanceByContractID(value, + mOuter, + aIID, + aInstancePtr); + if (NS_FAILED(rv)) { + error: + *aInstancePtr = 0; + } + + *mErrorPtr = rv; + return rv; +} + + +nsresult +nsGetServiceFromCategory::operator()(const nsIID& aIID, void** aInstancePtr) const +{ + nsresult rv; + nsXPIDLCString value; + nsCOMPtr<nsICategoryManager> catman = + do_GetService(kCategoryManagerCID, &rv); + if (NS_FAILED(rv)) goto error; + if (!mCategory || !mEntry) { + // when categories have defaults, use that for null mEntry + rv = NS_ERROR_NULL_POINTER; + goto error; + } + /* find the contractID for category.entry */ + rv = catman->GetCategoryEntry(mCategory, mEntry, + getter_Copies(value)); + if (NS_FAILED(rv)) goto error; + if (!value) { + rv = NS_ERROR_SERVICE_NOT_AVAILABLE; + goto error; + } + if (mServiceManager) { + rv = mServiceManager->GetServiceByContractID(value, aIID, (void**)aInstancePtr); + } else { + nsCOMPtr<nsIServiceManager> mgr; + NS_GetServiceManager(getter_AddRefs(mgr)); + if (mgr) + rv = mgr->GetServiceByContractID(value, aIID, (void**)aInstancePtr); + } + if (NS_FAILED(rv)) { + error: + *aInstancePtr = 0; + } + *mErrorPtr = rv; + return rv; +} + +//////////////////////////////////////////////////////////////////////////////// +// Arena helper functions +//////////////////////////////////////////////////////////////////////////////// +char * +ArenaStrndup(const char *s, PRUint32 len, PLArenaPool *arena) +{ + void *mem; + // Include trailing null in the len + PL_ARENA_ALLOCATE(mem, arena, len+1); + if (mem) + memcpy(mem, s, len+1); + return NS_STATIC_CAST(char *, mem); +} + +char* +ArenaStrdup(const char *s, PLArenaPool *arena) +{ + return ArenaStrndup(s, strlen(s), arena); +} + +//////////////////////////////////////////////////////////////////////////////// +// Hashtable Callbacks +//////////////////////////////////////////////////////////////////////////////// + +PRBool PR_CALLBACK +nsFactoryEntry_Destroy(nsHashKey *aKey, void *aData, void* closure); + +PR_STATIC_CALLBACK(const void *) +factory_GetKey(PLDHashTable *aTable, PLDHashEntryHdr *aHdr) +{ + nsFactoryTableEntry* entry = NS_STATIC_CAST(nsFactoryTableEntry*, aHdr); + + return &entry->mFactoryEntry->mCid; +} + +PR_STATIC_CALLBACK(PLDHashNumber) +factory_HashKey(PLDHashTable *aTable, const void *aKey) +{ + const nsCID *cidp = NS_REINTERPRET_CAST(const nsCID*, aKey); + + return cidp->m0; +} + +PR_STATIC_CALLBACK(PRBool) +factory_MatchEntry(PLDHashTable *aTable, const PLDHashEntryHdr *aHdr, + const void *aKey) +{ + const nsFactoryTableEntry* entry = + NS_STATIC_CAST(const nsFactoryTableEntry*, aHdr); + const nsCID *cidp = NS_REINTERPRET_CAST(const nsCID*, aKey); + + return (entry->mFactoryEntry->mCid).Equals(*cidp); +} + +PR_STATIC_CALLBACK(void) +factory_ClearEntry(PLDHashTable *aTable, PLDHashEntryHdr *aHdr) +{ + nsFactoryTableEntry* entry = NS_STATIC_CAST(nsFactoryTableEntry*, aHdr); + // nsFactoryEntry is arena allocated. So we dont delete it. + // We call the destructor by hand. + entry->mFactoryEntry->~nsFactoryEntry(); + PL_DHashClearEntryStub(aTable, aHdr); +} + +static const PLDHashTableOps factory_DHashTableOps = { + PL_DHashAllocTable, + PL_DHashFreeTable, + factory_GetKey, + factory_HashKey, + factory_MatchEntry, + PL_DHashMoveEntryStub, + factory_ClearEntry, + PL_DHashFinalizeStub, +}; + +PR_STATIC_CALLBACK(void) +contractID_ClearEntry(PLDHashTable *aTable, PLDHashEntryHdr *aHdr) +{ + nsContractIDTableEntry* entry = NS_STATIC_CAST(nsContractIDTableEntry*, aHdr); + if (entry->mFactoryEntry->mTypeIndex == NS_COMPONENT_TYPE_SERVICE_ONLY && + entry->mFactoryEntry->mCid.Equals(kEmptyCID)) { + // this object is owned by the hash. + // nsFactoryEntry is arena allocated. So we dont delete it. + // We call the destructor by hand. + entry->mFactoryEntry->~nsFactoryEntry(); + } + + // contractIDs are arena allocated. No need to free them. + + PL_DHashClearEntryStub(aTable, aHdr); +} + +static const PLDHashTableOps contractID_DHashTableOps = { + PL_DHashAllocTable, + PL_DHashFreeTable, + PL_DHashGetKeyStub, + PL_DHashStringKey, + PL_DHashMatchStringKey, + PL_DHashMoveEntryStub, + contractID_ClearEntry, + PL_DHashFinalizeStub, +}; + +//////////////////////////////////////////////////////////////////////////////// +// nsFactoryEntry +//////////////////////////////////////////////////////////////////////////////// + +MOZ_DECL_CTOR_COUNTER(nsFactoryEntry) +nsFactoryEntry::nsFactoryEntry(const nsCID &aClass, + const char *aLocation, + PRUint32 locationlen, + int aType, + class nsFactoryEntry* parent) +: mCid(aClass), mTypeIndex(aType), mParent(parent) +{ + // Arena allocate the location string + mLocation = ArenaStrndup(aLocation, locationlen, &nsComponentManagerImpl::gComponentManager->mArena); +} + +nsFactoryEntry::nsFactoryEntry(const nsCID &aClass, + nsIFactory *aFactory, + class nsFactoryEntry* parent) +: mCid(aClass), mTypeIndex(NS_COMPONENT_TYPE_FACTORY_ONLY), mParent(parent) +{ + mFactory = aFactory; + mLocation = nsnull; +} + +// nsFactoryEntry is usually arena allocated including the strings it +// holds. So we call destructor by hand. +nsFactoryEntry::~nsFactoryEntry(void) +{ + // Release the reference to the factory + mFactory = nsnull; + + // Release any service reference + mServiceObject = nsnull; + + // nsFactoryEntry is arena allocated. So we dont delete it. + // We call the destructor by hand. + if (mParent) + mParent->~nsFactoryEntry(); +} + +nsresult +nsFactoryEntry::ReInit(const nsCID &aClass, const char *aLocation, int aType) +{ + NS_ENSURE_TRUE(mTypeIndex != NS_COMPONENT_TYPE_FACTORY_ONLY, NS_ERROR_INVALID_ARG); + // cid has to match + // SERVICE_ONLY entries can be promoted to an entry of another type + NS_ENSURE_TRUE((mTypeIndex == NS_COMPONENT_TYPE_SERVICE_ONLY || mCid.Equals(aClass)), + NS_ERROR_INVALID_ARG); + + // Arena allocate the location string + mLocation = ArenaStrdup(aLocation, &nsComponentManagerImpl::gComponentManager->mArena); + + mTypeIndex = aType; + return NS_OK; +} + +//////////////////////////////////////////////////////////////////////////////// +// Hashtable Enumeration +//////////////////////////////////////////////////////////////////////////////// +typedef NS_CALLBACK(EnumeratorConverter)(PLDHashTable *table, + const PLDHashEntryHdr *hdr, + void *data, + nsISupports **retval); + +class PLDHashTableEnumeratorImpl : public nsIBidirectionalEnumerator, + public nsISimpleEnumerator +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIENUMERATOR + NS_DECL_NSIBIDIRECTIONALENUMERATOR + NS_DECL_NSISIMPLEENUMERATOR + + PLDHashTableEnumeratorImpl(PLDHashTable *table, + EnumeratorConverter converter, + void *converterData); + PRInt32 Count() { return mCount; } +private: + PLDHashTableEnumeratorImpl(); /* no implementation */ + + ~PLDHashTableEnumeratorImpl(); + NS_IMETHODIMP ReleaseElements(); + + nsVoidArray mElements; + PRInt32 mCount, mCurrent; + PRMonitor* mMonitor; + + struct Closure { + PRBool succeeded; + EnumeratorConverter converter; + void *data; + PLDHashTableEnumeratorImpl *impl; + }; + + static PLDHashOperator PR_CALLBACK Enumerator(PLDHashTable *table, + PLDHashEntryHdr *hdr, + PRUint32 number, + void *data); +}; + +// static +PLDHashOperator PR_CALLBACK +PLDHashTableEnumeratorImpl::Enumerator(PLDHashTable *table, + PLDHashEntryHdr *hdr, + PRUint32 number, + void *data) +{ + Closure *c = NS_REINTERPRET_CAST(Closure *, data); + nsISupports *converted; + if (NS_FAILED(c->converter(table, hdr, c->data, &converted)) || + !c->impl->mElements.AppendElement(converted)) { + c->succeeded = PR_FALSE; + return PL_DHASH_STOP; + } + + c->succeeded = PR_TRUE; + return PL_DHASH_NEXT; +} + +PLDHashTableEnumeratorImpl::PLDHashTableEnumeratorImpl(PLDHashTable *table, + EnumeratorConverter converter, + void *converterData) +: mCurrent(0) +{ + mMonitor = nsAutoMonitor::NewMonitor("PLDHashTableEnumeratorImpl"); + NS_ASSERTION(mMonitor, "NULL Monitor"); + + nsAutoMonitor mon(mMonitor); + + Closure c = { PR_FALSE, converter, converterData, this }; + mCount = PL_DHashTableEnumerate(table, Enumerator, &c); + if (!c.succeeded) { + ReleaseElements(); + mCount = 0; + } +} + +NS_IMPL_ISUPPORTS3(PLDHashTableEnumeratorImpl, + nsIBidirectionalEnumerator, + nsIEnumerator, + nsISimpleEnumerator) + +PLDHashTableEnumeratorImpl::~PLDHashTableEnumeratorImpl() +{ + (void) ReleaseElements(); + + // Destroy the Lock + if (mMonitor) + nsAutoMonitor::DestroyMonitor(mMonitor); +} + +NS_IMETHODIMP +PLDHashTableEnumeratorImpl::ReleaseElements() +{ + for (PRInt32 i = 0; i < mCount; i++) { + nsISupports *supports = NS_REINTERPRET_CAST(nsISupports *, + mElements[i]); + NS_IF_RELEASE(supports); + } + return NS_OK; +} + +NS_IMETHODIMP +PL_NewDHashTableEnumerator(PLDHashTable *table, + EnumeratorConverter converter, + void *converterData, + PLDHashTableEnumeratorImpl **retval) +{ + PLDHashTableEnumeratorImpl *impl = + new PLDHashTableEnumeratorImpl(table, converter, converterData); + + if (!impl) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(impl); + + if (impl->Count() == -1) { + // conversion failed + NS_RELEASE(impl); + return NS_ERROR_FAILURE; + } + + *retval = impl; + return NS_OK; +} + +NS_IMETHODIMP +PLDHashTableEnumeratorImpl::First() +{ + if (!mCount) + return NS_ERROR_FAILURE; + + mCurrent = 0; + return NS_OK; +} + +NS_IMETHODIMP +PLDHashTableEnumeratorImpl::Last() +{ + if (!mCount) + return NS_ERROR_FAILURE; + mCurrent = mCount - 1; + return NS_OK; +} + +NS_IMETHODIMP +PLDHashTableEnumeratorImpl::Prev() +{ + if (!mCurrent) + return NS_ERROR_FAILURE; + + mCurrent--; + return NS_OK; +} + +NS_IMETHODIMP +PLDHashTableEnumeratorImpl::Next() +{ + // If empty or we're past the end, or we are at the end return error + if (!mCount || (mCurrent == mCount) || (++mCurrent == mCount)) + return NS_ERROR_FAILURE; + + return NS_OK; +} + +NS_IMETHODIMP +PLDHashTableEnumeratorImpl::CurrentItem(nsISupports **retval) +{ + if (!mCount || mCurrent == mCount) + return NS_ERROR_FAILURE; + + *retval = NS_REINTERPRET_CAST(nsISupports *, mElements[mCurrent]); + if (*retval) + NS_ADDREF(*retval); + + return NS_OK; +} + +NS_IMETHODIMP +PLDHashTableEnumeratorImpl::IsDone() +{ + if (!mCount || (mCurrent == mCount)) + return NS_OK; + + return NS_ENUMERATOR_FALSE; +} + +NS_IMETHODIMP +PLDHashTableEnumeratorImpl::HasMoreElements(PRBool *_retval) +{ + if (!mCount || (mCurrent == mCount)) + *_retval = PR_FALSE; + else + *_retval = PR_TRUE; + + return NS_OK; +} + +NS_IMETHODIMP +PLDHashTableEnumeratorImpl::GetNext(nsISupports **_retval) +{ + nsresult rv = Next(); + if (NS_FAILED(rv)) return rv; + + return CurrentItem(_retval); +} + +static NS_IMETHODIMP +ConvertFactoryEntryToCID(PLDHashTable *table, + const PLDHashEntryHdr *hdr, + void *data, nsISupports **retval) +{ + nsresult rv; + nsCOMPtr<nsISupportsID> wrapper; + + nsComponentManagerImpl *cm = NS_STATIC_CAST(nsComponentManagerImpl *, data); + + rv = cm->CreateInstanceByContractID(NS_SUPPORTS_ID_CONTRACTID, nsnull, + NS_GET_IID(nsISupportsID), getter_AddRefs(wrapper)); + + NS_ENSURE_SUCCESS(rv, rv); + + const nsFactoryTableEntry *entry = + NS_REINTERPRET_CAST(const nsFactoryTableEntry *, hdr); + if (entry) { + nsFactoryEntry *fe = entry->mFactoryEntry; + + wrapper->SetData(&fe->mCid); + *retval = wrapper; + NS_ADDREF(*retval); + return NS_OK; + } + *retval = nsnull; + + return rv; +} + +static NS_IMETHODIMP +ConvertContractIDKeyToString(PLDHashTable *table, + const PLDHashEntryHdr *hdr, + void *data, nsISupports **retval) +{ + nsresult rv; + nsCOMPtr<nsISupportsCString> wrapper; + + nsComponentManagerImpl *cm = NS_STATIC_CAST(nsComponentManagerImpl *, data); + + rv = cm->CreateInstanceByContractID(NS_SUPPORTS_CSTRING_CONTRACTID, nsnull, + NS_GET_IID(nsISupportsCString), getter_AddRefs(wrapper)); + + NS_ENSURE_SUCCESS(rv, rv); + + const nsContractIDTableEntry *entry = + NS_REINTERPRET_CAST(const nsContractIDTableEntry *, hdr); + + wrapper->SetData(nsDependentCString(entry->mContractID, + entry->mContractIDLen)); + *retval = wrapper; + NS_ADDREF(*retval); + return NS_OK; +} + +// this is safe to call during InitXPCOM +static nsresult GetLocationFromDirectoryService(const char* prop, + nsIFile** aDirectory) +{ + nsCOMPtr<nsIProperties> directoryService; + nsDirectoryService::Create(nsnull, + NS_GET_IID(nsIProperties), + getter_AddRefs(directoryService)); + + if (!directoryService) + return NS_ERROR_FAILURE; + + return directoryService->Get(prop, + NS_GET_IID(nsIFile), + (void**)aDirectory); +} + + +//////////////////////////////////////////////////////////////////////////////// +// nsComponentManagerImpl +//////////////////////////////////////////////////////////////////////////////// + + +nsComponentManagerImpl::nsComponentManagerImpl() + : + mMon(NULL), + mNativeComponentLoader(0), +#ifdef ENABLE_STATIC_COMPONENT_LOADER + mStaticComponentLoader(0), +#endif + mShuttingDown(NS_SHUTDOWN_NEVERHAPPENED), + mLoaderData(nsnull), + mRegistryDirty(PR_FALSE) +{ + mFactories.ops = nsnull; + mContractIDs.ops = nsnull; +} + +nsresult nsComponentManagerImpl::Init(void) +{ + PR_ASSERT(mShuttingDown != NS_SHUTDOWN_INPROGRESS); + if (mShuttingDown == NS_SHUTDOWN_INPROGRESS) + return NS_ERROR_FAILURE; + + mShuttingDown = NS_SHUTDOWN_NEVERHAPPENED; + + if (nsComponentManagerLog == nsnull) + { + nsComponentManagerLog = PR_NewLogModule("nsComponentManager"); + } + + // Initialize our arena + PL_INIT_ARENA_POOL(&mArena, "ComponentManagerArena", NS_CM_BLOCK_SIZE); + + if (!mFactories.ops) { + if (!PL_DHashTableInit(&mFactories, &factory_DHashTableOps, + 0, sizeof(nsFactoryTableEntry), + 1024)) { + mFactories.ops = nsnull; + return NS_ERROR_OUT_OF_MEMORY; + } + + // Minimum alpha uses k=2 because nsFactoryTableEntry saves two + // words compared to what a chained hash table requires. + PL_DHashTableSetAlphaBounds(&mFactories, + 0.875, + PL_DHASH_MIN_ALPHA(&mFactories, 2)); + } + + if (!mContractIDs.ops) { + if (!PL_DHashTableInit(&mContractIDs, &contractID_DHashTableOps, + 0, sizeof(nsContractIDTableEntry), + 1024)) { + mContractIDs.ops = nsnull; + return NS_ERROR_OUT_OF_MEMORY; + } + + // Minimum alpha uses k=1 because nsContractIDTableEntry saves one + // word compared to what a chained hash table requires. +#if 0 + PL_DHashTableSetAlphaBounds(&mContractIDs, + 0.875, + PL_DHASH_MIN_ALPHA(&mContractIDs, 1)); +#endif + } + if (mMon == nsnull) { + mMon = nsAutoMonitor::NewMonitor("nsComponentManagerImpl"); + if (mMon == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + } + + if (mNativeComponentLoader == nsnull) { + /* Create the NativeComponentLoader */ + mNativeComponentLoader = new nsNativeComponentLoader(); + if (!mNativeComponentLoader) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(mNativeComponentLoader); + + nsresult rv = mNativeComponentLoader->Init(this, nsnull); + if (NS_FAILED(rv)) + return rv; + } + + // Add predefined loaders + mLoaderData = (nsLoaderdata *) PR_Malloc(sizeof(nsLoaderdata) * NS_LOADER_DATA_ALLOC_STEP); + if (!mLoaderData) + return NS_ERROR_OUT_OF_MEMORY; + mMaxNLoaderData = NS_LOADER_DATA_ALLOC_STEP; + + mNLoaderData = NS_COMPONENT_TYPE_NATIVE; + mLoaderData[mNLoaderData].type = PL_strdup(nativeComponentType); + mLoaderData[mNLoaderData].loader = mNativeComponentLoader; + NS_ADDREF(mLoaderData[mNLoaderData].loader); + mNLoaderData++; + +#ifdef ENABLE_STATIC_COMPONENT_LOADER + if (mStaticComponentLoader == nsnull) { + extern nsresult NS_NewStaticComponentLoader(nsIComponentLoader **); + NS_NewStaticComponentLoader(&mStaticComponentLoader); + if (!mStaticComponentLoader) + return NS_ERROR_OUT_OF_MEMORY; + } + + mLoaderData[mNLoaderData].type = PL_strdup(staticComponentType); + mLoaderData[mNLoaderData].loader = mStaticComponentLoader; + NS_ADDREF(mLoaderData[mNLoaderData].loader); + mNLoaderData++; + + if (mStaticComponentLoader) { + /* Init the static loader */ + mStaticComponentLoader->Init(this, nsnull); + } +#endif + GetLocationFromDirectoryService(NS_XPCOM_COMPONENT_DIR, getter_AddRefs(mComponentsDir)); + if (!mComponentsDir) + return NS_ERROR_OUT_OF_MEMORY; + + nsCAutoString componentDescriptor; + nsresult rv = mComponentsDir->GetNativePath(componentDescriptor); + if (NS_FAILED(rv)) + return rv; + + mComponentsOffset = componentDescriptor.Length(); + + GetLocationFromDirectoryService(NS_GRE_COMPONENT_DIR, getter_AddRefs(mGREComponentsDir)); + if (mGREComponentsDir) { + nsresult rv = mGREComponentsDir->GetNativePath(componentDescriptor); + if (NS_FAILED(rv)) { + NS_WARNING("No GRE component manager"); + return rv; + } + mGREComponentsOffset = componentDescriptor.Length(); + } + + GetLocationFromDirectoryService(NS_XPCOM_COMPONENT_REGISTRY_FILE, + getter_AddRefs(mRegistryFile)); + + if(!mRegistryFile) { + NS_WARNING("No Component Registry file was found in the directory service"); + return NS_ERROR_FAILURE; + } + + PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG, + ("nsComponentManager: Initialized.")); + + return NS_OK; +} + +PRIntn PR_CALLBACK AutoRegEntryDestroy(nsHashKey *aKey, void *aData, void* aClosure) +{ + delete (AutoRegEntry*)aData; + return kHashEnumerateNext; +} + +nsresult nsComponentManagerImpl::Shutdown(void) +{ + PR_ASSERT(mShuttingDown == NS_SHUTDOWN_NEVERHAPPENED); + if (mShuttingDown != NS_SHUTDOWN_NEVERHAPPENED) + return NS_ERROR_FAILURE; + + mShuttingDown = NS_SHUTDOWN_INPROGRESS; + + // Shutdown the component manager + PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG, ("nsComponentManager: Beginning Shutdown.")); + + PRInt32 i; + + // Write out our component data file. + if (mRegistryDirty) { + nsresult rv = WritePersistentRegistry(); + if (NS_FAILED(rv)) { + PR_LOG(nsComponentManagerLog, PR_LOG_ERROR, ("nsComponentManager: Could not write out perisistant registry.")); +#ifdef DEBUG + printf("Could not write out perisistant registry!\n"); +#endif + } + } + + mAutoRegEntries.Reset(AutoRegEntryDestroy); + + // Release all cached factories + if (mContractIDs.ops) { + PL_DHashTableFinish(&mContractIDs); + mContractIDs.ops = nsnull; + } + if (mFactories.ops) { + PL_DHashTableFinish(&mFactories); + mFactories.ops = nsnull; + } + // Unload libraries + UnloadLibraries(nsnull, NS_Shutdown); + + // delete arena for strings and small objects + PL_FinishArenaPool(&mArena); + + mComponentsDir = 0; + + mCategoryManager = 0; + + // Release all the component data - loaders and type strings + for (i=0; i < mNLoaderData; i++) { + NS_IF_RELEASE(mLoaderData[i].loader); + PL_strfree((char *)mLoaderData[i].type); + } + PR_Free(mLoaderData); + mLoaderData = nsnull; + + // we have an extra reference on this one, which is probably a good thing + NS_IF_RELEASE(mNativeComponentLoader); +#ifdef ENABLE_STATIC_COMPONENT_LOADER + NS_IF_RELEASE(mStaticComponentLoader); +#endif + + mShuttingDown = NS_SHUTDOWN_COMPLETE; + + PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG, ("nsComponentManager: Shutdown complete.")); + + return NS_OK; +} + +nsComponentManagerImpl::~nsComponentManagerImpl() +{ + PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG, ("nsComponentManager: Beginning destruction.")); + + if (mShuttingDown != NS_SHUTDOWN_COMPLETE) + Shutdown(); + + if (mMon) { + nsAutoMonitor::DestroyMonitor(mMon); + } + PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG, ("nsComponentManager: Destroyed.")); +} + +NS_IMPL_THREADSAFE_ISUPPORTS8(nsComponentManagerImpl, + nsIComponentManager, + nsIServiceManager, + nsISupportsWeakReference, + nsIInterfaceRequestor, + nsIComponentRegistrar, + nsIServiceManagerObsolete, + nsIComponentManagerObsolete, + nsIComponentLoaderManager) + + +nsresult +nsComponentManagerImpl::GetInterface(const nsIID & uuid, void **result) +{ + if (uuid.Equals(NS_GET_IID(nsINativeComponentLoader))) + { + if (!mNativeComponentLoader) + return NS_ERROR_NOT_INITIALIZED; + + return mNativeComponentLoader->QueryInterface(uuid, result); + } + + NS_WARNING("This isn't supported"); + // fall through to QI as anything QIable is a superset of what can be + // got via the GetInterface() + return QueryInterface(uuid, result); +} + +//////////////////////////////////////////////////////////////////////////////// +// nsComponentManagerImpl: Platform methods +//////////////////////////////////////////////////////////////////////////////// + +#define PERSISTENT_REGISTRY_VERSION_MINOR 5 +#define PERSISTENT_REGISTRY_VERSION_MAJOR 0 + + +AutoRegEntry::AutoRegEntry(const nsACString& name, PRInt64* modDate) : + mName(ToNewCString(name)), + mNameLen(name.Length()), + mData(nsnull), + mModDate(*modDate) +{ +} + +AutoRegEntry::~AutoRegEntry() +{ + if (mName) PL_strfree(mName); + if (mData) PL_strfree(mData); +} + +PRBool +AutoRegEntry::Modified(PRInt64 *date) +{ + return !LL_EQ(*date, mModDate); +} + +void +AutoRegEntry::SetOptionalData(const char* data) +{ + if (mData) + PL_strfree(mData); + + if (!data) { + mData = nsnull; + return; + } + + mData = PL_strdup(data); +} + +static +PRBool ReadSectionHeader(nsManifestLineReader& reader, const char *token) +{ + while (1) + { + if (*reader.LinePtr() == '[') + { + char* p = reader.LinePtr() + (reader.LineLength() - 1); + if (*p != ']') + break; + *p = 0; + + char* values[1]; + int lengths[1]; + if (2 != reader.ParseLine(values, lengths, 1)) + break; + + // ignore the leading '[' + if (0 != PL_strcmp(values[0]+1, token)) + break; + + return PR_TRUE; + } + + if (!reader.NextLine()) + break; + } + return PR_FALSE; +} + +nsresult +nsComponentManagerImpl::ReadPersistentRegistry() +{ + + // populate Category Manager. need to get this early so that we don't get + // skipped by 'goto out' + nsresult rv = GetService(kCategoryManagerCID, + NS_GET_IID(nsICategoryManager), + getter_AddRefs(mCategoryManager)); + if (NS_FAILED(rv)) + return rv; + + nsAutoMonitor mon(mMon); + nsManifestLineReader reader; + + if (!mComponentsDir) + return NS_ERROR_NOT_INITIALIZED; // this should have been set by Init(). + + PRFileDesc* fd = nsnull; + + // Set From Init + if (!mRegistryFile) { + return NS_ERROR_FILE_NOT_FOUND; + } + + nsCOMPtr<nsIFile> file; + mRegistryFile->Clone(getter_AddRefs(file)); + if (!file) + return NS_ERROR_OUT_OF_MEMORY; + + nsCOMPtr<nsILocalFile> localFile(do_QueryInterface(file)); + + rv = localFile->OpenNSPRFileDesc(PR_RDONLY, 0444, &fd); + if (NS_FAILED(rv)) + return rv; + + PRInt64 fileSize; + rv = localFile->GetFileSize(&fileSize); + if (NS_FAILED(rv)) + { + PR_Close(fd); + return rv; + } + + PRInt32 flen = nsInt64(fileSize); + if (flen == 0) + { + PR_Close(fd); + NS_WARNING("Persistent Registry Empty!"); + return NS_OK; // ERROR CONDITION + } + + char* registry = new char[flen+1]; + if (!registry) + goto out; + + if (flen > PR_Read(fd, registry, flen)) + { + rv = NS_ERROR_FAILURE; + goto out; + } + registry[flen] = '\0'; + + reader.Init(registry, flen); + + if (ReadSectionHeader(reader, "HEADER")) + goto out; + + if (!reader.NextLine()) + goto out; + + char* values[6]; + int lengths[6]; + + // VersionLiteral,major,minor + if (3 != reader.ParseLine(values, lengths, 3)) + goto out; + + // VersionLiteral + if (!nsDependentCString(values[0], lengths[0]).EqualsLiteral("Version")) + goto out; + + // major + if (PERSISTENT_REGISTRY_VERSION_MAJOR != atoi(values[1])) + goto out; + + // minor + if (PERSISTENT_REGISTRY_VERSION_MINOR != atoi(values[2])) + goto out; + + if (ReadSectionHeader(reader, "COMPONENTS")) + goto out; + + while (1) + { + if (!reader.NextLine()) + break; + + //name,last_modification_date[,optionaldata] + int parts = reader.ParseLine(values, lengths, 3); + if (2 > parts) + break; + + PRInt64 a = nsCRT::atoll(values[1]); + AutoRegEntry *entry = + new AutoRegEntry(nsDependentCString(values[0], lengths[0]), &a); + + if (!entry) + return NS_ERROR_OUT_OF_MEMORY; + + if (parts == 3) + entry->SetOptionalData(values[2]); + + nsCStringKey key((const char*)values[0]); + mAutoRegEntries.Put(&key, entry); + } + + if (ReadSectionHeader(reader, "CLASSIDS")) + goto out; + + while (1) + { + if (!reader.NextLine()) + break; + + // cid,contract_id,type,class_name,inproc_server + if (5 != reader.ParseLine(values, lengths, 5)) + break; + + nsCID aClass; + if (!aClass.Parse(values[0])) + continue; + + int loadertype = GetLoaderType(values[2]); + if (loadertype < 0) { + rv = AddLoaderType(values[2], &loadertype); + if (NS_FAILED(rv)) + continue; + } + + void *mem; + PL_ARENA_ALLOCATE(mem, &mArena, sizeof(nsFactoryEntry)); + if (!mem) + return NS_ERROR_OUT_OF_MEMORY; + + nsFactoryEntry *entry = new (mem) nsFactoryEntry(aClass, values[4], lengths[4], loadertype); + + nsFactoryTableEntry* factoryTableEntry = + NS_STATIC_CAST(nsFactoryTableEntry*, + PL_DHashTableOperate(&mFactories, + &aClass, + PL_DHASH_ADD)); + + if (!factoryTableEntry) + return NS_ERROR_OUT_OF_MEMORY; + + factoryTableEntry->mFactoryEntry = entry; + + } + + if (ReadSectionHeader(reader, "CONTRACTIDS")) + goto out; + + while (1) + { + if (!reader.NextLine()) + break; + + //contractID,cid + if (2 != reader.ParseLine(values, lengths, 2)) + break; + + nsCID aClass; + if (!aClass.Parse(values[1])) + continue; + + + //need to find the location for this cid. + nsFactoryEntry *cidEntry = GetFactoryEntry(aClass); + if (!cidEntry || cidEntry->mTypeIndex < 0) + continue; //what should we really do? + + nsContractIDTableEntry* contractIDTableEntry = + NS_STATIC_CAST(nsContractIDTableEntry*, + PL_DHashTableOperate(&mContractIDs, + values[0], + PL_DHASH_ADD)); + if (!contractIDTableEntry) { + continue; + } + + if (!contractIDTableEntry->mContractID) { + contractIDTableEntry->mContractID = ArenaStrndup(values[0], lengths[0], &mArena); + contractIDTableEntry->mContractIDLen = lengths[0]; + } + + contractIDTableEntry->mFactoryEntry = cidEntry; + } + +#ifdef XPCOM_CHECK_PENDING_CIDS + { +/* + * If you get Asserts when you define SHOW_CI_ON_EXISTING_SERVICE and want to + * track down their cause, then you should add the contracts listed by the + * assertion to abusedContracts. The next time you run your xpcom app, xpcom + * will assert the first time the object associated with the contract is + * instantiated (which in many cases is the source of the problem). + * + * If you're doing this then you might want to NOP and soft breakpoint the + * lines labeled: NOP_AND_BREAK. + * + * Otherwise XPCOM will refuse to create the object for the caller, which + * while reasonable at some level, will almost certainly cause the app to + * stop functioning normally. + */ + static char abusedContracts[][128] = { + /*// Example contracts: + "@mozilla.org/rdf/container;1", + "@mozilla.org/intl/charsetalias;1", + "@mozilla.org/locale/win32-locale;1", + "@mozilla.org/widget/lookandfeel/win;1", + // */ + { 0 } + }; + for (int i=0; abusedContracts[i] && *abusedContracts[i]; i++) { + nsFactoryEntry *entry = nsnull; + nsContractIDTableEntry* contractIDTableEntry = + NS_STATIC_CAST(nsContractIDTableEntry*, + PL_DHashTableOperate(&mContractIDs, abusedContracts[i], + PL_DHASH_LOOKUP)); + + if (PL_DHASH_ENTRY_IS_BUSY(contractIDTableEntry)) { + entry = contractIDTableEntry->mFactoryEntry; + AddPendingCID(entry->mCid); + } + } + } +#endif + + if (ReadSectionHeader(reader, "CATEGORIES")) + goto out; + + while (1) + { + if (!reader.NextLine()) + break; + + //type,name,value + if (3 != reader.ParseLine(values, lengths, 3)) + break; + + mCategoryManager->AddCategoryEntry(values[0], + values[1], + values[2], + PR_TRUE, + PR_TRUE, + 0); + } + + mRegistryDirty = PR_FALSE; +out: + if (fd) + PR_Close(fd); + + if (registry) + delete [] registry; + + return rv; +} + +struct PersistentWriterArgs +{ + PRFileDesc *mFD; + nsLoaderdata *mLoaderData; +}; + +PR_STATIC_CALLBACK(PLDHashOperator) +ContractIDWriter(PLDHashTable *table, + PLDHashEntryHdr *hdr, + PRUint32 number, + void *arg) +{ + char *contractID = ((nsContractIDTableEntry*)hdr)->mContractID; + nsFactoryEntry *factoryEntry = ((nsContractIDTableEntry*)hdr)->mFactoryEntry; + + // for now, we only save out the top most parent. + while (factoryEntry->mParent) + factoryEntry = factoryEntry->mParent; + + if (factoryEntry->mTypeIndex < 0) + return PL_DHASH_NEXT; + + PRFileDesc* fd = ((PersistentWriterArgs*)arg)->mFD; + + char cidString[UID_STRING_LENGTH]; + GetIDString(factoryEntry->mCid, cidString); + PR_fprintf(fd, "%s,%s\n", contractID, cidString); // what if this fails? + return PL_DHASH_NEXT; +} + +PR_STATIC_CALLBACK(PLDHashOperator) +ClassIDWriter(PLDHashTable *table, + PLDHashEntryHdr *hdr, + PRUint32 number, + void *arg) +{ + nsFactoryEntry *factoryEntry = ((nsFactoryTableEntry*)hdr)->mFactoryEntry; + PRFileDesc* fd = ((PersistentWriterArgs*)arg)->mFD; + nsLoaderdata *loaderData = ((PersistentWriterArgs*)arg)->mLoaderData; + + // for now, we only save out the top most parent. + while (factoryEntry->mParent) + factoryEntry = factoryEntry->mParent; + + if (factoryEntry->mTypeIndex < 0) { + return PL_DHASH_NEXT; + } + + char cidString[UID_STRING_LENGTH]; + GetIDString(factoryEntry->mCid, cidString); + + char *contractID = nsnull, *className = nsnull; + + nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(factoryEntry->mFactory); + if (classInfo) + { + classInfo->GetContractID(&contractID); + classInfo->GetClassDescription(&className); + } + + const char * loaderName = nsnull; + if (factoryEntry->mTypeIndex) + loaderName = loaderData[factoryEntry->mTypeIndex].type; + + char* location = factoryEntry->mLocation; + + // cid,contract_id,type,class_name,inproc_server + PR_fprintf(fd, + "%s,%s,%s,%s,%s\n", + cidString, + (contractID ? contractID : ""), + (loaderName ? loaderName : ""), + (className ? className : ""), + (location ? location : "")); + + if (contractID) + PR_Free(contractID); + if (className) + PR_Free(className); + + return PL_DHASH_NEXT; +} + +PRIntn PR_CALLBACK +AutoRegEntryWriter(nsHashKey *aKey, void *aData, void* aClosure) +{ + PRFileDesc* fd = (PRFileDesc*) aClosure; + AutoRegEntry* entry = (AutoRegEntry*) aData; + + const char* extraData = entry->GetOptionalData(); + const char *fmt; + if (extraData) + fmt = "%s,%lld,%s\n"; + else + fmt = "%s,%lld\n"; + PR_fprintf(fd, fmt, entry->GetName().get(), entry->GetDate(), extraData); + + return PR_TRUE; +} + +nsresult +nsComponentManagerImpl::WritePersistentRegistry() +{ + if (!mRegistryFile) + return NS_ERROR_FAILURE; // this should have been set by Init(). + + nsCOMPtr<nsIFile> file; + mRegistryFile->Clone(getter_AddRefs(file)); + if (!file) + return NS_ERROR_OUT_OF_MEMORY; + + nsCOMPtr<nsILocalFile> localFile(do_QueryInterface(file)); + + nsCAutoString originalLeafName; + localFile->GetNativeLeafName(originalLeafName); + + nsCAutoString leafName; + leafName.Assign(originalLeafName + NS_LITERAL_CSTRING(".tmp")); + + localFile->SetNativeLeafName(leafName); + + PRFileDesc* fd = nsnull; + nsresult rv = localFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd); + if (NS_FAILED(rv)) + return rv; + + if (PR_fprintf(fd, "Generated File. Do not edit.\n") == (PRUint32) -1) { + rv = NS_ERROR_UNEXPECTED; + goto out; + } + + if (PR_fprintf(fd, "\n[HEADER]\nVersion,%d,%d\n", + PERSISTENT_REGISTRY_VERSION_MAJOR, + PERSISTENT_REGISTRY_VERSION_MINOR) == (PRUint32) -1) { + rv = NS_ERROR_UNEXPECTED; + goto out; + } + + if (PR_fprintf(fd, "\n[COMPONENTS]\n") == (PRUint32) -1) { + rv = NS_ERROR_UNEXPECTED; + goto out; + } + + mAutoRegEntries.Enumerate(AutoRegEntryWriter, (void*)fd); + + PersistentWriterArgs args; + args.mFD = fd; + args.mLoaderData = mLoaderData; + + if (PR_fprintf(fd, "\n[CLASSIDS]\n") == (PRUint32) -1) { + rv = NS_ERROR_UNEXPECTED; + goto out; + } + + + PL_DHashTableEnumerate(&mFactories, ClassIDWriter, (void*)&args); + + if (PR_fprintf(fd, "\n[CONTRACTIDS]\n") == (PRUint32) -1) { + rv = NS_ERROR_UNEXPECTED; + goto out; + } + + + PL_DHashTableEnumerate(&mContractIDs, ContractIDWriter, (void*)&args); + + if (PR_fprintf(fd, "\n[CATEGORIES]\n") == (PRUint32) -1) { + rv = NS_ERROR_UNEXPECTED; + goto out; + } + + + if (!mCategoryManager) { + NS_WARNING("Could not access category manager. Will not be able to save categories!"); + rv = NS_ERROR_UNEXPECTED; + } else { + rv = mCategoryManager->WriteCategoryManagerToRegistry(fd); + } + +out: + if (fd) + PR_Close(fd); + + // don't create the file is there was a problem???? + NS_ENSURE_SUCCESS(rv, rv); + + if (!mRegistryFile) + return NS_ERROR_NOT_INITIALIZED; + + PRBool exists; + if(NS_FAILED(mRegistryFile->Exists(&exists))) + return PR_FALSE; + + if(exists && NS_FAILED(mRegistryFile->Remove(PR_FALSE))) + return PR_FALSE; + + nsCOMPtr<nsIFile> parent; + mRegistryFile->GetParent(getter_AddRefs(parent)); + + rv = localFile->MoveToNative(parent, originalLeafName); + mRegistryDirty = PR_FALSE; + + return rv; +} + + +//////////////////////////////////////////////////////////////////////////////// +// Hash Functions +//////////////////////////////////////////////////////////////////////////////// +nsresult +nsComponentManagerImpl::HashContractID(const char *aContractID, + PRUint32 aContractIDLen, + nsFactoryEntry *fe) +{ + if(!aContractID || !aContractIDLen) + return NS_ERROR_NULL_POINTER; + + nsAutoMonitor mon(mMon); + + nsContractIDTableEntry* contractIDTableEntry = + NS_STATIC_CAST(nsContractIDTableEntry*, + PL_DHashTableOperate(&mContractIDs, aContractID, + PL_DHASH_ADD)); + if (!contractIDTableEntry) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ASSERTION(!contractIDTableEntry->mContractID || !strcmp(contractIDTableEntry->mContractID, aContractID), "contractid conflict"); + + if (!contractIDTableEntry->mContractID) { + contractIDTableEntry->mContractID = ArenaStrndup(aContractID, aContractIDLen, &mArena); + contractIDTableEntry->mContractIDLen = aContractIDLen; + } + + contractIDTableEntry->mFactoryEntry = fe; + + return NS_OK; +} + +/** + * LoadFactory() + * + * Given a FactoryEntry, this loads the dll if it has to, find the NSGetFactory + * symbol, calls the routine to create a new factory and returns it to the + * caller. + * + * No attempt is made to store the factory in any form anywhere. + */ +nsresult +nsComponentManagerImpl::LoadFactory(nsFactoryEntry *aEntry, + nsIFactory **aFactory) +{ + + if (!aFactory) + return NS_ERROR_NULL_POINTER; + *aFactory = nsnull; + + nsresult rv; + rv = aEntry->GetFactory(aFactory, this); + if (NS_FAILED(rv)) { + PR_LOG(nsComponentManagerLog, PR_LOG_ERROR, + ("nsComponentManager: FAILED to load factory from %s (%s)\n", + (const char *)aEntry->mLocation, mLoaderData[aEntry->mTypeIndex].type)); + return rv; + } + + return NS_OK; +} + +nsFactoryEntry * +nsComponentManagerImpl::GetFactoryEntry(const char *aContractID, + PRUint32 aContractIDLen) +{ + nsFactoryEntry *fe = nsnull; + { + nsAutoMonitor mon(mMon); + + nsContractIDTableEntry* contractIDTableEntry = + NS_STATIC_CAST(nsContractIDTableEntry*, + PL_DHashTableOperate(&mContractIDs, aContractID, + PL_DHASH_LOOKUP)); + + + if (PL_DHASH_ENTRY_IS_BUSY(contractIDTableEntry)) { + fe = contractIDTableEntry->mFactoryEntry; + } + } //exit monitor + + return fe; +} + + +nsFactoryEntry * +nsComponentManagerImpl::GetFactoryEntry(const nsCID &aClass) +{ + nsFactoryEntry *entry = nsnull; + { + nsAutoMonitor mon(mMon); + + nsFactoryTableEntry* factoryTableEntry = + NS_STATIC_CAST(nsFactoryTableEntry*, + PL_DHashTableOperate(&mFactories, &aClass, + PL_DHASH_LOOKUP)); + + if (PL_DHASH_ENTRY_IS_BUSY(factoryTableEntry)) { + entry = factoryTableEntry->mFactoryEntry; + } + } // exit monitor + + return entry; +} + + +/** + * FindFactory() + * + * Given a classID, this finds the factory for this CID by first searching the + * local CID<->factory mapping. Next it searches for a Dll that implements + * this classID and calls LoadFactory() to create the factory. + * + * Again, no attempt is made at storing the factory. + */ +nsresult +nsComponentManagerImpl::FindFactory(const nsCID &aClass, + nsIFactory **aFactory) +{ + PR_ASSERT(aFactory != nsnull); + + nsFactoryEntry *entry = GetFactoryEntry(aClass); + + if (!entry) + return NS_ERROR_FACTORY_NOT_REGISTERED; + + return entry->GetFactory(aFactory, this); +} + + +nsresult +nsComponentManagerImpl::FindFactory(const char *contractID, + PRUint32 aContractIDLen, + nsIFactory **aFactory) +{ + PR_ASSERT(aFactory != nsnull); + + nsFactoryEntry *entry = GetFactoryEntry(contractID, aContractIDLen); + + if (!entry) + return NS_ERROR_FACTORY_NOT_REGISTERED; + + return entry->GetFactory(aFactory, this); +} + +/** + * GetClassObject() + * + * Given a classID, this finds the singleton ClassObject that implements the CID. + * Returns an interface of type aIID off the singleton classobject. + */ +nsresult +nsComponentManagerImpl::GetClassObject(const nsCID &aClass, const nsIID &aIID, + void **aResult) +{ + nsresult rv; + + nsCOMPtr<nsIFactory> factory; + +#ifdef PR_LOGGING + if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_DEBUG)) + { + char *buf = aClass.ToString(); + PR_LogPrint("nsComponentManager: GetClassObject(%s)", buf); + if (buf) + PR_Free(buf); + } +#endif + + PR_ASSERT(aResult != nsnull); + + rv = FindFactory(aClass, getter_AddRefs(factory)); + if (NS_FAILED(rv)) return rv; + + rv = factory->QueryInterface(aIID, aResult); + + PR_LOG(nsComponentManagerLog, PR_LOG_WARNING, + ("\t\tGetClassObject() %s", NS_SUCCEEDED(rv) ? "succeeded" : "FAILED")); + + return rv; +} + + +nsresult +nsComponentManagerImpl::GetClassObjectByContractID(const char *contractID, + const nsIID &aIID, + void **aResult) +{ + nsresult rv; + + nsCOMPtr<nsIFactory> factory; + +#ifdef PR_LOGGING + if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_DEBUG)) + { + PR_LogPrint("nsComponentManager: GetClassObject(%s)", contractID); + } +#endif + + PR_ASSERT(aResult != nsnull); + + rv = FindFactory(contractID, strlen(contractID), getter_AddRefs(factory)); + if (NS_FAILED(rv)) return rv; + + rv = factory->QueryInterface(aIID, aResult); + + PR_LOG(nsComponentManagerLog, PR_LOG_WARNING, + ("\t\tGetClassObject() %s", NS_SUCCEEDED(rv) ? "succeeded" : "FAILED")); + + return rv; +} + +/** + * ContractIDToClassID() + * + * Mapping function from a ContractID to a classID. Directly talks to the registry. + * + */ +nsresult +nsComponentManagerImpl::ContractIDToClassID(const char *aContractID, nsCID *aClass) +{ + NS_PRECONDITION(aContractID != nsnull, "null ptr"); + if (!aContractID) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aClass != nsnull, "null ptr"); + if (!aClass) + return NS_ERROR_NULL_POINTER; + + nsresult rv = NS_ERROR_FACTORY_NOT_REGISTERED; + + nsFactoryEntry *fe = GetFactoryEntry(aContractID, strlen(aContractID)); + if (fe) { + *aClass = fe->mCid; + rv = NS_OK; + } +#ifdef PR_LOGGING + if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_WARNING)) { + char *buf = 0; + if (NS_SUCCEEDED(rv)) + buf = aClass->ToString(); + PR_LOG(nsComponentManagerLog, PR_LOG_WARNING, + ("nsComponentManager: ContractIDToClassID(%s)->%s", aContractID, + NS_SUCCEEDED(rv) ? buf : "[FAILED]")); + if (buf) + PR_Free(buf); + } +#endif + return rv; +} + +/** + * CLSIDToContractID() + * + * Translates a classID to a {ContractID, Class Name}. Does direct registry + * access to do the translation. + * + * NOTE: Since this isn't heavily used, we arent caching this. + */ +nsresult +nsComponentManagerImpl::CLSIDToContractID(const nsCID &aClass, + char* *aClassName, + char* *aContractID) +{ + NS_WARNING("Need to implement CLSIDToContractID"); + + nsresult rv = NS_ERROR_FACTORY_NOT_REGISTERED; +#ifdef PR_LOGGING + if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_WARNING)) + { + char *buf = aClass.ToString(); + PR_LOG(nsComponentManagerLog, PR_LOG_WARNING, + ("nsComponentManager: CLSIDToContractID(%s)->%s", buf, + NS_SUCCEEDED(rv) ? *aContractID : "[FAILED]")); + if (buf) + PR_Free(buf); + } +#endif + return rv; +} + +#ifdef XPCOM_CHECK_PENDING_CIDS + +// This method must be called from within the mMon monitor +nsresult +nsComponentManagerImpl::AddPendingCID(const nsCID &aClass) +{ + int max = mPendingCIDs.Count(); + for (int index = 0; index < max; index++) + { + nsCID *cidp = (nsCID*) mPendingCIDs.ElementAt(index); + NS_ASSERTION(cidp, "Bad CID in pending list"); + if (cidp->Equals(aClass)) { + nsXPIDLCString cid; + cid.Adopt(aClass.ToString()); + nsCAutoString message; + message = NS_LITERAL_CSTRING("Creation of \"") + + cid + NS_LITERAL_CSTRING("\" in progress (Reentrant GS - see bug 194568)"); + // Note that you may see this assertion by near-simultaneous + // calls to GetService on multiple threads. + NS_WARNING(message.get()); + return NS_ERROR_NOT_AVAILABLE; + } + } + mPendingCIDs.AppendElement((void*)&aClass); + return NS_OK; +} + +// This method must be called from within the mMon monitor +void +nsComponentManagerImpl::RemovePendingCID(const nsCID &aClass) +{ + mPendingCIDs.RemoveElement((void*)&aClass); +} +#endif +/** + * CreateInstance() + * + * Create an instance of an object that implements an interface and belongs + * to the implementation aClass using the factory. The factory is immediately + * released and not held onto for any longer. + */ +nsresult +nsComponentManagerImpl::CreateInstance(const nsCID &aClass, + nsISupports *aDelegate, + const nsIID &aIID, + void **aResult) +{ + // test this first, since there's no point in creating a component during + // shutdown -- whether it's available or not would depend on the order it + // occurs in the list + if (gXPCOMShuttingDown) { + // When processing shutdown, dont process new GetService() requests +#ifdef SHOW_DENIED_ON_SHUTDOWN + nsXPIDLCString cid, iid; + cid.Adopt(aClass.ToString()); + iid.Adopt(aIID.ToString()); + fprintf(stderr, "Creating new instance on shutdown. Denied.\n" + " CID: %s\n IID: %s\n", cid.get(), iid.get()); +#endif /* SHOW_DENIED_ON_SHUTDOWN */ + return NS_ERROR_UNEXPECTED; + } + + if (aResult == nsnull) + { + return NS_ERROR_NULL_POINTER; + } + *aResult = nsnull; + + nsFactoryEntry *entry = GetFactoryEntry(aClass); + + if (!entry) + return NS_ERROR_FACTORY_NOT_REGISTERED; + +#ifdef SHOW_CI_ON_EXISTING_SERVICE + if (entry->mServiceObject) { + nsXPIDLCString cid; + cid.Adopt(aClass.ToString()); + nsCAutoString message; + message = NS_LITERAL_CSTRING("You are calling CreateInstance \"") + + cid + NS_LITERAL_CSTRING("\" when a service for this CID already exists!"); + NS_ERROR(message.get()); + } +#endif + + nsIFactory *factory = nsnull; + nsresult rv = entry->GetFactory(&factory, this); + + if (NS_SUCCEEDED(rv)) + { + rv = factory->CreateInstance(aDelegate, aIID, aResult); + NS_RELEASE(factory); + } + else + { + // Translate error values + rv = NS_ERROR_FACTORY_NOT_REGISTERED; + } + +#ifdef PR_LOGGING + if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_WARNING)) + { + char *buf = aClass.ToString(); + PR_LOG(nsComponentManagerLog, PR_LOG_WARNING, + ("nsComponentManager: CreateInstance(%s) %s", buf, + NS_SUCCEEDED(rv) ? "succeeded" : "FAILED")); + if (buf) + PR_Free(buf); + } +#endif + + return rv; +} + +/** + * CreateInstanceByContractID() + * + * A variant of CreateInstance() that creates an instance of the object that + * implements the interface aIID and whose implementation has a contractID aContractID. + * + * This is only a convenience routine that turns around can calls the + * CreateInstance() with classid and iid. + */ +nsresult +nsComponentManagerImpl::CreateInstanceByContractID(const char *aContractID, + nsISupports *aDelegate, + const nsIID &aIID, + void **aResult) +{ + // test this first, since there's no point in creating a component during + // shutdown -- whether it's available or not would depend on the order it + // occurs in the list + if (gXPCOMShuttingDown) { + // When processing shutdown, dont process new GetService() requests +#ifdef SHOW_DENIED_ON_SHUTDOWN + nsXPIDLCString iid; + iid.Adopt(aIID.ToString()); + fprintf(stderr, "Creating new instance on shutdown. Denied.\n" + " ContractID: %s\n IID: %s\n", aContractID, iid.get()); +#endif /* SHOW_DENIED_ON_SHUTDOWN */ + return NS_ERROR_UNEXPECTED; + } + + if (aResult == nsnull) + { + return NS_ERROR_NULL_POINTER; + } + *aResult = nsnull; + + nsFactoryEntry *entry = GetFactoryEntry(aContractID, strlen(aContractID)); + + if (!entry) + return NS_ERROR_FACTORY_NOT_REGISTERED; + +#ifdef SHOW_CI_ON_EXISTING_SERVICE + if (entry->mServiceObject) { + nsCAutoString message; + message = + NS_LITERAL_CSTRING("You are calling CreateInstance \"") + + nsDependentCString(aContractID) + + NS_LITERAL_CSTRING("\" when a service for this CID already exists! " + "Add it to abusedContracts to track down the service consumer."); + NS_ERROR(message.get()); + } +#endif + + nsIFactory *factory = nsnull; + nsresult rv = entry->GetFactory(&factory, this); + + if (NS_SUCCEEDED(rv)) + { + + rv = factory->CreateInstance(aDelegate, aIID, aResult); + NS_RELEASE(factory); + } + else + { + // Translate error values + if (rv != NS_ERROR_SOCKET_FAIL) + rv = NS_ERROR_FACTORY_NOT_REGISTERED; + } + + PR_LOG(nsComponentManagerLog, PR_LOG_WARNING, + ("nsComponentManager: CreateInstanceByContractID(%s) %s", aContractID, + NS_SUCCEEDED(rv) ? "succeeded" : "FAILED")); + + return rv; +} + +// Service Manager Impl +static +PLDHashOperator PR_CALLBACK +FreeServiceFactoryEntryEnumerate(PLDHashTable *aTable, + PLDHashEntryHdr *aHdr, + PRUint32 aNumber, + void *aData) +{ + nsFactoryTableEntry* entry = NS_STATIC_CAST(nsFactoryTableEntry*, aHdr); + + if (!entry->mFactoryEntry) + return PL_DHASH_NEXT; + + nsFactoryEntry* factoryEntry = entry->mFactoryEntry; + factoryEntry->mServiceObject = nsnull; + return PL_DHASH_NEXT; +} + +static +PLDHashOperator PR_CALLBACK +FreeServiceContractIDEntryEnumerate(PLDHashTable *aTable, + PLDHashEntryHdr *aHdr, + PRUint32 aNumber, + void *aData) +{ + nsContractIDTableEntry* entry = NS_STATIC_CAST(nsContractIDTableEntry*, aHdr); + + if (!entry->mFactoryEntry) + return PL_DHASH_NEXT; + + nsFactoryEntry* factoryEntry = entry->mFactoryEntry; + factoryEntry->mServiceObject = nsnull; + return PL_DHASH_NEXT; +} + +nsresult +nsComponentManagerImpl::FreeServices() +{ + NS_ASSERTION(gXPCOMShuttingDown, "Must be shutting down in order to free all services"); + + if (!gXPCOMShuttingDown) + return NS_ERROR_FAILURE; + + if (mContractIDs.ops) { + PL_DHashTableEnumerate(&mContractIDs, FreeServiceContractIDEntryEnumerate, nsnull); + } + + + if (mFactories.ops) { + PL_DHashTableEnumerate(&mFactories, FreeServiceFactoryEntryEnumerate, nsnull); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::GetService(const nsCID& aClass, + const nsIID& aIID, + void* *result) +{ + // test this first, since there's no point in returning a service during + // shutdown -- whether it's available or not would depend on the order it + // occurs in the list + if (gXPCOMShuttingDown) { + // When processing shutdown, dont process new GetService() requests +#ifdef SHOW_DENIED_ON_SHUTDOWN + nsXPIDLCString cid, iid; + cid.Adopt(aClass.ToString()); + iid.Adopt(aIID.ToString()); + fprintf(stderr, "Getting service on shutdown. Denied.\n" + " CID: %s\n IID: %s\n", cid.get(), iid.get()); +#endif /* SHOW_DENIED_ON_SHUTDOWN */ + return NS_ERROR_UNEXPECTED; + } + + nsAutoMonitor mon(mMon); + + nsresult rv = NS_OK; + nsIDKey key(aClass); + nsFactoryEntry* entry = nsnull; + nsFactoryTableEntry* factoryTableEntry = + NS_STATIC_CAST(nsFactoryTableEntry*, + PL_DHashTableOperate(&mFactories, &aClass, + PL_DHASH_LOOKUP)); + + if (PL_DHASH_ENTRY_IS_BUSY(factoryTableEntry)) { + entry = factoryTableEntry->mFactoryEntry; + } + + if (entry && entry->mServiceObject) { + return entry->mServiceObject->QueryInterface(aIID, result); + } + +#ifdef XPCOM_CHECK_PENDING_CIDS + rv = AddPendingCID(aClass); + if (NS_FAILED(rv)) + return rv; // NOP_AND_BREAK +#endif + nsCOMPtr<nsISupports> service; + // We need to not be holding the service manager's monitor while calling + // CreateInstance, because it invokes user code which could try to re-enter + // the service manager: + mon.Exit(); + + rv = CreateInstance(aClass, nsnull, aIID, getter_AddRefs(service)); + + mon.Enter(); + +#ifdef XPCOM_CHECK_PENDING_CIDS + RemovePendingCID(aClass); +#endif + + if (NS_FAILED(rv)) + return rv; + + if (!entry) { // second hash lookup for GetService + nsFactoryTableEntry* factoryTableEntry = + NS_STATIC_CAST(nsFactoryTableEntry*, + PL_DHashTableOperate(&mFactories, &aClass, + PL_DHASH_LOOKUP)); + if (PL_DHASH_ENTRY_IS_BUSY(factoryTableEntry)) { + entry = factoryTableEntry->mFactoryEntry; + } + NS_ASSERTION(entry, "we should have a factory entry since CI succeeded - we should not get here"); + if (!entry) return NS_ERROR_FAILURE; + } + + entry->mServiceObject = service; + *result = service.get(); + NS_ADDREF(NS_STATIC_CAST(nsISupports*, (*result))); + return rv; +} + +NS_IMETHODIMP +nsComponentManagerImpl::RegisterService(const nsCID& aClass, nsISupports* aService) +{ + nsAutoMonitor mon(mMon); + + // check to see if we have a factory entry for the service + nsFactoryEntry *entry = GetFactoryEntry(aClass); + + if (!entry) { // XXXdougt - should we require that all services register factories?? probably not. + void *mem; + PL_ARENA_ALLOCATE(mem, &mArena, sizeof(nsFactoryEntry)); + if (!mem) + return NS_ERROR_OUT_OF_MEMORY; + entry = new (mem) nsFactoryEntry(aClass, nsnull); + + entry->mTypeIndex = NS_COMPONENT_TYPE_SERVICE_ONLY; + nsFactoryTableEntry* factoryTableEntry = + NS_STATIC_CAST(nsFactoryTableEntry*, + PL_DHashTableOperate(&mFactories, &aClass, + PL_DHASH_ADD)); + if (!factoryTableEntry) + return NS_ERROR_OUT_OF_MEMORY; + + factoryTableEntry->mFactoryEntry = entry; + } + else { + if (entry->mServiceObject) + return NS_ERROR_FAILURE; + } + + entry->mServiceObject = aService; + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::UnregisterService(const nsCID& aClass) +{ + nsresult rv = NS_OK; + + nsFactoryEntry* entry = nsnull; + + nsAutoMonitor mon(mMon); + + nsFactoryTableEntry* factoryTableEntry = + NS_STATIC_CAST(nsFactoryTableEntry*, + PL_DHashTableOperate(&mFactories, &aClass, + PL_DHASH_LOOKUP)); + + if (PL_DHASH_ENTRY_IS_BUSY(factoryTableEntry)) { + entry = factoryTableEntry->mFactoryEntry; + } + + if (!entry || !entry->mServiceObject) + return NS_ERROR_SERVICE_NOT_AVAILABLE; + + entry->mServiceObject = nsnull; + return rv; +} + +NS_IMETHODIMP +nsComponentManagerImpl::RegisterService(const char* aContractID, nsISupports* aService) +{ + + nsAutoMonitor mon(mMon); + + // check to see if we have a factory entry for the service + PRUint32 contractIDLen = strlen(aContractID); + nsFactoryEntry *entry = GetFactoryEntry(aContractID, contractIDLen); + + if (!entry) { // XXXdougt - should we require that all services register factories?? probably not. + void *mem; + PL_ARENA_ALLOCATE(mem, &mArena, sizeof(nsFactoryEntry)); + if (!mem) + return NS_ERROR_OUT_OF_MEMORY; + entry = new (mem) nsFactoryEntry(kEmptyCID, nsnull); + + entry->mTypeIndex = NS_COMPONENT_TYPE_SERVICE_ONLY; + + nsContractIDTableEntry* contractIDTableEntry = + NS_STATIC_CAST(nsContractIDTableEntry*, + PL_DHashTableOperate(&mContractIDs, aContractID, + PL_DHASH_ADD)); + if (!contractIDTableEntry) { + delete entry; + return NS_ERROR_OUT_OF_MEMORY; + } + + if (!contractIDTableEntry->mContractID) { + contractIDTableEntry->mContractID = + ArenaStrndup(aContractID, contractIDLen, &mArena); + + contractIDTableEntry->mContractIDLen = contractIDLen; + } + + contractIDTableEntry->mFactoryEntry = entry; + } + else { + if (entry->mServiceObject) + return NS_ERROR_FAILURE; + } + + entry->mServiceObject = aService; + return NS_OK; +} + + +NS_IMETHODIMP +nsComponentManagerImpl::IsServiceInstantiated(const nsCID & aClass, + const nsIID& aIID, + PRBool *result) +{ + // Now we want to get the service if we already got it. If not, we dont want + // to create an instance of it. mmh! + + // test this first, since there's no point in returning a service during + // shutdown -- whether it's available or not would depend on the order it + // occurs in the list + if (gXPCOMShuttingDown) { + // When processing shutdown, dont process new GetService() requests +#ifdef SHOW_DENIED_ON_SHUTDOWN + nsXPIDLCString cid, iid; + cid.Adopt(aClass.ToString()); + iid.Adopt(aIID.ToString()); + fprintf(stderr, "Checking for service on shutdown. Denied.\n" + " CID: %s\n IID: %s\n", cid.get(), iid.get()); +#endif /* SHOW_DENIED_ON_SHUTDOWN */ + return NS_ERROR_UNEXPECTED; + } + + nsresult rv = NS_ERROR_SERVICE_NOT_AVAILABLE; + nsFactoryEntry* entry = nsnull; + nsFactoryTableEntry* factoryTableEntry = + NS_STATIC_CAST(nsFactoryTableEntry*, + PL_DHashTableOperate(&mFactories, &aClass, + PL_DHASH_LOOKUP)); + + if (PL_DHASH_ENTRY_IS_BUSY(factoryTableEntry)) { + entry = factoryTableEntry->mFactoryEntry; + } + + if (entry && entry->mServiceObject) { + nsCOMPtr<nsISupports> service; + rv = entry->mServiceObject->QueryInterface(aIID, getter_AddRefs(service)); + *result = (service!=nsnull); + } + return rv; + +} + +NS_IMETHODIMP nsComponentManagerImpl::IsServiceInstantiatedByContractID(const char *aContractID, + const nsIID& aIID, + PRBool *result) +{ + // Now we want to get the service if we already got it. If not, we dont want + // to create an instance of it. mmh! + + // test this first, since there's no point in returning a service during + // shutdown -- whether it's available or not would depend on the order it + // occurs in the list + if (gXPCOMShuttingDown) { + // When processing shutdown, dont process new GetService() requests +#ifdef SHOW_DENIED_ON_SHUTDOWN + nsXPIDLCString iid; + iid.Adopt(aIID.ToString()); + fprintf(stderr, "Checking for service on shutdown. Denied.\n" + " ContractID: %s\n IID: %s\n", aContractID, iid.get()); +#endif /* SHOW_DENIED_ON_SHUTDOWN */ + return NS_ERROR_UNEXPECTED; + } + + nsresult rv = NS_ERROR_SERVICE_NOT_AVAILABLE; + nsFactoryEntry *entry = nsnull; + { + nsAutoMonitor mon(mMon); + + nsContractIDTableEntry* contractIDTableEntry = + NS_STATIC_CAST(nsContractIDTableEntry*, + PL_DHashTableOperate(&mContractIDs, aContractID, + PL_DHASH_LOOKUP)); + + if (PL_DHASH_ENTRY_IS_BUSY(contractIDTableEntry)) { + entry = contractIDTableEntry->mFactoryEntry; + } + } // exit monitor + + if (entry && entry->mServiceObject) { + nsCOMPtr<nsISupports> service; + rv = entry->mServiceObject->QueryInterface(aIID, getter_AddRefs(service)); + *result = (service!=nsnull); + } + return rv; +} + + +NS_IMETHODIMP +nsComponentManagerImpl::UnregisterService(const char* aContractID) +{ + nsresult rv = NS_OK; + + nsAutoMonitor mon(mMon); + + nsFactoryEntry *entry = nsnull; + nsContractIDTableEntry* contractIDTableEntry = + NS_STATIC_CAST(nsContractIDTableEntry*, + PL_DHashTableOperate(&mContractIDs, aContractID, + PL_DHASH_LOOKUP)); + + if (PL_DHASH_ENTRY_IS_BUSY(contractIDTableEntry)) { + entry = contractIDTableEntry->mFactoryEntry; + } + + if (!entry || !entry->mServiceObject) + return NS_ERROR_SERVICE_NOT_AVAILABLE; + + entry->mServiceObject = nsnull; + return rv; +} + +NS_IMETHODIMP +nsComponentManagerImpl::GetServiceByContractID(const char* aContractID, + const nsIID& aIID, + void* *result) +{ + // test this first, since there's no point in returning a service during + // shutdown -- whether it's available or not would depend on the order it + // occurs in the list + if (gXPCOMShuttingDown) { + // When processing shutdown, dont process new GetService() requests +#ifdef SHOW_DENIED_ON_SHUTDOWN + nsXPIDLCString iid; + iid.Adopt(aIID.ToString()); + fprintf(stderr, "Getting service on shutdown. Denied.\n" + " ContractID: %s\n IID: %s\n", aContractID, iid.get()); +#endif /* SHOW_DENIED_ON_SHUTDOWN */ + return NS_ERROR_UNEXPECTED; + } + + nsAutoMonitor mon(mMon); + + nsresult rv = NS_OK; + nsFactoryEntry *entry = nsnull; + nsContractIDTableEntry* contractIDTableEntry = + NS_STATIC_CAST(nsContractIDTableEntry*, + PL_DHashTableOperate(&mContractIDs, aContractID, + PL_DHASH_LOOKUP)); + + if (PL_DHASH_ENTRY_IS_BUSY(contractIDTableEntry)) { + entry = contractIDTableEntry->mFactoryEntry; + } + + if (entry) { + if (entry->mServiceObject) { + return entry->mServiceObject->QueryInterface(aIID, result); + } +#ifdef XPCOM_CHECK_PENDING_CIDS + rv = AddPendingCID(entry->mCid); + if (NS_FAILED(rv)) + return rv; // NOP_AND_BREAK +#endif + } + + nsCOMPtr<nsISupports> service; + // We need to not be holding the service manager's monitor while calling + // CreateInstance, because it invokes user code which could try to re-enter + // the service manager: + mon.Exit(); + + rv = CreateInstanceByContractID(aContractID, nsnull, aIID, getter_AddRefs(service)); + + mon.Enter(); + +#ifdef XPCOM_CHECK_PENDING_CIDS + if (entry) + RemovePendingCID(entry->mCid); +#endif + + if (NS_FAILED(rv)) + return rv; + + if (!entry) { // second hash lookup for GetService + nsContractIDTableEntry* contractIDTableEntry = + NS_STATIC_CAST(nsContractIDTableEntry*, + PL_DHashTableOperate(&mContractIDs, aContractID, + PL_DHASH_LOOKUP)); + + if (PL_DHASH_ENTRY_IS_BUSY(contractIDTableEntry)) { + entry = contractIDTableEntry->mFactoryEntry; + } + NS_ASSERTION(entry, "we should have a factory entry since CI succeeded - we should not get here"); + if (!entry) return NS_ERROR_FAILURE; + } + + entry->mServiceObject = service; + *result = service.get(); + NS_ADDREF(NS_STATIC_CAST(nsISupports*, *result)); + return rv; +} + +NS_IMETHODIMP +nsComponentManagerImpl::GetService(const nsCID& aClass, const nsIID& aIID, + nsISupports* *result, + nsIShutdownListener* shutdownListener) +{ + return GetService(aClass, aIID, (void**)result); +} + +NS_IMETHODIMP +nsComponentManagerImpl::GetService(const char* aContractID, const nsIID& aIID, + nsISupports* *result, + nsIShutdownListener* shutdownListener) +{ + return GetServiceByContractID(aContractID, aIID, (void**)result); +} + + +NS_IMETHODIMP +nsComponentManagerImpl::ReleaseService(const nsCID& aClass, nsISupports* service, + nsIShutdownListener* shutdownListener) +{ + NS_IF_RELEASE(service); + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::ReleaseService(const char* aContractID, nsISupports* service, + nsIShutdownListener* shutdownListener) +{ + NS_IF_RELEASE(service); + return NS_OK; +} + +/* + * I want an efficient way to allocate a buffer to the right size + * and stick the prefix and dllName in, then be able to hand that buffer + * off to the FactoryEntry. Is that so wrong? + * + * *regName is allocated on success. + * + * This should live in nsNativeComponentLoader.cpp, I think. + */ +static nsresult +MakeRegistryName(const char *aDllName, const char *prefix, char **regName) +{ + char *registryName; + + PRUint32 len = strlen(prefix); + + PRUint32 registryNameLen = strlen(aDllName) + len; + registryName = (char *)nsMemory::Alloc(registryNameLen + 1); + + // from here on it, we want len sans terminating NUL + + if (!registryName) + return NS_ERROR_OUT_OF_MEMORY; + + memcpy(registryName, prefix, len); + strcpy(registryName + len, aDllName); + registryName[registryNameLen] = '\0'; + *regName = registryName; + +#ifdef DEBUG_shaver_off + fprintf(stderr, "MakeRegistryName(%s, %s, &[%s])\n", + aDllName, prefix, *regName); +#endif + + return NS_OK; +} + +nsresult +nsComponentManagerImpl::RegistryLocationForSpec(nsIFile *aSpec, + char **aRegistryName) +{ + nsresult rv; + + if (!mComponentsDir) + return NS_ERROR_NOT_INITIALIZED; + + if (!aSpec) { + *aRegistryName = PL_strdup(""); + return NS_OK; + } + + + // First check to see if this component is in the application + // components directory + PRBool containedIn; + mComponentsDir->Contains(aSpec, PR_TRUE, &containedIn); + + nsCAutoString nativePathString; + + if (containedIn){ + rv = aSpec->GetNativePath(nativePathString); + if (NS_FAILED(rv)) + return rv; + + const char* relativeLocation = nativePathString.get() + mComponentsOffset + 1; + return MakeRegistryName(relativeLocation, XPCOM_RELCOMPONENT_PREFIX, aRegistryName); + } + + // Next check to see if this component is in the GRE + // components directory + + mGREComponentsDir->Contains(aSpec, PR_TRUE, &containedIn); + + if (containedIn){ + rv = aSpec->GetNativePath(nativePathString); + if (NS_FAILED(rv)) + return rv; + + const char* relativeLocation = nativePathString.get() + mGREComponentsOffset + 1; + return MakeRegistryName(relativeLocation, XPCOM_GRECOMPONENT_PREFIX, aRegistryName); + } + + /* absolute names include volume info on Mac, so persistent descriptor */ + rv = aSpec->GetNativePath(nativePathString); + if (NS_FAILED(rv)) + return rv; + return MakeRegistryName(nativePathString.get(), XPCOM_ABSCOMPONENT_PREFIX, aRegistryName); +} + +nsresult +nsComponentManagerImpl::SpecForRegistryLocation(const char *aLocation, + nsIFile **aSpec) +{ + // i18n: assuming aLocation is encoded for the current locale + + nsresult rv; + if (!aLocation || !aSpec) + return NS_ERROR_NULL_POINTER; + + /* abs:/full/path/to/libcomponent.so */ + if (!strncmp(aLocation, XPCOM_ABSCOMPONENT_PREFIX, 4)) { + + nsLocalFile* file = new nsLocalFile; + if (!file) return NS_ERROR_FAILURE; + + rv = file->InitWithNativePath(nsDependentCString((char *)aLocation + 4)); + file->QueryInterface(NS_GET_IID(nsILocalFile), (void**)aSpec); + return rv; + } + + if (!strncmp(aLocation, XPCOM_RELCOMPONENT_PREFIX, 4)) { + + if (!mComponentsDir) + return NS_ERROR_NOT_INITIALIZED; + + nsILocalFile* file = nsnull; + rv = mComponentsDir->Clone((nsIFile**)&file); + + if (NS_FAILED(rv)) return rv; + + rv = file->AppendRelativeNativePath(nsDependentCString(aLocation + 4)); + *aSpec = file; + return rv; + } + + if (!strncmp(aLocation, XPCOM_GRECOMPONENT_PREFIX, 4)) { + + if (!mGREComponentsDir) + return NS_ERROR_NOT_INITIALIZED; + + nsILocalFile* file = nsnull; + rv = mGREComponentsDir->Clone((nsIFile**)&file); + + if (NS_FAILED(rv)) return rv; + + rv = file->AppendRelativeNativePath(nsDependentCString(aLocation + 4)); + *aSpec = file; + return rv; + } + + *aSpec = nsnull; + return NS_ERROR_INVALID_ARG; +} + +/** + * RegisterFactory() + * + * Register a factory to be responsible for creation of implementation of + * classID aClass. Plus creates as association of aClassName and aContractID + * to the classID. If replace is PR_TRUE, we replace any existing registrations + * with this one. + * + * Once registration is complete, we add the class to the factories cache + * that we maintain. The factories cache is the ONLY place where these + * registrations are ever kept. + * + * The other RegisterFunctions create a loader mapping and persistent + * location, but we just slam it into the cache here. And we don't call the + * loader's OnRegister function, either. + */ +nsresult +nsComponentManagerImpl::RegisterFactory(const nsCID &aClass, + const char *aClassName, + const char *aContractID, + nsIFactory *aFactory, + PRBool aReplace) +{ + nsAutoMonitor mon(mMon); +#ifdef PR_LOGGING + if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_WARNING)) + { + char *buf = aClass.ToString(); + PR_LOG(nsComponentManagerLog, PR_LOG_WARNING, + ("nsComponentManager: RegisterFactory(%s, %s)", buf, + (aContractID ? aContractID : "(null)"))); + if (buf) + PR_Free(buf); + } +#endif + nsFactoryEntry *entry = nsnull; + nsFactoryTableEntry* factoryTableEntry = NS_STATIC_CAST(nsFactoryTableEntry*, + PL_DHashTableOperate(&mFactories, + &aClass, + PL_DHASH_ADD)); + + if (!factoryTableEntry) + return NS_ERROR_OUT_OF_MEMORY; + + + if (PL_DHASH_ENTRY_IS_BUSY(factoryTableEntry)) { + entry = factoryTableEntry->mFactoryEntry; + } + + if (entry && !aReplace) + { + // Already registered + PR_LOG(nsComponentManagerLog, PR_LOG_ERROR, + ("\t\tFactory already registered.")); + return NS_ERROR_FACTORY_EXISTS; + } + + void *mem; + PL_ARENA_ALLOCATE(mem, &mArena, sizeof(nsFactoryEntry)); + if (!mem) + return NS_ERROR_OUT_OF_MEMORY; + + entry = new (mem) nsFactoryEntry(aClass, aFactory, entry); + + if (!entry) + return NS_ERROR_OUT_OF_MEMORY; + + factoryTableEntry->mFactoryEntry = entry; + + // Update the ContractID->CLSID Map + if (aContractID) { + nsresult rv = HashContractID(aContractID, strlen(aContractID), entry); + if (NS_FAILED(rv)) { + PR_LOG(nsComponentManagerLog, PR_LOG_WARNING, + ("\t\tFactory register succeeded. " + "Hashing contractid (%s) FAILED.", aContractID)); + return rv; + } + } + + PR_LOG(nsComponentManagerLog, PR_LOG_WARNING, + ("\t\tFactory register succeeded contractid=%s.", + aContractID ? aContractID : "<none>")); + + return NS_OK; +} + +nsresult +nsComponentManagerImpl::RegisterComponent(const nsCID &aClass, + const char *aClassName, + const char *aContractID, + const char *aPersistentDescriptor, + PRBool aReplace, + PRBool aPersist) +{ + return RegisterComponentCommon(aClass, aClassName, + aContractID, + aContractID ? strlen(aContractID) : 0, + aPersistentDescriptor, + aPersistentDescriptor ? + strlen(aPersistentDescriptor) : 0, + aReplace, aPersist, + nativeComponentType); +} + +nsresult +nsComponentManagerImpl::RegisterComponentWithType(const nsCID &aClass, + const char *aClassName, + const char *aContractID, + nsIFile *aSpec, + const char *aLocation, + PRBool aReplace, + PRBool aPersist, + const char *aType) +{ + return RegisterComponentCommon(aClass, aClassName, + aContractID, + aContractID ? strlen(aContractID) : 0, + aLocation, + aLocation ? strlen(aLocation) : 0, + aReplace, aPersist, + aType); +} + +/* + * Register a component, using whatever they stuck in the nsIFile. + */ +nsresult +nsComponentManagerImpl::RegisterComponentSpec(const nsCID &aClass, + const char *aClassName, + const char *aContractID, + nsIFile *aLibrarySpec, + PRBool aReplace, + PRBool aPersist) +{ + nsXPIDLCString registryName; + nsresult rv = RegistryLocationForSpec(aLibrarySpec, getter_Copies(registryName)); + if (NS_FAILED(rv)) + return rv; + + rv = RegisterComponentWithType(aClass, aClassName, + aContractID, + aLibrarySpec, + registryName, + aReplace, aPersist, + nativeComponentType); + return rv; +} + +nsresult +nsComponentManagerImpl::RegisterComponentLib(const nsCID &aClass, + const char *aClassName, + const char *aContractID, + const char *aDllName, + PRBool aReplace, + PRBool aPersist) +{ + // deprecated and obsolete. + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* + * Add a component to the known universe of components. + + * Once we enter this function, we own aRegistryName, and must free it + * or hand it to nsFactoryEntry. Common exit point ``out'' helps keep us + * sane. + */ + +nsresult +nsComponentManagerImpl::RegisterComponentCommon(const nsCID &aClass, + const char *aClassName, + const char *aContractID, + PRUint32 aContractIDLen, + const char *aRegistryName, + PRUint32 aRegistryNameLen, + PRBool aReplace, + PRBool aPersist, + const char *aType) +{ + nsIDKey key(aClass); + nsAutoMonitor mon(mMon); + + nsFactoryEntry *entry = GetFactoryEntry(aClass); + + // Normalize proid and classname + const char *contractID = (aContractID && *aContractID) ? aContractID : nsnull; + const char *className = (aClassName && *aClassName) ? aClassName : nsnull; +#ifdef PR_LOGGING + if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_WARNING)) + { + char *buf = aClass.ToString(); + PR_LOG(nsComponentManagerLog, PR_LOG_WARNING, + ("nsComponentManager: RegisterComponentCommon(%s, %s, %s, %s)", + buf, + contractID ? contractID : "(null)", + aRegistryName, aType)); + if (buf) + PR_Free(buf); + } +#endif + if (entry && !aReplace) { + PR_LOG(nsComponentManagerLog, PR_LOG_ERROR, + ("\t\tFactory already registered.")); + return NS_ERROR_FACTORY_EXISTS; + } + + int typeIndex = GetLoaderType(aType); + + nsCOMPtr<nsIComponentLoader> loader; + nsresult rv = GetLoaderForType(typeIndex, getter_AddRefs(loader)); + if (NS_FAILED(rv)) { + PR_LOG(nsComponentManagerLog, PR_LOG_ERROR, + ("\t\tgetting loader for %s FAILED\n", aType)); + return rv; + } + + if (entry) { + entry->ReInit(aClass, aRegistryName, typeIndex); + } + else { + + // Arena allocate the nsFactoryEntry + void *mem; + PL_ARENA_ALLOCATE(mem, &mArena, sizeof(nsFactoryEntry)); + if (!mem) + return NS_ERROR_OUT_OF_MEMORY; + + mRegistryDirty = PR_TRUE; + entry = new (mem) nsFactoryEntry(aClass, + aRegistryName, aRegistryNameLen, + typeIndex); + if (!entry) + return NS_ERROR_OUT_OF_MEMORY; + + nsFactoryTableEntry* factoryTableEntry = + NS_STATIC_CAST(nsFactoryTableEntry*, + PL_DHashTableOperate(&mFactories, &aClass, + PL_DHASH_ADD)); + + if (!factoryTableEntry) + return NS_ERROR_OUT_OF_MEMORY; + + factoryTableEntry->mFactoryEntry = entry; + } + + // Update the ContractID->CLSID Map + if (contractID) { + rv = HashContractID(contractID, aContractIDLen, entry); + if (NS_FAILED(rv)) { + PR_LOG(nsComponentManagerLog, PR_LOG_ERROR, + ("\t\tHashContractID(%s) FAILED\n", contractID)); + return rv; + } + } + return rv; +} + + +nsresult +nsComponentManagerImpl::GetLoaderForType(int aType, + nsIComponentLoader **aLoader) +{ + nsresult rv; + + // Make sure we have a valid type + if (aType < 0 || aType >= mNLoaderData) + return NS_ERROR_INVALID_ARG; + + *aLoader = mLoaderData[aType].loader; + if (*aLoader) { + NS_ADDREF(*aLoader); + return NS_OK; + } + + nsCOMPtr<nsIComponentLoader> loader; + loader = do_GetServiceFromCategory("component-loader", mLoaderData[aType].type, &rv); + if (NS_FAILED(rv)) + return rv; + + rv = loader->Init(this, nsnull); + + if (NS_SUCCEEDED(rv)) { + mLoaderData[aType].loader = loader; + NS_ADDREF(mLoaderData[aType].loader); + *aLoader = loader; + NS_ADDREF(*aLoader); + } + return rv; +} + + + +// Convert a loader type string into an index into the component data +// array. Empty loader types are converted to NATIVE. Returns -1 if +// loader type cannot be determined. +int +nsComponentManagerImpl::GetLoaderType(const char *typeStr) +{ + if (!typeStr || !*typeStr) { + // Empty type strings are NATIVE + return NS_COMPONENT_TYPE_NATIVE; + } + + for (int i=NS_COMPONENT_TYPE_NATIVE; i<mNLoaderData; i++) { + if (!strcmp(typeStr, mLoaderData[i].type)) + return i; + } + // Not found + return NS_COMPONENT_TYPE_FACTORY_ONLY; +} + +// Add a loader type if not already known. Out the typeIndex +// if the loader type is either added or already there. +nsresult +nsComponentManagerImpl::AddLoaderType(const char *typeStr, int *aTypeIndex) +{ + int typeIndex = GetLoaderType(typeStr); + if (typeIndex >= 0) { + *aTypeIndex = typeIndex; + return NS_OK; + } + + // Add the loader type + if (mNLoaderData >= mMaxNLoaderData) { + NS_ASSERTION(mNLoaderData == mMaxNLoaderData, + "Memory corruption. nsComponentManagerImpl::mLoaderData array overrun."); + // Need to increase our loader array + nsLoaderdata *new_mLoaderData = (nsLoaderdata *) PR_Realloc(mLoaderData, (mMaxNLoaderData + NS_LOADER_DATA_ALLOC_STEP) * sizeof(nsLoaderdata)); + if (!new_mLoaderData) + return NS_ERROR_OUT_OF_MEMORY; + mLoaderData = new_mLoaderData; + mMaxNLoaderData += NS_LOADER_DATA_ALLOC_STEP; + } + + typeIndex = mNLoaderData; + mLoaderData[typeIndex].type = PL_strdup(typeStr); + if (!mLoaderData[typeIndex].type) { + // mmh! no memory. return failure. + return NS_ERROR_OUT_OF_MEMORY; + } + mLoaderData[typeIndex].loader = nsnull; + mNLoaderData++; + + *aTypeIndex = typeIndex; + return NS_OK; +} + +typedef struct +{ + const nsCID* cid; + const char* regName; + nsIFactory* factory; +} UnregisterConditions; + +static PLDHashOperator PR_CALLBACK +DeleteFoundCIDs(PLDHashTable *aTable, + PLDHashEntryHdr *aHdr, + PRUint32 aNumber, + void *aData) +{ + nsContractIDTableEntry* entry = NS_STATIC_CAST(nsContractIDTableEntry*, aHdr); + + if (!entry->mFactoryEntry) + return PL_DHASH_NEXT; + + UnregisterConditions* data = (UnregisterConditions*)aData; + + nsFactoryEntry* factoryEntry = entry->mFactoryEntry; + if (data->cid->Equals(factoryEntry->mCid) && + ((data->regName && !PL_strcasecmp(factoryEntry->mLocation, data->regName)) || + (data->factory && data->factory == factoryEntry->mFactory.get()))) + return PL_DHASH_REMOVE; + + return PL_DHASH_NEXT; +} + +void +nsComponentManagerImpl::DeleteContractIDEntriesByCID(const nsCID* aClass, const char*registryName) +{ + UnregisterConditions aData; + aData.cid = aClass; + aData.regName = registryName; + aData.factory = nsnull; + PL_DHashTableEnumerate(&mContractIDs, DeleteFoundCIDs, (void*)&aData); + +} + +void +nsComponentManagerImpl::DeleteContractIDEntriesByCID(const nsCID* aClass, nsIFactory* factory) +{ + UnregisterConditions aData; + aData.cid = aClass; + aData.regName = nsnull; + aData.factory = factory; + PL_DHashTableEnumerate(&mContractIDs, DeleteFoundCIDs, (void*)&aData); +} + +nsresult +nsComponentManagerImpl::UnregisterFactory(const nsCID &aClass, + nsIFactory *aFactory) +{ +#ifdef PR_LOGGING + if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_WARNING)) + { + char *buf = aClass.ToString(); + PR_LOG(nsComponentManagerLog, PR_LOG_WARNING, + ("nsComponentManager: UnregisterFactory(%s)", buf)); + if (buf) + PR_Free(buf); + } +#endif + nsFactoryEntry *old; + + // first delete all contract id entries that are registered with this cid. + DeleteContractIDEntriesByCID(&aClass, aFactory); + + // next check to see if there is a CID registered + nsresult rv = NS_ERROR_FACTORY_NOT_REGISTERED; + old = GetFactoryEntry(aClass); + + if (old && (old->mFactory.get() == aFactory)) + { + nsAutoMonitor mon(mMon); + PL_DHashTableOperate(&mFactories, &aClass, PL_DHASH_REMOVE); + rv = NS_OK; + } + + PR_LOG(nsComponentManagerLog, PR_LOG_WARNING, + ("\t\tUnregisterFactory() %s", + NS_SUCCEEDED(rv) ? "succeeded" : "FAILED")); + return rv; +} + +nsresult +nsComponentManagerImpl::UnregisterComponent(const nsCID &aClass, + const char *registryName) +{ +#ifdef PR_LOGGING + if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_WARNING)) + { + char *buf = aClass.ToString(); + PR_LOG(nsComponentManagerLog, PR_LOG_WARNING, + ("nsComponentManager: UnregisterComponent(%s)", buf)); + if (buf) + PR_Free(buf); + } +#endif + + NS_ENSURE_ARG_POINTER(registryName); + nsFactoryEntry *old; + + // first delete all contract id entries that are registered with this cid. + DeleteContractIDEntriesByCID(&aClass, registryName); + + // next check to see if there is a CID registered + old = GetFactoryEntry(aClass); + if (old && old->mLocation && !PL_strcasecmp(old->mLocation, registryName)) + { + nsAutoMonitor mon(mMon); + PL_DHashTableOperate(&mFactories, &aClass, PL_DHASH_REMOVE); + } + + PR_LOG(nsComponentManagerLog, PR_LOG_WARNING, + ("nsComponentManager: Factory unregister(%s) succeeded.", registryName)); + + return NS_OK; +} + +nsresult +nsComponentManagerImpl::UnregisterComponentSpec(const nsCID &aClass, + nsIFile *aLibrarySpec) +{ + nsXPIDLCString registryName; + nsresult rv = RegistryLocationForSpec(aLibrarySpec, getter_Copies(registryName)); + if (NS_FAILED(rv)) return rv; + return UnregisterComponent(aClass, registryName); +} + +// XXX Need to pass in aWhen and servicemanager +nsresult +nsComponentManagerImpl::FreeLibraries(void) +{ + return UnloadLibraries(NS_STATIC_CAST(nsIServiceManager*, this), NS_Timer); // XXX when +} + +// Private implementation of unloading libraries +nsresult +nsComponentManagerImpl::UnloadLibraries(nsIServiceManager *serviceMgr, PRInt32 aWhen) +{ + nsresult rv = NS_OK; + + nsAutoMonitor mon(mMon); + + PR_LOG(nsComponentManagerLog, PR_LOG_WARNING, + ("nsComponentManager: Unloading Libraries.")); + + // UnloadAll the loaders + /* iterate over all known loaders and ask them to autoregister. */ + // Skip mNativeComponentLoader + for (int i=NS_COMPONENT_TYPE_NATIVE + 1; i<mNLoaderData; i++) { + if (mLoaderData[i].loader) { + rv = mLoaderData[i].loader->UnloadAll(aWhen); + if (NS_FAILED(rv)) + break; + } + } + + // UnloadAll the native loader + rv = mNativeComponentLoader->UnloadAll(aWhen); + return rv; +} + +//////////////////////////////////////////////////////////////////////////////// + +/** + * AutoRegister(RegistrationInstant, const char *directory) + * + * Given a directory in the following format, this will ensure proper registration + * of all components. No default directory is looked at. + * + * Directory and fullname are what NSPR will accept. For eg. + * WIN y:/home/dp/mozilla/dist/bin + * UNIX /home/dp/mozilla/dist/bin + * MAC /Hard drive/mozilla/dist/apprunner + * + * This will take care not loading already registered dlls, finding and + * registering new dlls, re-registration of modified dlls + * + */ + +nsresult +nsComponentManagerImpl::AutoRegister(PRInt32 when, nsIFile *inDirSpec) +{ + return AutoRegisterImpl(when, inDirSpec); +} + +nsresult +nsComponentManagerImpl::AutoRegisterImpl(PRInt32 when, + nsIFile *inDirSpec, + PRBool fileIsCompDir) +{ + nsCOMPtr<nsIFile> dir; + nsresult rv; + +#ifdef DEBUG + // testing release behaviour + if (getenv("XPCOM_NO_AUTOREG")) + return NS_OK; +#endif + if (inDirSpec) + { + // Use supplied components' directory + dir = inDirSpec; + } + else + { + mComponentsDir->Clone(getter_AddRefs(dir)); + if (!dir) + return NS_ERROR_NOT_INITIALIZED; + } + + nsCOMPtr<nsIInterfaceInfoManager> iim = + dont_AddRef(XPTI_GetInterfaceInfoManager()); + + if (!iim) + return NS_ERROR_UNEXPECTED; + + // Notify observers of xpcom autoregistration start + NS_CreateServicesFromCategory(NS_XPCOM_AUTOREGISTRATION_OBSERVER_ID, + nsnull, + "start"); + + /* do the native loader first, so we can find other loaders */ + rv = mNativeComponentLoader->AutoRegisterComponents((PRInt32)when, dir); + if (NS_FAILED(rv)) return rv; + +#ifdef ENABLE_STATIC_COMPONENT_LOADER + rv = mStaticComponentLoader->AutoRegisterComponents((PRInt32)when, inDirSpec); + if (NS_FAILED(rv)) return rv; +#endif + + /* do InterfaceInfoManager after native loader so it can use components. */ + rv = iim->AutoRegisterInterfaces(); + if (NS_FAILED(rv)) return rv; + + if (!mCategoryManager) { + NS_WARNING("mCategoryManager is null"); + return NS_ERROR_UNEXPECTED; + } + + nsCOMPtr<nsISimpleEnumerator> loaderEnum; + rv = mCategoryManager->EnumerateCategory("component-loader", + getter_AddRefs(loaderEnum)); + if (NS_FAILED(rv)) return rv; + + PRBool hasMore; + while (NS_SUCCEEDED(loaderEnum->HasMoreElements(&hasMore)) && hasMore) { + nsCOMPtr<nsISupports> supports; + if (NS_FAILED(loaderEnum->GetNext(getter_AddRefs(supports)))) + continue; + + nsCOMPtr<nsISupportsCString> supStr = do_QueryInterface(supports); + if (!supStr) + continue; + + nsCAutoString loaderType; + if (NS_FAILED(supStr->GetData(loaderType))) + continue; + + // We depend on the loader being created. Add the loader type and + // create the loader object too. + nsCOMPtr<nsIComponentLoader> loader; + int typeIndex; + rv = AddLoaderType(loaderType.get(), &typeIndex); + if (NS_FAILED(rv)) + return rv; + GetLoaderForType(typeIndex, getter_AddRefs(loader)); + } + + rv = AutoRegisterNonNativeComponents(dir.get()); + + // Notify observers of xpcom autoregistration completion + NS_CreateServicesFromCategory(NS_XPCOM_AUTOREGISTRATION_OBSERVER_ID, + nsnull, + "end"); + + if (mRegistryDirty) + FlushPersistentStore(PR_TRUE); + return rv; +} + +nsresult +nsComponentManagerImpl::AutoRegisterNonNativeComponents(nsIFile* spec) +{ + nsresult rv = NS_OK; + nsCOMPtr<nsIFile> directory = spec; + + if (!directory) { + mComponentsDir->Clone(getter_AddRefs(directory)); + if (!directory) + return NS_ERROR_NOT_INITIALIZED; + } + + for (int i = 1; i < mNLoaderData; i++) { + if (!mLoaderData[i].loader) { + rv = GetLoaderForType(i, &mLoaderData[i].loader); + if (NS_FAILED(rv)) + continue; + } + rv = mLoaderData[i].loader->AutoRegisterComponents(0, directory); + if (NS_FAILED(rv)) + break; + } + + if (NS_SUCCEEDED(rv)) + { + PRBool registered; + do { + registered = PR_FALSE; + for (int i = 0; i < mNLoaderData; i++) { + PRBool b = PR_FALSE; + if (mLoaderData[i].loader) { + rv = mLoaderData[i].loader->RegisterDeferredComponents(0, &b); + if (NS_FAILED(rv)) + continue; + registered |= b; + } + } + } while (NS_SUCCEEDED(rv) && registered); + } + return rv; +} +nsresult +nsComponentManagerImpl::AutoRegisterComponent(PRInt32 when, + nsIFile *component) +{ + nsresult rv = NS_OK, res = NS_ERROR_FACTORY_NOT_REGISTERED; + /* + * Do we have to give the native loader first crack at it? + * I vote ``no''. + */ + for (int i = 0; i < mNLoaderData; i++) { + PRBool didRegister; + if (!mLoaderData[i].loader) { + nsCOMPtr<nsIComponentLoader> loader; + rv = GetLoaderForType(i, getter_AddRefs(loader)); + if (NS_FAILED(rv)) + continue; + // |GetLoaderForType| has filled in |mLoaderData[i].loader|: + NS_ASSERTION(loader == mLoaderData[i].loader, "oops"); + } + rv = mLoaderData[i].loader->AutoRegisterComponent((int)when, component, &didRegister); + if (NS_FAILED(rv)) { + res = rv; + } else if (didRegister) { + return rv; + } + } + return res; +} + +nsresult +nsComponentManagerImpl::AutoUnregisterComponent(PRInt32 when, + nsIFile *component) +{ + nsresult rv = NS_OK; + for (int i = 0; i < mNLoaderData; i++) { + PRBool didUnRegister; + if (!mLoaderData[i].loader) { + rv = GetLoaderForType(i, &mLoaderData[i].loader); + if (NS_FAILED(rv)) + continue; + } + rv = mLoaderData[i].loader->AutoUnregisterComponent(when, component, &didUnRegister); + if (NS_SUCCEEDED(rv) && didUnRegister) { + // we need to remove this file from our list of known libraries. + RemoveFileInfo(component, nsnull); + mRegistryDirty = PR_TRUE; + break; + } + } + return NS_FAILED(rv) ? NS_ERROR_FACTORY_NOT_REGISTERED : NS_OK; +} + +nsresult +nsComponentManagerImpl::IsRegistered(const nsCID &aClass, + PRBool *aRegistered) +{ + if (!aRegistered) + { + NS_ASSERTION(0, "null ptr"); + return NS_ERROR_NULL_POINTER; + } + *aRegistered = (nsnull != GetFactoryEntry(aClass)); + return NS_OK; +} + +nsresult +nsComponentManagerImpl::EnumerateCLSIDs(nsIEnumerator** aEnumerator) +{ + NS_ASSERTION(aEnumerator != nsnull, "null ptr"); + if (!aEnumerator) + { + return NS_ERROR_NULL_POINTER; + } + *aEnumerator = nsnull; + + nsresult rv; + + PLDHashTableEnumeratorImpl *aEnum; + rv = PL_NewDHashTableEnumerator(&mFactories, + ConvertFactoryEntryToCID, + (void*)this, + &aEnum); + if (NS_FAILED(rv)) + return rv; + + *aEnumerator = NS_STATIC_CAST(nsIEnumerator*, aEnum); + return NS_OK; +} + +nsresult +nsComponentManagerImpl::EnumerateContractIDs(nsIEnumerator** aEnumerator) +{ + NS_ASSERTION(aEnumerator != nsnull, "null ptr"); + if (!aEnumerator) + { + return NS_ERROR_NULL_POINTER; + } + + *aEnumerator = nsnull; + + nsresult rv; + PLDHashTableEnumeratorImpl *aEnum; + rv = PL_NewDHashTableEnumerator(&mContractIDs, + ConvertContractIDKeyToString, + (void*)this, + &aEnum); + if (NS_FAILED(rv)) + return rv; + + *aEnumerator = NS_STATIC_CAST(nsIEnumerator*, aEnum); + return NS_OK; +} + +// nsIComponentRegistrar + +NS_IMETHODIMP +nsComponentManagerImpl::AutoRegister(nsIFile *aSpec) +{ + if (aSpec == nsnull) + return AutoRegisterImpl(0, aSpec); + + PRBool directory; + aSpec->IsDirectory(&directory); + + if (directory) + return AutoRegisterImpl(0, aSpec, PR_FALSE); + + return AutoRegisterComponent(0, aSpec); +} + +NS_IMETHODIMP +nsComponentManagerImpl::AutoUnregister(nsIFile *aSpec) +{ + // unregistering a complete directory is not implmeneted yet...FIX + if (aSpec == nsnull) + return NS_ERROR_NOT_IMPLEMENTED; + + PRBool directory; + aSpec->IsDirectory(&directory); + + if (directory) + return NS_ERROR_NOT_IMPLEMENTED; + + return AutoUnregisterComponent(0, aSpec); +} + +NS_IMETHODIMP +nsComponentManagerImpl::RegisterFactory(const nsCID & aClass, + const char *aClassName, + const char *aContractID, + nsIFactory *aFactory) +{ + return RegisterFactory(aClass, + aClassName, + aContractID, + aFactory, + PR_TRUE); +} + +NS_IMETHODIMP +nsComponentManagerImpl::RegisterFactoryLocation(const nsCID & aClass, + const char *aClassName, + const char *aContractID, + nsIFile *aFile, + const char *loaderStr, + const char *aType) +{ + nsXPIDLCString registryName; + + if (!loaderStr) + { + nsresult rv = RegistryLocationForSpec(aFile, getter_Copies(registryName)); + if (NS_FAILED(rv)) + return rv; + } + + nsresult rv; + rv = RegisterComponentWithType(aClass, + aClassName, + aContractID, + aFile, + (loaderStr ? loaderStr : registryName.get()), + PR_TRUE, + PR_TRUE, + (aType ? aType : nativeComponentType)); + return rv; +} + +NS_IMETHODIMP +nsComponentManagerImpl::UnregisterFactoryLocation(const nsCID & aClass, + nsIFile *aFile) +{ + return UnregisterComponentSpec(aClass, aFile); +} + +NS_IMETHODIMP +nsComponentManagerImpl::IsCIDRegistered(const nsCID & aClass, + PRBool *_retval) +{ + return IsRegistered(aClass, _retval); +} + +NS_IMETHODIMP +nsComponentManagerImpl::IsContractIDRegistered(const char *aClass, + PRBool *_retval) +{ + nsFactoryEntry *entry = GetFactoryEntry(aClass, strlen(aClass)); + + if (entry) + *_retval = PR_TRUE; + else + *_retval = PR_FALSE; + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::EnumerateCIDs(nsISimpleEnumerator **aEnumerator) +{ + NS_ASSERTION(aEnumerator != nsnull, "null ptr"); + + if (!aEnumerator) + return NS_ERROR_NULL_POINTER; + + *aEnumerator = nsnull; + + nsresult rv; + PLDHashTableEnumeratorImpl *aEnum; + rv = PL_NewDHashTableEnumerator(&mFactories, + ConvertFactoryEntryToCID, + (void*)this, + &aEnum); + if (NS_FAILED(rv)) + return rv; + + *aEnumerator = NS_STATIC_CAST(nsISimpleEnumerator*, aEnum); + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::EnumerateContractIDs(nsISimpleEnumerator **aEnumerator) +{ + NS_ASSERTION(aEnumerator != nsnull, "null ptr"); + if (!aEnumerator) + return NS_ERROR_NULL_POINTER; + + *aEnumerator = nsnull; + + nsresult rv; + PLDHashTableEnumeratorImpl *aEnum; + rv = PL_NewDHashTableEnumerator(&mContractIDs, + ConvertContractIDKeyToString, + (void*)this, + &aEnum); + if (NS_FAILED(rv)) + return rv; + + *aEnumerator = NS_STATIC_CAST(nsISimpleEnumerator*, aEnum); + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::CIDToContractID(const nsCID & aClass, + char **_retval) +{ + return CLSIDToContractID(aClass, + nsnull, + _retval); +} + +NS_IMETHODIMP +nsComponentManagerImpl::ContractIDToCID(const char *aContractID, + nsCID * *_retval) +{ + *_retval = (nsCID*) nsMemory::Alloc(sizeof(nsCID)); + if (!*_retval) + return NS_ERROR_OUT_OF_MEMORY; + + nsresult rv = ContractIDToClassID(aContractID, *_retval); + if (NS_FAILED(rv)) { + nsMemory::Free(*_retval); + *_retval = nsnull; + } + return rv; +} + +// end nsIComponentRegistrar + + + + +NS_IMETHODIMP +nsComponentManagerImpl::HasFileChanged(nsIFile *file, const char *loaderString, PRInt64 modDate, PRBool *_retval) +{ + *_retval = PR_TRUE; + + nsXPIDLCString registryName; + nsresult rv = RegistryLocationForSpec(file, getter_Copies(registryName)); + if (NS_FAILED(rv)) + return rv; + + nsCStringKey key(registryName); + AutoRegEntry* entry = (AutoRegEntry*)mAutoRegEntries.Get(&key); + if (entry) + *_retval = entry->Modified(&modDate); + else + *_retval = PR_TRUE; + + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::SaveFileInfo(nsIFile *file, const char *loaderString, PRInt64 modDate) +{ + mRegistryDirty = PR_TRUE; + nsXPIDLCString registryName; + nsresult rv = RegistryLocationForSpec(file, getter_Copies(registryName)); + if (NS_FAILED(rv)) + return rv; + + // check to see if exists in the array before adding it so that we don't have dups. + nsCStringKey key(registryName); + AutoRegEntry* entry = (AutoRegEntry*)mAutoRegEntries.Get(&key); + + if (entry) + { + entry->SetDate(&modDate); + return NS_OK; + } + + entry = new AutoRegEntry(registryName, &modDate); + if (!entry) + return NS_ERROR_OUT_OF_MEMORY; + + mAutoRegEntries.Put(&key, entry); + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::RemoveFileInfo(nsIFile *file, const char *loaderString) +{ + mRegistryDirty = PR_TRUE; + nsXPIDLCString registryName; + nsresult rv = RegistryLocationForSpec(file, getter_Copies(registryName)); + if (NS_FAILED(rv)) + return rv; + + nsCStringKey key(registryName); + AutoRegEntry* entry = (AutoRegEntry*)mAutoRegEntries.Remove(&key); + if (entry) + delete entry; + + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::GetOptionalData(nsIFile *file, + const char *loaderString, + char **_retval) +{ + nsXPIDLCString registryName; + nsresult rv = RegistryLocationForSpec(file, getter_Copies(registryName)); + if (NS_FAILED(rv)) + return rv; + + nsCStringKey key(registryName); + AutoRegEntry* entry = (AutoRegEntry*)mAutoRegEntries.Get(&key); + if (!entry) { + return NS_ERROR_NOT_INITIALIZED; + } + const char* opData = entry->GetOptionalData(); + + if (opData) + *_retval = ToNewCString(nsDependentCString(opData)); + else + *_retval = nsnull; + return NS_OK; + } + +NS_IMETHODIMP +nsComponentManagerImpl::SetOptionalData(nsIFile *file, + const char *loaderString, + const char *data) +{ + nsXPIDLCString registryName; + nsresult rv = RegistryLocationForSpec(file, getter_Copies(registryName)); + if (NS_FAILED(rv)) + return rv; + + nsCStringKey key(registryName); + AutoRegEntry* entry = (AutoRegEntry*)mAutoRegEntries.Get(&key); + + if (!entry) { + PRInt64 zero = LL_Zero(); + entry = new AutoRegEntry(registryName, &zero); + if (!entry) + return NS_ERROR_OUT_OF_MEMORY; + + mAutoRegEntries.Put(&key, entry); + } + + entry->SetOptionalData(data); + + return NS_OK; + } + + +NS_IMETHODIMP +nsComponentManagerImpl::FlushPersistentStore(PRBool now) +{ + mRegistryDirty = PR_TRUE; + if (now) + return WritePersistentRegistry(); + + return NS_OK; +} + + +//////////////////////////////////////////////////////////////////////////////// +// Static Access Functions +//////////////////////////////////////////////////////////////////////////////// + +NS_COM nsresult +NS_GetGlobalComponentManager(nsIComponentManager* *result) +{ +#ifdef DEBUG_dougt + // NS_WARNING("DEPRECATED FUNCTION: Use NS_GetComponentManager"); +#endif + nsresult rv = NS_OK; + + if (nsComponentManagerImpl::gComponentManager == nsnull) + { + // XPCOM needs initialization. + rv = NS_InitXPCOM2(nsnull, nsnull, nsnull); + } + + if (NS_SUCCEEDED(rv)) + { + // NO ADDREF since this is never intended to be released. + // See nsComponentManagerObsolete.h for the reason for such + // casting uglyness + *result = (nsIComponentManager*)(void*)(nsIComponentManagerObsolete*) nsComponentManagerImpl::gComponentManager; + } + + return rv; +} + +NS_COM nsresult +NS_GetComponentManager(nsIComponentManager* *result) +{ + if (nsComponentManagerImpl::gComponentManager == nsnull) + { + // XPCOM needs initialization. + nsresult rv = NS_InitXPCOM2(nsnull, nsnull, nsnull); + if (NS_FAILED(rv)) + return rv; + } + + *result = NS_STATIC_CAST(nsIComponentManager*, + nsComponentManagerImpl::gComponentManager); + NS_IF_ADDREF(*result); + return NS_OK; +} + +NS_COM nsresult +NS_GetServiceManager(nsIServiceManager* *result) +{ + nsresult rv = NS_OK; + + if (nsComponentManagerImpl::gComponentManager == nsnull) + { +#ifdef VBOX + // While XPCOM might need initialization, we're not in a position + // to pass the right values to this call. This is actually triggered + // on object destruction, so there is no point in re-initializing, + // and actually the attempt would lead to nested calls to + // xptiInterfaceInfoManager::BuildFileSearchPath, which it detects + // as unsafe in debug builds. Just fail, no real problem. +#ifdef DEBUG + printf("NS_GetServiceManager: no current instance, suppressed XPCOM initialization!\n"); +#endif + rv = NS_ERROR_SERVICE_NOT_AVAILABLE; +#else /* !VBOX */ + // XPCOM needs initialization. + rv = NS_InitXPCOM2(nsnull, nsnull, nsnull); +#endif /* !VBOX */ + } + + if (NS_FAILED(rv)) + return rv; + + *result = NS_STATIC_CAST(nsIServiceManager*, + nsComponentManagerImpl::gComponentManager); + NS_IF_ADDREF(*result); + return NS_OK; +} + + +NS_COM nsresult +NS_GetComponentRegistrar(nsIComponentRegistrar* *result) +{ + nsresult rv = NS_OK; + + if (nsComponentManagerImpl::gComponentManager == nsnull) + { + // XPCOM needs initialization. + rv = NS_InitXPCOM2(nsnull, nsnull, nsnull); + } + + if (NS_FAILED(rv)) + return rv; + + *result = NS_STATIC_CAST(nsIComponentRegistrar*, + nsComponentManagerImpl::gComponentManager); + NS_IF_ADDREF(*result); + return NS_OK; +} + + +// nsIComponentLoaderManager is not frozen, but is defined here +// so that I can use it internally in xpcom. +nsresult +NS_GetComponentLoaderManager(nsIComponentLoaderManager* *result) +{ + nsresult rv = NS_OK; + + if (nsComponentManagerImpl::gComponentManager == NULL) + { + // XPCOM needs initialization. + rv = NS_InitXPCOM2(nsnull, nsnull, nsnull); + } + + if (NS_FAILED(rv)) + return rv; + + *result = NS_STATIC_CAST(nsIComponentLoaderManager*, + nsComponentManagerImpl::gComponentManager); + NS_IF_ADDREF(*result); + return NS_OK; +} |