/* -*- 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] = ""; 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 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 base; rv = dllEnum->CurrentItem(getter_AddRefs(base)); if (NS_FAILED(rv)) continue; // Get specific interface. nsIID nodeIID = NS_IREGISTRYNODE_IID; nsCOMPtr 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 nsCOMPtrdirIterator; 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, ®istered); } } 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 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 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 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 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 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 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 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 */ }