Mozilla/mozilla/xpcom/components/nsNativeComponentLoader.cpp
dougt%netscape.com c01e94cad7 nsIComponentManager API Changes (bug 98553)
a) create a new nsIComponentManager with only four functions on it:
CreateInstance CreateInstanceByContractID GetClassInfo GetClassInfoByContractID.

b) rename the old nsIComponentManager to nsIComponentManagerObsolete.

c) fixes callers which use to access the nsIComponentManager for component
registration functionality.  These callers will temporary use the
nsIComponentManagerObsolete interface.

d) Create a new API NS_GetComponentManager() which mirrors the
NS_GetServiceManager()

e) Perserves the old NS_GetGlobalComponentManager().  Note the cast usage.

r/sr = rpotts@netscape.com  alecf@netscape.com  brendan@mozilla.org


git-svn-id: svn://10.0.0.236/trunk@110748 18797224-902f-48f8-a5cc-f745e15eee43
2001-12-19 00:12:41 +00:00

1237 lines
40 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.
*
* Contributors:
* 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 "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"
#include "nsXPIDLString.h"
#include "nsCRT.h"
#include "nsIObserverService.h"
#ifndef OBSOLETE_MODULE_LOADING
#include "nsObsoleteModuleLoading.h"
#endif
#if defined(XP_MAC) // sdagley dougt fix
#include <Files.h>
#include <Errors.h>
#include "nsILocalFileMac.h"
#endif
#define PRINT_CRITICAL_ERROR_TO_SCREEN 1
#define USE_REGISTRY 1
#define XPCOM_USE_NSGETFACTORY 1
// Logging of debug output
#ifdef MOZ_LOGGING
#define FORCE_PR_LOG /* Allow logging in the release build */
#endif
#include "prlog.h"
extern PRLogModuleInfo *nsComponentManagerLog;
nsNativeComponentLoader::nsNativeComponentLoader() :
mRegistry(nsnull), mCompMgr(nsnull), mDllStore(nsnull)
{
NS_INIT_REFCNT();
}
static PRBool PR_CALLBACK
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_ISUPPORTS1(nsNativeComponentLoader, 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;
PRInt64 mod = LL_Zero(), size = LL_Zero();
rv = CreateDll(nsnull, aLocation, &mod, &size, &dll);
if (NS_FAILED(rv))
return rv;
if (!dll)
return NS_ERROR_OUT_OF_MEMORY;
if (!dll->IsLoaded()) {
PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG,
("nsNativeComponentLoader: loading \"%s\"",
dll->GetDisplayPath()));
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 */
nsCOMPtr<nsIServiceManager> serviceMgr;
rv = NS_GetServiceManager(getter_AddRefs(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, PR_LOG_ERROR,
("nsNativeComponentLoader: Factory creation %s for %s",
(NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"),
aLocation));
// 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;
mCompMgr = aCompMgr;
mRegistry = do_QueryInterface(aReg);
if (!mCompMgr || !mRegistry)
return NS_ERROR_INVALID_ARG;
rv = mRegistry->GetSubtree(nsIRegistry::Common, xpcomComponentsKeyName,
&mXPCOMKey);
if (NS_FAILED(rv))
return rv;
if (!mDllStore) {
mDllStore = new nsObjectHashtable(nsnull, nsnull, // never copy
nsDll_Destroy, nsnull,
256, /* Thead Safe */ PR_TRUE);
if (!mDllStore)
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
nsXPIDLCString library;
rv = node->GetNameUTF8(getter_Copies(library));
if (NS_FAILED(rv)) continue;
char* uLibrary;
char* eLibrary = (char*)library.operator const char*();
PRUint32 length = strlen(eLibrary);
rv = mRegistry->UnescapeKey((PRUint8*)eLibrary, 1, &length, (PRUint8**)&uLibrary);
if (NS_FAILED(rv)) continue;
if (uLibrary == nsnull)
uLibrary = eLibrary;
// Get key associated with library
nsRegistryKey libKey;
rv = node->GetKey(&libKey);
if (NS_SUCCEEDED(rv)) // Cannot continue here, because we have to free unescape
{
// Create nsDll with this name
nsDll *dll = NULL;
PRInt64 lastModTime;
PRInt64 fileSize;
GetRegistryDllInfo(libKey, &lastModTime, &fileSize);
rv = CreateDll(NULL, uLibrary, &lastModTime, &fileSize, &dll);
}
if (uLibrary && (uLibrary != eLibrary))
nsMemory::Free(uLibrary);
if (NS_FAILED(rv)) continue;
}
return NS_OK;
}
NS_IMETHODIMP
nsNativeComponentLoader::AutoRegisterComponents(PRInt32 aWhen,
nsIFile *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,
nsIFile *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<nsISimpleEnumerator> dirIterator;
rv = dir->GetDirectoryEntries(getter_AddRefs(dirIterator));
if (NS_FAILED(rv)) return rv;
// whip through the directory to register every file
nsCOMPtr<nsIFile> dirEntry;
PRBool more = PR_FALSE;
rv = dirIterator->HasMoreElements(&more);
if (NS_FAILED(rv)) return rv;
while (more == PR_TRUE)
{
rv = dirIterator->GetNext((nsISupports**)getter_AddRefs(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);
}
}
}
rv = dirIterator->HasMoreElements(&more);
if (NS_FAILED(rv)) return rv;
}
return rv;
}
static nsresult PR_CALLBACK
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_ERROR,
("nsNativeComponentLoader: Unload cant get nsIModule or NSCanUnload for %s",
dll->GetDisplayPath()));
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 == nsIComponentManagerObsolete::NS_Shutdown)
{
dll->Shutdown();
}
// Check error status on CanUnload() call
if (NS_FAILED(rv))
{
PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
("nsNativeComponentLoader: nsIModule::CanUnload() returned error for %s.",
dll->GetDisplayPath()));
return rv;
}
if (canUnload)
{
if (dllMarkedForUnload)
{
PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG,
("nsNativeComponentLoader: + Unloading \"%s\".", dll->GetDisplayPath()));
#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_DEBUG,
("nsNativeComponentLoader: Ready for unload \"%s\".", dll->GetDisplayPath()));
}
}
else
{
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
("nsNativeComponentLoader: NOT ready for unload %s", dll->GetDisplayPath()));
rv = NS_ERROR_FAILURE;
}
return rv;
}
struct freeLibrariesClosure
{
nsIServiceManager *serviceMgr;
PRInt32 when;
};
static PRBool PR_CALLBACK
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 : nsIComponentManagerObsolete::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,
PRBool deferred)
{
// Precondition: dll is not loaded already, unless we're deferred
PR_ASSERT(deferred || dll->IsLoaded() == PR_FALSE);
nsresult res;
nsCOMPtr<nsIServiceManager> serviceMgr;
res = NS_GetServiceManager(getter_AddRefs(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_DEBUG,
("nsNativeComponentLoader: Loaded \"%s\".", dll->GetDisplayPath()));
// Tell the module to self register
nsCOMPtr<nsIModule> mobj;
res = dll->GetModule(mCompMgr, getter_AddRefs(mobj));
if (NS_SUCCEEDED(res))
{
nsCOMPtr<nsIFile> fs;
/*************************************************************
* WARNING: Why are use introducing 'res2' here and then *
* later assigning it to 'res' rather than just using 'res'? *
* This is because this code turns up a code-generation bug *
* in VC6 on NT. Assigning to 'res' on the next line causes *
* the value of 'dll' to get nulled out! The two seem to be *
* getting aliased together during compilation. *
*************************************************************/
nsresult res2 = dll->GetDllSpec(getter_AddRefs(fs)); // don't change 'res2' -- see warning, above
if (NS_SUCCEEDED(res2)) {
res = mobj->RegisterSelf(mCompMgr, fs, registryLocation,
nativeComponentType);
}
else
{
res = res2; // don't take this out -- see warning, above
PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
("nsNativeComponentLoader: dll->GetDllSpec() on %s FAILED.",
dll->GetDisplayPath()));
}
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_ERROR,
("nsNativeComponentLoader: %s using OBSOLETE NSRegisterSelf()",
dll->GetDisplayPath()));
}
}
#endif /* OBSOLETE_MODULE_LOADING */
// Update the timestamp and size of the dll in registry
// Don't enter deferred modules in the registry, because it might only be
// able to register on some later autoreg, after another component has been
// installed.
if (res != NS_ERROR_FACTORY_REGISTER_AGAIN) {
dll->Sync();
PRInt64 modTime, size;
dll->GetLastModifiedTime(&modTime);
dll->GetSize(&size);
SetRegistryDllInfo(registryLocation, &modTime, &size);
}
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.get(),demangled,sizeof(demangled));
if (demangled && *demangled != '\0')
demangledSymbol = demangled;
if (!demangledSymbol.IsEmpty())
{
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_ERROR,
("nsNativeComponentLoader: %s(%s) Load FAILED with error:%s",
aCallerName,
dll->GetDisplayPath(),
errorMsg.get()));
// 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->GetDisplayPath(),
errorMsg.get());
#endif
return NS_OK;
}
nsresult
nsNativeComponentLoader::SelfUnregisterDll(nsDll *dll)
{
nsresult res;
nsCOMPtr<nsIServiceManager> serviceMgr;
res = NS_GetServiceManager(getter_AddRefs(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->GetDisplayPath()));
nsCOMPtr<nsIFile> fs;
res = dll->GetDllSpec(getter_AddRefs(fs));
if (NS_FAILED(res)) return res;
// Get registry location for spec
nsXPIDLCString registryName;
// what I want to do here is QI for a Component Registration Manager. Since this
// has not been invented yet, QI to the obsolete manager. Kids, don't do this at home.
nsCOMPtr<nsIComponentManagerObsolete> obsoleteManager = do_QueryInterface(mCompMgr, &res);
if (obsoleteManager)
res = obsoleteManager->RegistryLocationForSpec(fs, getter_Copies(registryName));
if (NS_FAILED(res)) return res;
mobj->UnregisterSelf(mCompMgr, fs, registryName);
}
#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 */
return res;
}
nsresult
nsNativeComponentLoader::AutoUnregisterComponent(PRInt32 when,
nsIFile *component,
PRBool *unregistered)
{
nsresult rv = NS_ERROR_FAILURE;
nsXPIDLCString persistentDescriptor;
// what I want to do here is QI for a Component Registration Manager. Since this
// has not been invented yet, QI to the obsolete manager. Kids, don't do this at home.
nsCOMPtr<nsIComponentManagerObsolete> obsoleteManager = do_QueryInterface(mCompMgr, &rv);
if (obsoleteManager)
rv = obsoleteManager->RegistryLocationForSpec(component,
getter_Copies(persistentDescriptor));
if (NS_FAILED(rv)) return rv;
// Notify observers, if any, of autoregistration work
nsCOMPtr<nsIObserverService> observerService =
do_GetService("@mozilla.org/observer-service;1", &rv);
if (NS_SUCCEEDED(rv))
{
nsCOMPtr<nsIServiceManager> mgr;
rv = NS_GetServiceManager(getter_AddRefs(mgr));
if (NS_SUCCEEDED(rv))
{
(void) observerService->NotifyObservers(mgr,
NS_XPCOM_AUTOREGISTRATION_OBSERVER_ID,
NS_LITERAL_STRING("Unregistering native component").get());
}
}
nsDll *dll = NULL;
PRInt64 mod = LL_Zero(), size = LL_Zero();
rv = CreateDll(component, persistentDescriptor, &mod, &size, &dll);
if (NS_FAILED(rv) || dll == NULL) return rv;
rv = SelfUnregisterDll(dll);
// Remove any autoreg info about this dll
if (NS_SUCCEEDED(rv))
RemoveRegistryDllInfo(persistentDescriptor);
PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
("nsNativeComponentLoader: AutoUnregistration for %s %s.",
(NS_FAILED(rv) ? "FAILED" : "succeeded"), dll->GetDisplayPath()));
return rv;
}
nsresult
nsNativeComponentLoader::AutoRegisterComponent(PRInt32 when,
nsIFile *component,
PRBool *registered)
{
nsresult rv;
if (!registered)
return NS_ERROR_NULL_POINTER;
/* this should be a pref or registry entry, or something */
static const char *ValidDllExtensions[] = {
".dll", /* Windows */
".so", /* Unix */
".shlb", /* Mac ? */
".dso", /* Unix ? */
".dylib", /* Unix: Mach */
".so.1.0", /* Unix: BSD */
".sl", /* Unix: HP-UX */
#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;
#if defined(XP_MAC) // sdagley dougt fix
// rjc - on Mac, check the file's type code (skip checking the creator code)
nsCOMPtr<nsILocalFileMac> localFileMac = do_QueryInterface(component);
if (localFileMac)
{
OSType type, creator;
rv = localFileMac->GetFileTypeAndCreator(&type, &creator);
if (NS_SUCCEEDED(rv))
{
// 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!
validExtension = ((type == 'shlb') || (type == 'NSPL'));
}
}
#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) nsMemory::Free(leafName);
#endif
if (validExtension == PR_FALSE)
// Skip invalid extensions
return NS_OK;
nsXPIDLCString persistentDescriptor;
// what I want to do here is QI for a Component Registration Manager. Since this
// has not been invented yet, QI to the obsolete manager. Kids, don't do this at home.
nsCOMPtr<nsIComponentManagerObsolete> obsoleteManager = do_QueryInterface(mCompMgr, &rv);
if (obsoleteManager)
rv = obsoleteManager->RegistryLocationForSpec(component,
getter_Copies(persistentDescriptor));
if (NS_FAILED(rv))
return rv;
nsCStringKey key(persistentDescriptor);
// Get the registry representation of the dll, if any
nsDll *dll;
PRInt64 mod = LL_Zero(), size = LL_Zero();
rv = CreateDll(component, persistentDescriptor, &mod, &size, &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->GetDisplayPath()));
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,
("nsNativeComponentLoader: + nsDll not changed \"%s\". Skipping...",
dll->GetDisplayPath()));
return NS_OK;
}
// Aagh! the dll has changed since the last time we saw it.
// re-register dll
// Notify observers, if any, of autoregistration work
nsCOMPtr<nsIObserverService> observerService =
do_GetService("@mozilla.org/observer-service;1", &rv);
if (NS_SUCCEEDED(rv))
{
nsCOMPtr<nsIServiceManager> mgr;
rv = NS_GetServiceManager(getter_AddRefs(mgr));
if (NS_SUCCEEDED(rv))
{
// this string can't come from a string bundle, because we
// don't have string bundles yet.
NS_ConvertASCIItoUCS2 fileName("(no name)");
// get the file name
nsCOMPtr<nsIFile> dllSpec;
if (NS_SUCCEEDED(dll->GetDllSpec(getter_AddRefs(dllSpec))) && dllSpec)
{
nsXPIDLCString dllLeafName;
dllSpec->GetLeafName(getter_Copies(dllLeafName));
fileName.AssignWithConversion(dllLeafName);
}
// this string can't come from a string bundle, because we
// don't have string bundles yet.
(void) observerService->
NotifyObservers(mgr,
NS_XPCOM_AUTOREGISTRATION_OBSERVER_ID,
PromiseFlatString(NS_LITERAL_STRING("Registering native component ") +
fileName).get());
}
}
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.
nsCOMPtr<nsIServiceManager> serviceMgr;
rv = NS_GetServiceManager(getter_AddRefs(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->GetDisplayPath()));
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->GetDisplayPath()));
}
} // 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->GetDisplayPath()));
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, PR_FALSE);
if (NS_FAILED(res))
{
if (res == NS_ERROR_FACTORY_REGISTER_AGAIN) {
/* defer for later loading */
mDeferredComponents.AppendElement(dll);
return NS_OK;
} else {
PR_LOG(nsComponentManagerLog, PR_LOG_ERROR,
("nsNativeComponentLoader: Autoregistration FAILED for "
"\"%s\". Skipping...", dll->GetDisplayPath()));
return NS_ERROR_FACTORY_NOT_REGISTERED;
}
}
else
{
PR_LOG(nsComponentManagerLog, PR_LOG_ALWAYS,
("nsNativeComponentLoader: Autoregistration Passed for "
"\"%s\".", dll->GetDisplayPath()));
// Marking dll along with modified time and size in the
// registry happens at PlatformRegister(). No need to do it
// here again.
}
return NS_OK;
}
nsresult
nsNativeComponentLoader::RegisterDeferredComponents(PRInt32 aWhen,
PRBool *aRegistered)
{
#ifdef DEBUG
fprintf(stderr, "nNCL: registering deferred (%d)\n",
mDeferredComponents.Count());
#endif
*aRegistered = PR_FALSE;
if (!mDeferredComponents.Count())
return NS_OK;
for (int i = mDeferredComponents.Count() - 1; i >= 0; i--) {
nsDll *dll = NS_STATIC_CAST(nsDll *, mDeferredComponents[i]);
nsresult rv = SelfRegisterDll(dll,
dll->GetRegistryLocation(),
PR_TRUE);
if (rv != NS_ERROR_FACTORY_REGISTER_AGAIN) {
if (NS_SUCCEEDED(rv))
*aRegistered = PR_TRUE;
mDeferredComponents.RemoveElementAt(i);
}
}
if (*aRegistered)
fprintf(stderr, "nNCL: registered deferred, %d left\n",
mDeferredComponents.Count());
else
fprintf(stderr, "nNCL: didn't register any components, %d left\n",
mDeferredComponents.Count());
/* are there any fatal errors? */
return NS_OK;
}
nsresult
nsNativeComponentLoader::OnRegister(const nsIID &aCID, const char *aType,
const char *aClassName,
const char *aContractID, 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
if (mDllStore) {
mDllStore->Enumerate(nsFreeLibraryEnum, &callData);
}
return NS_OK;
}
nsresult
nsNativeComponentLoader::GetRegistryDllInfo(const char *aLocation,
PRInt64 *lastModifiedTime,
PRInt64 *fileSize)
{
nsresult rv;
PRUint32 length = strlen(aLocation);
char* eLocation;
rv = mRegistry->EscapeKey((PRUint8*)aLocation, 1, &length, (PRUint8**)&eLocation);
if (rv != NS_OK)
{
return rv;
}
if (eLocation == nsnull) // No escaping required
eLocation = (char*)aLocation;
nsRegistryKey key;
rv = mRegistry->GetSubtreeRaw(mXPCOMKey, eLocation, &key);
if (NS_FAILED(rv)) return rv;
rv = GetRegistryDllInfo(key, lastModifiedTime, fileSize);
if (aLocation != eLocation)
nsMemory::Free(eLocation);
return rv;
}
nsresult
nsNativeComponentLoader::GetRegistryDllInfo(nsRegistryKey key,
PRInt64 *lastModifiedTime,
PRInt64 *fileSize)
{
PRInt64 lastMod;
nsresult rv = mRegistry->GetLongLong(key, lastModValueName, &lastMod);
if (NS_FAILED(rv)) return rv;
*lastModifiedTime = lastMod;
PRInt64 fsize;
rv = mRegistry->GetLongLong(key, fileSizeValueName, &fsize);
if (NS_FAILED(rv)) return rv;
*fileSize = fsize;
return NS_OK;
}
nsresult
nsNativeComponentLoader::SetRegistryDllInfo(const char *aLocation,
PRInt64 *lastModifiedTime,
PRInt64 *fileSize)
{
nsresult rv;
PRUint32 length = strlen(aLocation);
char* eLocation;
rv = mRegistry->EscapeKey((PRUint8*)aLocation, 1, &length, (PRUint8**)&eLocation);
if (rv != NS_OK)
{
return rv;
}
if (eLocation == nsnull) // No escaping required
eLocation = (char*)aLocation;
nsRegistryKey key;
rv = mRegistry->GetSubtreeRaw(mXPCOMKey, eLocation, &key);
if (NS_FAILED(rv)) return rv;
rv = mRegistry->SetLongLong(key, lastModValueName, lastModifiedTime);
if (NS_FAILED(rv)) return rv;
rv = mRegistry->SetLongLong(key, fileSizeValueName, fileSize);
if (aLocation != eLocation)
nsMemory::Free(eLocation);
return rv;
}
nsresult
nsNativeComponentLoader::RemoveRegistryDllInfo(const char *aLocation)
{
nsresult rv;
PRUint32 length = strlen(aLocation);
char* eLocation;
rv = mRegistry->EscapeKey((PRUint8*)aLocation, 1, &length, (PRUint8**)&eLocation);
if (rv != NS_OK)
{
return rv;
}
if (eLocation == nsnull) // No escaping required
eLocation = (char*)aLocation;
rv = mRegistry->RemoveSubtree(mXPCOMKey, eLocation);
if (aLocation != eLocation)
nsMemory::Free(eLocation);
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(nsIFile *aSpec, const char *aLocation,
PRInt64 *modificationTime, PRInt64 *fileSize,
nsDll **aDll)
{
nsDll *dll;
nsCOMPtr<nsIFile> dllSpec;
nsCOMPtr<nsIFile> spec;
nsresult rv;
nsCStringKey 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 {
// what I want to do here is QI for a Component Registration Manager. Since this
// has not been invented yet, QI to the obsolete manager. Kids, don't do this at home.
nsCOMPtr<nsIComponentManagerObsolete> obsoleteManager = do_QueryInterface(mCompMgr, &rv);
if (obsoleteManager)
rv = obsoleteManager->SpecForRegistryLocation(aLocation,
getter_AddRefs(spec));
if (NS_FAILED(rv))
return rv;
}
}
else
{
spec = aSpec;
}
if (!dll)
{
PR_ASSERT(modificationTime != NULL);
PR_ASSERT(fileSize != NULL);
PRInt64 zit = LL_Zero();
if (LL_EQ(*modificationTime,zit) && LL_EQ(*fileSize,zit))
{
// 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;
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->GetDisplayPath()));
/*
* There was a time when CLSIDToContractID was used to get className
* and contractID, 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 /* contractID */, aFactory);
#else /* !XPCOM_USE_NSGETFACTORY */
return NS_ERROR_FACTORY_NOT_LOADED;
#endif /* XPCOM_USE_NSGETFACTORY */
}