/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * 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 #include #ifdef _WIN32 #include #endif #include "plstr.h" #include "prlink.h" #include "nsRepository.h" #ifdef USE_NSREG #define XP_BEGIN_PROTOS extern "C" { #define XP_END_PROTOS }; #include "NSReg.h" #endif #ifdef XP_MAC #ifdef MOZ_NGLAYOUT #define IMPL_MAC_REPOSITORY #include "nsMacRepository.h" #endif #endif nsHashtable *NSRepository::factories = NULL; PRMonitor *NSRepository::monitor = NULL; static PRLogModuleInfo *logmodule = NULL; static NS_DEFINE_IID(kFactory2IID, NS_IFACTORY2_IID); class FactoryEntry { public: nsCID cid; nsIFactory *factory; char *library; PRLibrary *instance; FactoryEntry(const nsCID &aClass, nsIFactory *aFactory, const char *aLibrary) { cid = aClass; factory = aFactory; if (aLibrary != NULL) { library = PL_strdup(aLibrary); } else { library = NULL; } instance = NULL; } ~FactoryEntry(void) { if (library != NULL) { free(library); } if (instance != NULL) { PR_UnloadLibrary(instance); } if (factory != NULL) { factory->Release(); } } }; class IDKey: public nsHashKey { private: nsID id; public: IDKey(const nsID &aID) { id = aID; } PRUint32 HashValue(void) const { return id.m0; } PRBool Equals(const nsHashKey *aKey) const { return (id.Equals(((const IDKey *) aKey)->id)); } nsHashKey *Clone(void) const { return new IDKey(id); } }; #ifdef USE_NSREG #define USE_REGISTRY static nsresult platformRegister(const nsCID &aCID, const char *aLibrary) { HREG hreg; REGERR err = NR_RegOpen(NULL, &hreg); if (err == REGERR_OK) { RKEY key; err = NR_RegAddKey(hreg, ROOTKEY_COMMON, "Classes", &key); if (err == REGERR_OK) { char *cidString = aCID.ToString(); err = NR_RegSetEntryString(hreg, key, cidString, (char *) aLibrary); delete [] cidString; } NR_RegClose(hreg); } return err; } static nsresult platformUnregister(const nsCID &aCID, const char *aLibrary) { HREG hreg; REGERR err = NR_RegOpen(NULL, &hreg); if (err == REGERR_OK) { RKEY key; err = NR_RegAddKey(hreg, ROOTKEY_COMMON, "Classes", &key); if (err == REGERR_OK) { char *cidString = aCID.ToString(); err = NR_RegDeleteEntry(hreg, key, cidString); delete [] cidString; } NR_RegClose(hreg); } return err; } static FactoryEntry *platformFind(const nsCID &aCID) { FactoryEntry *res = NULL; HREG hreg; REGERR err = NR_RegOpen(NULL, &hreg); if (err == REGERR_OK) { RKEY key; err = NR_RegGetKey(hreg, ROOTKEY_COMMON, "Classes", &key); if (err == REGERR_OK) { char *cidString = aCID.ToString(); char library[256]; uint32 len = 256; err = NR_RegGetEntryString(hreg, key, cidString, library, len); delete [] cidString; if (err == REGERR_OK) { res = new FactoryEntry(aCID, NULL, library); } } NR_RegClose(hreg); } return res; } #else // USE_NSREG #ifdef _WIN32 #define USE_REGISTRY static nsresult platformRegister(const nsCID &aCID, const char *aLibrary) { HKEY key; LONG res = RegCreateKey(HKEY_CURRENT_USER, "SOFTWARE\\Netscape\\CID", &key); if (res == ERROR_SUCCESS) { char *cidString = aCID.ToString(); RegSetValue(key, cidString, REG_SZ, aLibrary, PL_strlen(aLibrary)); delete [] cidString; RegCloseKey(key); } return NS_OK; } static nsresult platformUnregister(const nsCID &aCID, const char *aLibrary) { HKEY key; nsresult res = NS_OK; LONG err = RegCreateKey(HKEY_CURRENT_USER, "SOFTWARE\\Netscape\\CID", &key); if (err == ERROR_SUCCESS) { char *cidString = aCID.ToString(); err = RegDeleteKey(key, cidString); if (err != ERROR_SUCCESS) { res = NS_ERROR_FACTORY_NOT_UNREGISTERED; } delete [] cidString; RegCloseKey(key); } return res; } static FactoryEntry *platformFind(const nsCID &aCID) { HKEY key; LONG res = RegOpenKey(HKEY_CURRENT_USER, "SOFTWARE\\Netscape\\CID", &key); if (res == ERROR_SUCCESS) { char *cidString = aCID.ToString(); char library[_MAX_PATH]; LONG len = _MAX_PATH; res = RegQueryValue(key, cidString, library, &len); delete [] cidString; if (res == ERROR_SUCCESS) { FactoryEntry *entry = new FactoryEntry(aCID, NULL, library); return entry; } } return NULL; } #endif // _WIN32 #endif // USE_NSREG nsresult NSRepository::loadFactory(FactoryEntry *aEntry, nsIFactory **aFactory) { if (aFactory == NULL) { return NS_ERROR_NULL_POINTER; } *aFactory = NULL; if (aEntry->instance == NULL) { PR_LOG(logmodule, PR_LOG_ALWAYS, ("NSRepository: + Loading \"%s\".", aEntry->library)); aEntry->instance = PR_LoadLibrary(aEntry->library); } if (aEntry->instance != NULL) { nsFactoryProc proc = (nsFactoryProc) PR_FindSymbol(aEntry->instance, "NSGetFactory"); if (proc != NULL) { nsresult res = proc(aEntry->cid, aFactory); return res; } PR_LOG(logmodule, PR_LOG_ERROR, ("NSRepository: NSGetFactory entrypoint not found.")); } PR_LOG(logmodule, PR_LOG_ERROR, ("NSRepository: Library load unsuccessful.")); return NS_ERROR_FACTORY_NOT_LOADED; } nsresult NSRepository::FindFactory(const nsCID &aClass, nsIFactory **aFactory) { checkInitialized(); if (PR_LOG_TEST(logmodule, PR_LOG_ALWAYS)) { char *buf = aClass.ToString(); PR_LogPrint("NSRepository: Finding Factory."); PR_LogPrint("NSRepository: + %s", buf); delete [] buf; } if (aFactory == NULL) { PR_LOG(logmodule, PR_LOG_ERROR, ("NSRepository: !! NULL pointer.")); return NS_ERROR_NULL_POINTER; } PR_EnterMonitor(monitor); IDKey key(aClass); FactoryEntry *entry = (FactoryEntry*) factories->Get(&key); nsresult res = NS_ERROR_FACTORY_NOT_REGISTERED; #ifdef USE_REGISTRY if (entry == NULL) { entry = platformFind(aClass); // If we got one, cache it in our hashtable if (entry != NULL) { factories->Put(&key, entry); } } #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, ("NSRepository: ! Find Factory %s", res == NS_OK ? "succeeded" : "failed")); return res; } nsresult NSRepository::checkInitialized(void) { nsresult res = NS_OK; if (factories == NULL) { res = Initialize(); } return res; } nsresult NSRepository::Initialize(void) { if (factories == NULL) { factories = new nsHashtable(); } if (monitor == NULL) { monitor = PR_NewMonitor(); } if (logmodule == NULL) { logmodule = PR_NewLogModule("NSRepository"); } PR_LOG(logmodule, PR_LOG_ALWAYS, ("NSRepository: Initialized.")); #ifdef USE_NSREG NR_StartupRegistry(); #endif return NS_OK; } 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: Creating Instance."); PR_LogPrint("NSRepository: + %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(); return res; } return NS_ERROR_FACTORY_NOT_REGISTERED; } 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; } nsresult NSRepository::RegisterFactory(const nsCID &aClass, nsIFactory *aFactory, PRBool aReplace) { checkInitialized(); if (PR_LOG_TEST(logmodule, PR_LOG_ALWAYS)) { char *buf = aClass.ToString(); PR_LogPrint("NSRepository: Registering Factory."); PR_LogPrint("NSRepository: + %s", buf); PR_LogPrint("NSRepository: + Replace = %d.", (int) aReplace); delete [] buf; } nsIFactory *old = NULL; FindFactory(aClass, &old); if (old != NULL) { old->Release(); if (!aReplace) { PR_LOG(logmodule, PR_LOG_WARNING, ("NSRepository: ! Factory already registered.")); return NS_ERROR_FACTORY_EXISTS; } } PR_EnterMonitor(monitor); IDKey key(aClass); factories->Put(&key, new FactoryEntry(aClass, aFactory, NULL)); PR_ExitMonitor(monitor); PR_LOG(logmodule, PR_LOG_WARNING, ("NSRepository: ! Factory register succeeded.")); return NS_OK; } nsresult NSRepository::RegisterFactory(const nsCID &aClass, const char *aLibrary, PRBool aReplace, PRBool aPersist) { checkInitialized(); if (PR_LOG_TEST(logmodule, PR_LOG_ALWAYS)) { char *buf = aClass.ToString(); PR_LogPrint("NSRepository: Registering Factory."); PR_LogPrint("NSRepository: + %s in \"%s\".", buf, aLibrary); PR_LogPrint("NSRepository: + Replace = %d, Persist = %d.", (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, ("NSRepository: ! Factory already registered.")); return NS_ERROR_FACTORY_EXISTS; } } PR_EnterMonitor(monitor); #ifdef USE_REGISTRY if (aPersist == PR_TRUE) { platformRegister(aClass, aLibrary); } else #endif { IDKey key(aClass); factories->Put(&key, new FactoryEntry(aClass, NULL, aLibrary)); } PR_ExitMonitor(monitor); PR_LOG(logmodule, PR_LOG_WARNING, ("NSRepository: ! Factory 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; } nsIFactory *old = NULL; FindFactory(aClass, &old); nsresult res = NS_ERROR_FACTORY_NOT_REGISTERED; if (old != NULL) { if (old == aFactory) { PR_EnterMonitor(monitor); IDKey key(aClass); FactoryEntry *entry = (FactoryEntry *) factories->Remove(&key); delete entry; PR_ExitMonitor(monitor); res = NS_OK; } old->Release(); } 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; } IDKey key(aClass); FactoryEntry *old = (FactoryEntry *) factories->Get(&key); nsresult res = NS_ERROR_FACTORY_NOT_REGISTERED; PR_EnterMonitor(monitor); if (old != NULL) { #ifdef XP_UNIX if (old->library != NULL && PL_strcasecmp(old->library, aLibrary)) #else if (old->library != NULL && PL_strcmp(old->library, aLibrary)) #endif { FactoryEntry *entry = (FactoryEntry *) factories->Remove(&key); delete entry; res = NS_OK; } } #ifdef USE_REGISTRY res = platformUnregister(aClass, aLibrary); #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) { FactoryEntry *entry = (FactoryEntry *) aData; if (entry->instance != NULL) { nsCanUnloadProc proc = (nsCanUnloadProc) PR_FindSymbol(entry->instance, "NSCanUnload"); if (proc != NULL) { PRBool res = proc(); if (res) { PR_LOG(logmodule, PR_LOG_ALWAYS, ("NSRepository: + Unloading \"%s\".", entry->library)); PR_UnloadLibrary(entry->instance); entry->instance = NULL; } } } 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; }