/* -*- 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.0 (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) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #include #ifndef XP_MAC // including this on mac causes odd link errors in static initialization // stuff that we (pinkerton & scc) don't yet understand. If you want to // turn this on for mac, talk to one of us. #include #endif #ifdef XP_MAC #include #include #include #include #endif #include "plstr.h" #include "prlink.h" #include "prsystem.h" #include "prprf.h" #include "nsRepository.h" /** * When using the registry we put a version number in it. * If the version number that is in the registry doesn't match * the following, we ignore the registry. This lets news versions * of the software deal with old formats of registry and not * * alpha0.20 : First time we did versioning * alpha0.30 : Changing autoreg to begin registration from ./components on unix */ #define NS_XPCOM_REPOSITORY_VERSION_STRING "alpha0.33" #include "NSReg.h" #if 0 #ifdef XP_MAC #ifdef MOZ_NGLAYOUT #define IMPL_MAC_REPOSITORY #include "nsMacRepository.h" #endif #endif #endif #include "xcDllStore.h" #include "nsIServiceManager.h" nsHashtable *nsRepository::factories = NULL; nsHashtable *nsRepository::progIDs = NULL; PRMonitor *nsRepository::monitor = NULL; /** * dllStore * * The dllStore maintains a collection of dlls that are hashed by the dll * name. The repository uses the DllStore only to store these dlls: * * 1. dlls that store components in them. * The inmemory nsDll that is stored here always reflects the values of * {lastModTime, fileSize} that are stored in the registry. * It is the job of the autoregistration mechanism to do the right * thing if the dll on disk is newer. * 2. dlls that dont store components in them. * This is for the purpose of the autoregistration mechanism only. * These are marked in the registry too so that unless they change * they wont have to loaded every session. * */ nsDllStore *nsRepository::dllStore = NULL; static PRLogModuleInfo *logmodule = NULL; #if 0 // Factory2 commented out until proven required static NS_DEFINE_IID(kFactory2IID, NS_IFACTORY2_IID); #endif /* 0 */ /***************************************************************************/ /** * Class: FactoryEntry() * * There are two types of FactoryEntries. * * 1. {CID, dll} mapping. * Factory is a consequence of the dll. These can be either session * specific or persistent based on whether we write this * to the registry or not. * * 2. {CID, factory} mapping * These are strictly session specific and in memory only. */ class FactoryEntry { public: nsCID cid; nsIFactory *factory; FactoryEntry(const nsCID &aClass, const char *aLibrary, PRTime lastModTime, PRUint32 fileSize); FactoryEntry(const nsCID &aClass, nsIFactory *aFactory); ~FactoryEntry(); // DO NOT DELETE THIS. Many FactoryEntry(s) could be sharing the same Dll. // This gets deleted from the dllStore going away. nsDll *dll; }; FactoryEntry::FactoryEntry(const nsCID &aClass, const char *aLibrary, PRTime lastModTime, PRUint32 fileSize) : cid(aClass), factory(NULL), dll(NULL) { nsDllStore *dllCollection = nsRepository::dllStore; if (aLibrary == NULL) { return; } // If dll not already in dllCollection, add it. // PR_EnterMonitor(nsRepository::monitor); dll = dllCollection->Get(aLibrary); // PR_ExitMonitor(nsRepository::monitor); if (dll == NULL) { PR_LOG(logmodule, PR_LOG_ALWAYS, ("nsRepository: New dll \"%s\".", aLibrary)); // Add a new Dll into the nsDllStore dll = new nsDll(aLibrary, lastModTime, fileSize); if (dll->GetStatus() != DLL_OK) { // Cant create a nsDll. Backoff. PR_LOG(logmodule, PR_LOG_ALWAYS, ("nsRepository: New dll status error. \"%s\".", aLibrary)); delete dll; dll = NULL; } else { PR_LOG(logmodule, PR_LOG_ALWAYS, ("nsRepository: Adding New dll \"%s\" to dllStore.", aLibrary)); // PR_EnterMonitor(nsRepository::monitor); dllCollection->Put(aLibrary, dll); // PR_ExitMonitor(nsRepository::monitor); } } else { PR_LOG(logmodule, PR_LOG_ALWAYS, ("nsRepository: Found in dllStore \"%s\".", aLibrary)); // XXX We found the dll in the dllCollection. // XXX Consistency check: dll needs to have the same // XXX lastModTime and fileSize. If not that would mean // XXX that the dll wasn't registered properly. } } FactoryEntry::FactoryEntry(const nsCID &aClass, nsIFactory *aFactory) : cid(aClass), factory(aFactory), dll(NULL) { } FactoryEntry::~FactoryEntry(void) { if (factory != NULL) { factory->Release(); } // DO NOT DELETE nsDll *dll; } /***************************************************************************/ class ProgIDKey : public nsHashKey { private: char mProgIDBuf[64]; char* mProgID; public: ProgIDKey(const char* aProgID) : mProgID(mProgIDBuf) { PRInt32 len = PL_strlen(aProgID); if (len >= sizeof(mProgIDBuf)) { mProgID = new char[PL_strlen(aProgID) + 1]; NS_ASSERTION(mProgID, "out of memory"); if (! mProgID) return; } PL_strcpy(mProgID, aProgID); } virtual ~ProgIDKey() { if (mProgID != mProgIDBuf) delete[] mProgID; } virtual PRUint32 HashValue(void) const { return (PRUint32) PL_HashString((const void*) mProgID); } virtual PRBool Equals(const nsHashKey* aKey) const { return PL_strcmp( ((ProgIDKey*)aKey)->mProgID, mProgID ) == 0; } virtual nsHashKey* Clone() const { return new ProgIDKey(mProgID); } }; /***************************************************************************/ #ifdef USE_NSREG #define USE_REGISTRY /* * platformDeleteKey() * * Deletes a key sub tree entirely. */ static nsresult platformDeleteKey(HREG hreg, RKEY rootkey, const char *keyname) { RKEY key; char subkeyname[MAXREGPATHLEN+1]; int n = sizeof(subkeyname); REGENUM state = 0; REGERR rerr = REGERR_OK; REGERR err = NR_RegGetKeyRaw(hreg, rootkey, (char *)keyname, &key); if (err != REGERR_OK) return (err); // Now recurse through and delete all keys under hierarchy subkeyname[0] = '\0'; while (NR_RegEnumSubkeys(hreg, key, &state, subkeyname, n, REGENUM_NORMAL) == REGERR_OK) { PR_LOG(logmodule, PR_LOG_ALWAYS, ("nsRepository: ...deleting %s", subkeyname)); rerr = platformDeleteKey(hreg, key, subkeyname); if (rerr != REGERR_OK) { break; } } // If success in deleting all subkeys, delete this key too if (rerr == REGERR_OK) { err = NR_RegDeleteKeyRaw(hreg, rootkey, (char *)keyname); } else { err = rerr; } return (err); } /** * platformVersionCheck() * * Checks to see if the XPCOM hierarchy in the registry is the same as that of * the software as defined by NS_XPCOM_REPOSITORY_VERSION_STRING */ static nsresult platformVersionCheck() { HREG hreg; REGERR err = NR_RegOpen(NULL, &hreg); if (err != REGERR_OK) { return (NS_ERROR_FAILURE); } RKEY xpcomKey; if (NR_RegAddKey(hreg, ROOTKEY_COMMON, "Software/Netscape/XPCOM", &xpcomKey) != REGERR_OK) { NR_RegClose(hreg); return (NS_ERROR_FAILURE); } char buf[MAXREGNAMELEN]; uint32 len = sizeof(buf); buf[0] = '\0'; err = NR_RegGetEntryString(hreg, xpcomKey, "VersionString", buf, len); // If there is a version mismatch or no version string, we got an old registry. // Delete the old repository hierarchies and recreate version string if (err != REGERR_OK || PL_strcmp(buf, NS_XPCOM_REPOSITORY_VERSION_STRING)) { PR_LOG(logmodule, PR_LOG_ALWAYS, ("nsRepository: platformVersionCheck() failed. " "Mismatch (%s vs %s). " "Nuking xpcom registry hierarchy.", buf, NS_XPCOM_REPOSITORY_VERSION_STRING)); // Delete the XPCOM and CLSID hierarchy RKEY akey; NR_RegGetKey(hreg, ROOTKEY_COMMON, "Software/Netscape", &akey); platformDeleteKey(hreg, akey, "XPCOM"); NR_RegGetKey(hreg, ROOTKEY_COMMON, "Classes", &akey); platformDeleteKey(hreg, akey, "Classes/CLSID"); // Recreate XPCOM and CLSID keys NR_RegAddKey(hreg, ROOTKEY_COMMON, "Software/Netscape/XPCOM", &xpcomKey); NR_RegAddKey(hreg, ROOTKEY_COMMON, "Classes/CLSID", NULL); NR_RegSetEntryString(hreg, xpcomKey, "VersionString", NS_XPCOM_REPOSITORY_VERSION_STRING); } else { PR_LOG(logmodule, PR_LOG_ALWAYS, ("nsRepository: platformVersionCheck() passed.")); } NR_RegClose(hreg); return (NS_OK); } /** * platformCreateDll(const char *fullname) * * Creates a nsDll from the registry representation of dll 'fullname'. * Looks under * ROOTKEY_COMMON/Software/Netscape/XPCOM/fullname */ static nsDll *platformCreateDll(const char *fullname) { HREG hreg; REGERR err = NR_RegOpen(NULL, &hreg); if (err != REGERR_OK) { return (NULL); } RKEY xpcomKey; if (NR_RegAddKey(hreg, ROOTKEY_COMMON, "Software/Netscape/XPCOM", &xpcomKey) != REGERR_OK) { NR_RegClose(hreg); return (NULL); } RKEY key; err = NR_RegGetKeyRaw(hreg, xpcomKey, (char *)fullname, &key); if (err != REGERR_OK) { return (NULL); } PRTime lastModTime = LL_ZERO; PRUint32 fileSize = 0; uint32 n = sizeof(lastModTime); NR_RegGetEntry(hreg, key, "LastModTimeStamp", &lastModTime, &n); n = sizeof(fileSize); NR_RegGetEntry(hreg, key, "FileSize", &fileSize, &n); nsDll *dll = new nsDll(fullname, lastModTime, fileSize); return (dll); } /** * platformMarkNoComponents(nsDll *dll) * * Stores the dll name, last modified time, size and 0 for number of * components in dll in the registry at location * ROOTKEY_COMMON/Software/Netscape/XPCOM/dllname */ static nsresult platformMarkNoComponents(nsDll *dll) { HREG hreg; REGERR err = NR_RegOpen(NULL, &hreg); if (err != REGERR_OK) { return (NS_ERROR_FAILURE); } // XXX Gross. LongLongs dont have a serialization format. This makes // XXX the registry non-xp. Someone beat on the nspr people to get // XXX a longlong serialization function please! RKEY xpcomKey; if (NR_RegAddKey(hreg, ROOTKEY_COMMON, "Software/Netscape/XPCOM", &xpcomKey) != REGERR_OK) { NR_RegClose(hreg); return (NS_ERROR_FAILURE); } RKEY key; err = NR_RegAddKeyRaw(hreg, xpcomKey, (char *)dll->GetFullPath(), &key); if (err != REGERR_OK) { NR_RegClose(hreg); return (NS_ERROR_FAILURE); } PRTime lastModTime = dll->GetLastModifiedTime(); PRUint32 fileSize = dll->GetSize(); NR_RegSetEntry(hreg, key, "LastModTimeStamp", REGTYPE_ENTRY_BYTES, &lastModTime, sizeof(lastModTime)); NR_RegSetEntry(hreg, key, "FileSize", REGTYPE_ENTRY_BYTES, &fileSize, sizeof(fileSize)); char *ncomponentsString = "0"; NR_RegSetEntryString(hreg, key, "ComponentsCount", ncomponentsString); NR_RegClose(hreg); return (NS_OK); } static nsresult platformRegister(NSQuickRegisterData regd, nsDll *dll) { HREG hreg; // Preconditions PR_ASSERT(regd != NULL); PR_ASSERT(regd->CIDString != NULL); PR_ASSERT(dll != NULL); REGERR err = NR_RegOpen(NULL, &hreg); if (err != REGERR_OK) { return (NS_ERROR_FAILURE); } RKEY classesKey; if (NR_RegAddKey(hreg, ROOTKEY_COMMON, "Classes", &classesKey) != REGERR_OK) { NR_RegClose(hreg); return (NS_ERROR_FAILURE); } RKEY key; NR_RegAddKey(hreg, classesKey, "CLSID", &key); NR_RegAddKeyRaw(hreg, key, (char *)regd->CIDString, &key); NR_RegSetEntryString(hreg, key, "ClassName", (char *)regd->className); if (regd->progID) NR_RegSetEntryString(hreg, key, "ProgID", (char *)(regd->progID)); char *libName = (char *)dll->GetFullPath(); NR_RegSetEntryString(hreg, key, "InprocServer", libName); if (regd->progID) { NR_RegAddKeyRaw(hreg, classesKey, (char *)regd->progID, &key); NR_RegSetEntryString(hreg, key, "CLSID", (char *)regd->CIDString); } // XXX Gross. LongLongs dont have a serialization format. This makes // XXX the registry non-xp. Someone beat on the nspr people to get // XXX a longlong serialization function please! RKEY xpcomKey; if (NR_RegAddKey(hreg, ROOTKEY_COMMON, "Software/Netscape/XPCOM", &xpcomKey) != REGERR_OK) { NR_RegClose(hreg); // This aint a fatal error. It causes autoregistration to fail // and hence the dll would be registered again the next time // we startup. Let us run with it. return (NS_OK); } NR_RegAddKeyRaw(hreg, xpcomKey, (char *)dll->GetFullPath(), &key); PRTime lastModTime = dll->GetLastModifiedTime(); PRUint32 fileSize = dll->GetSize(); NR_RegSetEntry(hreg, key, "LastModTimeStamp", REGTYPE_ENTRY_BYTES, &lastModTime, sizeof(lastModTime)); NR_RegSetEntry(hreg, key, "FileSize", REGTYPE_ENTRY_BYTES, &fileSize, sizeof(fileSize)); unsigned int nComponents = 0; char buf[MAXREGNAMELEN]; uint32 len = sizeof(buf); if (NR_RegGetEntryString(hreg, key, "ComponentsCount", buf, len) == REGERR_OK) { nComponents = atoi(buf); } nComponents++; PR_snprintf(buf, sizeof(buf), "%d", nComponents); NR_RegSetEntryString(hreg, key, "ComponentsCount", buf); NR_RegClose(hreg); return err; } static nsresult platformUnregister(NSQuickRegisterData regd, const char *aLibrary) { HREG hreg; REGERR err = NR_RegOpen(NULL, &hreg); if (err != REGERR_OK) { return (NS_ERROR_FAILURE); } RKEY classesKey; if (NR_RegAddKey(hreg, ROOTKEY_COMMON, "Classes", &classesKey) != REGERR_OK) { NR_RegClose(hreg); return (NS_ERROR_FAILURE); } RKEY key; NR_RegAddKey(hreg, classesKey, "CLSID", &key); RKEY cidKey; NR_RegAddKeyRaw(hreg, key, (char *)regd->CIDString, &cidKey); char progID[MAXREGNAMELEN]; uint32 plen = sizeof(progID); if (NR_RegGetEntryString(hreg, cidKey, "ProgID", progID, plen) == REGERR_OK) { NR_RegDeleteKey(hreg, classesKey, progID); } NR_RegDeleteKey(hreg, key, (char *)regd->CIDString); RKEY xpcomKey; if (NR_RegAddKey(hreg, ROOTKEY_COMMON, "Software/Netscape/XPCOM", &xpcomKey) != REGERR_OK) { NR_RegClose(hreg); // This aint a fatal error. It causes autoregistration to fail // and hence the dll would be registered again the next time // we startup. Let us run with it. return (NS_OK); } NR_RegGetKeyRaw(hreg, xpcomKey, (char *)aLibrary, &key); // We need to reduce the ComponentCount by 1. // If the ComponentCount hits 0, delete the entire key. int nComponents = 0; char buf[MAXREGNAMELEN]; uint32 len = sizeof(buf); if (NR_RegGetEntryString(hreg, key, "ComponentsCount", buf, len) == REGERR_OK) { nComponents = atoi(buf); nComponents--; } if (nComponents <= 0) { NR_RegDeleteKey(hreg, key, (char *)aLibrary); } else { PR_snprintf(buf, sizeof(buf), "%d", nComponents); NR_RegSetEntryString(hreg, key, "ComponentsCount", buf); } NR_RegClose(hreg); return (NS_OK); } static FactoryEntry *platformFind(const nsCID &aCID) { HREG hreg; REGERR err = NR_RegOpen(NULL, &hreg); if (err != REGERR_OK) { return (NULL); } RKEY classesKey; if (NR_RegAddKey(hreg, ROOTKEY_COMMON, "Classes", &classesKey) != REGERR_OK) { NR_RegClose(hreg); return (NULL); } RKEY key; NR_RegAddKey(hreg, classesKey, "CLSID", &key); FactoryEntry *res = NULL; RKEY cidKey; char *cidString = aCID.ToString(); err = NR_RegGetKeyRaw(hreg, key, cidString, &cidKey); delete [] cidString; if (err != REGERR_OK) { NR_RegClose(hreg); return (NULL); } // Get the library name, modifiedtime and size PRTime lastModTime = LL_ZERO; PRUint32 fileSize = 0; char buf[MAXREGNAMELEN]; uint32 len = sizeof(buf); err = NR_RegGetEntryString(hreg, cidKey, "InprocServer", buf, len); if (err != REGERR_OK) { // Registry inconsistent. No File name for CLSID. NR_RegClose(hreg); return (NULL); } char *library = buf; // XXX Gross. LongLongs dont have a serialization format. This makes // XXX the registry non-xp. Someone beat on the nspr people to get // XXX a longlong serialization function please! RKEY xpcomKey; if (NR_RegAddKey(hreg, ROOTKEY_COMMON, "Software/Netscape/XPCOM", &xpcomKey) == REGERR_OK) { if (NR_RegGetKeyRaw(hreg, xpcomKey, library, &key) == REGERR_OK) { uint32 n = sizeof(lastModTime); NR_RegGetEntry(hreg, key, "LastModTimeStamp", &lastModTime, &n); PR_ASSERT(n == sizeof(lastModTime)); n = sizeof(fileSize); NR_RegGetEntry(hreg, key, "FileSize", &fileSize, &n); PR_ASSERT(n == sizeof(fileSize)); } } res = new FactoryEntry(aCID, library, lastModTime, fileSize); NR_RegClose(hreg); return (res); } static nsresult platformProgIDToCLSID(const char *aProgID, nsCID *aClass) { HREG hreg; nsresult res = NS_ERROR_FAILURE; PR_ASSERT(aClass != NULL); REGERR err = NR_RegOpen(NULL, &hreg); if (err != REGERR_OK) { return (NS_ERROR_FAILURE); } RKEY classesKey; if (NR_RegAddKey(hreg, ROOTKEY_COMMON, "Classes", &classesKey) != REGERR_OK) { NR_RegClose(hreg); return (NS_ERROR_FAILURE); } RKEY key; err = NR_RegGetKeyRaw(hreg, classesKey, (char *)aProgID, &key); if (err != REGERR_OK) { NR_RegClose(hreg); return (NS_ERROR_FAILURE); } char cidString[MAXREGNAMELEN]; err = NR_RegGetEntryString(hreg, key, "CLSID", cidString, MAXREGNAMELEN); if (err != REGERR_OK) { NR_RegClose(hreg); return (NS_ERROR_FAILURE); } NR_RegClose(hreg); if (!(aClass->Parse(cidString))) { return (NS_ERROR_FAILURE); } res = NS_OK; return (res); } static nsresult platformCLSIDToProgID(nsCID *aClass, char* *aClassName, char* *aProgID) { HREG hreg; char* classnameString; char* progidString; nsresult res = NS_ERROR_FAILURE; char* cidStr = aClass->ToString(); PR_ASSERT(aClass != NULL); REGERR err = NR_RegOpen(NULL, &hreg); if (err != REGERR_OK) { res = NS_ERROR_FAILURE; goto done1; } RKEY classesKey; if (NR_RegAddKey(hreg, ROOTKEY_COMMON, "Classes", &classesKey) != REGERR_OK) { res = NS_ERROR_FAILURE; goto done2; } RKEY key; err = NR_RegGetKeyRaw(hreg, classesKey, cidStr, &key); if (err != REGERR_OK) { res = NS_ERROR_FAILURE; goto done2; } classnameString = new char[MAXREGNAMELEN]; if (classnameString == NULL) { res = NS_ERROR_OUT_OF_MEMORY; goto done2; } err = NR_RegGetEntryString(hreg, key, "ClassName", classnameString, MAXREGNAMELEN); if (err != REGERR_OK) { delete[] classnameString; res = NS_ERROR_FAILURE; goto done2; } *aClassName = classnameString; progidString = new char[MAXREGNAMELEN]; if (progidString == NULL) { delete[] classnameString; res = NS_ERROR_OUT_OF_MEMORY; goto done2; } err = NR_RegGetEntryString(hreg, key, "ProgID", progidString, MAXREGNAMELEN); if (err != REGERR_OK) { delete[] progidString; delete[] classnameString; res = NS_ERROR_FAILURE; goto done2; } *aProgID = progidString; res = NS_OK; done2: NR_RegClose(hreg); done1: delete[] cidStr; return (res); } #endif // USE_NSREG /***************************************************************************/ /** * LoadFactory() * * Given a FactoryEntry, this loads the dll if it has to, find the NSGetFactory * symbol, calls the routine to create a new factory and returns it to the * caller. * * No attempt is made to store the factory in any form anywhere. */ nsresult nsRepository::loadFactory(FactoryEntry *aEntry, nsIFactory **aFactory) { if (aFactory == NULL) { return NS_ERROR_NULL_POINTER; } *aFactory = NULL; // loadFactory() cannot be called for entries that are CID<->factory // mapping entries for the session. PR_ASSERT(aEntry->dll != NULL); if (aEntry->dll->IsLoaded() == PR_FALSE) { // Load the dll PR_LOG(logmodule, PR_LOG_ALWAYS, ("nsRepository: + Loading \"%s\".", aEntry->dll->GetFullPath())); if (aEntry->dll->Load() == PR_FALSE) { PR_LOG(logmodule, PR_LOG_ERROR, ("nsRepository: Library load unsuccessful.")); return (NS_ERROR_FAILURE); } } #ifdef MOZ_TRACE_XPCOM_REFCNT // Inform refcnt tracer of new library so that calls through the // new library can be traced. nsTraceRefcnt::LoadLibrarySymbols(aEntry->dll->GetFullPath(), aEntry->dll->GetInstance()); #endif nsFactoryProc proc = (nsFactoryProc) aEntry->dll->FindSymbol("NSGetFactory"); if (proc != NULL) { char* className = NULL; char* progID = NULL; // XXX dp, warren: deal with this! //(void)CLSIDToProgID(&aEntry->cid, &className, &progID); nsIServiceManager* serviceMgr = NULL; nsresult res = nsServiceManager::GetGlobalServiceManager(&serviceMgr); NS_ASSERTION(NS_SUCCEEDED(res), "no service manager"); res = proc(serviceMgr, aEntry->cid, className, progID, aFactory); if (className) delete[] className; if (progID) delete[] progID; return res; } PR_LOG(logmodule, PR_LOG_ERROR, ("nsRepository: NSGetFactory entrypoint not found.")); return NS_ERROR_FACTORY_NOT_LOADED; } /** * FindFactory() * * Given a classID, this finds the factory for this CID by first searching the * local CID<->factory mapping. Next it searches for a Dll that implements * this classID and calls LoadFactory() to create the factory. * * Again, no attempt is made at storing the factory. */ nsresult nsRepository::FindFactory(const nsCID &aClass, nsIFactory **aFactory) { checkInitialized(); if (PR_LOG_TEST(logmodule, PR_LOG_ALWAYS)) { char *buf = aClass.ToString(); PR_LogPrint("nsRepository: FindFactory(%s)", buf); delete [] buf; } PR_ASSERT(aFactory != NULL); PR_EnterMonitor(monitor); nsIDKey key(aClass); FactoryEntry *entry = (FactoryEntry*) factories->Get(&key); nsresult res = NS_ERROR_FACTORY_NOT_REGISTERED; #ifdef USE_REGISTRY if (entry == NULL) { PR_LOG(logmodule, PR_LOG_ALWAYS, ("\t\tnot found in factory cache. Looking in registry")); entry = platformFind(aClass); // XXX This should go into platformFind(), and platformFind() // should just become a static method on nsRepository. // If we got one, cache it in our hashtable if (entry != NULL) { PR_LOG(logmodule, PR_LOG_ALWAYS, ("\t\tfound in registry.")); factories->Put(&key, entry); } // XXX update ProgID cache, if necessary } else { PR_LOG(logmodule, PR_LOG_ALWAYS, ("\t\tfound in factory cache.")); } #endif PR_ExitMonitor(monitor); if (entry != NULL) { if ((entry)->factory == NULL) { res = loadFactory(entry, aFactory); } else { *aFactory = entry->factory; (*aFactory)->AddRef(); res = NS_OK; } } PR_LOG(logmodule, PR_LOG_WARNING, ("\t\tFindFactory() %s", NS_SUCCEEDED(res) ? "succeeded" : "FAILED")); return res; } /** * ProgIDToCLSID() * * Mapping function from a ProgID to a classID. Directly talks to the registry. * * XXX We need to add a cache this translation as this is done a lot. */ nsresult nsRepository::ProgIDToCLSID(const char *aProgID, nsCID *aClass) { NS_PRECONDITION(aProgID != NULL, "null ptr"); if (! aProgID) return NS_ERROR_NULL_POINTER; NS_PRECONDITION(aClass != NULL, "null ptr"); if (! aClass) return NS_ERROR_NULL_POINTER; nsresult res = NS_ERROR_FACTORY_NOT_REGISTERED; checkInitialized(); #ifdef USE_REGISTRY // XXX This isn't quite the best way to do this: we should // probably move an nsArray into the FactoryEntry class, // and then have the construct/destructor of the factory entry // keep the ProgID to CID cache up-to-date. However, doing this // significantly improves performance, so it'll do for now. #define NS_NO_CID { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } } static NS_DEFINE_CID(kNoCID, NS_NO_CID); ProgIDKey key(aProgID); nsCID* cid = (nsCID*) progIDs->Get(&key); if (cid) { if (cid == &kNoCID) { // we've already tried to map this ProgID to a CLSID, and found // that there _was_ no such mapping in the registry. } else { *aClass = *cid; res = NS_OK; } } else { // This is the first time someone has asked for this // ProgID. Go to the registry to find the CID. res = platformProgIDToCLSID(aProgID, aClass); if (NS_SUCCEEDED(res)) { // Found it. So put it into the cache. if (! (cid = new nsCID(*aClass))) return NS_ERROR_OUT_OF_MEMORY; progIDs->Put(&key, cid); } else { // Didn't find it. Put a special CID in the cache so we // don't need to hit the registry on subsequent requests // for the same ProgID. progIDs->Put(&key, (void*) &kNoCID); } } #endif /* USE_REGISTRY */ if (PR_LOG_TEST(logmodule, PR_LOG_ALWAYS)) { char *buf; if (NS_SUCCEEDED(res)) buf = aClass->ToString(); PR_LOG(logmodule, PR_LOG_ALWAYS, ("nsRepository: ProgIDToCLSID(%s)->%s", aProgID, NS_SUCCEEDED(res) ? buf : "[FAILED]")); if (NS_SUCCEEDED(res)) delete [] buf; } return res; } /** * CLSIDToProgID() * * Translates a classID to a {ProgID, Class Name}. Does direct registry * access to do the translation. * * XXX Would be nice to hook in a cache here too. */ nsresult nsRepository::CLSIDToProgID(nsCID *aClass, char* *aClassName, char* *aProgID) { nsresult res = NS_ERROR_FACTORY_NOT_REGISTERED; checkInitialized(); #ifdef USE_REGISTRY res = platformCLSIDToProgID(aClass, aClassName, aProgID); #endif /* USE_REGISTRY */ if (PR_LOG_TEST(logmodule, PR_LOG_ALWAYS)) { char *buf = aClass->ToString(); PR_LOG(logmodule, PR_LOG_WARNING, ("nsRepository: CLSIDToProgID(%s)->%s", buf, NS_SUCCEEDED(res) ? *aProgID : "[FAILED]")); delete [] buf; } return res; } /** * checkInitialized() * * Local function to make sure the repository is initialized. The repository * is mostly full of static functions. We dont want to initialize ourselves * with a static constructor as that wont make the code portable. Hence, * in all our public entry points, we check if we are initialized and * initialize ourselves if we aren't already. */ nsresult nsRepository::checkInitialized(void) { nsresult res = NS_OK; if (factories == NULL) { res = Initialize(); } return res; } /** * Initialize() * * Initialization of our global pointers. Also, the libreg/ is started here and * if the xpcom hierarchy in the registry isn't the same as the version that we * expect, it is nuked. * * XXX Plus for now autoregistration() begins here. We will remove it once the * XXX application takes control of it. */ nsresult nsRepository::Initialize(void) { if (factories == NULL) { factories = new nsHashtable(); } if (progIDs == NULL) { progIDs = new nsHashtable(); } if (monitor == NULL) { monitor = PR_NewMonitor(); } if (logmodule == NULL) { logmodule = PR_NewLogModule("nsRepository"); } if (dllStore == NULL) { dllStore = new nsDllStore(); } PR_LOG(logmodule, PR_LOG_ALWAYS, ("nsRepository: Initialized.")); #ifdef USE_NSREG NR_StartupRegistry(); // Check the version of registry. Nuke old versions. platformVersionCheck(); #endif // Initiate autoreg AutoRegister(NS_Startup, NULL); return NS_OK; } /** * CreateInstance() * * Create an instance of an object that implements an interface and belongs * to the implementation aClass using the factory. The factory is immediately * released and not held onto for any longer. */ nsresult nsRepository::CreateInstance(const nsCID &aClass, nsISupports *aDelegate, const nsIID &aIID, void **aResult) { checkInitialized(); if (PR_LOG_TEST(logmodule, PR_LOG_ALWAYS)) { char *buf = aClass.ToString(); PR_LogPrint("nsRepository: CreateInstance(%s)", buf); delete [] buf; } if (aResult == NULL) { return NS_ERROR_NULL_POINTER; } *aResult = NULL; nsIFactory *factory = NULL; nsresult res = FindFactory(aClass, &factory); if (NS_SUCCEEDED(res)) { res = factory->CreateInstance(aDelegate, aIID, aResult); factory->Release(); PR_LOG(logmodule, PR_LOG_ALWAYS, ("\t\tCreateInstance() succeeded.")); return res; } PR_LOG(logmodule, PR_LOG_ALWAYS, ("\t\tCreateInstance() FAILED.")); return NS_ERROR_FACTORY_NOT_REGISTERED; } /** * CreateInstance() * * An overload of CreateInstance() that creates an instance of the object that * implements the interface aIID and whose implementation has a progID aProgID. * * This is only a convenience routine that turns around can calls the * CreateInstance() with classid and iid. * * XXX This is a function overload. We need to remove it. */ nsresult nsRepository::CreateInstance(const char *aProgID, nsISupports *aDelegate, const nsIID &aIID, void **aResult) { nsCID clsid; nsresult rv = ProgIDToCLSID(aProgID, &clsid); if (NS_FAILED(rv)) return rv; return CreateInstance(clsid, aDelegate, aIID, aResult); } #if 0 /* nsresult nsRepository::CreateInstance2(const nsCID &aClass, nsISupports *aDelegate, const nsIID &aIID, void *aSignature, void **aResult) { if (PR_LOG_TEST(logmodule, PR_LOG_ALWAYS)) { char *buf = aClass.ToString(); PR_LogPrint("nsRepository: Creating Instance."); PR_LogPrint("nsRepository: + %s.", buf); PR_LogPrint("nsRepository: + Signature = %p.", aSignature); delete [] buf; } if (aResult == NULL) { return NS_ERROR_NULL_POINTER; } *aResult = NULL; nsIFactory *factory = NULL; nsresult res = FindFactory(aClass, &factory); if (NS_SUCCEEDED(res)) { nsIFactory2 *factory2 = NULL; res = NS_ERROR_FACTORY_NO_SIGNATURE_SUPPORT; factory->QueryInterface(kFactory2IID, (void **) &factory2); if (factory2 != NULL) { res = factory2->CreateInstance2(aDelegate, aIID, aSignature, aResult); factory2->Release(); } factory->Release(); return res; } return NS_ERROR_FACTORY_NOT_REGISTERED; } */ #endif /* 0 */ /** * RegisterFactory() * * Register a factory to be responsible for creation of implementation of * classID aClass. Plus creates as association of aClassName and aProgID * to the classID. If replace is PR_TRUE, we replace any existing registrations * with this one. * * Once registration is complete, we add the class to the factories cache * that we maintain. The factories cache is the ONLY place where these * registrations are ever kept. * * XXX This uses FindFactory() to test if a factory already exists. This * XXX has the bad side effect of loading the factory if the previous * XXX registration was a dll for this class. We might be able to do away * XXX with such a load. */ nsresult nsRepository::RegisterFactory(const nsCID &aClass, const char *aClassName, const char *aProgID, nsIFactory *aFactory, PRBool aReplace) { checkInitialized(); if (PR_LOG_TEST(logmodule, PR_LOG_ALWAYS)) { char *buf = aClass.ToString(); PR_LogPrint("nsRepository: RegisterFactory(%s, factory), replace = %d.", buf, (int)aReplace); delete [] buf; } nsIFactory *old = NULL; FindFactory(aClass, &old); if (old != NULL) { old->Release(); if (!aReplace) { PR_LOG(logmodule, PR_LOG_WARNING, ("\t\tFactory already registered.")); return NS_ERROR_FACTORY_EXISTS; } else { PR_LOG(logmodule, PR_LOG_WARNING, ("\t\tdeleting old Factory Entry.")); } } PR_EnterMonitor(monitor); nsIDKey key(aClass); factories->Put(&key, new FactoryEntry(aClass, aFactory)); // XXX update ProgID to CID cache as well, if necessary PR_ExitMonitor(monitor); PR_LOG(logmodule, PR_LOG_WARNING, ("\t\tFactory register succeeded.")); return NS_OK; } nsresult nsRepository::RegisterComponent(const nsCID &aClass, const char *aClassName, const char *aProgID, const char *aLibrary, PRBool aReplace, PRBool aPersist) { checkInitialized(); if (PR_LOG_TEST(logmodule, PR_LOG_ALWAYS)) { char *buf = aClass.ToString(); PR_LogPrint("nsRepository: RegisterComponent(%s, %s, %s, %s), replace = %d, persist = %d.", buf, aClassName, aProgID, aLibrary, (int)aReplace, (int)aPersist); delete [] buf; } nsIFactory *old = NULL; FindFactory(aClass, &old); if (old != NULL) { old->Release(); if (!aReplace) { PR_LOG(logmodule, PR_LOG_WARNING,("\t\tFactory already registered.")); return NS_ERROR_FACTORY_EXISTS; } else { PR_LOG(logmodule, PR_LOG_WARNING,("\t\tdeleting registered Factory.")); } } PR_EnterMonitor(monitor); #ifdef USE_REGISTRY if (aPersist == PR_TRUE) { // Add it to the registry nsDll *dll = new nsDll(aLibrary); // XXX temp hack until we get the dll to give us the entire // XXX NSQuickRegisterClassData NSQuickRegisterClassData cregd = {0}; cregd.CIDString = aClass.ToString(); cregd.className = aClassName; cregd.progID = aProgID; platformRegister(&cregd, dll); delete [] (char *)cregd.CIDString; delete dll; } else #endif { nsDll *dll = new nsDll(aLibrary); nsIDKey key(aClass); factories->Put(&key, new FactoryEntry(aClass, aLibrary, dll->GetLastModifiedTime(), dll->GetSize())); // XXX Update ProgID to CID cache as well, if necessary. delete dll; } PR_ExitMonitor(monitor); PR_LOG(logmodule, PR_LOG_WARNING, ("\t\tFactory register succeeded.")); return NS_OK; } nsresult nsRepository::UnregisterFactory(const nsCID &aClass, nsIFactory *aFactory) { checkInitialized(); if (PR_LOG_TEST(logmodule, PR_LOG_ALWAYS)) { char *buf = aClass.ToString(); PR_LogPrint("nsRepository: Unregistering Factory."); PR_LogPrint("nsRepository: + %s.", buf); delete [] buf; } nsIDKey key(aClass); nsresult res = NS_ERROR_FACTORY_NOT_REGISTERED; FactoryEntry *old = (FactoryEntry *) factories->Get(&key); if (old != NULL) { if (old->factory == aFactory) { PR_EnterMonitor(monitor); old = (FactoryEntry *) factories->Remove(&key); PR_ExitMonitor(monitor); delete old; res = NS_OK; } } PR_LOG(logmodule, PR_LOG_WARNING, ("nsRepository: ! Factory unregister %s.", res == NS_OK ? "succeeded" : "failed")); return res; } nsresult nsRepository::UnregisterComponent(const nsCID &aClass, const char *aLibrary) { checkInitialized(); if (PR_LOG_TEST(logmodule, PR_LOG_ALWAYS)) { char *buf = aClass.ToString(); PR_LogPrint("nsRepository: Unregistering Factory."); PR_LogPrint("nsRepository: + %s in \"%s\".", buf, aLibrary); delete [] buf; } nsIDKey key(aClass); FactoryEntry *old = (FactoryEntry *) factories->Get(&key); nsresult res = NS_ERROR_FACTORY_NOT_REGISTERED; PR_EnterMonitor(monitor); if (old != NULL && old->dll != NULL) { if (old->dll->GetFullPath() != NULL && #ifdef XP_UNIX PL_strcasecmp(old->dll->GetFullPath(), aLibrary) #else PL_strcmp(old->dll->GetFullPath(), aLibrary) #endif ) { FactoryEntry *entry = (FactoryEntry *) factories->Remove(&key); delete entry; res = NS_OK; } #ifdef USE_REGISTRY // XXX temp hack until we get the dll to give us the entire // XXX NSQuickRegisterClassData NSQuickRegisterClassData cregd = {0}; cregd.CIDString = aClass.ToString(); res = platformUnregister(&cregd, aLibrary); delete [] (char *)cregd.CIDString; #endif } PR_ExitMonitor(monitor); PR_LOG(logmodule, PR_LOG_WARNING, ("nsRepository: ! Factory unregister %s.", res == NS_OK ? "succeeded" : "failed")); return res; } nsresult nsRepository::UnregisterFactory(const nsCID &aClass, const char *aLibrary) { checkInitialized(); if (PR_LOG_TEST(logmodule, PR_LOG_ALWAYS)) { char *buf = aClass.ToString(); PR_LogPrint("nsRepository: Unregistering Factory."); PR_LogPrint("nsRepository: + %s in \"%s\".", buf, aLibrary); delete [] buf; } nsIDKey key(aClass); FactoryEntry *old = (FactoryEntry *) factories->Get(&key); nsresult res = NS_ERROR_FACTORY_NOT_REGISTERED; PR_EnterMonitor(monitor); if (old != NULL && old->dll != NULL) { if (old->dll->GetFullPath() != NULL && #ifdef XP_UNIX PL_strcasecmp(old->dll->GetFullPath(), aLibrary) #else PL_strcmp(old->dll->GetFullPath(), aLibrary) #endif ) { FactoryEntry *entry = (FactoryEntry *) factories->Remove(&key); delete entry; res = NS_OK; } #ifdef USE_REGISTRY // XXX temp hack until we get the dll to give us the entire // XXX NSQuickRegisterClassData NSQuickRegisterClassData cregd = {0}; cregd.CIDString = aClass.ToString(); res = platformUnregister(&cregd, aLibrary); delete [] (char *)cregd.CIDString; #endif } PR_ExitMonitor(monitor); PR_LOG(logmodule, PR_LOG_WARNING, ("nsRepository: ! Factory unregister %s.", res == NS_OK ? "succeeded" : "failed")); return res; } static PRBool freeLibraryEnum(nsHashKey *aKey, void *aData, void* closure) { FactoryEntry *entry = (FactoryEntry *) aData; if (entry->dll->IsLoaded() == PR_TRUE) { nsCanUnloadProc proc = (nsCanUnloadProc) entry->dll->FindSymbol("NSCanUnload"); if (proc != NULL) { nsIServiceManager* serviceMgr = NULL; nsresult res = nsServiceManager::GetGlobalServiceManager(&serviceMgr); NS_ASSERTION(res == NS_OK, "no service manager"); res = proc(serviceMgr); if (res) { PR_LOG(logmodule, PR_LOG_ALWAYS, ("nsRepository: + Unloading \"%s\".", entry->dll->GetFullPath())); entry->dll->Unload(); } } } return PR_TRUE; } nsresult nsRepository::FreeLibraries(void) { PR_EnterMonitor(monitor); PR_LOG(logmodule, PR_LOG_ALWAYS, ("nsRepository: Freeing Libraries.")); factories->Enumerate(freeLibraryEnum); PR_ExitMonitor(monitor); return NS_OK; } /** * AutoRegister(RegistrationInstant, const char *pathlist) * * Given a ; separated list of paths, this will ensure proper registration * of all components. A default pathlist is maintained in the registry at * \\HKEYROOT_COMMON\\Classes\\PathList * In addition to looking at the pathlist, the default pathlist is looked at. * * This will take care not loading already registered dlls, finding and * registering new dlls, re-registration of modified dlls * */ nsresult nsRepository::AutoRegister(NSRegistrationInstant when, const char* pathlist) { #ifdef XP_MAC CInfoPBRec catInfo; Handle pathH; OSErr err; ProcessSerialNumber psn; ProcessInfoRec pInfo; FSSpec appFSSpec; FSSpec tempSpec; long theDirID; Str255 name; #endif if (pathlist != NULL) { SyncComponentsInPathList(pathlist); } #ifdef XP_MAC // get info for the the current process to determine the directory its located in if (!(err = GetCurrentProcess(&psn))) { // initialize ProcessInfoRec before calling GetProcessInformation() or die horribly. pInfo.processName = nil; pInfo.processAppSpec = &tempSpec; pInfo.processInfoLength = sizeof(ProcessInfoRec); if (!(err = GetProcessInformation(&psn, &pInfo))) { appFSSpec = *(pInfo.processAppSpec); if ((pathH = NewHandle(1)) != NULL) { **pathH = '\0'; // initially null terminate the string HNoPurge(pathH); HUnlock(pathH); theDirID = appFSSpec.parID; do { catInfo.dirInfo.ioCompletion = NULL; catInfo.dirInfo.ioNamePtr = (StringPtr)&name; catInfo.dirInfo.ioVRefNum = appFSSpec.vRefNum; catInfo.dirInfo.ioDrDirID = theDirID; catInfo.dirInfo.ioFDirIndex = -1; // -1 = query dir in ioDrDirID if (!(err = PBGetCatInfoSync(&catInfo))) { // build up a Unix style pathname due to NSPR // XXX Note: this breaks if any of the parent // directories contain a "slash" (blame NSPR) Munger(pathH, 0L, NULL, 0L, (const void *)&name[1], (long)name[0]); // prepend dir name Munger(pathH, 0L, NULL, 0L, "/", 1); // prepend slash // move up to parent directory theDirID = catInfo.dirInfo.ioDrParID; } } while ((!err) && (catInfo.dirInfo.ioDrDirID != 2)); // 2 = root if (!err) { Munger(pathH, GetHandleSize(pathH)-1, NULL, 0L, "/components", 11); // append "/components" HLock(pathH); SyncComponentsInPathList((const char *)(*pathH)); HUnlock(pathH); } DisposeHandle(pathH); } } } #else //XXX get default pathlist from registry //XXX Temporary hack. Registering components from components directory const char *defaultPathList = "./components"; SyncComponentsInPathList(defaultPathList); #endif return (NS_OK); } nsresult nsRepository::AddToDefaultPathList(const char *pathlist) { //XXX add pathlist to the defaultpathlist in the registry return (NS_ERROR_FAILURE); } nsresult nsRepository::SyncComponentsInPathList(const char *pathlist) { char *paths = PL_strdup(pathlist); if (paths == NULL || *paths == '\0') return(NS_ERROR_FAILURE); char *pathsMem = paths; while (paths != NULL) { char *nextpath = PL_strchr(paths, NS_PATH_SEPARATOR); if (nextpath != NULL) *nextpath = '\0'; SyncComponentsInDir(paths); paths = nextpath; } PL_strfree(pathsMem); return (NS_OK); } nsresult nsRepository::SyncComponentsInDir(const char *dir) { PRDir *prdir = PR_OpenDir(dir); if (prdir == NULL) return (NS_ERROR_FAILURE); // Create a buffer that has dir/ in it so we can append // the filename each time in the loop char fullname[NS_MAX_FILENAME_LEN]; PL_strncpyz(fullname, dir, sizeof(fullname)); unsigned int n = strlen(fullname); if (n+1 < sizeof(fullname)) { fullname[n] = '/'; n++; } char *filepart = fullname + n; PRDirEntry *dirent = NULL; while ((dirent = PR_ReadDir(prdir, PR_SKIP_BOTH)) != NULL) { PL_strncpyz(filepart, dirent->name, sizeof(fullname)-n); nsresult ret = SyncComponentsInFile(fullname); if (NS_FAILED(ret) && NS_ERROR_GET_CODE(ret) == NS_XPCOM_ERRORCODE_IS_DIR) { SyncComponentsInDir(fullname); } } // foreach file PR_CloseDir(prdir); return (NS_OK); } nsresult nsRepository::SyncComponentsInFile(const char *fullname) { const char *ValidDllExtensions[] = { ".dll", /* Windows */ ".dso", /* Unix */ ".so", /* Unix */ ".sl", /* Unix: HP */ ".shlb", /* Mac ? */ ".dlm", /* new for all platforms */ NULL }; PRFileInfo statbuf; if (PR_GetFileInfo(fullname,&statbuf) != PR_SUCCESS) { // Skip files that cannot be stat return (NS_ERROR_FAILURE); } if (statbuf.type == PR_FILE_DIRECTORY) { // Cant register a directory return (NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_XPCOM, NS_XPCOM_ERRORCODE_IS_DIR)); } else if (statbuf.type != PR_FILE_FILE) { // Skip non-files return (NS_ERROR_FAILURE); } // deal with only files that have the right extension PRBool validExtension = PR_FALSE; int flen = PL_strlen(fullname); 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(&(fullname[flen - extlen]), ValidDllExtensions[i]) ) { validExtension = PR_TRUE; break; } } if (validExtension == PR_FALSE) { // Skip invalid extensions return (NS_ERROR_FAILURE); } // Check if dll is one that we have already seen nsDll *dll = dllStore->Get(fullname); if (dll == NULL) { // XXX Create nsDll for this from registry and // XXX add it to our dll cache dllStore. #ifdef USE_REGISTRY dll = platformCreateDll(fullname); #endif /* USE_REGISTRY */ } if (dll != NULL) { // Make sure the dll is OK if (dll->GetStatus() != NS_OK) { PR_LOG(logmodule, PR_LOG_ALWAYS, ("nsRepository: + nsDll not NS_OK \"%s\". Skipping...", dll->GetFullPath())); return (NS_ERROR_FAILURE); } // We already have seen this dll. Check if this dll changed if (LL_EQ(dll->GetLastModifiedTime(), statbuf.modifyTime) && (dll->GetSize() == statbuf.size)) { // Dll hasn't changed. Skip. PR_LOG(logmodule, PR_LOG_ALWAYS, ("nsRepository: + nsDll not changed \"%s\". Skipping...", dll->GetFullPath())); return (NS_OK); } // Aagh! the dll has changed since the last time we saw it. // re-register dll if (dll->IsLoaded()) { // We are screwed. We loaded the old version of the dll // and now we find that the on-disk copy if newer. // The only thing to do would be to ask the dll if it can // unload itself. It can do that if it hasn't created objects // yet. nsCanUnloadProc proc = (nsCanUnloadProc) dll->FindSymbol("NSCanUnload"); if (proc != NULL) { nsIServiceManager* serviceMgr = NULL; nsresult rv = nsServiceManager::GetGlobalServiceManager(&serviceMgr); NS_ASSERTION(rv == NS_OK, "no service manager"); PRBool res = proc(serviceMgr /*, PR_TRUE*/); if (res) { PR_LOG(logmodule, PR_LOG_ALWAYS, ("nsRepository: + Unloading \"%s\".", dll->GetFullPath())); dll->Unload(); } else { // THIS IS THE WORST SITUATION TO BE IN. // Dll doesn't want to be unloaded. Cannot re-register // this dll. PR_LOG(logmodule, PR_LOG_ALWAYS, ("nsRepository: *** Dll already loaded. " "Cannot unload either. Hence cannot re-register " "\"%s\". Skipping...", dll->GetFullPath())); return (NS_ERROR_FAILURE); } } else { // dll doesn't have a CanUnload proc. Guess it is // ok to unload it. dll->Unload(); PR_LOG(logmodule, PR_LOG_ALWAYS, ("nsRepository: + Unloading \"%s\". (no CanUnloadProc).", dll->GetFullPath())); } } // 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(logmodule, PR_LOG_ALWAYS, ("nsRepository: Dll still loaded. Cannot re-register " "\"%s\". Skipping...", dll->GetFullPath())); return (NS_ERROR_FAILURE); } } // dll != NULL else { // Create and add the dll to the dllStore // 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(fullname); dllStore->Put(fullname, 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); nsresult ret = NS_OK; if (NS_FAILED(res)) { PR_LOG(logmodule, PR_LOG_ALWAYS, ("nsRepository: Autoregistration FAILED for " "\"%s\". Skipping...", dll->GetFullPath())); // 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 platformMarkNoComponents(dll); #endif /* USE_REGISTRY */ ret = NS_ERROR_FAILURE; } else { PR_LOG(logmodule, PR_LOG_ALWAYS, ("nsRepository: Autoregistration Passed for " "\"%s\". Skipping...", dll->GetFullPath())); // Marking dll along with modified time and size in the // registry happens at platformregister(). No need to do it // here again. } return (ret); } /* * SelfRegisterDll * * Given a dll abstraction, this will load, selfregister the dll and * unload the dll. * */ nsresult nsRepository::SelfRegisterDll(nsDll *dll) { // Precondition: dll is not loaded already PR_ASSERT(dll->IsLoaded() == PR_FALSE); nsresult res = NS_ERROR_FAILURE; if (dll->Load() == PR_FALSE) { // Cannot load. Probably not a dll. return(NS_ERROR_FAILURE); } nsRegisterProc regproc = (nsRegisterProc)dll->FindSymbol("NSRegisterSelf"); if (regproc == NULL) { // Smart registration NSQuickRegisterData qr = (NSQuickRegisterData)dll->FindSymbol( NS_QUICKREGISTER_DATA_SYMBOL); if (qr == NULL) { res = NS_ERROR_NO_INTERFACE; } else { // XXX register the quick registration data on behalf of the dll // XXX for now return failure res = NS_ERROR_FAILURE; } } else { // Call the NSRegisterSelfProc to enable dll registration nsIServiceManager* serviceMgr = NULL; res = nsServiceManager::GetGlobalServiceManager(&serviceMgr); NS_ASSERTION(res == NS_OK, "no service manager"); res = regproc(serviceMgr, dll->GetFullPath()); } dll->Unload(); return (res); } nsresult nsRepository::SelfUnregisterDll(nsDll *dll) { // Precondition: dll is not loaded PR_ASSERT(dll->IsLoaded() == PR_FALSE); if (dll->Load() == PR_FALSE) { // Cannot load. Probably not a dll. return(NS_ERROR_FAILURE); } nsUnregisterProc unregproc = (nsUnregisterProc) dll->FindSymbol("NSUnregisterSelf"); nsresult res = NS_OK; if (unregproc == NULL) { // Smart unregistration NSQuickRegisterData qr = (NSQuickRegisterData) dll->FindSymbol(NS_QUICKREGISTER_DATA_SYMBOL); if (qr == NULL) { return(NS_ERROR_NO_INTERFACE); } // XXX unregister the dll based on the quick registration data } else { // Call the NSUnregisterSelfProc to enable dll de-registration nsIServiceManager* serviceMgr = NULL; res = nsServiceManager::GetGlobalServiceManager(&serviceMgr); NS_ASSERTION(res == NS_OK, "no service manager"); res = unregproc(serviceMgr, dll->GetFullPath()); } dll->Unload(); return (res); }