/* -*- 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; }