Added a simple test to CreateInstance, similar to the existing test in GetService in the service manager, to prevent instances from being created during shutdown. We detected no calls to CreateInstance in normal code we tested during shutdown. If such occur, the fix is NOT to back out the check, but rather to eliminate the calls to CreateInstance either by registering a shutdown observer which gets called just before the shutdown, or creating the instance before shutdown. r=scc git-svn-id: svn://10.0.0.236/trunk@68281 18797224-902f-48f8-a5cc-f745e15eee43
2281 lines
69 KiB
C++
2281 lines
69 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* The contents of this file are subject to the Netscape 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/NPL/
|
|
*
|
|
* 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 Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Pierre Phaneuf <pp@ludusdesign.com>
|
|
*
|
|
* 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"
|
|
// this after nsISupports, to pick up IID
|
|
// so that xpt stuff doesn't try to define it itself...
|
|
#include "xptinfo.h"
|
|
#include "nsIInterfaceInfoManager.h"
|
|
|
|
#include "nsCOMPtr.h"
|
|
#include "nsComponentManager.h"
|
|
#include "nsIServiceManager.h"
|
|
#include "nsCRT.h"
|
|
#include "nsIEnumerator.h"
|
|
#include "nsIModule.h"
|
|
#include "nsHashtableEnumerator.h"
|
|
#include "nsISupportsPrimitives.h"
|
|
#include "nsIComponentLoader.h"
|
|
#include "nsNativeComponentLoader.h"
|
|
#include "nsXPIDLString.h"
|
|
|
|
#include "nsILocalFile.h"
|
|
#include "nsLocalFile.h"
|
|
#include "nsDirectoryService.h"
|
|
|
|
#include "plstr.h"
|
|
#include "prlink.h"
|
|
#include "prsystem.h"
|
|
#include "prprf.h"
|
|
#include "xcDll.h"
|
|
#include "prerror.h"
|
|
#include "prmem.h"
|
|
#include "nsIFile.h"
|
|
//#include "mozreg.h"
|
|
#include "NSReg.h"
|
|
|
|
#include "prcmon.h"
|
|
#include "prthread.h" /* XXX: only used for the NSPR initialization hack (rick) */
|
|
|
|
#ifdef XP_BEOS
|
|
#include <FindDirectory.h>
|
|
#include <Path.h>
|
|
#endif
|
|
|
|
// Logging of debug output
|
|
#define FORCE_PR_LOG /* Allow logging in the release build */
|
|
#include "prlog.h"
|
|
PRLogModuleInfo* nsComponentManagerLog = NULL;
|
|
|
|
// Enable printing of critical errors on screen even for release builds
|
|
#define PRINT_CRITICAL_ERROR_TO_SCREEN
|
|
|
|
// Common Key Names
|
|
const char xpcomKeyName[]="software/mozilla/XPCOM";
|
|
const char classesKeyName[]="progID";
|
|
const char classIDKeyName[]="classID";
|
|
const char componentsKeyName[]="components";
|
|
const char componentLoadersKeyName[]="componentLoaders";
|
|
const char xpcomComponentsKeyName[]="software/mozilla/XPCOM/components";
|
|
|
|
// Common Value Names
|
|
const char classIDValueName[]="ClassID";
|
|
const char versionValueName[]="VersionString";
|
|
const char lastModValueName[]="LastModTimeStamp";
|
|
const char fileSizeValueName[]="FileSize";
|
|
const char componentCountValueName[]="ComponentsCount";
|
|
const char progIDValueName[]="ProgID";
|
|
const char classNameValueName[]="ClassName";
|
|
const char inprocServerValueName[]="InprocServer";
|
|
const char componentTypeValueName[]="ComponentType";
|
|
const char nativeComponentType[]="application/x-mozilla-native";
|
|
|
|
const static char XPCOM_ABSCOMPONENT_PREFIX[] = "abs:";
|
|
const static char XPCOM_RELCOMPONENT_PREFIX[] = "rel:";
|
|
const char XPCOM_LIB_PREFIX[] = "lib:";
|
|
|
|
// We define a CID that is used to indicate the non-existence of a
|
|
// progid in the hash table.
|
|
#define NS_NO_CID { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } }
|
|
static NS_DEFINE_CID(kNoCID, NS_NO_CID);
|
|
|
|
// Build is using USE_NSREG to turn off xpcom using registry
|
|
// but internally we use USE_REGISTRY. Map them propertly.
|
|
#ifdef USE_NSREG
|
|
#define USE_REGISTRY
|
|
#endif /* USE_NSREG */
|
|
|
|
|
|
extern PRBool gShuttingDown;
|
|
nsresult
|
|
|
|
nsCreateInstanceByCID::operator()( const nsIID& aIID, void** aInstancePtr ) const
|
|
{
|
|
nsresult status = nsComponentManager::CreateInstance(mCID, mOuter, aIID, aInstancePtr);
|
|
if ( !NS_SUCCEEDED(status) )
|
|
*aInstancePtr = 0;
|
|
|
|
if ( mErrorPtr )
|
|
*mErrorPtr = status;
|
|
return status;
|
|
}
|
|
|
|
nsresult
|
|
nsCreateInstanceByProgID::operator()( const nsIID& aIID, void** aInstancePtr ) const
|
|
{
|
|
nsresult status;
|
|
if ( mProgID )
|
|
{
|
|
if ( !NS_SUCCEEDED(status = nsComponentManager::CreateInstance(mProgID, mOuter, aIID, aInstancePtr)) )
|
|
*aInstancePtr = 0;
|
|
}
|
|
else
|
|
status = NS_ERROR_NULL_POINTER;
|
|
|
|
if ( mErrorPtr )
|
|
*mErrorPtr = status;
|
|
return status;
|
|
}
|
|
|
|
/* prototypes for the Mac */
|
|
PRBool PR_CALLBACK
|
|
nsFactoryEntry_Destroy(nsHashKey *aKey, void *aData, void* closure);
|
|
|
|
PRBool PR_CALLBACK
|
|
nsCID_Destroy(nsHashKey *aKey, void *aData, void* closure);
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// nsFactoryEntry
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
nsFactoryEntry::nsFactoryEntry(const nsCID &aClass,
|
|
const char *aLocation,
|
|
const char *aType,
|
|
nsIComponentLoader *aLoader)
|
|
: cid(aClass), factory(nsnull), loader(aLoader)
|
|
{
|
|
loader = aLoader;
|
|
type = aType;
|
|
location = aLocation;
|
|
}
|
|
|
|
nsFactoryEntry::nsFactoryEntry(const nsCID &aClass, nsIFactory *aFactory)
|
|
: cid(aClass), loader(nsnull)
|
|
|
|
{
|
|
factory = aFactory;
|
|
}
|
|
|
|
nsFactoryEntry::~nsFactoryEntry(void)
|
|
{
|
|
factory = 0;
|
|
loader = 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// nsComponentManagerImpl
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
nsComponentManagerImpl::nsComponentManagerImpl()
|
|
: mFactories(NULL), mProgIDs(NULL), mLoaders(0), mMon(NULL),
|
|
mRegistry(NULL), mPrePopulationDone(PR_FALSE),
|
|
mNativeComponentLoader(0), mShuttingDown(NS_SHUTDOWN_NEVERHAPPENED)
|
|
{
|
|
NS_INIT_REFCNT();
|
|
}
|
|
|
|
PRBool
|
|
nsFactoryEntry_Destroy(nsHashKey *aKey, void *aData, void* closure)
|
|
{
|
|
nsFactoryEntry* entry = NS_STATIC_CAST(nsFactoryEntry*, aData);
|
|
delete entry;
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PRBool
|
|
nsCID_Destroy(nsHashKey *aKey, void *aData, void* closure)
|
|
{
|
|
nsCID* entry = NS_STATIC_CAST(nsCID*, aData);
|
|
// nasty hack. We "know" that kNoCID was entered into the hash table.
|
|
if (entry != &kNoCID)
|
|
delete entry;
|
|
return PR_TRUE;
|
|
}
|
|
|
|
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 == NULL)
|
|
{
|
|
nsComponentManagerLog = PR_NewLogModule("nsComponentManager");
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
|
|
("xpcom-log-version : " NS_XPCOM_COMPONENT_MANAGER_VERSION_STRING));
|
|
}
|
|
|
|
if (mFactories == NULL) {
|
|
mFactories = new nsObjectHashtable(nsnull, nsnull, // should never be copied
|
|
nsFactoryEntry_Destroy, nsnull,
|
|
256, /* Thread Safe */ PR_TRUE);
|
|
if (mFactories == NULL)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
if (mProgIDs == NULL) {
|
|
mProgIDs = new nsObjectHashtable(nsnull, nsnull, // should never be copied
|
|
nsCID_Destroy, nsnull,
|
|
256, /* Thread Safe */ PR_TRUE);
|
|
if (mProgIDs == NULL)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (mMon == NULL) {
|
|
mMon = PR_NewMonitor();
|
|
if (mMon == NULL)
|
|
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);
|
|
}
|
|
|
|
if (mLoaders == nsnull) {
|
|
mLoaders = new nsSupportsHashtable(16, /* Thread safe */ PR_TRUE);
|
|
if (mLoaders == nsnull)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
nsStringKey loaderKey(nativeComponentType);
|
|
mLoaders->Put(&loaderKey, mNativeComponentLoader);
|
|
}
|
|
|
|
#ifdef USE_REGISTRY
|
|
NR_StartupRegistry();
|
|
PlatformInit();
|
|
#endif
|
|
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
|
|
("nsComponentManager: Initialized."));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
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_ALWAYS, ("nsComponentManager: Beginning Shutdown."));
|
|
|
|
// Release all cached factories
|
|
if (mFactories)
|
|
delete mFactories;
|
|
|
|
// Unload libraries
|
|
UnloadLibraries(NULL, NS_Shutdown);
|
|
|
|
// Release Progid hash tables
|
|
if (mProgIDs)
|
|
delete mProgIDs;
|
|
|
|
#ifdef USE_REGISTRY
|
|
// Release registry
|
|
NS_IF_RELEASE(mRegistry);
|
|
#endif /* USE_REGISTRY */
|
|
|
|
// This is were the nsFileSpec was deleted, so I am
|
|
// going to assign zero to
|
|
mComponentsDir = 0;
|
|
|
|
// Release all the component loaders
|
|
if (mLoaders)
|
|
delete mLoaders;
|
|
|
|
// we have an extra reference on this one, which is probably a good thing
|
|
NS_IF_RELEASE(mNativeComponentLoader);
|
|
|
|
// Destroy the Lock
|
|
if (mMon)
|
|
PR_DestroyMonitor(mMon);
|
|
|
|
#ifdef USE_REGISTRY
|
|
NR_ShutdownRegistry();
|
|
#endif /* USE_REGISTRY */
|
|
|
|
mShuttingDown = NS_SHUTDOWN_COMPLETE;
|
|
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS, ("nsComponentManager: Shutdown complete."));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsComponentManagerImpl::~nsComponentManagerImpl()
|
|
{
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS, ("nsComponentManager: Beginning destruction."));
|
|
|
|
if (mShuttingDown != NS_SHUTDOWN_COMPLETE)
|
|
Shutdown();
|
|
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS, ("nsComponentManager: Destroyed."));
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS3(nsComponentManagerImpl, nsIComponentManager,
|
|
nsISupportsWeakReference, nsIInterfaceRequestor)
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// nsComponentManagerImpl: Platform methods
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifdef USE_REGISTRY
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::PlatformInit(void)
|
|
{
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
|
|
// We need to create our registry. Since we are in the constructor
|
|
// we haven't gone as far as registering the registry factory.
|
|
// Hence, we hand create a registry.
|
|
if (mRegistry == NULL) {
|
|
nsIFactory *registryFactory = NULL;
|
|
rv = NS_RegistryGetFactory(®istryFactory);
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
rv = registryFactory->CreateInstance(NULL, NS_GET_IID(nsIRegistry),(void **)&mRegistry);
|
|
if (NS_FAILED(rv)) return rv;
|
|
NS_RELEASE(registryFactory);
|
|
}
|
|
}
|
|
|
|
#ifdef XP_UNIX
|
|
// Create ~/.mozilla as that is the default place for the registry file
|
|
|
|
/* The default registry on the unix system is $HOME/.mozilla/registry per
|
|
* vr_findGlobalRegName(). vr_findRegFile() will create the registry file
|
|
* if it doesn't exist. But it wont create directories.
|
|
*
|
|
* Hence we need to create the directory if it doesn't exist already.
|
|
*
|
|
* Why create it here as opposed to the app ?
|
|
* ------------------------------------------
|
|
* The app cannot create the directory in main() as most of the registry
|
|
* and initialization happens due to use of static variables.
|
|
* And we dont want to be dependent on the order in which
|
|
* these static stuff happen.
|
|
*
|
|
* Permission for the $HOME/.mozilla will be Read,Write,Execute
|
|
* for user only. Nothing to group and others.
|
|
*/
|
|
char *home = getenv("HOME");
|
|
if (home != NULL)
|
|
{
|
|
char dotMozillaDir[1024];
|
|
PR_snprintf(dotMozillaDir, sizeof(dotMozillaDir),
|
|
"%s/" NS_MOZILLA_DIR_NAME, home);
|
|
if (PR_Access(dotMozillaDir, PR_ACCESS_EXISTS) != PR_SUCCESS)
|
|
{
|
|
PR_MkDir(dotMozillaDir, NS_MOZILLA_DIR_PERMISSION);
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
|
|
("nsComponentManager: Creating Directory %s", dotMozillaDir));
|
|
}
|
|
}
|
|
#endif /* XP_UNIX */
|
|
|
|
#ifdef XP_BEOS
|
|
BPath p;
|
|
const char *settings = "/boot/home/config/settings";
|
|
if(find_directory(B_USER_SETTINGS_DIRECTORY, &p) == B_OK)
|
|
settings = p.Path();
|
|
char settingsMozillaDir[1024];
|
|
PR_snprintf(settingsMozillaDir, sizeof(settingsMozillaDir),
|
|
"%s/" NS_MOZILLA_DIR_NAME, settings);
|
|
if (PR_Access(settingsMozillaDir, PR_ACCESS_EXISTS) != PR_SUCCESS) {
|
|
PR_MkDir(settingsMozillaDir, NS_MOZILLA_DIR_PERMISSION);
|
|
printf("nsComponentManager: Creating Directory %s\n", settingsMozillaDir);
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
|
|
("nsComponentManager: Creating Directory %s", settingsMozillaDir));
|
|
}
|
|
#endif
|
|
|
|
// Open the App Components registry. We will keep it open forever!
|
|
rv = mRegistry->OpenWellKnownRegistry(nsIRegistry::ApplicationComponentRegistry);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// Check the version of registry. Nuke old versions.
|
|
nsRegistryKey xpcomRoot;
|
|
rv = PlatformVersionCheck(&xpcomRoot);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// Open common registry keys here to speed access
|
|
// Do this after PlatformVersionCheck as it may re-create our keys
|
|
rv = mRegistry->AddSubtree(xpcomRoot, componentsKeyName, &mXPCOMKey);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = mRegistry->AddSubtree(xpcomRoot, classesKeyName, &mClassesKey);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = mRegistry->AddSubtree(xpcomRoot, classIDKeyName, &mCLSIDKey);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = mRegistry->AddSubtree(xpcomRoot, componentLoadersKeyName, &mLoadersKey);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
nsCOMPtr<nsIProperties> directoryService;
|
|
rv = nsDirectoryService::Create(nsnull,
|
|
NS_GET_IID(nsIProperties),
|
|
getter_AddRefs(directoryService));
|
|
|
|
directoryService->Get("xpcom.currentProcess.componentDirectory", NS_GET_IID(nsIFile), getter_AddRefs(mComponentsDir));
|
|
|
|
if (!mComponentsDir)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
char* componentDescriptor;
|
|
mComponentsDir->GetPath(&componentDescriptor);
|
|
if (!componentDescriptor)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
mComponentsOffset = strlen(componentDescriptor);
|
|
|
|
if (componentDescriptor)
|
|
nsAllocator::Free(componentDescriptor);
|
|
|
|
|
|
|
|
if (mNativeComponentLoader) {
|
|
/* now that we have the registry, Init the native loader */
|
|
rv = mNativeComponentLoader->Init(this, mRegistry);
|
|
} else {
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
|
|
("no native component loader available for init"));
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* PlatformVersionCheck()
|
|
*
|
|
* Checks to see if the XPCOM hierarchy in the registry is the same as that of
|
|
* the software as defined by NS_XPCOM_COMPONENT_MANAGER_VERSION_STRING
|
|
*/
|
|
nsresult
|
|
nsComponentManagerImpl::PlatformVersionCheck(nsRegistryKey *aXPCOMRootKey)
|
|
{
|
|
nsRegistryKey xpcomKey;
|
|
nsresult rv;
|
|
rv = mRegistry->AddSubtree(nsIRegistry::Common, xpcomKeyName, &xpcomKey);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
nsXPIDLCString buf;
|
|
nsresult err = mRegistry->GetStringUTF8(xpcomKey, versionValueName,
|
|
getter_Copies(buf));
|
|
|
|
// If there is a version mismatch or no version string, we got an old registry.
|
|
// Delete the old repository hierarchies and recreate version string
|
|
if (NS_FAILED(err) || PL_strcmp(buf, NS_XPCOM_COMPONENT_MANAGER_VERSION_STRING))
|
|
{
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
|
|
("nsComponentManager: Registry version mismatch (old:%s vs new:%s)."
|
|
"Nuking xpcom registry hierarchy.", (const char *)buf,
|
|
NS_XPCOM_COMPONENT_MANAGER_VERSION_STRING));
|
|
|
|
// Delete the XPCOM hierarchy
|
|
rv = mRegistry->RemoveSubtree(nsIRegistry::Common, xpcomKeyName);
|
|
if(NS_FAILED(rv))
|
|
{
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
|
|
("nsComponentManager: Failed To Nuke Subtree (%s)",xpcomKeyName));
|
|
return rv;
|
|
}
|
|
|
|
// The top-level Classes and CLSID trees are from an early alpha version,
|
|
// we can probably remove these two deletions after the second beta or so.
|
|
(void) mRegistry->RemoveSubtree(nsIRegistry::Common, classIDKeyName);
|
|
(void) mRegistry->RemoveSubtree(nsIRegistry::Common, classesKeyName);
|
|
|
|
// Recreate XPCOM key and version
|
|
rv = mRegistry->AddSubtree(nsIRegistry::Common,xpcomKeyName, &xpcomKey);
|
|
if(NS_FAILED(rv))
|
|
{
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
|
|
("nsComponentManager: Failed To Add Subtree (%s)",xpcomKeyName));
|
|
return rv;
|
|
}
|
|
|
|
rv = mRegistry->SetStringUTF8(xpcomKey,versionValueName, NS_XPCOM_COMPONENT_MANAGER_VERSION_STRING);
|
|
if(NS_FAILED(rv))
|
|
{
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
|
|
("nsComponentManager: Failed To Set String (Version) Under (%s)",xpcomKeyName));
|
|
return rv;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
|
|
("nsComponentManager: platformVersionCheck() passed."));
|
|
}
|
|
|
|
|
|
// return the XPCOM key (null check deferred so cleanup always happens)
|
|
if (!aXPCOMRootKey)
|
|
return NS_ERROR_NULL_POINTER;
|
|
else
|
|
*aXPCOMRootKey = xpcomKey;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
#if 0
|
|
void
|
|
nsComponentManagerImpl::PlatformSetFileInfo(nsRegistryKey key, PRUint32 lastModifiedTime, PRUint32 fileSize)
|
|
{
|
|
mRegistry->SetInt(key, lastModValueName, lastModifiedTime);
|
|
mRegistry->SetInt(key, fileSizeValueName, fileSize);
|
|
}
|
|
|
|
/**
|
|
* PlatformMarkNoComponents(nsDll *dll)
|
|
*
|
|
* Stores the dll name, last modified time, size and 0 for number of
|
|
* components in dll in the registry at location
|
|
* ROOTKEY_COMMON/Software/Mozilla/XPCOM/Components/dllname
|
|
*/
|
|
nsresult
|
|
nsComponentManagerImpl::PlatformMarkNoComponents(nsDll *dll)
|
|
{
|
|
PR_ASSERT(mRegistry!=NULL);
|
|
|
|
nsresult rv;
|
|
|
|
nsRegistryKey dllPathKey;
|
|
rv = mRegistry->AddSubtreeRaw(mXPCOMKey, dll->GetPersistentDescriptorString(), &dllPathKey);
|
|
if(NS_FAILED(rv))
|
|
{
|
|
return rv;
|
|
}
|
|
|
|
PlatformSetFileInfo(dllPathKey, dll->GetLastModifiedTime(), dll->GetSize());
|
|
rv = mRegistry->SetInt(dllPathKey, componentCountValueName, 0);
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::PlatformRegister(const char *cidString,
|
|
const char *className,
|
|
const char * progID, nsDll *dll)
|
|
{
|
|
// Preconditions
|
|
PR_ASSERT(cidString != NULL);
|
|
PR_ASSERT(dll != NULL);
|
|
PR_ASSERT(mRegistry !=NULL);
|
|
|
|
nsresult rv;
|
|
|
|
nsRegistryKey IDkey;
|
|
rv = mRegistry->AddSubtreeRaw(mCLSIDKey, cidString, &IDkey);
|
|
if (NS_FAILED(rv)) return (rv);
|
|
|
|
|
|
rv = mRegistry->SetStringUTF8(IDkey,classNameValueName, className);
|
|
if (progID)
|
|
{
|
|
rv = mRegistry->SetStringUTF8(IDkey,progIDValueName, progID);
|
|
}
|
|
rv = mRegistry->SetStringUTF8(IDkey, inprocServerValueName, dll->GetPersistentDescriptorString());
|
|
|
|
if (progID)
|
|
{
|
|
nsRegistryKey progIDKey;
|
|
rv = mRegistry->AddSubtreeRaw(mClassesKey, progID, &progIDKey);
|
|
rv = mRegistry->SetStringUTF8(progIDKey, classIDValueName, cidString);
|
|
}
|
|
|
|
// XXX Gross. LongLongs dont have a serialization format. This makes
|
|
// XXX the registry non-xp. Someone beat on the nspr people to get
|
|
// XXX a longlong serialization function please!
|
|
|
|
nsRegistryKey dllPathKey;
|
|
rv = mRegistry->AddSubtreeRaw(mXPCOMKey,dll->GetPersistentDescriptorString(), &dllPathKey);
|
|
|
|
PlatformSetFileInfo(dllPathKey, dll->GetLastModifiedTime(), dll->GetSize());
|
|
|
|
PRInt32 nComponents = 0;
|
|
rv = mRegistry->GetInt(dllPathKey, componentCountValueName, &nComponents);
|
|
nComponents++;
|
|
rv = mRegistry->SetInt(dllPathKey,componentCountValueName, nComponents);
|
|
|
|
return rv;
|
|
}
|
|
#endif
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::PlatformUnregister(const char *cidString,
|
|
const char *aLibrary)
|
|
{
|
|
PR_ASSERT(mRegistry!=NULL);
|
|
|
|
nsresult rv;
|
|
|
|
nsRegistryKey cidKey;
|
|
rv = mRegistry->AddSubtreeRaw(mCLSIDKey, cidString, &cidKey);
|
|
|
|
char *progID = NULL;
|
|
rv = mRegistry->GetStringUTF8(cidKey, progIDValueName, &progID);
|
|
if(NS_SUCCEEDED(rv))
|
|
{
|
|
mRegistry->RemoveSubtreeRaw(mClassesKey, progID);
|
|
PR_FREEIF(progID);
|
|
}
|
|
|
|
mRegistry->RemoveSubtree(mCLSIDKey, cidString);
|
|
|
|
nsRegistryKey libKey;
|
|
rv = mRegistry->GetSubtreeRaw(mXPCOMKey, aLibrary, &libKey);
|
|
if(NS_FAILED(rv)) return rv;
|
|
|
|
// We need to reduce the ComponentCount by 1.
|
|
// If the ComponentCount hits 0, delete the entire key.
|
|
PRInt32 nComponents = 0;
|
|
rv = mRegistry->GetInt(libKey, componentCountValueName, &nComponents);
|
|
if(NS_FAILED(rv)) return rv;
|
|
nComponents--;
|
|
|
|
if (nComponents <= 0)
|
|
{
|
|
rv = mRegistry->RemoveSubtreeRaw(mXPCOMKey, aLibrary);
|
|
}
|
|
else
|
|
{
|
|
rv = mRegistry->SetInt(libKey, componentCountValueName, nComponents);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::PlatformFind(const nsCID &aCID, nsFactoryEntry* *result)
|
|
{
|
|
PR_ASSERT(mRegistry!=NULL);
|
|
|
|
nsresult rv;
|
|
|
|
char *cidString = aCID.ToString();
|
|
|
|
nsRegistryKey cidKey;
|
|
rv = mRegistry->GetSubtreeRaw(mCLSIDKey, cidString, &cidKey);
|
|
delete [] cidString;
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
nsXPIDLCString library;
|
|
rv = mRegistry->GetStringUTF8(cidKey, inprocServerValueName,
|
|
getter_Copies(library));
|
|
if (NS_FAILED(rv))
|
|
{
|
|
// Registry inconsistent. No File name for CLSID.
|
|
return rv;
|
|
}
|
|
|
|
nsXPIDLCString componentType;
|
|
rv = mRegistry->GetStringUTF8(cidKey, componentTypeValueName,
|
|
getter_Copies(componentType));
|
|
|
|
if (NS_FAILED(rv))
|
|
if (rv == NS_ERROR_REG_NOT_FOUND)
|
|
/* missing componentType, we assume application/x-moz-native */
|
|
componentType = nativeComponentType;
|
|
else
|
|
return rv; // XXX translate error code?
|
|
|
|
nsCOMPtr<nsIComponentLoader> loader;
|
|
|
|
rv = GetLoaderForType(componentType, getter_AddRefs(loader));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
nsFactoryEntry *res = new nsFactoryEntry(aCID, library, componentType,
|
|
loader);
|
|
if (res == NULL)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
*result = res;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::PlatformProgIDToCLSID(const char *aProgID, nsCID *aClass)
|
|
{
|
|
PR_ASSERT(aClass != NULL);
|
|
PR_ASSERT(mRegistry);
|
|
|
|
nsresult rv;
|
|
|
|
nsRegistryKey progIDKey;
|
|
rv = mRegistry->GetSubtreeRaw(mClassesKey, aProgID, &progIDKey);
|
|
if (NS_FAILED(rv)) return NS_ERROR_FACTORY_NOT_REGISTERED;
|
|
|
|
char *cidString;
|
|
rv = mRegistry->GetStringUTF8(progIDKey, classIDValueName, &cidString);
|
|
if(NS_FAILED(rv)) return rv;
|
|
if (!(aClass->Parse(cidString)))
|
|
{
|
|
rv = NS_ERROR_FAILURE;
|
|
}
|
|
|
|
PR_FREEIF(cidString);
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::PlatformCLSIDToProgID(const nsCID *aClass,
|
|
char* *aClassName, char* *aProgID)
|
|
{
|
|
|
|
PR_ASSERT(aClass);
|
|
PR_ASSERT(mRegistry);
|
|
|
|
nsresult rv;
|
|
|
|
char* cidStr = aClass->ToString();
|
|
nsRegistryKey cidKey;
|
|
rv = mRegistry->GetSubtreeRaw(mCLSIDKey, cidStr, &cidKey);
|
|
if(NS_FAILED(rv)) return rv;
|
|
PR_FREEIF(cidStr);
|
|
|
|
char* classnameString;
|
|
rv = mRegistry->GetStringUTF8(cidKey, classNameValueName, &classnameString);
|
|
if(NS_FAILED(rv)) return rv;
|
|
*aClassName = classnameString;
|
|
|
|
char* progidString;
|
|
rv = mRegistry->GetStringUTF8(cidKey,progIDValueName,&progidString);
|
|
if (NS_FAILED(rv)) return rv;
|
|
*aProgID = progidString;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
nsresult nsComponentManagerImpl::PlatformPrePopulateRegistry()
|
|
{
|
|
nsresult rv;
|
|
|
|
if (mPrePopulationDone)
|
|
return NS_OK;
|
|
// Read in all CID entries and populate the mFactories
|
|
nsCOMPtr<nsIEnumerator> cidEnum;
|
|
rv = mRegistry->EnumerateSubtrees( mCLSIDKey, getter_AddRefs(cidEnum));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
nsCOMPtr<nsIRegistryEnumerator> regEnum = do_QueryInterface(cidEnum, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = regEnum->First();
|
|
for (rv = regEnum->First();
|
|
NS_SUCCEEDED(rv) && (regEnum->IsDone() != NS_OK);
|
|
rv = regEnum->Next())
|
|
{
|
|
const char *cidString;
|
|
nsRegistryKey cidKey;
|
|
/*
|
|
* CurrentItemInPlaceUTF8 will give us back a _shared_ pointer in
|
|
* cidString. This is bad XPCOM practice. It is evil, and requires
|
|
* great care with the relative lifetimes of cidString and regEnum.
|
|
*
|
|
* It is also faster, and less painful in the allocation department.
|
|
*/
|
|
rv = regEnum->CurrentItemInPlaceUTF8(&cidKey, &cidString);
|
|
if (NS_FAILED(rv)) continue;
|
|
|
|
// Create the CID entry
|
|
nsXPIDLCString library;
|
|
rv = mRegistry->GetStringUTF8(cidKey, inprocServerValueName,
|
|
getter_Copies(library));
|
|
if (NS_FAILED(rv)) continue;
|
|
nsCID aClass;
|
|
|
|
if (!(aClass.Parse(cidString))) continue;
|
|
|
|
nsXPIDLCString componentType;
|
|
if (NS_FAILED(mRegistry->GetStringUTF8(cidKey, componentTypeValueName,
|
|
getter_Copies(componentType))))
|
|
continue;
|
|
|
|
nsFactoryEntry* entry =
|
|
new nsFactoryEntry(aClass, library, componentType,
|
|
nsCRT::strcmp(componentType,
|
|
nativeComponentType) ?
|
|
0 : mNativeComponentLoader);
|
|
if (!entry)
|
|
continue;
|
|
|
|
nsIDKey key(aClass);
|
|
mFactories->Put(&key, entry);
|
|
}
|
|
|
|
// Finally read in PROGID -> CID mappings
|
|
nsCOMPtr<nsIEnumerator> progidEnum;
|
|
rv = mRegistry->EnumerateSubtrees( mClassesKey, getter_AddRefs(progidEnum));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
regEnum = do_QueryInterface(progidEnum, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = regEnum->First();
|
|
for (rv = regEnum->First();
|
|
NS_SUCCEEDED(rv) && (regEnum->IsDone() != NS_OK);
|
|
rv = regEnum->Next())
|
|
{
|
|
const char *progidString;
|
|
nsRegistryKey progidKey;
|
|
/*
|
|
* CurrentItemInPlaceUTF8 will give us back a _shared_ pointer in
|
|
* progidString. This is bad XPCOM practice. It is evil, and requires
|
|
* great care with the relative lifetimes of progidString and regEnum.
|
|
*
|
|
* It is also faster, and less painful in the allocation department.
|
|
*/
|
|
rv = regEnum->CurrentItemInPlaceUTF8(&progidKey, &progidString);
|
|
if (NS_FAILED(rv)) continue;
|
|
|
|
nsXPIDLCString cidString;
|
|
rv = mRegistry->GetStringUTF8(progidKey, classIDValueName,
|
|
getter_Copies(cidString));
|
|
if (NS_FAILED(rv)) continue;
|
|
|
|
nsCID *aClass = new nsCID();
|
|
if (!aClass) continue; // Protect against out of memory.
|
|
if (!(aClass->Parse(cidString)))
|
|
{
|
|
delete aClass;
|
|
continue;
|
|
}
|
|
|
|
// put the {progid, Cid} mapping into our map
|
|
nsStringKey key(progidString);
|
|
mProgIDs->Put(&key, aClass);
|
|
// printf("Populating [ %s, %s ]\n", cidString, progidString);
|
|
}
|
|
|
|
mPrePopulationDone = PR_TRUE;
|
|
return NS_OK;
|
|
}
|
|
|
|
#endif /* USE_REGISTRY */
|
|
|
|
//
|
|
// HashProgID
|
|
//
|
|
nsresult
|
|
nsComponentManagerImpl::HashProgID(const char *aProgID, const nsCID &aClass)
|
|
{
|
|
if(!aProgID)
|
|
{
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
nsStringKey key(aProgID);
|
|
nsCID* cid = (nsCID*) mProgIDs->Get(&key);
|
|
if (cid)
|
|
{
|
|
if (cid == &kNoCID)
|
|
{
|
|
// we don't delete this ptr as it's static (ugh)
|
|
}
|
|
else
|
|
{
|
|
delete cid;
|
|
}
|
|
}
|
|
|
|
cid = new nsCID(aClass);
|
|
if (!cid)
|
|
{
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
mProgIDs->Put(&key, cid);
|
|
return NS_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// nsComponentManagerImpl: Public methods
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* 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 = NULL;
|
|
|
|
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->location, (const char *)aEntry->type));
|
|
return rv;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
nsFactoryEntry *
|
|
nsComponentManagerImpl::GetFactoryEntry(const nsCID &aClass, PRBool checkRegistry)
|
|
{
|
|
nsIDKey key(aClass);
|
|
nsFactoryEntry *entry = (nsFactoryEntry*) mFactories->Get(&key);
|
|
|
|
#ifdef USE_REGISTRY
|
|
if (!entry)
|
|
{
|
|
if (checkRegistry)
|
|
{
|
|
nsresult rv = PlatformFind(aClass, &entry);
|
|
|
|
// If we got one, cache it in our hashtable
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
mFactories->Put(&key, entry);
|
|
}
|
|
}
|
|
}
|
|
#endif /* USE_REGISTRY */
|
|
|
|
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 != NULL);
|
|
|
|
nsFactoryEntry *entry = GetFactoryEntry(aClass, !mPrePopulationDone);
|
|
|
|
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;
|
|
|
|
if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_ALWAYS))
|
|
{
|
|
char *buf = aClass.ToString();
|
|
PR_LogPrint("nsComponentManager: GetClassObject(%s)", buf);
|
|
delete [] buf;
|
|
}
|
|
|
|
PR_ASSERT(aResult != NULL);
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* ProgIDToClassID()
|
|
*
|
|
* Mapping function from a ProgID to a classID. Directly talks to the registry.
|
|
*
|
|
*/
|
|
nsresult
|
|
nsComponentManagerImpl::ProgIDToClassID(const char *aProgID, nsCID *aClass)
|
|
{
|
|
NS_PRECONDITION(aProgID != NULL, "null ptr");
|
|
if (! aProgID)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_PRECONDITION(aClass != NULL, "null ptr");
|
|
if (! aClass)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
nsresult res = NS_ERROR_FACTORY_NOT_REGISTERED;
|
|
|
|
#ifdef USE_REGISTRY
|
|
// XXX This isn't quite the best way to do this: we should
|
|
// probably move an nsArray<ProgID> into the FactoryEntry class,
|
|
// and then have the construct/destructor of the factory entry
|
|
// keep the ProgID to CID cache up-to-date. However, doing this
|
|
// significantly improves performance, so it'll do for now.
|
|
|
|
nsStringKey key(aProgID);
|
|
nsCID* cid = (nsCID*) mProgIDs->Get(&key);
|
|
if (cid) {
|
|
if (cid == &kNoCID) {
|
|
// we've already tried to map this ProgID to a CLSID, and found
|
|
// that there _was_ no such mapping in the registry.
|
|
}
|
|
else {
|
|
*aClass = *cid;
|
|
res = NS_OK;
|
|
}
|
|
}
|
|
else {
|
|
// This is the first time someone has asked for this
|
|
// ProgID. Go to the registry to find the CID.
|
|
if (!mPrePopulationDone)
|
|
res = PlatformProgIDToCLSID(aProgID, aClass);
|
|
|
|
if (NS_SUCCEEDED(res)) {
|
|
// Found it. So put it into the cache.
|
|
cid = new nsCID(*aClass);
|
|
if (!cid)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
mProgIDs->Put(&key, cid);
|
|
}
|
|
else {
|
|
// Didn't find it. Put a special CID in the cache so we
|
|
// don't need to hit the registry on subsequent requests
|
|
// for the same ProgID.
|
|
mProgIDs->Put(&key, (void *)&kNoCID);
|
|
}
|
|
}
|
|
#endif /* USE_REGISTRY */
|
|
|
|
if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_ALWAYS)) {
|
|
char *buf = 0;
|
|
if (NS_SUCCEEDED(res))
|
|
buf = aClass->ToString();
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
|
|
("nsComponentManager: ProgIDToClassID(%s)->%s", aProgID,
|
|
NS_SUCCEEDED(res) ? buf : "[FAILED]"));
|
|
if (NS_SUCCEEDED(res))
|
|
delete [] buf;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* CLSIDToProgID()
|
|
*
|
|
* Translates a classID to a {ProgID, Class Name}. Does direct registry
|
|
* access to do the translation.
|
|
*
|
|
* XXX Would be nice to hook in a cache here too.
|
|
*/
|
|
nsresult
|
|
nsComponentManagerImpl::CLSIDToProgID(const nsCID &aClass,
|
|
char* *aClassName,
|
|
char* *aProgID)
|
|
{
|
|
nsresult res = NS_ERROR_FACTORY_NOT_REGISTERED;
|
|
|
|
#ifdef USE_REGISTRY
|
|
res = PlatformCLSIDToProgID(&aClass, aClassName, aProgID);
|
|
#endif /* USE_REGISTRY */
|
|
|
|
if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_ALWAYS))
|
|
{
|
|
char *buf = aClass.ToString();
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_WARNING,
|
|
("nsComponentManager: CLSIDToProgID(%s)->%s", buf,
|
|
NS_SUCCEEDED(res) ? *aProgID : "[FAILED]"));
|
|
delete [] buf;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* 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 (gShuttingDown) {
|
|
// When processing shutdown, dont process new GetService() requests
|
|
#ifdef DEBUG_dp
|
|
NS_WARN_IF_FALSE(PR_FALSE, "Creating new instance on shutdown. Denied.");
|
|
#endif /* DEBUG_dp */
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
if (aResult == NULL)
|
|
{
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
*aResult = NULL;
|
|
|
|
nsIFactory *factory = NULL;
|
|
nsresult res = FindFactory(aClass, &factory);
|
|
if (NS_SUCCEEDED(res))
|
|
{
|
|
res = factory->CreateInstance(aDelegate, aIID, aResult);
|
|
NS_RELEASE(factory);
|
|
}
|
|
else
|
|
{
|
|
// Translate error values
|
|
res = NS_ERROR_FACTORY_NOT_REGISTERED;
|
|
}
|
|
|
|
if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_ALWAYS))
|
|
{
|
|
char *buf = aClass.ToString();
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
|
|
("nsComponentManager: CreateInstance(%s) %s", buf,
|
|
NS_SUCCEEDED(res) ? "succeeded" : "FAILED"));
|
|
delete [] buf;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* CreateInstanceByProgID()
|
|
*
|
|
* A variant of CreateInstance() that creates an instance of the object that
|
|
* implements the interface aIID and whose implementation has a progID aProgID.
|
|
*
|
|
* This is only a convenience routine that turns around can calls the
|
|
* CreateInstance() with classid and iid.
|
|
*/
|
|
nsresult
|
|
nsComponentManagerImpl::CreateInstanceByProgID(const char *aProgID,
|
|
nsISupports *aDelegate,
|
|
const nsIID &aIID,
|
|
void **aResult)
|
|
{
|
|
nsCID clsid;
|
|
nsresult rv = ProgIDToClassID(aProgID, &clsid);
|
|
if (NS_FAILED(rv)) return rv;
|
|
return CreateInstance(clsid, aDelegate, aIID, aResult);
|
|
}
|
|
|
|
/*
|
|
* 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 = nsCRT::strlen(prefix);
|
|
|
|
PRUint32 registryNameLen = nsCRT::strlen(aDllName) + len;
|
|
registryName = (char *)nsAllocator::Alloc(registryNameLen + 1);
|
|
|
|
// from here on it, we want len sans terminating NUL
|
|
|
|
if (!registryName)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
nsCRT::memcpy(registryName, prefix, len);
|
|
strcpy(registryName + len, aDllName); // no nsCRT::strcpy? for shame!
|
|
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::RegistryNameForLib(const char *aLibName,
|
|
char **aRegistryName)
|
|
{
|
|
return MakeRegistryName(aLibName, XPCOM_LIB_PREFIX, aRegistryName);
|
|
}
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::RegistryLocationForSpec(nsIFile *aSpec,
|
|
char **aRegistryName)
|
|
{
|
|
nsresult rv;
|
|
|
|
if (!mComponentsDir)
|
|
return NS_ERROR_NOT_INITIALIZED;
|
|
|
|
PRBool containedIn;
|
|
mComponentsDir->Contains(aSpec, PR_TRUE, &containedIn);
|
|
|
|
char *persistentDescriptor;
|
|
|
|
if (containedIn){
|
|
|
|
rv = aSpec->GetPath(&persistentDescriptor);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
char* relativeLocation = persistentDescriptor + mComponentsOffset + 1;
|
|
|
|
rv = MakeRegistryName(relativeLocation, XPCOM_RELCOMPONENT_PREFIX,
|
|
aRegistryName);
|
|
} else {
|
|
/* absolute names include volume info on Mac, so persistent descriptor */
|
|
rv = aSpec->GetPath(&persistentDescriptor);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
rv = MakeRegistryName(persistentDescriptor, XPCOM_ABSCOMPONENT_PREFIX,
|
|
aRegistryName);
|
|
}
|
|
|
|
if (persistentDescriptor)
|
|
nsAllocator::Free(persistentDescriptor);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::SpecForRegistryLocation(const char *aLocation,
|
|
nsIFile **aSpec)
|
|
{
|
|
nsresult rv;
|
|
if (!aLocation || !aSpec)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
/* abs:/full/path/to/libcomponent.so */
|
|
if (!nsCRT::strncmp(aLocation, XPCOM_ABSCOMPONENT_PREFIX, 4)) {
|
|
|
|
nsLocalFile* file = new nsLocalFile;
|
|
if (!file) return NS_ERROR_FAILURE;
|
|
|
|
rv = file->InitWithPath(((char *)aLocation + 4));
|
|
file->QueryInterface(NS_GET_IID(nsILocalFile), (void**)aSpec);
|
|
return rv;
|
|
}
|
|
|
|
if (!nsCRT::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->Append(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 aProgID
|
|
* 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 *aProgID,
|
|
nsIFactory *aFactory,
|
|
PRBool aReplace)
|
|
{
|
|
nsFactoryEntry *entry = NULL;
|
|
|
|
nsIDKey key(aClass);
|
|
entry = (nsFactoryEntry *)mFactories->Get(&key);
|
|
|
|
if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_ALWAYS))
|
|
{
|
|
char *buf = aClass.ToString();
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
|
|
("nsComponentManager: RegisterFactory(%s, %s)", buf,
|
|
(aProgID ? aProgID : "(null)")));
|
|
delete [] buf;
|
|
|
|
}
|
|
|
|
|
|
if (entry && !aReplace) {
|
|
// Already registered
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
|
|
("\t\tFactory already registered."));
|
|
return NS_ERROR_FACTORY_EXISTS;
|
|
}
|
|
|
|
nsFactoryEntry *newEntry = new nsFactoryEntry(aClass, aFactory);
|
|
if (newEntry == NULL)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
if (entry) { // aReplace implied by above check
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_WARNING,
|
|
("\t\tdeleting old Factory Entry."));
|
|
mFactories->RemoveAndDelete(&key);
|
|
entry = NULL;
|
|
}
|
|
mFactories->Put(&key, newEntry);
|
|
|
|
// Update the ProgID->CLSID Map
|
|
if (aProgID) {
|
|
nsresult rv = HashProgID(aProgID, aClass);
|
|
if(NS_FAILED(rv)) {
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_WARNING,
|
|
("\t\tFactory register succeeded. "
|
|
"Hashing progid (%s) FAILED.", aProgID));
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_WARNING,
|
|
("\t\tFactory register succeeded progid=%s.",
|
|
aProgID ? aProgID : "<none>"));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::RegisterComponent(const nsCID &aClass,
|
|
const char *aClassName,
|
|
const char *aProgID,
|
|
const char *aPersistentDescriptor,
|
|
PRBool aReplace,
|
|
PRBool aPersist)
|
|
{
|
|
return RegisterComponentCommon(aClass, aClassName, aProgID,
|
|
aPersistentDescriptor, aReplace, aPersist,
|
|
nativeComponentType);
|
|
}
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::RegisterComponentWithType(const nsCID &aClass,
|
|
const char *aClassName,
|
|
const char *aProgID,
|
|
nsIFile *aSpec,
|
|
const char *aLocation,
|
|
PRBool aReplace,
|
|
PRBool aPersist,
|
|
const char *aType)
|
|
{
|
|
return RegisterComponentCommon(aClass, aClassName, aProgID,
|
|
aLocation,
|
|
aReplace, aPersist,
|
|
aType);
|
|
}
|
|
|
|
/*
|
|
* Register a component, using whatever they stuck in the nsIFile.
|
|
*/
|
|
nsresult
|
|
nsComponentManagerImpl::RegisterComponentSpec(const nsCID &aClass,
|
|
const char *aClassName,
|
|
const char *aProgID,
|
|
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, aProgID, aLibrarySpec,
|
|
registryName,
|
|
aReplace, aPersist,
|
|
nativeComponentType);
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Register a ``library'', which is a DLL location named by a simple filename
|
|
* such as ``libnsappshell.so'', rather than a relative or absolute path.
|
|
*
|
|
* It implies application/x-moz-dll as the component type, and skips the
|
|
* FindLoaderForType phase.
|
|
*/
|
|
nsresult
|
|
nsComponentManagerImpl::RegisterComponentLib(const nsCID &aClass,
|
|
const char *aClassName,
|
|
const char *aProgID,
|
|
const char *aDllName,
|
|
PRBool aReplace,
|
|
PRBool aPersist)
|
|
{
|
|
nsXPIDLCString registryName;
|
|
nsresult rv = RegistryNameForLib(aDllName, getter_Copies(registryName));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
return RegisterComponentCommon(aClass, aClassName, aProgID, registryName,
|
|
aReplace, aPersist, nativeComponentType);
|
|
}
|
|
|
|
/*
|
|
* 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 *aProgID,
|
|
const char *aRegistryName,
|
|
PRBool aReplace,
|
|
PRBool aPersist,
|
|
const char *aType)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
nsFactoryEntry* newEntry = nsnull;
|
|
|
|
nsIDKey key(aClass);
|
|
nsFactoryEntry *entry = GetFactoryEntry(aClass, !mPrePopulationDone);
|
|
nsCOMPtr<nsIComponentLoader> loader;
|
|
PRBool sanity;
|
|
|
|
// Normalize proid and classname
|
|
const char *progID = (aProgID && *aProgID) ? aProgID : NULL;
|
|
const char *className = (aClassName && *aClassName) ? aClassName : NULL;
|
|
|
|
if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_ALWAYS))
|
|
{
|
|
char *buf = aClass.ToString();
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG,
|
|
("nsComponentManager: RegisterComponentCommon(%s, %s, %s, %s)",
|
|
buf,
|
|
progID ? progID : "(null)",
|
|
aRegistryName, aType));
|
|
delete [] buf;
|
|
}
|
|
|
|
if (entry && !aReplace) {
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
|
|
("\t\tFactory already registered."));
|
|
rv = NS_ERROR_FACTORY_EXISTS;
|
|
goto out;
|
|
}
|
|
|
|
#ifdef USE_REGISTRY
|
|
if (aPersist) {
|
|
/* Add to the registry */
|
|
rv = AddComponentToRegistry(aClass, className, progID,
|
|
aRegistryName, aType);
|
|
if (NS_FAILED(rv)) {
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
|
|
("\t\tadding %s %s to registry FAILED", className, progID));
|
|
goto out;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
rv = GetLoaderForType(aType, getter_AddRefs(loader));
|
|
if (NS_FAILED(rv)) {
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
|
|
("\t\tgetting loader for %s FAILED\n", aType));
|
|
goto out;
|
|
}
|
|
|
|
newEntry = new nsFactoryEntry(aClass, aRegistryName, aType, loader);
|
|
if (!newEntry) {
|
|
rv = NS_ERROR_OUT_OF_MEMORY;
|
|
goto out;
|
|
}
|
|
|
|
if (entry) { // aReplace implicit from test above
|
|
delete entry;
|
|
}
|
|
|
|
/* unless the fabric of the universe bends, we'll get entry back */
|
|
sanity = (entry == mFactories->Put(&key, newEntry));
|
|
PR_ASSERT(sanity);
|
|
|
|
/* don't try to clean up, just drop everything and run */
|
|
if (!sanity)
|
|
return NS_ERROR_FACTORY_NOT_REGISTERED;
|
|
|
|
/* we've put the new entry in the hash table, so don't delete on error */
|
|
newEntry = nsnull;
|
|
|
|
// Update the ProgID->CLSID Map
|
|
if (progID
|
|
#ifdef USE_REGISTRY
|
|
&& (mPrePopulationDone || !aPersist)
|
|
#endif
|
|
) {
|
|
rv = HashProgID(progID, aClass);
|
|
if (NS_FAILED(rv)) {
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
|
|
("\t\tHashProgID(%s) FAILED\n", progID));
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
// Let the loader do magic things now
|
|
rv = loader->OnRegister(aClass, aType, className, progID, aRegistryName,
|
|
aReplace, aPersist);
|
|
if (NS_FAILED(rv)) {
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
|
|
("\t\tloader->OnRegister FAILED for %s \"%s\" %s %s", aType,
|
|
className, progID, aRegistryName));
|
|
goto out;
|
|
}
|
|
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
|
|
("\t\tRegisterComponentCommon() %s",
|
|
NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"));
|
|
out:
|
|
if (NS_FAILED(rv)) {
|
|
if (newEntry)
|
|
delete newEntry;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::GetLoaderForType(const char *aType,
|
|
nsIComponentLoader **aLoader)
|
|
{
|
|
nsStringKey typeKey(aType);
|
|
nsIComponentLoader *loader;
|
|
nsresult rv;
|
|
|
|
loader = (nsIComponentLoader *)mLoaders->Get(&typeKey);
|
|
if (loader) {
|
|
// nsSupportsHashtable does the AddRef
|
|
*aLoader = loader;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsRegistryKey loaderKey;
|
|
rv = mRegistry->GetSubtreeRaw(mLoadersKey, aType, &loaderKey);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
char *progID;
|
|
rv = mRegistry->GetStringUTF8(loaderKey, progIDValueName, &progID);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
#ifdef DEBUG_shaver_off
|
|
fprintf(stderr, "nCMI: constructing loader for type %s = %s\n", aType, progID);
|
|
#endif
|
|
|
|
rv = CreateInstanceByProgID(progID, nsnull, NS_GET_IID(nsIComponentLoader), (void **)&loader);
|
|
PR_FREEIF(progID);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
rv = loader->Init(this, mRegistry);
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
mLoaders->Put(&typeKey, loader);
|
|
*aLoader = loader;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::RegisterComponentLoader(const char *aType, const char *aProgID,
|
|
PRBool aReplace)
|
|
{
|
|
nsRegistryKey loaderKey;
|
|
nsresult rv = mRegistry->AddSubtreeRaw(mLoadersKey, aType, &loaderKey);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
/* XXX honour aReplace */
|
|
|
|
rv = mRegistry->SetStringUTF8(loaderKey, progIDValueName, aProgID);
|
|
|
|
#ifdef DEBUG_shaver_off
|
|
fprintf(stderr, "nNCI: registered %s as component loader for %s\n",
|
|
aProgID, aType);
|
|
#endif
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::AddComponentToRegistry(const nsCID &aClass,
|
|
const char *aClassName,
|
|
const char *aProgID,
|
|
const char *aRegistryName,
|
|
const char *aType)
|
|
{
|
|
nsresult rv;
|
|
nsRegistryKey IDKey;
|
|
PRInt32 nComponents = 0;
|
|
|
|
/* so why do we use strings here rather than writing bytes, anyway? */
|
|
char *cidString = aClass.ToString();
|
|
if (!cidString)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
rv = mRegistry->AddSubtreeRaw(mCLSIDKey, cidString, &IDKey);
|
|
if (NS_FAILED(rv))
|
|
goto out;
|
|
|
|
if (aClassName) {
|
|
rv = mRegistry->SetStringUTF8(IDKey, classNameValueName, aClassName);
|
|
if (NS_FAILED(rv))
|
|
goto out;
|
|
}
|
|
|
|
rv = mRegistry->SetStringUTF8(IDKey, inprocServerValueName, aRegistryName);
|
|
if (NS_FAILED(rv))
|
|
goto out;
|
|
|
|
rv = mRegistry->SetStringUTF8(IDKey, componentTypeValueName, aType);
|
|
if (NS_FAILED(rv))
|
|
goto out;
|
|
|
|
if (aProgID) {
|
|
rv = mRegistry->SetStringUTF8(IDKey, progIDValueName, aProgID);
|
|
if (NS_FAILED(rv))
|
|
goto out;
|
|
|
|
nsRegistryKey progIDKey;
|
|
rv = mRegistry->AddSubtreeRaw(mClassesKey, aProgID, &progIDKey);
|
|
if (NS_FAILED(rv))
|
|
goto out;
|
|
rv = mRegistry->SetStringUTF8(progIDKey, classIDValueName, cidString);
|
|
if (NS_FAILED(rv))
|
|
goto out;
|
|
}
|
|
|
|
nsRegistryKey compKey;
|
|
rv = mRegistry->AddSubtreeRaw(mXPCOMKey, aRegistryName, &compKey);
|
|
|
|
// update component count
|
|
rv = mRegistry->GetInt(compKey, componentCountValueName, &nComponents);
|
|
nComponents++;
|
|
rv = mRegistry->SetInt(compKey, componentCountValueName, nComponents);
|
|
if (NS_FAILED(rv))
|
|
goto out;
|
|
|
|
out:
|
|
// XXX if failed, undo registry adds or set invalid bit? How?
|
|
nsCRT::free(cidString);
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::UnregisterFactory(const nsCID &aClass,
|
|
nsIFactory *aFactory)
|
|
{
|
|
if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_ALWAYS))
|
|
{
|
|
char *buf = aClass.ToString();
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG,
|
|
("nsComponentManager: UnregisterFactory(%s)", buf));
|
|
delete [] buf;
|
|
}
|
|
|
|
nsIDKey key(aClass);
|
|
nsresult res = NS_ERROR_FACTORY_NOT_REGISTERED;
|
|
nsFactoryEntry *old = (nsFactoryEntry *) mFactories->Get(&key);
|
|
if (old != NULL)
|
|
{
|
|
if (old->factory.get() == aFactory)
|
|
{
|
|
PR_EnterMonitor(mMon);
|
|
old = (nsFactoryEntry *) mFactories->RemoveAndDelete(&key);
|
|
old = NULL;
|
|
PR_ExitMonitor(mMon);
|
|
res = NS_OK;
|
|
}
|
|
|
|
}
|
|
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_WARNING,
|
|
("\t\tUnregisterFactory() %s",
|
|
NS_SUCCEEDED(res) ? "succeeded" : "FAILED"));
|
|
return res;
|
|
}
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::UnregisterComponent(const nsCID &aClass,
|
|
const char *aLibrary)
|
|
{
|
|
nsresult rv;
|
|
|
|
// Convert the persistent descriptor into a nsIFile
|
|
nsLocalFile* libSpec = new nsLocalFile;
|
|
if (!libSpec) return NS_ERROR_FAILURE;
|
|
|
|
rv = libSpec->InitWithPath((char *)aLibrary);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
return UnregisterComponentSpec(aClass, libSpec);
|
|
}
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::UnregisterComponentSpec(const nsCID &aClass,
|
|
nsIFile *aLibrarySpec)
|
|
{
|
|
nsXPIDLCString registryName;
|
|
nsresult rv = RegistryLocationForSpec(aLibrarySpec, getter_Copies(registryName));
|
|
if (NS_FAILED(rv)) return NS_ERROR_INVALID_ARG;
|
|
|
|
PR_EnterMonitor(mMon);
|
|
|
|
// Remove any stored factory entries
|
|
nsIDKey key(aClass);
|
|
nsFactoryEntry *entry = (nsFactoryEntry *) mFactories->Get(&key);
|
|
if (entry && entry->location && PL_strcasecmp(entry->location, registryName))
|
|
{
|
|
mFactories->RemoveAndDelete(&key);
|
|
entry = NULL;
|
|
}
|
|
|
|
#ifdef USE_REGISTRY
|
|
// Remove registry entries for this cid
|
|
char *cidString = aClass.ToString();
|
|
rv = PlatformUnregister(cidString, registryName);
|
|
delete [] cidString;
|
|
#endif
|
|
|
|
PR_ExitMonitor(mMon);
|
|
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_WARNING,
|
|
("nsComponentManager: Factory unregister(%s) %s.", (const char *)registryName,
|
|
NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"));
|
|
|
|
return rv;
|
|
}
|
|
|
|
struct CanUnload_closure {
|
|
int when;
|
|
nsresult status; // this is a hack around Enumerate's void return
|
|
nsIComponentLoader *native;
|
|
};
|
|
|
|
static PRBool PR_CALLBACK
|
|
CanUnload_enumerate(nsHashKey *key, void *aData, void *aClosure)
|
|
{
|
|
nsIComponentLoader *loader = (nsIComponentLoader *)aData;
|
|
struct CanUnload_closure *closure =
|
|
(struct CanUnload_closure *)aClosure;
|
|
|
|
if (loader == closure->native) {
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "CanUnload_enumerate: skipping native\n");
|
|
#endif
|
|
return PR_TRUE;
|
|
}
|
|
|
|
closure->status = loader->UnloadAll(closure->when);
|
|
if (NS_FAILED(closure->status))
|
|
return PR_FALSE;
|
|
return PR_TRUE;
|
|
}
|
|
|
|
// XXX Need to pass in aWhen and servicemanager
|
|
nsresult
|
|
nsComponentManagerImpl::FreeLibraries(void)
|
|
{
|
|
nsIServiceManager* serviceMgr = NULL;
|
|
nsresult rv = nsServiceManager::GetGlobalServiceManager(&serviceMgr);
|
|
if (NS_FAILED(rv)) return rv;
|
|
rv = UnloadLibraries(serviceMgr, NS_Timer); // XXX when
|
|
return rv;
|
|
}
|
|
|
|
// Private implementation of unloading libraries
|
|
nsresult
|
|
nsComponentManagerImpl::UnloadLibraries(nsIServiceManager *serviceMgr, PRInt32 aWhen)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
PR_EnterMonitor(mMon);
|
|
|
|
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
|
|
("nsComponentManager: Unloading Libraries."));
|
|
|
|
// UnloadAll the loaders
|
|
/* iterate over all known loaders and ask them to autoregister. */
|
|
struct CanUnload_closure closure;
|
|
closure.when = aWhen;
|
|
closure.status = NS_OK;
|
|
closure.native = mNativeComponentLoader;
|
|
mLoaders->Enumerate(CanUnload_enumerate, &closure);
|
|
|
|
// UnloadAll the native loader
|
|
rv = mNativeComponentLoader->UnloadAll(aWhen);
|
|
|
|
PR_ExitMonitor(mMon);
|
|
|
|
return rv;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* AutoRegister(RegistrationInstant, const char *directory)
|
|
*
|
|
* Given a directory in the following format, this will ensure proper registration
|
|
* of all components. No default director 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
|
|
*
|
|
*/
|
|
|
|
struct AutoReg_closure {
|
|
int when;
|
|
nsIFile *spec;
|
|
nsresult status; // this is a hack around Enumerate's void return
|
|
nsIComponentLoader *native;
|
|
PRBool registered;
|
|
};
|
|
|
|
static PRBool PR_CALLBACK
|
|
AutoRegister_enumerate(nsHashKey *key, void *aData, void *aClosure)
|
|
{
|
|
nsIComponentLoader *loader = NS_STATIC_CAST(nsIComponentLoader *, aData);
|
|
struct AutoReg_closure *closure =
|
|
(struct AutoReg_closure *)aClosure;
|
|
|
|
if (loader == closure->native)
|
|
return PR_TRUE;
|
|
|
|
PR_ASSERT(NS_SUCCEEDED(closure->status));
|
|
|
|
closure->status = loader->AutoRegisterComponents(closure->when,
|
|
closure->spec);
|
|
return NS_SUCCEEDED(closure->status) ? PR_TRUE : PR_FALSE;
|
|
}
|
|
|
|
static PRBool PR_CALLBACK
|
|
RegisterDeferred_enumerate(nsHashKey *key, void *aData, void *aClosure)
|
|
{
|
|
nsIComponentLoader *loader = NS_STATIC_CAST(nsIComponentLoader *, aData);
|
|
struct AutoReg_closure *closure =
|
|
(struct AutoReg_closure *)aClosure;
|
|
PR_ASSERT(NS_SUCCEEDED(closure->status));
|
|
|
|
PRBool registered;
|
|
closure->status = loader->RegisterDeferredComponents(closure->when,
|
|
®istered);
|
|
closure->registered |= registered;
|
|
return NS_SUCCEEDED(closure->status) ? PR_TRUE : PR_FALSE;
|
|
}
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::AutoRegister(PRInt32 when, nsIFile *inDirSpec)
|
|
{
|
|
nsCOMPtr<nsIFile> dir;
|
|
nsresult rv;
|
|
|
|
if (inDirSpec)
|
|
{
|
|
// Use supplied components' directory
|
|
dir = inDirSpec;
|
|
|
|
// Set components' directory for AutoRegisterInterfces to query
|
|
NS_WITH_SERVICE(nsIProperties, directoryService, NS_DIRECTORY_SERVICE_PROGID, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// Don't care if undefining fails
|
|
directoryService->Undefine("xpcom.currentProcess.componentDirectory");
|
|
rv = directoryService->Define("xpcom.currentProcess.componentDirectory", dir);
|
|
if (NS_FAILED(rv)) return rv;
|
|
}
|
|
else
|
|
{
|
|
// Do default components directory
|
|
NS_WITH_SERVICE(nsIProperties, directoryService, NS_DIRECTORY_SERVICE_PROGID, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = directoryService->Get("xpcom.currentProcess.componentDirectory", NS_GET_IID(nsIFile), getter_AddRefs(dir));
|
|
if (NS_FAILED(rv)) return rv; // XXX translate error code?
|
|
}
|
|
|
|
// Force the InterfaceInfoManager to do its AutoReg first.
|
|
nsCOMPtr<nsIInterfaceInfoManager> iim =
|
|
dont_AddRef(XPTI_GetInterfaceInfoManager());
|
|
|
|
if (!iim)
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
rv = iim->AutoRegisterInterfaces();
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
/* do the native loader first, so we can find other loaders */
|
|
rv = mNativeComponentLoader->AutoRegisterComponents((PRInt32)when, dir);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
/* XXX eagerly instantiate all known loaders */
|
|
nsCOMPtr<nsIEnumerator> loaderEnum;
|
|
rv = mRegistry->EnumerateSubtrees(mLoadersKey, getter_AddRefs(loaderEnum));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
nsCOMPtr<nsIRegistryEnumerator> regEnum =
|
|
do_QueryInterface(loaderEnum, &rv);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
for (rv = regEnum->First();
|
|
NS_SUCCEEDED(rv) && (regEnum->IsDone() != NS_OK);
|
|
rv = regEnum->Next()) {
|
|
const char * type;
|
|
nsRegistryKey throwAway;
|
|
/*
|
|
* CurrentItemInPlaceUTF8 will give us back a _shared_ pointer in
|
|
* type. This is bad XPCOM practice. It is evil, and requires
|
|
* great care with the relative lifetimes of type and regEnum.
|
|
*
|
|
* It is also faster, and less painful in the allocation department.
|
|
*/
|
|
rv = regEnum->CurrentItemInPlaceUTF8(&throwAway, &type);
|
|
if (NS_FAILED(rv))
|
|
continue;
|
|
|
|
nsCOMPtr<nsIComponentLoader> loader;
|
|
/* this will create it if we haven't already */
|
|
GetLoaderForType(type, getter_AddRefs(loader));
|
|
|
|
continue;
|
|
}
|
|
|
|
/* iterate over all known loaders and ask them to autoregister. */
|
|
struct AutoReg_closure closure;
|
|
/* XXX convert when to nsIComponentLoader::(when) properly */
|
|
closure.when = when;
|
|
closure.spec = dir.get();
|
|
closure.status = NS_OK;
|
|
closure.native = mNativeComponentLoader; // prevent duplicate autoreg
|
|
|
|
mLoaders->Enumerate(AutoRegister_enumerate, &closure);
|
|
if (NS_FAILED(closure.status))
|
|
return closure.status;
|
|
|
|
do {
|
|
closure.registered = PR_FALSE;
|
|
mLoaders->Enumerate(RegisterDeferred_enumerate, &closure);
|
|
} while (NS_SUCCEEDED(closure.status) && closure.registered);
|
|
|
|
return closure.status;
|
|
}
|
|
|
|
static PRBool PR_CALLBACK
|
|
AutoRegisterComponent_enumerate(nsHashKey *key, void *aData, void *aClosure)
|
|
{
|
|
PRBool didRegister;
|
|
nsIComponentLoader *loader = (nsIComponentLoader *)aData;
|
|
struct AutoReg_closure *closure =
|
|
(struct AutoReg_closure *)aClosure;
|
|
|
|
closure->status = loader->AutoRegisterComponent(closure->when,
|
|
closure->spec,
|
|
&didRegister);
|
|
|
|
if (NS_SUCCEEDED(closure->status) && didRegister)
|
|
return PR_FALSE; // Stop enumeration as we are done
|
|
return PR_TRUE;
|
|
}
|
|
|
|
static PRBool
|
|
AutoUnregisterComponent_enumerate(nsHashKey *key, void *aData, void *aClosure)
|
|
{
|
|
PRBool didUnregister;
|
|
nsIComponentLoader *loader = (nsIComponentLoader *)aData;
|
|
struct AutoReg_closure *closure =
|
|
(struct AutoReg_closure *)aClosure;
|
|
|
|
closure->status = loader->AutoUnregisterComponent(closure->when,
|
|
closure->spec,
|
|
&didUnregister);
|
|
if (NS_SUCCEEDED(closure->status) && didUnregister)
|
|
return PR_FALSE; // Stop enumeration as we are done
|
|
return PR_TRUE; // Let enumeration continue
|
|
|
|
}
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::AutoRegisterComponent(PRInt32 when,
|
|
nsIFile *component)
|
|
{
|
|
struct AutoReg_closure closure;
|
|
|
|
/* XXX convert when to nsIComponentLoader::(when) properly */
|
|
closure.when = (PRInt32)when;
|
|
closure.spec = component;
|
|
closure.status = NS_OK;
|
|
|
|
/*
|
|
* Do we have to give the native loader first crack at it?
|
|
* I vote ``no''.
|
|
*/
|
|
mLoaders->Enumerate(AutoRegisterComponent_enumerate, &closure);
|
|
return NS_FAILED(closure.status)
|
|
? NS_ERROR_FACTORY_NOT_REGISTERED : NS_OK;
|
|
|
|
}
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::AutoUnregisterComponent(PRInt32 when,
|
|
nsIFile *component)
|
|
{
|
|
struct AutoReg_closure closure;
|
|
|
|
/* XXX convert when to nsIComponentLoader::(when) properly */
|
|
closure.when = (PRInt32)when;
|
|
closure.spec = component;
|
|
closure.status = NS_OK;
|
|
|
|
mLoaders->Enumerate(AutoUnregisterComponent_enumerate, &closure);
|
|
|
|
return NS_FAILED(closure.status)
|
|
? 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, !mPrePopulationDone));
|
|
return NS_OK;
|
|
}
|
|
|
|
static NS_IMETHODIMP
|
|
ConvertFactoryEntryToCID(nsHashKey *key, void *data, void *convert_data,
|
|
nsISupports **retval)
|
|
{
|
|
nsComponentManagerImpl *compMgr = (nsComponentManagerImpl*) convert_data;
|
|
nsresult rv;
|
|
|
|
nsISupportsID* cidHolder;
|
|
|
|
if(NS_SUCCEEDED(rv =
|
|
compMgr->CreateInstanceByProgID(NS_SUPPORTS_ID_PROGID,
|
|
nsnull,
|
|
NS_GET_IID(nsISupportsID),
|
|
(void **)&cidHolder)))
|
|
{
|
|
nsFactoryEntry *fe = (nsFactoryEntry *) data;
|
|
cidHolder->SetData(&fe->cid);
|
|
*retval = cidHolder;
|
|
}
|
|
else
|
|
*retval = nsnull;
|
|
|
|
return rv;
|
|
}
|
|
|
|
static NS_IMETHODIMP
|
|
ConvertProgIDKeyToString(nsHashKey *key, void *data, void *convert_data,
|
|
nsISupports **retval)
|
|
{
|
|
nsComponentManagerImpl *compMgr = (nsComponentManagerImpl*) convert_data;
|
|
nsresult rv;
|
|
|
|
nsISupportsString* strHolder;
|
|
|
|
|
|
rv = compMgr->CreateInstanceByProgID(NS_SUPPORTS_STRING_PROGID, nsnull,
|
|
NS_GET_IID(nsISupportsString),
|
|
(void **)&strHolder);
|
|
if(NS_SUCCEEDED(rv))
|
|
{
|
|
nsStringKey *strKey = (nsStringKey *) key;
|
|
const nsString& str = strKey->GetString();
|
|
char* yetAnotherCopyOfTheString = str.ToNewCString();
|
|
if(yetAnotherCopyOfTheString)
|
|
{
|
|
strHolder->SetData(yetAnotherCopyOfTheString);
|
|
delete [] yetAnotherCopyOfTheString;
|
|
}
|
|
*retval = strHolder;
|
|
}
|
|
else
|
|
*retval = nsnull;
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::EnumerateCLSIDs(nsIEnumerator** aEmumerator)
|
|
{
|
|
if(!aEmumerator)
|
|
{
|
|
NS_ASSERTION(0, "null ptr");
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
*aEmumerator = nsnull;
|
|
|
|
nsresult rv;
|
|
if(!mPrePopulationDone)
|
|
{
|
|
rv = PlatformPrePopulateRegistry();
|
|
if(NS_FAILED(rv))
|
|
return rv;
|
|
}
|
|
|
|
return NS_NewHashtableEnumerator(mFactories, ConvertFactoryEntryToCID,
|
|
this, aEmumerator);
|
|
}
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::EnumerateProgIDs(nsIEnumerator** aEmumerator)
|
|
{
|
|
if(!aEmumerator)
|
|
{
|
|
NS_ASSERTION(0, "null ptr");
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
*aEmumerator = nsnull;
|
|
|
|
nsresult rv;
|
|
if(!mPrePopulationDone)
|
|
{
|
|
rv = PlatformPrePopulateRegistry();
|
|
if(NS_FAILED(rv))
|
|
return rv;
|
|
}
|
|
|
|
return NS_NewHashtableEnumerator(mProgIDs, ConvertProgIDKeyToString,
|
|
this, aEmumerator);
|
|
}
|
|
|
|
|
|
nsresult
|
|
nsComponentManagerImpl::GetInterface(const nsIID & uuid, void **result)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
if (uuid.Equals(NS_GET_IID(nsIServiceManager)))
|
|
{
|
|
// Return the global service manager
|
|
rv = nsServiceManager::GetGlobalServiceManager((nsIServiceManager **)result);
|
|
}
|
|
else
|
|
{
|
|
// fall through to QI as anything QIable is a superset of what canbe
|
|
// got via the GetInterface()
|
|
rv = QueryInterface(uuid, result);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
NS_COM nsresult
|
|
NS_GetGlobalComponentManager(nsIComponentManager* *result)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
if (nsComponentManagerImpl::gComponentManager == NULL)
|
|
{
|
|
// XPCOM needs initialization.
|
|
rv = NS_InitXPCOM(NULL, NULL);
|
|
}
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
// NO ADDREF since this is never intended to be released.
|
|
*result = nsComponentManagerImpl::gComponentManager;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|