gecko-dev/xpcom/glue/nsGenericFactory.cpp
2002-09-27 13:07:01 +00:00

552 lines
17 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* 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 the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Pierre Phaneuf <pp@ludusdesign.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 NPL, 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 NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
// DO NOT COPY THIS CODE INTO YOUR SOURCE! USE NS_IMPL_NSGETMODULE()
#include "nsGenericFactory.h"
#include "nsMemory.h"
#include "nsCOMPtr.h"
#include "nsIComponentManager.h"
#include "nsIComponentRegistrar.h"
#ifdef XPCOM_GLUE
#include "nsXPCOMGlue.h"
#include "nsIServiceManager.h"
#include "nsIFile.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDirectoryService.h"
#endif
nsGenericFactory::nsGenericFactory(const nsModuleComponentInfo *info)
: mInfo(info)
{
NS_INIT_ISUPPORTS();
if (mInfo && mInfo->mClassInfoGlobal)
*mInfo->mClassInfoGlobal = NS_STATIC_CAST(nsIClassInfo *, this);
}
nsGenericFactory::~nsGenericFactory()
{
if (mInfo) {
if (mInfo->mFactoryDestructor)
mInfo->mFactoryDestructor();
if (mInfo->mClassInfoGlobal)
*mInfo->mClassInfoGlobal = 0;
}
}
NS_IMPL_THREADSAFE_ISUPPORTS3(nsGenericFactory,
nsIGenericFactory,
nsIFactory,
nsIClassInfo)
NS_IMETHODIMP nsGenericFactory::CreateInstance(nsISupports *aOuter,
REFNSIID aIID, void **aResult)
{
if (mInfo->mConstructor) {
return mInfo->mConstructor(aOuter, aIID, aResult);
}
return NS_ERROR_FACTORY_NOT_REGISTERED;
}
NS_IMETHODIMP nsGenericFactory::LockFactory(PRBool aLock)
{
// XXX do we care if (mInfo->mFlags & THREADSAFE)?
return NS_OK;
}
NS_IMETHODIMP nsGenericFactory::GetInterfaces(PRUint32 *countp,
nsIID* **array)
{
if (!mInfo->mGetInterfacesProc) {
*countp = 0;
*array = nsnull;
return NS_OK;
}
return mInfo->mGetInterfacesProc(countp, array);
}
NS_IMETHODIMP nsGenericFactory::GetHelperForLanguage(PRUint32 language,
nsISupports **helper)
{
if (mInfo->mGetLanguageHelperProc)
return mInfo->mGetLanguageHelperProc(language, helper);
*helper = nsnull;
return NS_OK;
}
NS_IMETHODIMP nsGenericFactory::GetContractID(char **aContractID)
{
if (mInfo->mContractID) {
*aContractID = (char *)nsMemory::Alloc(strlen(mInfo->mContractID) + 1);
if (!*aContractID)
return NS_ERROR_OUT_OF_MEMORY;
strcpy(*aContractID, mInfo->mContractID);
} else {
*aContractID = nsnull;
}
return NS_OK;
}
NS_IMETHODIMP nsGenericFactory::GetClassDescription(char * *aClassDescription)
{
if (mInfo->mDescription) {
*aClassDescription = (char *)
nsMemory::Alloc(strlen(mInfo->mDescription) + 1);
if (!*aClassDescription)
return NS_ERROR_OUT_OF_MEMORY;
strcpy(*aClassDescription, mInfo->mDescription);
} else {
*aClassDescription = nsnull;
}
return NS_OK;
}
NS_IMETHODIMP nsGenericFactory::GetClassID(nsCID * *aClassID)
{
*aClassID =
NS_REINTERPRET_CAST(nsCID*,
nsMemory::Clone(&mInfo->mCID, sizeof mInfo->mCID));
if (! *aClassID)
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
NS_IMETHODIMP nsGenericFactory::GetClassIDNoAlloc(nsCID *aClassID)
{
*aClassID = mInfo->mCID;
return NS_OK;
}
NS_IMETHODIMP nsGenericFactory::GetImplementationLanguage(PRUint32 *langp)
{
*langp = nsIProgrammingLanguage::CPLUSPLUS;
return NS_OK;
}
NS_IMETHODIMP nsGenericFactory::GetFlags(PRUint32 *flagsp)
{
*flagsp = mInfo->mFlags;
return NS_OK;
}
// nsIGenericFactory: component-info accessors
NS_IMETHODIMP nsGenericFactory::SetComponentInfo(const nsModuleComponentInfo *info)
{
if (mInfo && mInfo->mClassInfoGlobal)
*mInfo->mClassInfoGlobal = 0;
mInfo = info;
if (mInfo && mInfo->mClassInfoGlobal)
*mInfo->mClassInfoGlobal = NS_STATIC_CAST(nsIClassInfo *, this);
return NS_OK;
}
NS_IMETHODIMP nsGenericFactory::GetComponentInfo(const nsModuleComponentInfo **infop)
{
*infop = mInfo;
return NS_OK;
}
NS_METHOD nsGenericFactory::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
{
// sorry, aggregation not spoken here.
nsresult res = NS_ERROR_NO_AGGREGATION;
if (outer == NULL) {
nsGenericFactory* factory = new nsGenericFactory;
if (factory != NULL) {
res = factory->QueryInterface(aIID, aInstancePtr);
if (res != NS_OK)
delete factory;
} else {
res = NS_ERROR_OUT_OF_MEMORY;
}
}
return res;
}
NS_COM nsresult
NS_NewGenericFactory(nsIGenericFactory* *result,
const nsModuleComponentInfo *info)
{
nsresult rv;
nsIGenericFactory* fact;
rv = nsGenericFactory::Create(NULL, NS_GET_IID(nsIGenericFactory), (void**)&fact);
if (NS_FAILED(rv)) return rv;
rv = fact->SetComponentInfo(info);
if (NS_FAILED(rv)) goto error;
*result = fact;
return rv;
error:
NS_RELEASE(fact);
return rv;
}
////////////////////////////////////////////////////////////////////////////////
nsGenericModule::nsGenericModule(const char* moduleName, PRUint32 componentCount,
const nsModuleComponentInfo* components,
nsModuleConstructorProc ctor,
nsModuleDestructorProc dtor)
: mInitialized(PR_FALSE),
mModuleName(moduleName),
mComponentCount(componentCount),
mComponents(components),
mFactoriesNotToBeRegistered(nsnull),
mCtor(ctor),
mDtor(dtor)
{
NS_INIT_ISUPPORTS();
}
nsGenericModule::~nsGenericModule()
{
Shutdown();
#ifdef XPCOM_GLUE
XPCOMGlueShutdown();
#endif
}
NS_IMPL_THREADSAFE_ISUPPORTS1(nsGenericModule, nsIModule)
nsresult
nsGenericModule::AddFactoryNode(nsIGenericFactory* fact)
{
if (!fact)
return NS_ERROR_FAILURE;
FactoryNode *node = new FactoryNode(fact, mFactoriesNotToBeRegistered);
if (!node)
return NS_ERROR_OUT_OF_MEMORY;
mFactoriesNotToBeRegistered = node;
return NS_OK;
}
// Perform our one-time intialization for this module
nsresult
nsGenericModule::Initialize(nsIComponentManager *compMgr)
{
nsresult rv;
if (mInitialized) {
return NS_OK;
}
if (mCtor) {
rv = mCtor(this);
if (NS_FAILED(rv))
return rv;
}
#ifdef XPCOM_GLUE
nsCOMPtr<nsIServiceManager> servMgr = do_QueryInterface(compMgr, &rv);
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsIProperties> dirService;
rv = servMgr->GetServiceByContractID(NS_DIRECTORY_SERVICE_CONTRACTID,
NS_GET_IID(nsIProperties),
getter_AddRefs(dirService));
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsIFile> xpcomDll;
rv = dirService->Get(NS_XPCOM_LIBRARY_FILE, NS_GET_IID(nsIFile), getter_AddRefs(xpcomDll));
if (NS_FAILED(rv))
return rv;
nsCAutoString path;
xpcomDll->GetNativePath(path);
rv = XPCOMGlueStartup(path.get());
if (NS_FAILED(rv))
return rv;
#endif
nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(compMgr, &rv);
if (NS_FAILED(rv))
return rv;
// Eagerly populate factory/class object hash for entries
// without constructors. If we didn't, the class object would
// never get created. Also create the factory, which doubles
// as the class object, if the EAGER_CLASSINFO flag was given.
// This allows objects to be created (within their modules)
// via operator new rather than CreateInstance, yet still be
// QI'able to nsIClassInfo.
const nsModuleComponentInfo* desc = mComponents;
for (PRUint32 i = 0; i < mComponentCount; i++) {
if (!desc->mConstructor ||
(desc->mFlags & nsIClassInfo::EAGER_CLASSINFO)) {
nsCOMPtr<nsIGenericFactory> fact;
nsresult rv = NS_NewGenericFactory(getter_AddRefs(fact), desc);
if (NS_FAILED(rv)) return rv;
// if we don't have a mConstructor, then we should not populate
// the component manager.
if (!desc->mConstructor) {
rv = AddFactoryNode(fact);
} else {
rv = registrar->RegisterFactory(desc->mCID,
desc->mDescription,
desc->mContractID,
fact);
}
if (NS_FAILED(rv)) return rv;
}
desc++;
}
mInitialized = PR_TRUE;
return NS_OK;
}
// Shutdown this module, releasing all of the module resources
void
nsGenericModule::Shutdown()
{
// Free cached factories that were not registered.
FactoryNode* node;
while (mFactoriesNotToBeRegistered)
{
node = mFactoriesNotToBeRegistered->mNext;
delete mFactoriesNotToBeRegistered;
mFactoriesNotToBeRegistered = node;
}
if (mInitialized) {
mInitialized = PR_FALSE;
if (mDtor)
mDtor(this);
}
}
// Create a factory object for creating instances of aClass.
NS_IMETHODIMP
nsGenericModule::GetClassObject(nsIComponentManager *aCompMgr,
const nsCID& aClass,
const nsIID& aIID,
void** r_classObj)
{
nsresult rv;
// Defensive programming: Initialize *r_classObj in case of error below
if (!r_classObj) {
return NS_ERROR_INVALID_POINTER;
}
*r_classObj = NULL;
// Do one-time-only initialization if necessary
if (!mInitialized) {
rv = Initialize(aCompMgr);
if (NS_FAILED(rv)) {
// Initialization failed! yikes!
return rv;
}
}
// Choose the appropriate factory, based on the desired instance
// class type (aClass).
const nsModuleComponentInfo* desc = mComponents;
for (PRUint32 i = 0; i < mComponentCount; i++) {
if (desc->mCID.Equals(aClass)) {
nsCOMPtr<nsIGenericFactory> fact;
rv = NS_NewGenericFactory(getter_AddRefs(fact), desc);
if (NS_FAILED(rv)) return rv;
return fact->QueryInterface(aIID, r_classObj);
}
desc++;
}
// not found in descriptions
#ifndef XPCOM_GLUE
#ifdef DEBUG
char* cs = aClass.ToString();
fprintf(stderr, "+++ nsGenericModule %s: unable to create factory for %s\n", mModuleName, cs);
// leak until we resolve the nsID Allocator.
// nsCRT::free(cs);
#endif // XXX put in stop-gap so that we don't search for this one again
#endif
return NS_ERROR_FACTORY_NOT_REGISTERED;
}
NS_IMETHODIMP
nsGenericModule::RegisterSelf(nsIComponentManager *aCompMgr,
nsIFile* aPath,
const char* registryLocation,
const char* componentType)
{
nsresult rv = NS_OK;
#ifdef DEBUG
fprintf(stderr, "*** Registering %s components (all right -- a generic module!)\n", mModuleName);
#endif
const nsModuleComponentInfo* cp = mComponents;
for (PRUint32 i = 0; i < mComponentCount; i++) {
// Register the component only if it has a constructor
if (cp->mConstructor) {
nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(aCompMgr, &rv);
if (registrar)
rv = registrar->RegisterFactoryLocation(cp->mCID,
cp->mDescription,
cp->mContractID,
aPath,
registryLocation,
componentType);
if (NS_FAILED(rv)) {
#ifdef DEBUG
fprintf(stderr, "nsGenericModule %s: unable to register %s component => %x\n",
mModuleName?mModuleName:"(null)", cp->mDescription?cp->mDescription:"(null)", rv);
#endif
break;
}
}
// Call the registration hook of the component, if any
if (cp->mRegisterSelfProc)
{
rv = cp->mRegisterSelfProc(aCompMgr, aPath, registryLocation,
componentType, cp);
if (NS_FAILED(rv)) {
#ifdef DEBUG
fprintf(stderr, "nsGenericModule %s: Register hook for %s component returned error => %x\n",
mModuleName?mModuleName:"(null)", cp->mDescription?cp->mDescription:"(null)", rv);
#endif
break;
}
}
cp++;
}
return rv;
}
NS_IMETHODIMP
nsGenericModule::UnregisterSelf(nsIComponentManager* aCompMgr,
nsIFile* aPath,
const char* registryLocation)
{
#ifdef DEBUG
fprintf(stderr, "*** Unregistering %s components (all right -- a generic module!)\n", mModuleName);
#endif
const nsModuleComponentInfo* cp = mComponents;
for (PRUint32 i = 0; i < mComponentCount; i++) {
// Call the unregistration hook of the component, if any
if (cp->mUnregisterSelfProc)
{
cp->mUnregisterSelfProc(aCompMgr, aPath, registryLocation, cp);
}
// Unregister the component
nsresult rv;
nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(aCompMgr, &rv);
if (registrar)
rv = registrar->UnregisterFactoryLocation(cp->mCID, aPath);
if (NS_FAILED(rv)) {
#ifdef DEBUG
fprintf(stderr, "nsGenericModule %s: unable to unregister %s component => %x\n",
mModuleName, cp->mDescription, rv);
#endif
}
cp++;
}
return NS_OK;
}
NS_IMETHODIMP
nsGenericModule::CanUnload(nsIComponentManager *aCompMgr, PRBool *okToUnload)
{
if (!okToUnload) {
return NS_ERROR_INVALID_POINTER;
}
*okToUnload = PR_FALSE;
return NS_ERROR_FAILURE;
}
NS_COM nsresult
NS_NewGenericModule2(nsModuleInfo* info, nsIModule* *result)
{
nsresult rv = NS_OK;
// Create and initialize the module instance
nsGenericModule *m =
new nsGenericModule(info->mModuleName, info->mCount, info->mComponents,
info->mCtor, info->mDtor);
if (!m)
return NS_ERROR_OUT_OF_MEMORY;
// Increase refcnt and store away nsIModule interface to m in return_cobj
rv = m->QueryInterface(NS_GET_IID(nsIModule), (void**)result);
if (NS_FAILED(rv)) {
delete m;
m = nsnull;
}
return rv;
}
NS_COM nsresult
NS_NewGenericModule(const char* moduleName,
PRUint32 componentCount,
nsModuleComponentInfo* components,
nsModuleDestructorProc dtor,
nsIModule* *result)
{
nsModuleInfo info;
memset(&info, 0, sizeof(info));
info.mVersion = NS_MODULEINFO_VERSION;
info.mModuleName = moduleName;
info.mComponents = components;
info.mCount = componentCount;
info.mDtor = dtor;
return NS_NewGenericModule2(&info, result);
}
////////////////////////////////////////////////////////////////////////////////