diff options
Diffstat (limited to 'xpcom/components/nsComponentManager.cpp')
-rw-r--r-- | xpcom/components/nsComponentManager.cpp | 2083 |
1 files changed, 2083 insertions, 0 deletions
diff --git a/xpcom/components/nsComponentManager.cpp b/xpcom/components/nsComponentManager.cpp new file mode 100644 index 0000000000..b9eb8e275f --- /dev/null +++ b/xpcom/components/nsComponentManager.cpp @@ -0,0 +1,2083 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * 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 <stdlib.h> +#include "nscore.h" +#include "nsISupports.h" +#include "nspr.h" +#include "nsCRT.h" // for atoll + +// Arena used by component manager for storing contractid string, dll +// location strings and small objects +// CAUTION: Arena align mask needs to be defined before including plarena.h +// currently from nsComponentManager.h +#define PL_ARENA_CONST_ALIGN_MASK 7 +#define NS_CM_BLOCK_SIZE (1024 * 8) + +#include "nsCategoryManager.h" +#include "nsCOMPtr.h" +#include "nsComponentManager.h" +#include "nsDirectoryService.h" +#include "nsDirectoryServiceDefs.h" +#include "nsCategoryManager.h" +#include "nsCategoryManagerUtils.h" +#include "xptiprivate.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/XPTInterfaceInfoManager.h" +#include "nsIConsoleService.h" +#include "nsIObserverService.h" +#include "nsISimpleEnumerator.h" +#include "nsIStringEnumerator.h" +#include "nsXPCOM.h" +#include "nsXPCOMPrivate.h" +#include "nsISupportsPrimitives.h" +#include "nsIClassInfo.h" +#include "nsLocalFile.h" +#include "nsReadableUtils.h" +#include "nsString.h" +#include "nsXPIDLString.h" +#include "prcmon.h" +#include "xptinfo.h" // this after nsISupports, to pick up IID so that xpt stuff doesn't try to define it itself... +#include "nsThreadUtils.h" +#include "prthread.h" +#include "private/pprthred.h" +#include "nsTArray.h" +#include "prio.h" +#include "ManifestParser.h" +#include "nsNetUtil.h" +#include "mozilla/Services.h" + +#include "mozilla/GenericFactory.h" +#include "nsSupportsPrimitives.h" +#include "nsArray.h" +#include "nsIMutableArray.h" +#include "nsArrayEnumerator.h" +#include "nsStringEnumerator.h" +#include "mozilla/FileUtils.h" +#include "mozilla/UniquePtr.h" +#include "nsDataHashtable.h" + +#include <new> // for placement new + +#include "mozilla/Omnijar.h" + +#include "mozilla/Logging.h" +#include "LogModulePrefWatcher.h" + +using namespace mozilla; + +static LazyLogModule nsComponentManagerLog("nsComponentManager"); + +#if 0 || defined (DEBUG_timeless) + #define SHOW_DENIED_ON_SHUTDOWN + #define SHOW_CI_ON_EXISTING_SERVICE +#endif + +// Bloated registry buffer size to improve startup performance -- needs to +// be big enough to fit the entire file into memory or it'll thrash. +// 512K is big enough to allow for some future growth in the registry. +#define BIG_REGISTRY_BUFLEN (512*1024) + +// Common Key Names +const char xpcomComponentsKeyName[] = "software/mozilla/XPCOM/components"; +const char xpcomKeyName[] = "software/mozilla/XPCOM"; + +// Common Value Names +const char fileSizeValueName[] = "FileSize"; +const char lastModValueName[] = "LastModTimeStamp"; +const char nativeComponentType[] = "application/x-mozilla-native"; +const char staticComponentType[] = "application/x-mozilla-static"; + +NS_DEFINE_CID(kCategoryManagerCID, NS_CATEGORYMANAGER_CID); + +#define UID_STRING_LENGTH 39 + +nsresult +nsGetServiceFromCategory::operator()(const nsIID& aIID, + void** aInstancePtr) const +{ + nsresult rv; + nsXPIDLCString value; + nsCOMPtr<nsICategoryManager> catman; + nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager; + if (!compMgr) { + rv = NS_ERROR_NOT_INITIALIZED; + goto error; + } + + if (!mCategory || !mEntry) { + // when categories have defaults, use that for null mEntry + rv = NS_ERROR_NULL_POINTER; + goto error; + } + + rv = compMgr->nsComponentManagerImpl::GetService(kCategoryManagerCID, + NS_GET_IID(nsICategoryManager), + getter_AddRefs(catman)); + if (NS_FAILED(rv)) { + goto error; + } + + /* find the contractID for category.entry */ + rv = catman->GetCategoryEntry(mCategory, mEntry, + getter_Copies(value)); + if (NS_FAILED(rv)) { + goto error; + } + if (!value) { + rv = NS_ERROR_SERVICE_NOT_AVAILABLE; + goto error; + } + + rv = compMgr->nsComponentManagerImpl::GetServiceByContractID(value, + aIID, + aInstancePtr); + if (NS_FAILED(rv)) { +error: + *aInstancePtr = 0; + } + if (mErrorPtr) { + *mErrorPtr = rv; + } + return rv; +} + +//////////////////////////////////////////////////////////////////////////////// +// Arena helper functions +//////////////////////////////////////////////////////////////////////////////// +char* +ArenaStrndup(const char* aStr, uint32_t aLen, PLArenaPool* aArena) +{ + void* mem; + // Include trailing null in the aLen + PL_ARENA_ALLOCATE(mem, aArena, aLen + 1); + if (mem) { + memcpy(mem, aStr, aLen + 1); + } + return static_cast<char*>(mem); +} + +char* +ArenaStrdup(const char* aStr, PLArenaPool* aArena) +{ + return ArenaStrndup(aStr, strlen(aStr), aArena); +} + +// GetService and a few other functions need to exit their mutex mid-function +// without reentering it later in the block. This class supports that +// style of early-exit that MutexAutoUnlock doesn't. + +namespace { + +class MOZ_STACK_CLASS MutexLock +{ +public: + explicit MutexLock(SafeMutex& aMutex) + : mMutex(aMutex) + , mLocked(false) + { + Lock(); + } + + ~MutexLock() + { + if (mLocked) { + Unlock(); + } + } + + void Lock() + { + NS_ASSERTION(!mLocked, "Re-entering a mutex"); + mMutex.Lock(); + mLocked = true; + } + + void Unlock() + { + NS_ASSERTION(mLocked, "Exiting a mutex that isn't held!"); + mMutex.Unlock(); + mLocked = false; + } + +private: + SafeMutex& mMutex; + bool mLocked; +}; + +} // namespace + +// this is safe to call during InitXPCOM +static already_AddRefed<nsIFile> +GetLocationFromDirectoryService(const char* aProp) +{ + nsCOMPtr<nsIProperties> directoryService; + nsDirectoryService::Create(nullptr, + NS_GET_IID(nsIProperties), + getter_AddRefs(directoryService)); + + if (!directoryService) { + return nullptr; + } + + nsCOMPtr<nsIFile> file; + nsresult rv = directoryService->Get(aProp, + NS_GET_IID(nsIFile), + getter_AddRefs(file)); + if (NS_FAILED(rv)) { + return nullptr; + } + + return file.forget(); +} + +static already_AddRefed<nsIFile> +CloneAndAppend(nsIFile* aBase, const nsACString& aAppend) +{ + nsCOMPtr<nsIFile> f; + aBase->Clone(getter_AddRefs(f)); + if (!f) { + return nullptr; + } + + f->AppendNative(aAppend); + return f.forget(); +} + +//////////////////////////////////////////////////////////////////////////////// +// nsComponentManagerImpl +//////////////////////////////////////////////////////////////////////////////// + +nsresult +nsComponentManagerImpl::Create(nsISupports* aOuter, REFNSIID aIID, + void** aResult) +{ + if (aOuter) { + return NS_ERROR_NO_AGGREGATION; + } + + if (!gComponentManager) { + return NS_ERROR_FAILURE; + } + + return gComponentManager->QueryInterface(aIID, aResult); +} + +static const int CONTRACTID_HASHTABLE_INITIAL_LENGTH = 1024; + +nsComponentManagerImpl::nsComponentManagerImpl() + : mFactories(CONTRACTID_HASHTABLE_INITIAL_LENGTH) + , mContractIDs(CONTRACTID_HASHTABLE_INITIAL_LENGTH) + , mLock("nsComponentManagerImpl.mLock") + , mStatus(NOT_INITIALIZED) +{ +} + +nsTArray<const mozilla::Module*>* nsComponentManagerImpl::sStaticModules; + +NSMODULE_DEFN(start_kPStaticModules); +NSMODULE_DEFN(end_kPStaticModules); + +/* The content between start_kPStaticModules and end_kPStaticModules is gathered + * by the linker from various objects containing symbols in a specific section. + * ASAN considers (rightfully) the use of this content as a global buffer + * overflow. But this is a deliberate and well-considered choice, with no proper + * way to make ASAN happy. */ +MOZ_ASAN_BLACKLIST +/* static */ void +nsComponentManagerImpl::InitializeStaticModules() +{ + if (sStaticModules) { + return; + } + + sStaticModules = new nsTArray<const mozilla::Module*>; + for (const mozilla::Module * const* staticModules = + &NSMODULE_NAME(start_kPStaticModules) + 1; + staticModules < &NSMODULE_NAME(end_kPStaticModules); ++staticModules) + if (*staticModules) { // ASAN adds padding + sStaticModules->AppendElement(*staticModules); + } +} + +nsTArray<nsComponentManagerImpl::ComponentLocation>* +nsComponentManagerImpl::sModuleLocations; + +/* static */ void +nsComponentManagerImpl::InitializeModuleLocations() +{ + if (sModuleLocations) { + return; + } + + sModuleLocations = new nsTArray<ComponentLocation>; +} + +nsresult +nsComponentManagerImpl::Init() +{ + MOZ_ASSERT(NOT_INITIALIZED == mStatus); + + // Initialize our arena + PL_INIT_ARENA_POOL(&mArena, "ComponentManagerArena", NS_CM_BLOCK_SIZE); + + nsCOMPtr<nsIFile> greDir = + GetLocationFromDirectoryService(NS_GRE_DIR); + nsCOMPtr<nsIFile> appDir = + GetLocationFromDirectoryService(NS_XPCOM_CURRENT_PROCESS_DIR); + + InitializeStaticModules(); + + nsresult rv = mNativeModuleLoader.Init(); + if (NS_FAILED(rv)) { + return rv; + } + + nsCategoryManager::GetSingleton()->SuppressNotifications(true); + + RegisterModule(&kXPCOMModule, nullptr); + + for (uint32_t i = 0; i < sStaticModules->Length(); ++i) { + RegisterModule((*sStaticModules)[i], nullptr); + } + + bool loadChromeManifests = (XRE_GetProcessType() != GeckoProcessType_GPU); + if (loadChromeManifests) { + // The overall order in which chrome.manifests are expected to be treated + // is the following: + // - greDir + // - greDir's omni.ja + // - appDir + // - appDir's omni.ja + + InitializeModuleLocations(); + ComponentLocation* cl = sModuleLocations->AppendElement(); + nsCOMPtr<nsIFile> lf = CloneAndAppend(greDir, + NS_LITERAL_CSTRING("chrome.manifest")); + cl->type = NS_APP_LOCATION; + cl->location.Init(lf); + + RefPtr<nsZipArchive> greOmnijar = + mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE); + if (greOmnijar) { + cl = sModuleLocations->AppendElement(); + cl->type = NS_APP_LOCATION; + cl->location.Init(greOmnijar, "chrome.manifest"); + } + + bool equals = false; + appDir->Equals(greDir, &equals); + if (!equals) { + cl = sModuleLocations->AppendElement(); + cl->type = NS_APP_LOCATION; + lf = CloneAndAppend(appDir, NS_LITERAL_CSTRING("chrome.manifest")); + cl->location.Init(lf); + } + + RefPtr<nsZipArchive> appOmnijar = + mozilla::Omnijar::GetReader(mozilla::Omnijar::APP); + if (appOmnijar) { + cl = sModuleLocations->AppendElement(); + cl->type = NS_APP_LOCATION; + cl->location.Init(appOmnijar, "chrome.manifest"); + } + + RereadChromeManifests(false); + } + + nsCategoryManager::GetSingleton()->SuppressNotifications(false); + + RegisterWeakMemoryReporter(this); + + // NB: The logging preference watcher needs to be registered late enough in + // startup that it's okay to use the preference system, but also as soon as + // possible so that log modules enabled via preferences are turned on as + // early as possible. + // + // We can't initialize the preference watcher when the log module manager is + // initialized, as a number of things attempt to start logging before the + // preference system is initialized. + // + // The preference system is registered as a component so at this point during + // component manager initialization we know it is setup and we can register + // for notifications. + LogModulePrefWatcher::RegisterPrefWatcher(); + + // Unfortunately, we can't register the nsCategoryManager memory reporter + // in its constructor (which is triggered by the GetSingleton() call + // above) because the memory reporter manager isn't initialized at that + // point. So we wait until now. + nsCategoryManager::GetSingleton()->InitMemoryReporter(); + + MOZ_LOG(nsComponentManagerLog, LogLevel::Debug, + ("nsComponentManager: Initialized.")); + + mStatus = NORMAL; + + return NS_OK; +} + +static bool +ProcessSelectorMatches(Module::ProcessSelector aSelector) +{ + GeckoProcessType type = XRE_GetProcessType(); + if (type == GeckoProcessType_GPU) { + return !!(aSelector & Module::ALLOW_IN_GPU_PROCESS); + } + + if (aSelector & Module::MAIN_PROCESS_ONLY) { + return type == GeckoProcessType_Default; + } + if (aSelector & Module::CONTENT_PROCESS_ONLY) { + return type == GeckoProcessType_Content; + } + return true; +} + +static const int kModuleVersionWithSelector = 51; + +void +nsComponentManagerImpl::RegisterModule(const mozilla::Module* aModule, + FileLocation* aFile) +{ + mLock.AssertNotCurrentThreadOwns(); + + if (aModule->mVersion >= kModuleVersionWithSelector && + !ProcessSelectorMatches(aModule->selector)) + { + return; + } + + { + // Scope the monitor so that we don't hold it while calling into the + // category manager. + MutexLock lock(mLock); + + KnownModule* m; + if (aFile) { + nsCString uri; + aFile->GetURIString(uri); + NS_ASSERTION(!mKnownModules.Get(uri), + "Must not register a binary module twice."); + + m = new KnownModule(aModule, *aFile); + mKnownModules.Put(uri, m); + } else { + m = new KnownModule(aModule); + mKnownStaticModules.AppendElement(m); + } + + if (aModule->mCIDs) { + const mozilla::Module::CIDEntry* entry; + for (entry = aModule->mCIDs; entry->cid; ++entry) { + RegisterCIDEntryLocked(entry, m); + } + } + + if (aModule->mContractIDs) { + const mozilla::Module::ContractIDEntry* entry; + for (entry = aModule->mContractIDs; entry->contractid; ++entry) { + RegisterContractIDLocked(entry); + } + MOZ_ASSERT(!entry->cid, "Incorrectly terminated contract list"); + } + } + + if (aModule->mCategoryEntries) { + const mozilla::Module::CategoryEntry* entry; + for (entry = aModule->mCategoryEntries; entry->category; ++entry) + nsCategoryManager::GetSingleton()->AddCategoryEntry(entry->category, + entry->entry, + entry->value); + } +} + +void +nsComponentManagerImpl::RegisterCIDEntryLocked( + const mozilla::Module::CIDEntry* aEntry, + KnownModule* aModule) +{ + mLock.AssertCurrentThreadOwns(); + + if (!ProcessSelectorMatches(aEntry->processSelector)) { + return; + } + + nsFactoryEntry* f = mFactories.Get(*aEntry->cid); + if (f) { + NS_WARNING("Re-registering a CID?"); + + char idstr[NSID_LENGTH]; + aEntry->cid->ToProvidedString(idstr); + + nsCString existing; + if (f->mModule) { + existing = f->mModule->Description(); + } else { + existing = "<unknown module>"; + } + SafeMutexAutoUnlock unlock(mLock); + LogMessage("While registering XPCOM module %s, trying to re-register CID '%s' already registered by %s.", + aModule->Description().get(), + idstr, + existing.get()); + return; + } + + f = new nsFactoryEntry(aEntry, aModule); + mFactories.Put(*aEntry->cid, f); +} + +void +nsComponentManagerImpl::RegisterContractIDLocked( + const mozilla::Module::ContractIDEntry* aEntry) +{ + mLock.AssertCurrentThreadOwns(); + + if (!ProcessSelectorMatches(aEntry->processSelector)) { + return; + } + + nsFactoryEntry* f = mFactories.Get(*aEntry->cid); + if (!f) { + NS_WARNING("No CID found when attempting to map contract ID"); + + char idstr[NSID_LENGTH]; + aEntry->cid->ToProvidedString(idstr); + + SafeMutexAutoUnlock unlock(mLock); + LogMessage("Could not map contract ID '%s' to CID %s because no implementation of the CID is registered.", + aEntry->contractid, + idstr); + + return; + } + + mContractIDs.Put(nsDependentCString(aEntry->contractid), f); +} + +static void +CutExtension(nsCString& aPath) +{ + int32_t dotPos = aPath.RFindChar('.'); + if (kNotFound == dotPos) { + aPath.Truncate(); + } else { + aPath.Cut(0, dotPos + 1); + } +} + +static void +DoRegisterManifest(NSLocationType aType, + FileLocation& aFile, + bool aChromeOnly, + bool aXPTOnly) +{ + MOZ_ASSERT(!aXPTOnly || !nsComponentManagerImpl::gComponentManager); + uint32_t len; + FileLocation::Data data; + UniquePtr<char[]> buf; + nsresult rv = aFile.GetData(data); + if (NS_SUCCEEDED(rv)) { + rv = data.GetSize(&len); + } + if (NS_SUCCEEDED(rv)) { + buf = MakeUnique<char[]>(len + 1); + rv = data.Copy(buf.get(), len); + } + if (NS_SUCCEEDED(rv)) { + buf[len] = '\0'; + ParseManifest(aType, aFile, buf.get(), aChromeOnly, aXPTOnly); + } else if (NS_BOOTSTRAPPED_LOCATION != aType) { + nsCString uri; + aFile.GetURIString(uri); + LogMessage("Could not read chrome manifest '%s'.", uri.get()); + } +} + +void +nsComponentManagerImpl::RegisterManifest(NSLocationType aType, + FileLocation& aFile, + bool aChromeOnly) +{ + DoRegisterManifest(aType, aFile, aChromeOnly, false); +} + +void +nsComponentManagerImpl::ManifestManifest(ManifestProcessingContext& aCx, + int aLineNo, char* const* aArgv) +{ + char* file = aArgv[0]; + FileLocation f(aCx.mFile, file); + RegisterManifest(aCx.mType, f, aCx.mChromeOnly); +} + +void +nsComponentManagerImpl::ManifestBinaryComponent(ManifestProcessingContext& aCx, + int aLineNo, + char* const* aArgv) +{ + if (aCx.mFile.IsZip()) { + NS_WARNING("Cannot load binary components from a jar."); + LogMessageWithContext(aCx.mFile, aLineNo, + "Cannot load binary components from a jar."); + return; + } + + FileLocation f(aCx.mFile, aArgv[0]); + nsCString uri; + f.GetURIString(uri); + + if (mKnownModules.Get(uri)) { + NS_WARNING("Attempting to register a binary component twice."); + LogMessageWithContext(aCx.mFile, aLineNo, + "Attempting to register a binary component twice."); + return; + } + + const mozilla::Module* m = mNativeModuleLoader.LoadModule(f); + // The native module loader should report an error here, we don't have to + if (!m) { + return; + } + + RegisterModule(m, &f); +} + +static void +DoRegisterXPT(FileLocation& aFile) +{ + uint32_t len; + FileLocation::Data data; + UniquePtr<char[]> buf; + nsresult rv = aFile.GetData(data); + if (NS_SUCCEEDED(rv)) { + rv = data.GetSize(&len); + } + if (NS_SUCCEEDED(rv)) { + buf = MakeUnique<char[]>(len); + rv = data.Copy(buf.get(), len); + } + if (NS_SUCCEEDED(rv)) { + XPTInterfaceInfoManager::GetSingleton()->RegisterBuffer(buf.get(), len); + } else { + nsCString uri; + aFile.GetURIString(uri); + LogMessage("Could not read '%s'.", uri.get()); + } +} + +void +nsComponentManagerImpl::ManifestXPT(ManifestProcessingContext& aCx, + int aLineNo, char* const* aArgv) +{ + FileLocation f(aCx.mFile, aArgv[0]); + DoRegisterXPT(f); +} + +void +nsComponentManagerImpl::ManifestComponent(ManifestProcessingContext& aCx, + int aLineNo, char* const* aArgv) +{ + mLock.AssertNotCurrentThreadOwns(); + + char* id = aArgv[0]; + char* file = aArgv[1]; + + nsID cid; + if (!cid.Parse(id)) { + LogMessageWithContext(aCx.mFile, aLineNo, + "Malformed CID: '%s'.", id); + return; + } + + // Precompute the hash/file data outside of the lock + FileLocation fl(aCx.mFile, file); + nsCString hash; + fl.GetURIString(hash); + + MutexLock lock(mLock); + nsFactoryEntry* f = mFactories.Get(cid); + if (f) { + char idstr[NSID_LENGTH]; + cid.ToProvidedString(idstr); + + nsCString existing; + if (f->mModule) { + existing = f->mModule->Description(); + } else { + existing = "<unknown module>"; + } + + lock.Unlock(); + + LogMessageWithContext(aCx.mFile, aLineNo, + "Trying to re-register CID '%s' already registered by %s.", + idstr, + existing.get()); + return; + } + + KnownModule* km; + + km = mKnownModules.Get(hash); + if (!km) { + km = new KnownModule(fl); + mKnownModules.Put(hash, km); + } + + void* place; + + PL_ARENA_ALLOCATE(place, &mArena, sizeof(nsCID)); + nsID* permanentCID = static_cast<nsID*>(place); + *permanentCID = cid; + + PL_ARENA_ALLOCATE(place, &mArena, sizeof(mozilla::Module::CIDEntry)); + mozilla::Module::CIDEntry* e = new (place) mozilla::Module::CIDEntry(); + e->cid = permanentCID; + + f = new nsFactoryEntry(e, km); + mFactories.Put(cid, f); +} + +void +nsComponentManagerImpl::ManifestContract(ManifestProcessingContext& aCx, + int aLineNo, char* const* aArgv) +{ + mLock.AssertNotCurrentThreadOwns(); + + char* contract = aArgv[0]; + char* id = aArgv[1]; + + nsID cid; + if (!cid.Parse(id)) { + LogMessageWithContext(aCx.mFile, aLineNo, + "Malformed CID: '%s'.", id); + return; + } + + MutexLock lock(mLock); + nsFactoryEntry* f = mFactories.Get(cid); + if (!f) { + lock.Unlock(); + LogMessageWithContext(aCx.mFile, aLineNo, + "Could not map contract ID '%s' to CID %s because no implementation of the CID is registered.", + contract, id); + return; + } + + mContractIDs.Put(nsDependentCString(contract), f); +} + +void +nsComponentManagerImpl::ManifestCategory(ManifestProcessingContext& aCx, + int aLineNo, char* const* aArgv) +{ + char* category = aArgv[0]; + char* key = aArgv[1]; + char* value = aArgv[2]; + + nsCategoryManager::GetSingleton()-> + AddCategoryEntry(category, key, value); +} + +void +nsComponentManagerImpl::RereadChromeManifests(bool aChromeOnly) +{ + for (uint32_t i = 0; i < sModuleLocations->Length(); ++i) { + ComponentLocation& l = sModuleLocations->ElementAt(i); + RegisterManifest(l.type, l.location, aChromeOnly); + } +} + +bool +nsComponentManagerImpl::KnownModule::EnsureLoader() +{ + if (!mLoader) { + nsCString extension; + mFile.GetURIString(extension); + CutExtension(extension); + mLoader = + nsComponentManagerImpl::gComponentManager->LoaderForExtension(extension); + } + return !!mLoader; +} + +bool +nsComponentManagerImpl::KnownModule::Load() +{ + if (mFailed) { + return false; + } + if (!mModule) { + if (!EnsureLoader()) { + return false; + } + + mModule = mLoader->LoadModule(mFile); + + if (!mModule) { + mFailed = true; + return false; + } + } + if (!mLoaded) { + if (mModule->loadProc) { + nsresult rv = mModule->loadProc(); + if (NS_FAILED(rv)) { + mFailed = true; + return false; + } + } + mLoaded = true; + } + return true; +} + +nsCString +nsComponentManagerImpl::KnownModule::Description() const +{ + nsCString s; + if (mFile) { + mFile.GetURIString(s); + } else { + s = "<static module>"; + } + return s; +} + +nsresult nsComponentManagerImpl::Shutdown(void) +{ + MOZ_ASSERT(NORMAL == mStatus); + + mStatus = SHUTDOWN_IN_PROGRESS; + + // Shutdown the component manager + MOZ_LOG(nsComponentManagerLog, LogLevel::Debug, + ("nsComponentManager: Beginning Shutdown.")); + + UnregisterWeakMemoryReporter(this); + + // Release all cached factories + mContractIDs.Clear(); + mFactories.Clear(); // XXX release the objects, don't just clear + mLoaderMap.Clear(); + mKnownModules.Clear(); + mKnownStaticModules.Clear(); + + delete sStaticModules; + delete sModuleLocations; + + // Unload libraries + mNativeModuleLoader.UnloadLibraries(); + + // delete arena for strings and small objects + PL_FinishArenaPool(&mArena); + + mStatus = SHUTDOWN_COMPLETE; + + MOZ_LOG(nsComponentManagerLog, LogLevel::Debug, + ("nsComponentManager: Shutdown complete.")); + + return NS_OK; +} + +nsComponentManagerImpl::~nsComponentManagerImpl() +{ + MOZ_LOG(nsComponentManagerLog, LogLevel::Debug, + ("nsComponentManager: Beginning destruction.")); + + if (SHUTDOWN_COMPLETE != mStatus) { + Shutdown(); + } + + MOZ_LOG(nsComponentManagerLog, LogLevel::Debug, + ("nsComponentManager: Destroyed.")); +} + +NS_IMPL_ISUPPORTS(nsComponentManagerImpl, + nsIComponentManager, + nsIServiceManager, + nsIComponentRegistrar, + nsISupportsWeakReference, + nsIInterfaceRequestor, + nsIMemoryReporter) + +nsresult +nsComponentManagerImpl::GetInterface(const nsIID& aUuid, void** aResult) +{ + NS_WARNING("This isn't supported"); + // fall through to QI as anything QIable is a superset of what can be + // got via the GetInterface() + return QueryInterface(aUuid, aResult); +} + +nsFactoryEntry* +nsComponentManagerImpl::GetFactoryEntry(const char* aContractID, + uint32_t aContractIDLen) +{ + SafeMutexAutoLock lock(mLock); + return mContractIDs.Get(nsDependentCString(aContractID, aContractIDLen)); +} + + +nsFactoryEntry* +nsComponentManagerImpl::GetFactoryEntry(const nsCID& aClass) +{ + SafeMutexAutoLock lock(mLock); + return mFactories.Get(aClass); +} + +already_AddRefed<nsIFactory> +nsComponentManagerImpl::FindFactory(const nsCID& aClass) +{ + nsFactoryEntry* e = GetFactoryEntry(aClass); + if (!e) { + return nullptr; + } + + return e->GetFactory(); +} + +already_AddRefed<nsIFactory> +nsComponentManagerImpl::FindFactory(const char* aContractID, + uint32_t aContractIDLen) +{ + nsFactoryEntry* entry = GetFactoryEntry(aContractID, aContractIDLen); + if (!entry) { + return nullptr; + } + + return entry->GetFactory(); +} + +/** + * GetClassObject() + * + * Given a classID, this finds the singleton ClassObject that implements the CID. + * Returns an interface of type aIID off the singleton classobject. + */ +NS_IMETHODIMP +nsComponentManagerImpl::GetClassObject(const nsCID& aClass, const nsIID& aIID, + void** aResult) +{ + nsresult rv; + + if (MOZ_LOG_TEST(nsComponentManagerLog, LogLevel::Debug)) { + char* buf = aClass.ToString(); + PR_LogPrint("nsComponentManager: GetClassObject(%s)", buf); + if (buf) { + free(buf); + } + } + + MOZ_ASSERT(aResult != nullptr); + + nsCOMPtr<nsIFactory> factory = FindFactory(aClass); + if (!factory) { + return NS_ERROR_FACTORY_NOT_REGISTERED; + } + + rv = factory->QueryInterface(aIID, aResult); + + MOZ_LOG(nsComponentManagerLog, LogLevel::Warning, + ("\t\tGetClassObject() %s", NS_SUCCEEDED(rv) ? "succeeded" : "FAILED")); + + return rv; +} + + +NS_IMETHODIMP +nsComponentManagerImpl::GetClassObjectByContractID(const char* aContractID, + const nsIID& aIID, + void** aResult) +{ + if (NS_WARN_IF(!aResult) || + NS_WARN_IF(!aContractID)) { + return NS_ERROR_INVALID_ARG; + } + + nsresult rv; + + MOZ_LOG(nsComponentManagerLog, LogLevel::Debug, + ("nsComponentManager: GetClassObject(%s)", aContractID)); + + nsCOMPtr<nsIFactory> factory = FindFactory(aContractID, strlen(aContractID)); + if (!factory) { + return NS_ERROR_FACTORY_NOT_REGISTERED; + } + + rv = factory->QueryInterface(aIID, aResult); + + MOZ_LOG(nsComponentManagerLog, LogLevel::Warning, + ("\t\tGetClassObject() %s", NS_SUCCEEDED(rv) ? "succeeded" : "FAILED")); + + return rv; +} + +/** + * 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. + */ +NS_IMETHODIMP +nsComponentManagerImpl::CreateInstance(const nsCID& aClass, + nsISupports* aDelegate, + const nsIID& aIID, + void** aResult) +{ + // test this first, since there's no point in creating a component during + // shutdown -- whether it's available or not would depend on the order it + // occurs in the list + if (gXPCOMShuttingDown) { + // When processing shutdown, don't process new GetService() requests +#ifdef SHOW_DENIED_ON_SHUTDOWN + nsXPIDLCString cid, iid; + cid.Adopt(aClass.ToString()); + iid.Adopt(aIID.ToString()); + fprintf(stderr, "Creating new instance on shutdown. Denied.\n" + " CID: %s\n IID: %s\n", cid.get(), iid.get()); +#endif /* SHOW_DENIED_ON_SHUTDOWN */ + return NS_ERROR_UNEXPECTED; + } + + if (!aResult) { + return NS_ERROR_NULL_POINTER; + } + *aResult = nullptr; + + nsFactoryEntry* entry = GetFactoryEntry(aClass); + + if (!entry) { + return NS_ERROR_FACTORY_NOT_REGISTERED; + } + +#ifdef SHOW_CI_ON_EXISTING_SERVICE + if (entry->mServiceObject) { + nsXPIDLCString cid; + cid.Adopt(aClass.ToString()); + nsAutoCString message; + message = NS_LITERAL_CSTRING("You are calling CreateInstance \"") + + cid + + NS_LITERAL_CSTRING("\" when a service for this CID already exists!"); + NS_ERROR(message.get()); + } +#endif + + nsresult rv; + nsCOMPtr<nsIFactory> factory = entry->GetFactory(); + if (factory) { + rv = factory->CreateInstance(aDelegate, aIID, aResult); + if (NS_SUCCEEDED(rv) && !*aResult) { + NS_ERROR("Factory did not return an object but returned success!"); + rv = NS_ERROR_SERVICE_NOT_FOUND; + } + } else { + // Translate error values + rv = NS_ERROR_FACTORY_NOT_REGISTERED; + } + + if (MOZ_LOG_TEST(nsComponentManagerLog, LogLevel::Warning)) { + char* buf = aClass.ToString(); + MOZ_LOG(nsComponentManagerLog, LogLevel::Warning, + ("nsComponentManager: CreateInstance(%s) %s", buf, + NS_SUCCEEDED(rv) ? "succeeded" : "FAILED")); + if (buf) { + free(buf); + } + } + + return rv; +} + +/** + * CreateInstanceByContractID() + * + * A variant of CreateInstance() that creates an instance of the object that + * implements the interface aIID and whose implementation has a contractID aContractID. + * + * This is only a convenience routine that turns around can calls the + * CreateInstance() with classid and iid. + */ +NS_IMETHODIMP +nsComponentManagerImpl::CreateInstanceByContractID(const char* aContractID, + nsISupports* aDelegate, + const nsIID& aIID, + void** aResult) +{ + if (NS_WARN_IF(!aContractID)) { + return NS_ERROR_INVALID_ARG; + } + + // test this first, since there's no point in creating a component during + // shutdown -- whether it's available or not would depend on the order it + // occurs in the list + if (gXPCOMShuttingDown) { + // When processing shutdown, don't process new GetService() requests +#ifdef SHOW_DENIED_ON_SHUTDOWN + nsXPIDLCString iid; + iid.Adopt(aIID.ToString()); + fprintf(stderr, "Creating new instance on shutdown. Denied.\n" + " ContractID: %s\n IID: %s\n", aContractID, iid.get()); +#endif /* SHOW_DENIED_ON_SHUTDOWN */ + return NS_ERROR_UNEXPECTED; + } + + if (!aResult) { + return NS_ERROR_NULL_POINTER; + } + *aResult = nullptr; + + nsFactoryEntry* entry = GetFactoryEntry(aContractID, strlen(aContractID)); + + if (!entry) { + return NS_ERROR_FACTORY_NOT_REGISTERED; + } + +#ifdef SHOW_CI_ON_EXISTING_SERVICE + if (entry->mServiceObject) { + nsAutoCString message; + message = + NS_LITERAL_CSTRING("You are calling CreateInstance \"") + + nsDependentCString(aContractID) + + NS_LITERAL_CSTRING("\" when a service for this CID already exists! " + "Add it to abusedContracts to track down the service consumer."); + NS_ERROR(message.get()); + } +#endif + + nsresult rv; + nsCOMPtr<nsIFactory> factory = entry->GetFactory(); + if (factory) { + + rv = factory->CreateInstance(aDelegate, aIID, aResult); + if (NS_SUCCEEDED(rv) && !*aResult) { + NS_ERROR("Factory did not return an object but returned success!"); + rv = NS_ERROR_SERVICE_NOT_FOUND; + } + } else { + // Translate error values + rv = NS_ERROR_FACTORY_NOT_REGISTERED; + } + + MOZ_LOG(nsComponentManagerLog, LogLevel::Warning, + ("nsComponentManager: CreateInstanceByContractID(%s) %s", aContractID, + NS_SUCCEEDED(rv) ? "succeeded" : "FAILED")); + + return rv; +} + +nsresult +nsComponentManagerImpl::FreeServices() +{ + NS_ASSERTION(gXPCOMShuttingDown, + "Must be shutting down in order to free all services"); + + if (!gXPCOMShuttingDown) { + return NS_ERROR_FAILURE; + } + + for (auto iter = mFactories.Iter(); !iter.Done(); iter.Next()) { + nsFactoryEntry* entry = iter.UserData(); + entry->mFactory = nullptr; + entry->mServiceObject = nullptr; + } + + return NS_OK; +} + +// This should only ever be called within the monitor! +nsComponentManagerImpl::PendingServiceInfo* +nsComponentManagerImpl::AddPendingService(const nsCID& aServiceCID, + PRThread* aThread) +{ + PendingServiceInfo* newInfo = mPendingServices.AppendElement(); + if (newInfo) { + newInfo->cid = &aServiceCID; + newInfo->thread = aThread; + } + return newInfo; +} + +// This should only ever be called within the monitor! +void +nsComponentManagerImpl::RemovePendingService(const nsCID& aServiceCID) +{ + uint32_t pendingCount = mPendingServices.Length(); + for (uint32_t index = 0; index < pendingCount; ++index) { + const PendingServiceInfo& info = mPendingServices.ElementAt(index); + if (info.cid->Equals(aServiceCID)) { + mPendingServices.RemoveElementAt(index); + return; + } + } +} + +// This should only ever be called within the monitor! +PRThread* +nsComponentManagerImpl::GetPendingServiceThread(const nsCID& aServiceCID) const +{ + uint32_t pendingCount = mPendingServices.Length(); + for (uint32_t index = 0; index < pendingCount; ++index) { + const PendingServiceInfo& info = mPendingServices.ElementAt(index); + if (info.cid->Equals(aServiceCID)) { + return info.thread; + } + } + return nullptr; +} + +NS_IMETHODIMP +nsComponentManagerImpl::GetService(const nsCID& aClass, + const nsIID& aIID, + void** aResult) +{ + // test this first, since there's no point in returning a service during + // shutdown -- whether it's available or not would depend on the order it + // occurs in the list + if (gXPCOMShuttingDown) { + // When processing shutdown, don't process new GetService() requests +#ifdef SHOW_DENIED_ON_SHUTDOWN + nsXPIDLCString cid, iid; + cid.Adopt(aClass.ToString()); + iid.Adopt(aIID.ToString()); + fprintf(stderr, "Getting service on shutdown. Denied.\n" + " CID: %s\n IID: %s\n", cid.get(), iid.get()); +#endif /* SHOW_DENIED_ON_SHUTDOWN */ + return NS_ERROR_UNEXPECTED; + } + + // `service` must be released after the lock is released, so it must be + // declared before the lock in this C++ block. + nsCOMPtr<nsISupports> service; + MutexLock lock(mLock); + + nsFactoryEntry* entry = mFactories.Get(aClass); + if (!entry) { + return NS_ERROR_FACTORY_NOT_REGISTERED; + } + + if (entry->mServiceObject) { + lock.Unlock(); + return entry->mServiceObject->QueryInterface(aIID, aResult); + } + + PRThread* currentPRThread = PR_GetCurrentThread(); + MOZ_ASSERT(currentPRThread, "This should never be null!"); + + // Needed to optimize the event loop below. + nsIThread* currentThread = nullptr; + + PRThread* pendingPRThread; + while ((pendingPRThread = GetPendingServiceThread(aClass))) { + if (pendingPRThread == currentPRThread) { + NS_ERROR("Recursive GetService!"); + return NS_ERROR_NOT_AVAILABLE; + } + + + SafeMutexAutoUnlock unlockPending(mLock); + + if (!currentThread) { + currentThread = NS_GetCurrentThread(); + MOZ_ASSERT(currentThread, "This should never be null!"); + } + + // This will process a single event or yield the thread if no event is + // pending. + if (!NS_ProcessNextEvent(currentThread, false)) { + PR_Sleep(PR_INTERVAL_NO_WAIT); + } + } + + // It's still possible that the other thread failed to create the + // service so we're not guaranteed to have an entry or service yet. + if (entry->mServiceObject) { + lock.Unlock(); + return entry->mServiceObject->QueryInterface(aIID, aResult); + } + +#ifdef DEBUG + PendingServiceInfo* newInfo = +#endif + AddPendingService(aClass, currentPRThread); + NS_ASSERTION(newInfo, "Failed to add info to the array!"); + + // We need to not be holding the service manager's lock while calling + // CreateInstance, because it invokes user code which could try to re-enter + // the service manager: + + nsresult rv; + { + SafeMutexAutoUnlock unlock(mLock); + rv = CreateInstance(aClass, nullptr, aIID, getter_AddRefs(service)); + } + if (NS_SUCCEEDED(rv) && !service) { + NS_ERROR("Factory did not return an object but returned success"); + return NS_ERROR_SERVICE_NOT_FOUND; + } + +#ifdef DEBUG + pendingPRThread = GetPendingServiceThread(aClass); + MOZ_ASSERT(pendingPRThread == currentPRThread, + "Pending service array has been changed!"); +#endif + RemovePendingService(aClass); + + if (NS_FAILED(rv)) { + return rv; + } + + NS_ASSERTION(!entry->mServiceObject, "Created two instances of a service!"); + + entry->mServiceObject = service.forget(); + + lock.Unlock(); + nsISupports** sresult = reinterpret_cast<nsISupports**>(aResult); + *sresult = entry->mServiceObject; + (*sresult)->AddRef(); + + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::IsServiceInstantiated(const nsCID& aClass, + const nsIID& aIID, + bool* aResult) +{ + // Now we want to get the service if we already got it. If not, we don't want + // to create an instance of it. mmh! + + // test this first, since there's no point in returning a service during + // shutdown -- whether it's available or not would depend on the order it + // occurs in the list + if (gXPCOMShuttingDown) { + // When processing shutdown, don't process new GetService() requests +#ifdef SHOW_DENIED_ON_SHUTDOWN + nsXPIDLCString cid, iid; + cid.Adopt(aClass.ToString()); + iid.Adopt(aIID.ToString()); + fprintf(stderr, "Checking for service on shutdown. Denied.\n" + " CID: %s\n IID: %s\n", cid.get(), iid.get()); +#endif /* SHOW_DENIED_ON_SHUTDOWN */ + return NS_ERROR_UNEXPECTED; + } + + nsresult rv = NS_ERROR_SERVICE_NOT_AVAILABLE; + nsFactoryEntry* entry; + + { + SafeMutexAutoLock lock(mLock); + entry = mFactories.Get(aClass); + } + + if (entry && entry->mServiceObject) { + nsCOMPtr<nsISupports> service; + rv = entry->mServiceObject->QueryInterface(aIID, getter_AddRefs(service)); + *aResult = (service != nullptr); + } + + return rv; +} + +NS_IMETHODIMP +nsComponentManagerImpl::IsServiceInstantiatedByContractID( + const char* aContractID, + const nsIID& aIID, + bool* aResult) +{ + // Now we want to get the service if we already got it. If not, we don't want + // to create an instance of it. mmh! + + // test this first, since there's no point in returning a service during + // shutdown -- whether it's available or not would depend on the order it + // occurs in the list + if (gXPCOMShuttingDown) { + // When processing shutdown, don't process new GetService() requests +#ifdef SHOW_DENIED_ON_SHUTDOWN + nsXPIDLCString iid; + iid.Adopt(aIID.ToString()); + fprintf(stderr, "Checking for service on shutdown. Denied.\n" + " ContractID: %s\n IID: %s\n", aContractID, iid.get()); +#endif /* SHOW_DENIED_ON_SHUTDOWN */ + return NS_ERROR_UNEXPECTED; + } + + nsresult rv = NS_ERROR_SERVICE_NOT_AVAILABLE; + nsFactoryEntry* entry; + { + SafeMutexAutoLock lock(mLock); + entry = mContractIDs.Get(nsDependentCString(aContractID)); + } + + if (entry && entry->mServiceObject) { + nsCOMPtr<nsISupports> service; + rv = entry->mServiceObject->QueryInterface(aIID, getter_AddRefs(service)); + *aResult = (service != nullptr); + } + return rv; +} + + +NS_IMETHODIMP +nsComponentManagerImpl::GetServiceByContractID(const char* aContractID, + const nsIID& aIID, + void** aResult) +{ + // test this first, since there's no point in returning a service during + // shutdown -- whether it's available or not would depend on the order it + // occurs in the list + if (gXPCOMShuttingDown) { + // When processing shutdown, don't process new GetService() requests +#ifdef SHOW_DENIED_ON_SHUTDOWN + nsXPIDLCString iid; + iid.Adopt(aIID.ToString()); + fprintf(stderr, "Getting service on shutdown. Denied.\n" + " ContractID: %s\n IID: %s\n", aContractID, iid.get()); +#endif /* SHOW_DENIED_ON_SHUTDOWN */ + return NS_ERROR_UNEXPECTED; + } + + // `service` must be released after the lock is released, so it must be + // declared before the lock in this C++ block. + nsCOMPtr<nsISupports> service; + MutexLock lock(mLock); + + nsFactoryEntry* entry = mContractIDs.Get(nsDependentCString(aContractID)); + if (!entry) { + return NS_ERROR_FACTORY_NOT_REGISTERED; + } + + if (entry->mServiceObject) { + // We need to not be holding the service manager's monitor while calling + // QueryInterface, because it invokes user code which could try to re-enter + // the service manager, or try to grab some other lock/monitor/condvar + // and deadlock, e.g. bug 282743. + // `entry` is valid until XPCOM shutdown, so we can safely use it after + // exiting the lock. + lock.Unlock(); + return entry->mServiceObject->QueryInterface(aIID, aResult); + } + + PRThread* currentPRThread = PR_GetCurrentThread(); + MOZ_ASSERT(currentPRThread, "This should never be null!"); + + // Needed to optimize the event loop below. + nsIThread* currentThread = nullptr; + + PRThread* pendingPRThread; + while ((pendingPRThread = GetPendingServiceThread(*entry->mCIDEntry->cid))) { + if (pendingPRThread == currentPRThread) { + NS_ERROR("Recursive GetService!"); + return NS_ERROR_NOT_AVAILABLE; + } + + SafeMutexAutoUnlock unlockPending(mLock); + + if (!currentThread) { + currentThread = NS_GetCurrentThread(); + MOZ_ASSERT(currentThread, "This should never be null!"); + } + + // This will process a single event or yield the thread if no event is + // pending. + if (!NS_ProcessNextEvent(currentThread, false)) { + PR_Sleep(PR_INTERVAL_NO_WAIT); + } + } + + if (currentThread && entry->mServiceObject) { + // If we have a currentThread then we must have waited on another thread + // to create the service. Grab it now if that succeeded. + lock.Unlock(); + return entry->mServiceObject->QueryInterface(aIID, aResult); + } + +#ifdef DEBUG + PendingServiceInfo* newInfo = +#endif + AddPendingService(*entry->mCIDEntry->cid, currentPRThread); + NS_ASSERTION(newInfo, "Failed to add info to the array!"); + + // We need to not be holding the service manager's lock while calling + // CreateInstance, because it invokes user code which could try to re-enter + // the service manager: + + nsresult rv; + { + SafeMutexAutoUnlock unlock(mLock); + rv = CreateInstanceByContractID(aContractID, nullptr, aIID, + getter_AddRefs(service)); + } + if (NS_SUCCEEDED(rv) && !service) { + NS_ERROR("Factory did not return an object but returned success"); + return NS_ERROR_SERVICE_NOT_FOUND; + } + +#ifdef DEBUG + pendingPRThread = GetPendingServiceThread(*entry->mCIDEntry->cid); + MOZ_ASSERT(pendingPRThread == currentPRThread, + "Pending service array has been changed!"); +#endif + RemovePendingService(*entry->mCIDEntry->cid); + + if (NS_FAILED(rv)) { + return rv; + } + + NS_ASSERTION(!entry->mServiceObject, "Created two instances of a service!"); + + entry->mServiceObject = service.forget(); + + lock.Unlock(); + + nsISupports** sresult = reinterpret_cast<nsISupports**>(aResult); + *sresult = entry->mServiceObject; + (*sresult)->AddRef(); + + return NS_OK; +} + +already_AddRefed<mozilla::ModuleLoader> +nsComponentManagerImpl::LoaderForExtension(const nsACString& aExt) +{ + nsCOMPtr<mozilla::ModuleLoader> loader = mLoaderMap.Get(aExt); + if (!loader) { + loader = do_GetServiceFromCategory("module-loader", + PromiseFlatCString(aExt).get()); + if (!loader) { + return nullptr; + } + + mLoaderMap.Put(aExt, loader); + } + + return loader.forget(); +} + +NS_IMETHODIMP +nsComponentManagerImpl::RegisterFactory(const nsCID& aClass, + const char* aName, + const char* aContractID, + nsIFactory* aFactory) +{ + if (!aFactory) { + // If a null factory is passed in, this call just wants to reset + // the contract ID to point to an existing CID entry. + if (!aContractID) { + return NS_ERROR_INVALID_ARG; + } + + SafeMutexAutoLock lock(mLock); + nsFactoryEntry* oldf = mFactories.Get(aClass); + if (!oldf) { + return NS_ERROR_FACTORY_NOT_REGISTERED; + } + + mContractIDs.Put(nsDependentCString(aContractID), oldf); + return NS_OK; + } + + nsAutoPtr<nsFactoryEntry> f(new nsFactoryEntry(aClass, aFactory)); + + SafeMutexAutoLock lock(mLock); + nsFactoryEntry* oldf = mFactories.Get(aClass); + if (oldf) { + return NS_ERROR_FACTORY_EXISTS; + } + + if (aContractID) { + mContractIDs.Put(nsDependentCString(aContractID), f); + } + + mFactories.Put(aClass, f.forget()); + + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::UnregisterFactory(const nsCID& aClass, + nsIFactory* aFactory) +{ + // Don't release the dying factory or service object until releasing + // the component manager monitor. + nsCOMPtr<nsIFactory> dyingFactory; + nsCOMPtr<nsISupports> dyingServiceObject; + + { + SafeMutexAutoLock lock(mLock); + nsFactoryEntry* f = mFactories.Get(aClass); + if (!f || f->mFactory != aFactory) { + return NS_ERROR_FACTORY_NOT_REGISTERED; + } + + mFactories.Remove(aClass); + + // This might leave a stale contractid -> factory mapping in + // place, so null out the factory entry (see + // nsFactoryEntry::GetFactory) + f->mFactory.swap(dyingFactory); + f->mServiceObject.swap(dyingServiceObject); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::AutoRegister(nsIFile* aLocation) +{ + XRE_AddManifestLocation(NS_EXTENSION_LOCATION, aLocation); + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::AutoUnregister(nsIFile* aLocation) +{ + NS_ERROR("AutoUnregister not implemented."); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsComponentManagerImpl::RegisterFactoryLocation(const nsCID& aCID, + const char* aClassName, + const char* aContractID, + nsIFile* aFile, + const char* aLoaderStr, + const char* aType) +{ + NS_ERROR("RegisterFactoryLocation not implemented."); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsComponentManagerImpl::UnregisterFactoryLocation(const nsCID& aCID, + nsIFile* aFile) +{ + NS_ERROR("UnregisterFactoryLocation not implemented."); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsComponentManagerImpl::IsCIDRegistered(const nsCID& aClass, + bool* aResult) +{ + *aResult = (nullptr != GetFactoryEntry(aClass)); + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::IsContractIDRegistered(const char* aClass, + bool* aResult) +{ + if (NS_WARN_IF(!aClass)) { + return NS_ERROR_INVALID_ARG; + } + + nsFactoryEntry* entry = GetFactoryEntry(aClass, strlen(aClass)); + + if (entry) { + // UnregisterFactory might have left a stale nsFactoryEntry in + // mContractIDs, so we should check to see whether this entry has + // anything useful. + *aResult = (bool(entry->mModule) || + bool(entry->mFactory) || + bool(entry->mServiceObject)); + } else { + *aResult = false; + } + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::EnumerateCIDs(nsISimpleEnumerator** aEnumerator) +{ + nsCOMArray<nsISupports> array; + for (auto iter = mFactories.Iter(); !iter.Done(); iter.Next()) { + const nsID& id = iter.Key(); + nsCOMPtr<nsISupportsID> wrapper = new nsSupportsID(); + wrapper->SetData(&id); + array.AppendObject(wrapper); + } + return NS_NewArrayEnumerator(aEnumerator, array); +} + +NS_IMETHODIMP +nsComponentManagerImpl::EnumerateContractIDs(nsISimpleEnumerator** aEnumerator) +{ + nsTArray<nsCString>* array = new nsTArray<nsCString>; + for (auto iter = mContractIDs.Iter(); !iter.Done(); iter.Next()) { + const nsACString& contract = iter.Key(); + array->AppendElement(contract); + } + + nsCOMPtr<nsIUTF8StringEnumerator> e; + nsresult rv = NS_NewAdoptingUTF8StringEnumerator(getter_AddRefs(e), array); + if (NS_FAILED(rv)) { + return rv; + } + + return CallQueryInterface(e, aEnumerator); +} + +NS_IMETHODIMP +nsComponentManagerImpl::CIDToContractID(const nsCID& aClass, + char** aResult) +{ + NS_ERROR("CIDTOContractID not implemented"); + return NS_ERROR_FACTORY_NOT_REGISTERED; +} + +NS_IMETHODIMP +nsComponentManagerImpl::ContractIDToCID(const char* aContractID, + nsCID** aResult) +{ + { + SafeMutexAutoLock lock(mLock); + nsFactoryEntry* entry = mContractIDs.Get(nsDependentCString(aContractID)); + if (entry) { + *aResult = (nsCID*)moz_xmalloc(sizeof(nsCID)); + **aResult = *entry->mCIDEntry->cid; + return NS_OK; + } + } + *aResult = nullptr; + return NS_ERROR_FACTORY_NOT_REGISTERED; +} + +MOZ_DEFINE_MALLOC_SIZE_OF(ComponentManagerMallocSizeOf) + +NS_IMETHODIMP +nsComponentManagerImpl::CollectReports(nsIHandleReportCallback* aHandleReport, + nsISupports* aData, bool aAnonymize) +{ + MOZ_COLLECT_REPORT( + "explicit/xpcom/component-manager", KIND_HEAP, UNITS_BYTES, + SizeOfIncludingThis(ComponentManagerMallocSizeOf), + "Memory used for the XPCOM component manager."); + + return NS_OK; +} + +size_t +nsComponentManagerImpl::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) + const +{ + size_t n = aMallocSizeOf(this); + + n += mLoaderMap.ShallowSizeOfExcludingThis(aMallocSizeOf); + + n += mFactories.ShallowSizeOfExcludingThis(aMallocSizeOf); + for (auto iter = mFactories.ConstIter(); !iter.Done(); iter.Next()) { + n += iter.Data()->SizeOfIncludingThis(aMallocSizeOf); + } + + n += mContractIDs.ShallowSizeOfExcludingThis(aMallocSizeOf); + for (auto iter = mContractIDs.ConstIter(); !iter.Done(); iter.Next()) { + // We don't measure the nsFactoryEntry data because it's owned by + // mFactories (which is measured above). + n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf); + } + + n += sStaticModules->ShallowSizeOfIncludingThis(aMallocSizeOf); + n += sModuleLocations->ShallowSizeOfIncludingThis(aMallocSizeOf); + + n += mKnownStaticModules.ShallowSizeOfExcludingThis(aMallocSizeOf); + n += mKnownModules.ShallowSizeOfExcludingThis(aMallocSizeOf); + + n += PL_SizeOfArenaPoolExcludingPool(&mArena, aMallocSizeOf); + + n += mPendingServices.ShallowSizeOfExcludingThis(aMallocSizeOf); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mLoaderMap's keys and values + // - mMon + // - sStaticModules' entries + // - sModuleLocations' entries + // - mNativeModuleLoader + // - mKnownStaticModules' entries? + // - mKnownModules' keys and values? + + return n; +} + +//////////////////////////////////////////////////////////////////////////////// +// nsFactoryEntry +//////////////////////////////////////////////////////////////////////////////// + +nsFactoryEntry::nsFactoryEntry(const mozilla::Module::CIDEntry* aEntry, + nsComponentManagerImpl::KnownModule* aModule) + : mCIDEntry(aEntry) + , mModule(aModule) +{ +} + +nsFactoryEntry::nsFactoryEntry(const nsCID& aCID, nsIFactory* aFactory) + : mCIDEntry(nullptr) + , mModule(nullptr) + , mFactory(aFactory) +{ + mozilla::Module::CIDEntry* e = new mozilla::Module::CIDEntry(); + nsCID* cid = new nsCID; + *cid = aCID; + e->cid = cid; + mCIDEntry = e; +} + +nsFactoryEntry::~nsFactoryEntry() +{ + // If this was a RegisterFactory entry, we own the CIDEntry/CID + if (!mModule) { + delete mCIDEntry->cid; + delete mCIDEntry; + } +} + +already_AddRefed<nsIFactory> +nsFactoryEntry::GetFactory() +{ + nsComponentManagerImpl::gComponentManager->mLock.AssertNotCurrentThreadOwns(); + + if (!mFactory) { + // RegisterFactory then UnregisterFactory can leave an entry in mContractIDs + // pointing to an unusable nsFactoryEntry. + if (!mModule) { + return nullptr; + } + + if (!mModule->Load()) { + return nullptr; + } + + // Don't set mFactory directly, it needs to be locked + nsCOMPtr<nsIFactory> factory; + + if (mModule->Module()->getFactoryProc) { + factory = mModule->Module()->getFactoryProc(*mModule->Module(), + *mCIDEntry); + } else if (mCIDEntry->getFactoryProc) { + factory = mCIDEntry->getFactoryProc(*mModule->Module(), *mCIDEntry); + } else { + NS_ASSERTION(mCIDEntry->constructorProc, "no getfactory or constructor"); + factory = new mozilla::GenericFactory(mCIDEntry->constructorProc); + } + if (!factory) { + return nullptr; + } + + SafeMutexAutoLock lock(nsComponentManagerImpl::gComponentManager->mLock); + // Threads can race to set mFactory + if (!mFactory) { + factory.swap(mFactory); + } + } + nsCOMPtr<nsIFactory> factory = mFactory; + return factory.forget(); +} + +size_t +nsFactoryEntry::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) +{ + size_t n = aMallocSizeOf(this); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mCIDEntry; + // - mModule; + // - mFactory; + // - mServiceObject; + + return n; +} + +//////////////////////////////////////////////////////////////////////////////// +// Static Access Functions +//////////////////////////////////////////////////////////////////////////////// + +nsresult +NS_GetComponentManager(nsIComponentManager** aResult) +{ + if (!nsComponentManagerImpl::gComponentManager) { + return NS_ERROR_NOT_INITIALIZED; + } + + NS_ADDREF(*aResult = nsComponentManagerImpl::gComponentManager); + return NS_OK; +} + +nsresult +NS_GetServiceManager(nsIServiceManager** aResult) +{ + if (!nsComponentManagerImpl::gComponentManager) { + return NS_ERROR_NOT_INITIALIZED; + } + + NS_ADDREF(*aResult = nsComponentManagerImpl::gComponentManager); + return NS_OK; +} + + +nsresult +NS_GetComponentRegistrar(nsIComponentRegistrar** aResult) +{ + if (!nsComponentManagerImpl::gComponentManager) { + return NS_ERROR_NOT_INITIALIZED; + } + + NS_ADDREF(*aResult = nsComponentManagerImpl::gComponentManager); + return NS_OK; +} + +EXPORT_XPCOM_API(nsresult) +XRE_AddStaticComponent(const mozilla::Module* aComponent) +{ + nsComponentManagerImpl::InitializeStaticModules(); + nsComponentManagerImpl::sStaticModules->AppendElement(aComponent); + + if (nsComponentManagerImpl::gComponentManager && + nsComponentManagerImpl::NORMAL == + nsComponentManagerImpl::gComponentManager->mStatus) { + nsComponentManagerImpl::gComponentManager->RegisterModule(aComponent, + nullptr); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::AddBootstrappedManifestLocation(nsIFile* aLocation) +{ + nsString path; + nsresult rv = aLocation->GetPath(path); + if (NS_FAILED(rv)) { + return rv; + } + + if (Substring(path, path.Length() - 4).EqualsLiteral(".xpi")) { + return XRE_AddJarManifestLocation(NS_BOOTSTRAPPED_LOCATION, aLocation); + } + + nsCOMPtr<nsIFile> manifest = + CloneAndAppend(aLocation, NS_LITERAL_CSTRING("chrome.manifest")); + return XRE_AddManifestLocation(NS_BOOTSTRAPPED_LOCATION, manifest); +} + +NS_IMETHODIMP +nsComponentManagerImpl::RemoveBootstrappedManifestLocation(nsIFile* aLocation) +{ + nsCOMPtr<nsIChromeRegistry> cr = mozilla::services::GetChromeRegistryService(); + if (!cr) { + return NS_ERROR_FAILURE; + } + + nsString path; + nsresult rv = aLocation->GetPath(path); + if (NS_FAILED(rv)) { + return rv; + } + + nsComponentManagerImpl::ComponentLocation elem; + elem.type = NS_BOOTSTRAPPED_LOCATION; + + if (Substring(path, path.Length() - 4).EqualsLiteral(".xpi")) { + elem.location.Init(aLocation, "chrome.manifest"); + } else { + nsCOMPtr<nsIFile> lf = + CloneAndAppend(aLocation, NS_LITERAL_CSTRING("chrome.manifest")); + elem.location.Init(lf); + } + + // Remove reference. + nsComponentManagerImpl::sModuleLocations->RemoveElement(elem, + ComponentLocationComparator()); + + rv = cr->CheckForNewChrome(); + return rv; +} + +NS_IMETHODIMP +nsComponentManagerImpl::GetManifestLocations(nsIArray** aLocations) +{ + NS_ENSURE_ARG_POINTER(aLocations); + *aLocations = nullptr; + + if (!sModuleLocations) { + return NS_ERROR_NOT_INITIALIZED; + } + + nsCOMPtr<nsIMutableArray> locations = nsArray::Create(); + nsresult rv; + for (uint32_t i = 0; i < sModuleLocations->Length(); ++i) { + ComponentLocation& l = sModuleLocations->ElementAt(i); + FileLocation loc = l.location; + nsCString uriString; + loc.GetURIString(uriString); + nsCOMPtr<nsIURI> uri; + rv = NS_NewURI(getter_AddRefs(uri), uriString); + if (NS_SUCCEEDED(rv)) { + locations->AppendElement(uri, false); + } + } + + locations.forget(aLocations); + return NS_OK; +} + +EXPORT_XPCOM_API(nsresult) +XRE_AddManifestLocation(NSLocationType aType, nsIFile* aLocation) +{ + nsComponentManagerImpl::InitializeModuleLocations(); + nsComponentManagerImpl::ComponentLocation* c = + nsComponentManagerImpl::sModuleLocations->AppendElement(); + c->type = aType; + c->location.Init(aLocation); + + if (nsComponentManagerImpl::gComponentManager && + nsComponentManagerImpl::NORMAL == + nsComponentManagerImpl::gComponentManager->mStatus) { + nsComponentManagerImpl::gComponentManager->RegisterManifest(aType, + c->location, + false); + } + + return NS_OK; +} + +EXPORT_XPCOM_API(nsresult) +XRE_AddJarManifestLocation(NSLocationType aType, nsIFile* aLocation) +{ + nsComponentManagerImpl::InitializeModuleLocations(); + nsComponentManagerImpl::ComponentLocation* c = + nsComponentManagerImpl::sModuleLocations->AppendElement(); + + c->type = aType; + c->location.Init(aLocation, "chrome.manifest"); + + if (nsComponentManagerImpl::gComponentManager && + nsComponentManagerImpl::NORMAL == + nsComponentManagerImpl::gComponentManager->mStatus) { + nsComponentManagerImpl::gComponentManager->RegisterManifest(aType, + c->location, + false); + } + + return NS_OK; +} + |