gecko-dev/xpcom/components/nsNativeComponentLoader.cpp
1999-10-24 22:01:09 +00:00

1055 lines
33 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* The contents of this file are subject to the Netscape Public License
* Version 1.1 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "prlog.h"
#include "prmem.h"
#include "prerror.h"
#include "prsystem.h" // PR_GetDirectorySeparator
#include "nsNativeComponentLoader.h"
#include "nsComponentManager.h"
#include "nsCOMPtr.h"
#include "nsIServiceManager.h"
#include "nsIModule.h"
#include "xcDll.h"
#include "nsHashtable.h"
#include "nsHashtableEnumerator.h"
#define PRINT_CRITICAL_ERROR_TO_SCREEN 1
#define USE_REGISTRY 1
#define XPCOM_USE_NSGETFACTORY 1
extern PRLogModuleInfo *nsComponentManagerLog;
/* XXX consolidate with one in nsComponentManager.cpp */
//
// To prevent leaks, we are using this class. Typical use would be
// for each ptr to be deleted, create an object of these types with that ptr.
// Once that object goes out of scope, deletion and hence memory free will
// automatically happen.
//
class autoStringFree
{
public:
enum DeleteModel {
NSPR_Delete = 1,
nsCRT_String_Delete = 2
};
autoStringFree(char *Ptr, DeleteModel whichDelete): mPtr(Ptr), mWhichDelete(whichDelete) {}
~autoStringFree() {
if (mPtr)
if (mWhichDelete == NSPR_Delete) { PR_FREEIF(mPtr); }
else if (mWhichDelete == nsCRT_String_Delete) nsCRT::free(mPtr);
else PR_ASSERT(0);
}
private:
char *mPtr;
DeleteModel mWhichDelete;
};
nsNativeComponentLoader::nsNativeComponentLoader() :
mRegistry(nsnull), mCompMgr(nsnull), mDllStore(nsnull)
{
NS_INIT_REFCNT();
}
static PRBool
nsDll_Destroy(nsHashKey *aKey, void *aData, void* closure)
{
nsDll* entry = NS_STATIC_CAST(nsDll*, aData);
delete entry;
return PR_TRUE;
}
nsNativeComponentLoader::~nsNativeComponentLoader()
{
mRegistry = nsnull;
mCompMgr = nsnull;
delete mDllStore;
}
NS_IMPL_ISUPPORTS(nsNativeComponentLoader, NS_GET_IID(nsIComponentLoader));
NS_IMETHODIMP
nsNativeComponentLoader::GetFactory(const nsIID & aCID,
const char *aLocation,
const char *aType,
nsIFactory **_retval)
{
nsresult rv;
if (!_retval)
return NS_ERROR_NULL_POINTER;
/* use a hashtable of WeakRefs to store the factory object? */
/* Should this all live in xcDll? */
nsDll *dll;
rv = CreateDll(nsnull, aLocation, 0, 0, &dll);
if (NS_FAILED(rv))
return rv;
if (!dll)
return NS_ERROR_OUT_OF_MEMORY;
if (!dll->IsLoaded()) {
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
("nsNativeComponentLoader: loading \"%s\"",
dll->GetNativePath()));
if (!dll->Load()) {
PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
("nsNativeComponentLoader: load FAILED"));
char errorMsg[1024] = "<unknown; can't get error from NSPR>";
if (PR_GetErrorTextLength() < (int) sizeof(errorMsg))
PR_GetErrorText(errorMsg);
DumpLoadError(dll, "GetFactory", errorMsg);
return NS_ERROR_FAILURE;
}
}
/* Get service manager for factory */
nsIServiceManager* serviceMgr = NULL;
rv = nsServiceManager::GetGlobalServiceManager(&serviceMgr);
if (NS_FAILED(rv))
return rv; // XXX translate error code?
rv = GetFactoryFromModule(dll, aCID, _retval);
#ifdef XPCOM_USE_NSGETFACTORY
if (NS_FAILED(rv)) {
if (rv == NS_ERROR_FACTORY_NOT_LOADED) {
rv = GetFactoryFromNSGetFactory(dll, aCID, serviceMgr, _retval);
}
}
#endif
PR_LOG(nsComponentManagerLog, (NS_SUCCEEDED(rv) ? PR_LOG_DEBUG : PR_LOG_ERROR),
("nsNativeComponentLoader: Factory creation %s "
"%s -> %s\n", (NS_SUCCEEDED(rv) ? "passed" : "FAILED"),
aLocation, dll->GetNativePath()));
// If the dll failed to get us a factory. But the dll registered that
// it would be able to create a factory for this CID. mmh!
// We cannot just delete the dll as the dll could be hosting
// other CID for which factory creation can pass.
// We will just let it be. The effect will be next time we try
// creating the object, we will query the dll again. Since the
// dll is loaded, this aint a big hit. So for optimized builds
// this is ok to limp along.
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Factory creation failed");
return rv;
}
NS_IMETHODIMP
nsNativeComponentLoader::Init(nsIComponentManager *aCompMgr, nsISupports *aReg)
{
nsresult rv;
#ifdef DEBUG_shaver
fprintf(stderr, "nNCL: Init()\n");
#endif
mCompMgr = aCompMgr;
mRegistry = do_QueryInterface(aReg);
if (!mCompMgr || !mRegistry)
return NS_ERROR_INVALID_ARG;
rv = mRegistry->GetSubtree(nsIRegistry::Common, xpcomKeyName,
&mXPCOMKey);
if (NS_FAILED(rv)) {
#ifdef DEBUG_shaver
fprintf(stderr, "nsNCL::Init: registry is hosed\n");
#endif
return rv;
}
if (!mDllStore) {
mDllStore = new nsObjectHashtable(nsnull, nsnull, // never copy
nsDll_Destroy, nsnull,
256, /* Thead Safe */ PR_TRUE);
if (!mDllStore) {
#ifdef DEBUG_shaver
fprintf(stderr, "nNCL::Init: couldn't build hash table\n");
#endif
return NS_ERROR_OUT_OF_MEMORY;
}
}
// Read in all dll entries and populate the mDllStore
nsCOMPtr<nsIEnumerator> dllEnum;
rv = mRegistry->EnumerateSubtrees( mXPCOMKey, getter_AddRefs(dllEnum));
if (NS_FAILED(rv)) return rv;
rv = dllEnum->First();
for (; NS_SUCCEEDED(rv) && (dllEnum->IsDone() != NS_OK); (rv = dllEnum->Next()))
{
nsCOMPtr<nsISupports> base;
rv = dllEnum->CurrentItem(getter_AddRefs(base));
if (NS_FAILED(rv)) continue;
// Get specific interface.
nsIID nodeIID = NS_IREGISTRYNODE_IID;
nsCOMPtr<nsIRegistryNode> node;
rv = base->QueryInterface( nodeIID, getter_AddRefs(node) );
if (NS_FAILED(rv)) continue;
// Get library name
char *library = NULL;
rv = node->GetName(&library);
if (NS_FAILED(rv)) continue;
autoStringFree delete_library(library, autoStringFree::nsCRT_String_Delete);
// Get key associated with library
nsRegistryKey libKey;
rv = node->GetKey(&libKey);
if (NS_FAILED(rv)) continue;
// Create nsDll with this name
nsDll *dll = NULL;
PRUint32 lastModTime = 0;
PRUint32 fileSize = 0;
GetRegistryDllInfo(libKey, &lastModTime, &fileSize);
rv = CreateDll(NULL, library, lastModTime, fileSize, &dll);
if (NS_FAILED(rv)) continue;
}
return NS_OK;
}
NS_IMETHODIMP
nsNativeComponentLoader::AutoRegisterComponents(PRInt32 aWhen,
nsIFileSpec *aDirectory)
{
#ifdef DEBUG
/* do we _really_ want to print this every time? */
printf("nsNativeComponentLoader: autoregistering begins.\n");
#endif
nsresult rv = RegisterComponentsInDir(aWhen, aDirectory);
#ifdef DEBUG
printf("nsNativeComponentLoader: autoregistering %s\n",
NS_FAILED(rv) ? "FAILED" : "succeeded");
#endif
return rv;
}
nsresult
nsNativeComponentLoader::RegisterComponentsInDir(PRInt32 when,
nsIFileSpec *dir)
{
nsresult rv = NS_ERROR_FAILURE;
PRBool isDir = PR_FALSE;
#if 0
// Going to many of these checks is a performance hit on the mac.
// Since these routines are called relatively infrequently and
// we will fail anyway down the line if a directory aint there,
// we are commenting this check out.
// Make sure we are dealing with a directory
rv = dir->IsDirectory(&isDir);
if (NS_FAILED(rv)) return rv;
if (!isDir)
return NS_ERROR_INVALID_ARG;
#endif /* 0 */
// Create a directory iterator
nsCOMPtr<nsIDirectoryIterator>dirIterator;
rv = nsComponentManager::CreateInstance(NS_DIRECTORYITERATOR_PROGID, NULL,
NS_GET_IID(nsIDirectoryIterator),
getter_AddRefs(dirIterator));
if (NS_FAILED(rv)) return rv;
rv = dirIterator->Init(dir, PR_FALSE);
if (NS_FAILED(rv)) return rv;
// whip through the directory to register every file
nsIFileSpec *dirEntry = NULL;
PRBool more = PR_FALSE;
rv = dirIterator->Exists(&more);
if (NS_FAILED(rv)) return rv;
while (more == PR_TRUE)
{
rv = dirIterator->GetCurrentSpec(&dirEntry);
if (NS_SUCCEEDED(rv))
{
rv = dirEntry->IsDirectory(&isDir);
if (NS_SUCCEEDED(rv))
{
if (isDir == PR_TRUE)
{
// This is a directory. Grovel for components into the directory.
rv = RegisterComponentsInDir(when, dirEntry);
}
else
{
PRBool registered;
// This is a file. Try to register it.
rv = AutoRegisterComponent(when, dirEntry, &registered);
}
}
NS_RELEASE(dirEntry);
}
rv = dirIterator->Next();
if (NS_FAILED(rv)) return rv;
rv = dirIterator->Exists(&more);
if (NS_FAILED(rv)) return rv;
}
return rv;
}
static nsresult
nsFreeLibrary(nsDll *dll, nsIServiceManager *serviceMgr, PRInt32 when)
{
nsresult rv = NS_ERROR_FAILURE;
if (!dll || dll->IsLoaded() == PR_FALSE)
{
return NS_ERROR_INVALID_ARG;
}
// Get if the dll was marked for unload in an earlier round
PRBool dllMarkedForUnload = dll->IsMarkedForUnload();
// Reset dll marking for unload just in case we return with
// an error.
dll->MarkForUnload(PR_FALSE);
PRBool canUnload = PR_FALSE;
// Get the module object
nsCOMPtr<nsIModule> mobj;
/* XXXshaver cheat and use the global component manager */
rv = dll->GetModule(NS_STATIC_CAST(nsIComponentManager*, nsComponentManagerImpl::gComponentManager),
getter_AddRefs(mobj));
if (NS_SUCCEEDED(rv))
{
rv = mobj->CanUnload(nsComponentManagerImpl::gComponentManager, &canUnload);
}
#ifndef OBSOLETE_MODULE_LOADING
else
{
// Try the old method of module unloading
nsCanUnloadProc proc = (nsCanUnloadProc)dll->FindSymbol("NSCanUnload");
if (proc)
{
canUnload = proc(serviceMgr);
rv = NS_OK; // No error status returned by call.
}
else
{
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
("nsComponentManager: Unload cant get nsIModule or CanUnload for %s",
dll->GetNativePath()));
return rv;
}
}
#endif /* OBSOLETE_MODULE_LOADING */
mobj = nsnull; // Release our reference to the module object
// When shutting down, whether we can unload the dll or not,
// we will shutdown the dll to release any memory it has got
if (when == nsIComponentManager::NS_Shutdown)
{
dll->Shutdown();
}
// Check error status on CanUnload() call
if (NS_FAILED(rv))
{
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
("nsComponentManager: nsIModule::CanUnload() returned error for %s.",
dll->GetNativePath()));
return rv;
}
if (canUnload)
{
if (dllMarkedForUnload)
{
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
("nsComponentManager: + Unloading \"%s\".", dll->GetNativePath()));
#if 0
// XXX dlls aren't counting their outstanding instances correctly
// XXX hence, dont unload until this gets enforced.
rv = dll->Unload();
#endif /* 0 */
}
else
{
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
("nsComponentManager: Ready for unload \"%s\".", dll->GetNativePath()));
}
}
else
{
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
("nsComponentManager: + NOT Unloading %s", dll->GetNativePath()));
rv = NS_ERROR_FAILURE;
}
return rv;
}
struct freeLibrariesClosure
{
nsIServiceManager *serviceMgr;
PRInt32 when;
};
static PRBool
nsFreeLibraryEnum(nsHashKey *aKey, void *aData, void* closure)
{
nsDll *dll = (nsDll *) aData;
struct freeLibrariesClosure *callData = (struct freeLibrariesClosure *) closure;
nsFreeLibrary(dll,
(callData ? callData->serviceMgr : NULL),
(callData ? callData->when : nsIComponentManager::NS_Timer));
return PR_TRUE;
}
/*
* SelfRegisterDll
*
* Given a dll abstraction, this will load, selfregister the dll and
* unload the dll.
*
*/
nsresult
nsNativeComponentLoader::SelfRegisterDll(nsDll *dll, const char *registryLocation)
{
// Precondition: dll is not loaded already
PR_ASSERT(dll->IsLoaded() == PR_FALSE);
nsIServiceManager* serviceMgr = NULL;
nsresult res = nsServiceManager::GetGlobalServiceManager(&serviceMgr);
if (NS_FAILED(res)) return res;
if (dll->Load() == PR_FALSE)
{
// Cannot load. Probably not a dll.
char errorMsg[1024] = "Cannot get error from nspr. Not enough memory.";
if (PR_GetErrorTextLength() < (int) sizeof(errorMsg))
PR_GetErrorText(errorMsg);
DumpLoadError(dll, "SelfRegisterDll", errorMsg);
return NS_ERROR_FAILURE;
}
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
("nsNativeComponentLoader: + Loaded \"%s\".", dll->GetNativePath()));
// Tell the module to self register
nsCOMPtr<nsIModule> mobj;
res = dll->GetModule(mCompMgr, getter_AddRefs(mobj));
if (NS_SUCCEEDED(res))
{
PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
("nsNativeComponentLoader: %s using nsIModule to register self.",
dll->GetNativePath()));
nsCOMPtr<nsIFileSpec> fs;
res = dll->GetDllSpec(getter_AddRefs(fs));
if (NS_SUCCEEDED(res))
res = mobj->RegisterSelf(mCompMgr, fs, registryLocation,
nativeComponentType);
else
{
PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
("nsNativeComponentLoader: dll->GetDllSpec() on %s FAILED.",
dll->GetNativePath()));
}
mobj = NULL; // Force a release of the Module object before unload()
}
#ifndef OBSOLETE_MODULE_LOADING
else
{
res = NS_ERROR_NO_INTERFACE;
nsRegisterProc regproc = (nsRegisterProc)dll->FindSymbol("NSRegisterSelf");
if (regproc)
{
// Call the NSRegisterSelfProc to enable dll registration
res = regproc(serviceMgr, registryLocation);
PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG,
("NSRegisterSelf(%s, %s) %s",
serviceMgr ? "serviceMgr" : "(null)",
registryLocation,
NS_FAILED(res) ? "FAILED" : "succeeded"));
}
}
#endif /* OBSOLETE_MODULE_LOADING */
// Update the timestamp and size of the dll in registry
dll->Sync();
SetRegistryDllInfo(registryLocation, dll->GetLastModifiedTime(), dll->GetSize());
return res;
}
//
// MOZ_DEMANGLE_SYMBOLS is only a linux + MOZ_DEBUG thing.
//
#if defined(MOZ_DEMANGLE_SYMBOLS)
#include "nsTraceRefcnt.h" // for nsTraceRefcnt::DemangleSymbol()
#endif
nsresult
nsNativeComponentLoader::DumpLoadError(nsDll *dll,
const char *aCallerName,
const char *aNsprErrorMsg)
{
PR_ASSERT(aCallerName != NULL);
if (nsnull == dll || nsnull == aNsprErrorMsg)
return NS_OK;
nsCAutoString errorMsg(aNsprErrorMsg);
#if defined(MOZ_DEMANGLE_SYMBOLS)
// Demangle undefined symbols
nsCAutoString undefinedMagicString("undefined symbol:");
PRInt32 offset = errorMsg.Find(undefinedMagicString, PR_TRUE);
if (offset != kNotFound)
{
nsCAutoString symbol(errorMsg);
nsCAutoString demangledSymbol("");
symbol.Cut(0,offset);
symbol.Cut(0,undefinedMagicString.Length());
symbol.StripWhitespace();
char demangled[4096] = "\0";
nsTraceRefcnt::DemangleSymbol(symbol,demangled,sizeof(demangled));
if (demangled && strlen(demangled))
demangledSymbol = demangled;
if (demangledSymbol != (const char *) "")
{
nsCAutoString tmp(errorMsg);
tmp.Cut(offset + undefinedMagicString.Length(),
tmp.Length() - offset - undefinedMagicString.Length());
tmp += " \n";
tmp += demangledSymbol;
errorMsg = tmp;
}
}
#endif // MOZ_DEMANGLE_SYMBOLS
// Do NSPR log
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
("nsNativeComponentLoader: %s(%s) Load FAILED with error:%s",
aCallerName,
dll->GetNativePath(),
(const char *) errorMsg));
// Dump to screen if needed
#ifdef PRINT_CRITICAL_ERROR_TO_SCREEN
printf("**************************************************\n"
"nsNativeComponentLoader: %s(%s) Load FAILED with error: %s\n"
"**************************************************\n",
aCallerName,
dll->GetNativePath(),
(const char *) errorMsg);
#endif
return NS_OK;
}
nsresult
nsNativeComponentLoader::SelfUnregisterDll(nsDll *dll)
{
// Precondition: dll is not loaded
PR_ASSERT(dll->IsLoaded() == PR_FALSE);
nsIServiceManager* serviceMgr = NULL;
nsresult res = nsServiceManager::GetGlobalServiceManager(&serviceMgr);
if (NS_FAILED(res)) return res;
if (dll->Load() == PR_FALSE)
{
// Cannot load. Probably not a dll.
return(NS_ERROR_FAILURE);
}
// Tell the module to self register
nsCOMPtr<nsIModule> mobj;
res = dll->GetModule(mCompMgr, getter_AddRefs(mobj));
if (NS_SUCCEEDED(res))
{
PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
("nsNativeComponentLoader: %s using nsIModule to unregister self.", dll->GetNativePath()));
nsCOMPtr<nsIFileSpec> fs;
res = dll->GetDllSpec(getter_AddRefs(fs));
if (NS_SUCCEEDED(res))
res = mobj->UnregisterSelf(mCompMgr, fs, /* XXX location */ "");
else
{
PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
("nsNativeComponentLoader: dll->GetDllSpec() on %s FAILED.", dll->GetNativePath()));
}
mobj = NULL; // Force a release of the Module object before unload()
}
#ifndef OBSOLETE_MODULE_LOADING
else
{
res = NS_ERROR_NO_INTERFACE;
nsUnregisterProc unregproc =
(nsUnregisterProc) dll->FindSymbol("NSUnregisterSelf");
if (unregproc)
{
// Call the NSUnregisterSelfProc to enable dll de-registration
res = unregproc(serviceMgr, dll->GetPersistentDescriptorString());
}
}
#endif /* OBSOLETE_MODULE_LOADING */
dll->Unload();
return res;
}
nsresult
nsNativeComponentLoader::AutoRegisterComponent(PRInt32 when,
nsIFileSpec *component,
PRBool *registered)
{
nsresult rv;
if (!registered)
return NS_ERROR_NULL_POINTER;
/* this should be a pref or registry entry, or something */
const char *ValidDllExtensions[] = {
".dll", /* Windows */
".dso", /* Unix ? */
".dylib", /* Unix: Rhapsody */
".so", /* Unix */
".so.1.0", /* Unix: BSD */
".sl", /* Unix: HP-UX */
".shlb", /* Mac ? */
#if defined(VMS)
".exe", /* Open VMS */
#endif
".dlm", /* new for all platforms */
NULL
};
*registered = PR_FALSE;
#if 0
// This is a performance hit on mac. Since we have already checked
// this; plus is we dont, load will fail anyway later on, this
// is being commented out.
// Ensure we are dealing with a file as opposed to a dir
PRBool b = PR_FALSE;
rv = component->IsFile(&b);
if (NS_FAILED(rv) || !b)
return rv;
#endif /* 0 */
// deal only with files that have a valid extension
PRBool validExtension = PR_FALSE;
#ifdef XP_MAC
// rjc - on Mac, check the file's type code (skip checking the creator code)
nsFileSpec fs;
if (NS_FAILED(rv = component->GetFileSpec(&fs)))
return(rv);
CInfoPBRec catInfo;
OSErr err = fs.GetCatInfo(catInfo);
if (!err)
{
// on Mac, Mozilla shared libraries are of type 'shlb'
// Note: we don't check the creator (which for Mozilla is 'MOZZ')
// so that 3rd party shared libraries will be noticed!
if ((catInfo.hFileInfo.ioFlFndrInfo.fdType == 'shlb')
/* && (catInfo.hFileInfo.ioFlFndrInfo.fdCreator == 'MOZZ') */ )
{
validExtension = PR_TRUE;
}
}
#else
char *leafName = NULL;
rv = component->GetLeafName(&leafName);
if (NS_FAILED(rv)) return rv;
int flen = PL_strlen(leafName);
for (int i=0; ValidDllExtensions[i] != NULL; i++)
{
int extlen = PL_strlen(ValidDllExtensions[i]);
// Does fullname end with this extension
if (flen >= extlen &&
!PL_strcasecmp(&(leafName[flen - extlen]), ValidDllExtensions[i])
)
{
validExtension = PR_TRUE;
break;
}
}
if (leafName) nsCRT::free(leafName);
#endif
if (validExtension == PR_FALSE)
// Skip invalid extensions
return NS_OK;
/*
* Get the name of the dll
* I think I get to go through every type of string here.
* Pink would be so proud.
*/
char *persistentDescriptor;
rv = mCompMgr->RegistryLocationForSpec(component, &persistentDescriptor);
if (NS_FAILED(rv))
return rv;
autoStringFree
delete_persistentDescriptor(persistentDescriptor,
autoStringFree::nsCRT_String_Delete);
nsStringKey key(persistentDescriptor);
// Get the registry representation of the dll, if any
nsDll *dll;
rv = CreateDll(component, persistentDescriptor, 0, 0, &dll);
if (NS_FAILED(rv))
return rv;
if (dll != NULL)
{
// Make sure the dll is OK
if (dll->GetStatus() != NS_OK)
{
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
("nsNativeComponentLoader: + nsDll not NS_OK \"%s\". Skipping...",
dll->GetNativePath()));
return NS_ERROR_FAILURE;
}
// We already have seen this dll. Check if this dll changed
if (!dll->HasChanged())
{
// Dll hasn't changed. Skip.
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
("nsComponentManager: + nsDll not changed \"%s\". Skipping...",
dll->GetNativePath()));
return NS_OK;
}
// Aagh! the dll has changed since the last time we saw it.
// re-register dll
if (dll->IsLoaded())
{
// We loaded the old version of the dll and now we find that the
// on-disk copy if newer. Try to unload the dll.
nsIServiceManager *serviceMgr = NULL;
nsServiceManager::GetGlobalServiceManager(&serviceMgr);
rv = nsFreeLibrary(dll, serviceMgr, when);
if (NS_FAILED(rv))
{
// THIS IS THE WORST SITUATION TO BE IN.
// Dll doesn't want to be unloaded. Cannot re-register
// this dll.
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
("nsNativeComponentLoader: *** Dll already loaded. "
"Cannot unload either. Hence cannot re-register "
"\"%s\". Skipping...", dll->GetNativePath()));
return rv;
}
else {
// dll doesn't have a CanUnload proc. Guess it is
// ok to unload it.
dll->Unload();
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
("nsNativeComponentLoader: + Unloading \"%s\". (no CanUnloadProc).",
dll->GetNativePath()));
}
} // dll isloaded
// Sanity.
if (dll->IsLoaded())
{
// We went through all the above to make sure the dll
// is unloaded. And here we are with the dll still
// loaded. Whoever taught dp programming...
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
("nsNativeComponentLoader: Dll still loaded. Cannot re-register "
"\"%s\". Skipping...", dll->GetNativePath()));
return NS_ERROR_FAILURE;
}
} // dll != NULL
else
{
// Create and add the dll to the mDllStore
// It is ok to do this even if the creation of nsDll
// didnt succeed. That way we wont do this again
// when we encounter the same dll.
dll = new nsDll(persistentDescriptor);
if (dll == NULL)
return NS_ERROR_OUT_OF_MEMORY;
mDllStore->Put(&key, (void *) dll);
} // dll == NULL
// Either we are seeing the dll for the first time or the dll has
// changed since we last saw it and it is unloaded successfully.
//
// Now we can try register the dll for sure.
nsresult res = SelfRegisterDll(dll, persistentDescriptor);
nsresult ret = NS_OK;
if (NS_FAILED(res))
{
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
("nsNativeComponentLoader: Autoregistration FAILED for "
"\"%s\". Skipping...", dll->GetNativePath()));
// Mark dll as not xpcom dll along with modified time and size in
// the registry so that we wont need to load the dll again every
// session until the dll changes.
#ifdef USE_REGISTRY
#endif /* USE_REGISTRY */
ret = NS_ERROR_FAILURE;
}
else
{
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
("nsNativeComponentLoader: Autoregistration Passed for "
"\"%s\".", dll->GetNativePath()));
// Marking dll along with modified time and size in the
// registry happens at PlatformRegister(). No need to do it
// here again.
}
return ret;
}
nsresult
nsNativeComponentLoader::OnRegister(const nsIID &aCID, const char *aType,
const char *aClassName,
const char *aProgID, const char *aLocation,
PRBool aReplace, PRBool aPersist)
{
return NS_OK;
}
nsresult
nsNativeComponentLoader::UnloadAll(PRInt32 aWhen)
{
PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG, ("nsNativeComponentLoader: Unloading...."));
struct freeLibrariesClosure callData;
callData.serviceMgr = NULL; // XXX need to get this as a parameter
callData.when = aWhen;
// Cycle through the dlls checking to see if they want to be unloaded
mDllStore->Enumerate(nsFreeLibraryEnum, &callData);
return NS_OK;
}
nsresult
nsNativeComponentLoader::GetRegistryDllInfo(const char *aLocation,
PRUint32 *lastModifiedTime,
PRUint32 *fileSize)
{
nsresult rv;
nsRegistryKey key;
rv = mRegistry->GetSubtreeRaw(mXPCOMKey, aLocation, &key);
if (NS_FAILED(rv)) return rv;
return GetRegistryDllInfo(key, lastModifiedTime, fileSize);
}
nsresult
nsNativeComponentLoader::GetRegistryDllInfo(nsRegistryKey key,
PRUint32 *lastModifiedTime,
PRUint32 *fileSize)
{
PRInt32 lastMod;
nsresult rv = mRegistry->GetInt(key, lastModValueName, &lastMod);
if (NS_FAILED(rv)) return rv;
*lastModifiedTime = lastMod;
PRInt32 fsize;
rv = mRegistry->GetInt(key, fileSizeValueName, &fsize);
if (NS_FAILED(rv)) return rv;
*fileSize = fsize;
return NS_OK;
}
nsresult
nsNativeComponentLoader::SetRegistryDllInfo(const char *aLocation,
PRUint32 lastModifiedTime,
PRUint32 fileSize)
{
nsresult rv;
nsRegistryKey key;
rv = mRegistry->GetSubtreeRaw(mXPCOMKey, aLocation, &key);
if (NS_FAILED(rv)) return rv;
rv = mRegistry->SetInt(key, lastModValueName, lastModifiedTime);
if (NS_FAILED(rv)) return rv;
rv = mRegistry->SetInt(key, fileSizeValueName, fileSize);
return rv;
}
//
// CreateDll
// The only way to create a dll or get it from the dll cache. This will
// be called in multiple situations:
//
// 1. Autoregister will create one for each dll it is trying to register. This
// call will be passing a spec in.
// {spec, NULL, 0, 0}
//
// 2. GetFactory() This will call CreateDll() with a null spec but will give
// the registry represented name of the dll. If modtime and size are zero,
// we will go the registry to find the right modtime and size.
// {NULL, rel:libpref.so, 0, 0}
//
// 3. Prepopulation of dllCache A dll object created off a registry entry.
// Specifically dll name is stored in rel: or abs: or lib: formats in the
// registry along with its lastModTime and fileSize.
// {NULL, rel:libpref.so, 8985659, 20987}
nsresult
nsNativeComponentLoader::CreateDll(nsIFileSpec *aSpec, const char *aLocation,
PRUint32 modificationTime, PRUint32 fileSize,
nsDll **aDll)
{
nsDll *dll;
nsFileSpec dllSpec;
nsCOMPtr<nsIFileSpec> spec;
nsresult rv;
nsStringKey key(aLocation);
dll = (nsDll *)mDllStore->Get(&key);
if (dll)
{
*aDll = dll;
return NS_OK;
}
if (!aSpec)
{
if (!nsCRT::strncmp(aLocation, XPCOM_LIB_PREFIX, 4)) {
dll = new nsDll(aLocation+4, 1 /* dumb magic flag */);
if (!dll) return NS_ERROR_OUT_OF_MEMORY;
} else {
rv = mCompMgr->SpecForRegistryLocation(aLocation,
getter_AddRefs(spec));
if (NS_FAILED(rv))
return rv;
}
}
else
{
spec = aSpec;
}
if (!dll)
{
if (modificationTime == 0 && fileSize == 0)
{
// Get the modtime and filesize from the registry
rv = GetRegistryDllInfo(aLocation, &modificationTime, &fileSize);
}
dll = new nsDll(spec, aLocation, modificationTime, fileSize);
}
if (!dll)
return NS_ERROR_OUT_OF_MEMORY;
*aDll = dll;
mDllStore->Put(&key, dll);
return NS_OK;
}
nsresult
nsNativeComponentLoader::GetFactoryFromModule(nsDll *aDll, const nsCID &aCID,
nsIFactory **aFactory)
{
nsresult rv;
nsCOMPtr<nsIModule> module;
rv = aDll->GetModule(mCompMgr, getter_AddRefs(module));
if (NS_FAILED(rv))
return rv;
PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG,
("nsNativeComponentLoader: %s using nsIModule to get factory",
aDll->GetNativePath()));
return module->GetClassObject(mCompMgr, aCID, NS_GET_IID(nsIFactory),
(void **)aFactory);
}
nsresult
nsNativeComponentLoader::GetFactoryFromNSGetFactory(nsDll *aDll,
const nsCID &aCID,
nsIServiceManager *aServMgr,
nsIFactory **aFactory)
{
#ifdef XPCOM_USE_NSGETFACTORY
nsFactoryProc getFactory =
(nsFactoryProc) aDll->FindSymbol("NSGetFactory");
if (!getFactory)
return NS_ERROR_FACTORY_NOT_LOADED;
PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG,
("nsNativeComponentLoader: %s using OBSOLETE NSGetFactory\n",
aDll->GetNativePath()));
/*
* There was a time when CLSIDToProgID was used to get className
* and progID, but that day is long past. This code is not long
* for this earth, so we just pass nsnull.
*/
return getFactory(aServMgr, aCID, nsnull /*className */,
nsnull /* progID */, aFactory);
#else /* !XPCOM_USE_NSGETFACTORY */
return NS_ERROR_FACTORY_NOT_LOADED;
#endif /* XPCOM_USE_NSGETFACTORY */
}