summaryrefslogtreecommitdiffstats
path: root/xpcom/components/nsComponentManager.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
commit0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch)
treea31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /xpcom/components/nsComponentManager.cpp
parentInitial commit. (diff)
downloadfirefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz
firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--xpcom/components/nsComponentManager.cpp1575
1 files changed, 1575 insertions, 0 deletions
diff --git a/xpcom/components/nsComponentManager.cpp b/xpcom/components/nsComponentManager.cpp
new file mode 100644
index 0000000000..35954b1702
--- /dev/null
+++ b/xpcom/components/nsComponentManager.cpp
@@ -0,0 +1,1575 @@
+/* -*- 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/. */
+
+#include <stdlib.h>
+#include "nscore.h"
+#include "nsISupports.h"
+#include "nspr.h"
+#include "nsCRT.h" // for atoll
+
+#include "StaticComponents.h"
+
+#include "nsCategoryManager.h"
+#include "nsCOMPtr.h"
+#include "nsComponentManager.h"
+#include "nsDirectoryService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsCategoryManager.h"
+#include "nsLayoutModule.h"
+#include "mozilla/MemoryReporting.h"
+#include "nsIObserverService.h"
+#include "nsIStringEnumerator.h"
+#include "nsXPCOM.h"
+#include "nsXPCOMPrivate.h"
+#include "nsISupportsPrimitives.h"
+#include "nsLocalFile.h"
+#include "nsReadableUtils.h"
+#include "nsString.h"
+#include "prcmon.h"
+#include "nsThreadManager.h"
+#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 "mozilla/DebugOnly.h"
+#include "mozilla/FileUtils.h"
+#include "mozilla/ProfilerLabels.h"
+#include "mozilla/ProfilerMarkers.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/URLPreloader.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Variant.h"
+
+#include <new> // for placement new
+
+#include "mozilla/Omnijar.h"
+
+#include "mozilla/Logging.h"
+#include "LogModulePrefWatcher.h"
+
+#ifdef MOZ_MEMORY
+# include "mozmemory.h"
+#endif
+
+using namespace mozilla;
+using namespace mozilla::xpcom;
+
+static LazyLogModule nsComponentManagerLog("nsComponentManager");
+
+#if 0
+# define SHOW_DENIED_ON_SHUTDOWN
+# define SHOW_CI_ON_EXISTING_SERVICE
+#endif
+
+namespace {
+
+class AutoIDString : public nsAutoCStringN<NSID_LENGTH> {
+ public:
+ explicit AutoIDString(const nsID& aID) {
+ SetLength(NSID_LENGTH - 1);
+ aID.ToProvidedString(
+ *reinterpret_cast<char(*)[NSID_LENGTH]>(BeginWriting()));
+ }
+};
+
+} // namespace
+
+namespace mozilla {
+namespace xpcom {
+
+using ProcessSelector = Module::ProcessSelector;
+
+// Note: These must be kept in sync with the ProcessSelector definition in
+// Module.h.
+bool ProcessSelectorMatches(ProcessSelector aSelector) {
+ GeckoProcessType type = XRE_GetProcessType();
+ if (type == GeckoProcessType_GPU) {
+ return !!(aSelector & Module::ALLOW_IN_GPU_PROCESS);
+ }
+
+ if (type == GeckoProcessType_RDD) {
+ return !!(aSelector & Module::ALLOW_IN_RDD_PROCESS);
+ }
+
+ if (type == GeckoProcessType_Socket) {
+ return !!(aSelector & (Module::ALLOW_IN_SOCKET_PROCESS));
+ }
+
+ if (type == GeckoProcessType_VR) {
+ return !!(aSelector & Module::ALLOW_IN_VR_PROCESS);
+ }
+
+ if (type == GeckoProcessType_Utility) {
+ return !!(aSelector & Module::ALLOW_IN_UTILITY_PROCESS);
+ }
+
+ // Only allow XPCOM modules which can be loaded in all processes to be loaded
+ // in the IPDLUnitTest process.
+ if (type == GeckoProcessType_IPDLUnitTest) {
+ return size_t(aSelector) == Module::kMaxProcessSelector;
+ }
+
+ if (aSelector & Module::MAIN_PROCESS_ONLY) {
+ return type == GeckoProcessType_Default;
+ }
+ if (aSelector & Module::CONTENT_PROCESS_ONLY) {
+ return type == GeckoProcessType_Content;
+ }
+ return true;
+}
+
+static bool gProcessMatchTable[Module::kMaxProcessSelector + 1];
+
+bool FastProcessSelectorMatches(ProcessSelector aSelector) {
+ return gProcessMatchTable[size_t(aSelector)];
+}
+
+} // namespace xpcom
+} // namespace mozilla
+
+namespace {
+
+/**
+ * A wrapper simple wrapper class, which can hold either a dynamic
+ * nsFactoryEntry instance, or a static StaticModule entry, and transparently
+ * forwards method calls to the wrapped object.
+ *
+ * This allows the same code to work with either static or dynamic modules
+ * without caring about the difference.
+ */
+class MOZ_STACK_CLASS EntryWrapper final {
+ public:
+ explicit EntryWrapper(nsFactoryEntry* aEntry) : mEntry(aEntry) {}
+
+ explicit EntryWrapper(const StaticModule* aEntry) : mEntry(aEntry) {}
+
+#define MATCH(type, ifFactory, ifStatic) \
+ struct Matcher { \
+ type operator()(nsFactoryEntry* entry) { ifFactory; } \
+ type operator()(const StaticModule* entry) { ifStatic; } \
+ }; \
+ return mEntry.match((Matcher()))
+
+ const nsID& CID() {
+ MATCH(const nsID&, return entry->mCID, return entry->CID());
+ }
+
+ already_AddRefed<nsIFactory> GetFactory() {
+ MATCH(already_AddRefed<nsIFactory>, return entry->GetFactory(),
+ return entry->GetFactory());
+ }
+
+ /**
+ * Creates an instance of the underlying component. This should be used in
+ * preference to GetFactory()->CreateInstance() where appropriate, since it
+ * side-steps the necessity of creating a nsIFactory instance for static
+ * modules.
+ */
+ nsresult CreateInstance(const nsIID& aIID, void** aResult) {
+ if (mEntry.is<nsFactoryEntry*>()) {
+ return mEntry.as<nsFactoryEntry*>()->CreateInstance(aIID, aResult);
+ }
+ return mEntry.as<const StaticModule*>()->CreateInstance(aIID, aResult);
+ }
+
+ /**
+ * Returns the cached service instance for this entry, if any. This should
+ * only be accessed while mLock is held.
+ */
+ nsISupports* ServiceInstance() {
+ MATCH(nsISupports*, return entry->mServiceObject,
+ return entry->ServiceInstance());
+ }
+ void SetServiceInstance(already_AddRefed<nsISupports> aInst) {
+ if (mEntry.is<nsFactoryEntry*>()) {
+ mEntry.as<nsFactoryEntry*>()->mServiceObject = aInst;
+ } else {
+ return mEntry.as<const StaticModule*>()->SetServiceInstance(
+ std::move(aInst));
+ }
+ }
+
+ /**
+ * Returns the description string for the module this entry belongs to.
+ * Currently always returns "<unknown module>".
+ */
+ nsCString ModuleDescription() { return "<unknown module>"_ns; }
+
+ private:
+ Variant<nsFactoryEntry*, const StaticModule*> mEntry;
+};
+
+} // namespace
+
+// this is safe to call during InitXPCOM
+static already_AddRefed<nsIFile> GetLocationFromDirectoryService(
+ const char* aProp) {
+ nsCOMPtr<nsIProperties> directoryService;
+ nsDirectoryService::Create(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(REFNSIID aIID, void** aResult) {
+ if (!gComponentManager) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return gComponentManager->QueryInterface(aIID, aResult);
+}
+
+static const int CONTRACTID_HASHTABLE_INITIAL_LENGTH = 8;
+
+nsComponentManagerImpl::nsComponentManagerImpl()
+ : mFactories(CONTRACTID_HASHTABLE_INITIAL_LENGTH),
+ mContractIDs(CONTRACTID_HASHTABLE_INITIAL_LENGTH),
+ mLock("nsComponentManagerImpl.mLock"),
+ mStatus(NOT_INITIALIZED) {}
+
+nsTArray<nsComponentManagerImpl::ComponentLocation>*
+ nsComponentManagerImpl::sModuleLocations;
+
+/* static */
+void nsComponentManagerImpl::InitializeModuleLocations() {
+ if (sModuleLocations) {
+ return;
+ }
+
+ sModuleLocations = new nsTArray<ComponentLocation>;
+}
+
+nsresult nsComponentManagerImpl::Init() {
+ {
+ gProcessMatchTable[size_t(ProcessSelector::ANY_PROCESS)] =
+ ProcessSelectorMatches(ProcessSelector::ANY_PROCESS);
+ gProcessMatchTable[size_t(ProcessSelector::MAIN_PROCESS_ONLY)] =
+ ProcessSelectorMatches(ProcessSelector::MAIN_PROCESS_ONLY);
+ gProcessMatchTable[size_t(ProcessSelector::CONTENT_PROCESS_ONLY)] =
+ ProcessSelectorMatches(ProcessSelector::CONTENT_PROCESS_ONLY);
+ gProcessMatchTable[size_t(ProcessSelector::ALLOW_IN_GPU_PROCESS)] =
+ ProcessSelectorMatches(ProcessSelector::ALLOW_IN_GPU_PROCESS);
+ gProcessMatchTable[size_t(ProcessSelector::ALLOW_IN_VR_PROCESS)] =
+ ProcessSelectorMatches(ProcessSelector::ALLOW_IN_VR_PROCESS);
+ gProcessMatchTable[size_t(ProcessSelector::ALLOW_IN_SOCKET_PROCESS)] =
+ ProcessSelectorMatches(ProcessSelector::ALLOW_IN_SOCKET_PROCESS);
+ gProcessMatchTable[size_t(ProcessSelector::ALLOW_IN_RDD_PROCESS)] =
+ ProcessSelectorMatches(ProcessSelector::ALLOW_IN_RDD_PROCESS);
+ gProcessMatchTable[size_t(ProcessSelector::ALLOW_IN_GPU_AND_MAIN_PROCESS)] =
+ ProcessSelectorMatches(ProcessSelector::ALLOW_IN_GPU_AND_MAIN_PROCESS);
+ gProcessMatchTable[size_t(ProcessSelector::ALLOW_IN_GPU_AND_VR_PROCESS)] =
+ ProcessSelectorMatches(ProcessSelector::ALLOW_IN_GPU_AND_VR_PROCESS);
+ gProcessMatchTable[size_t(
+ ProcessSelector::ALLOW_IN_GPU_AND_SOCKET_PROCESS)] =
+ ProcessSelectorMatches(
+ ProcessSelector::ALLOW_IN_GPU_AND_SOCKET_PROCESS);
+ gProcessMatchTable[size_t(
+ ProcessSelector::ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS)] =
+ ProcessSelectorMatches(
+ ProcessSelector::ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS);
+ gProcessMatchTable[size_t(
+ ProcessSelector::ALLOW_IN_RDD_AND_SOCKET_PROCESS)] =
+ ProcessSelectorMatches(
+ ProcessSelector::ALLOW_IN_RDD_AND_SOCKET_PROCESS);
+ gProcessMatchTable[size_t(
+ ProcessSelector::ALLOW_IN_GPU_RDD_AND_SOCKET_PROCESS)] =
+ ProcessSelectorMatches(
+ ProcessSelector::ALLOW_IN_GPU_RDD_AND_SOCKET_PROCESS);
+ gProcessMatchTable[size_t(
+ ProcessSelector::ALLOW_IN_GPU_RDD_SOCKET_AND_UTILITY_PROCESS)] =
+ ProcessSelectorMatches(
+ ProcessSelector::ALLOW_IN_GPU_RDD_SOCKET_AND_UTILITY_PROCESS);
+ gProcessMatchTable[size_t(
+ ProcessSelector::ALLOW_IN_GPU_RDD_VR_AND_SOCKET_PROCESS)] =
+ ProcessSelectorMatches(
+ ProcessSelector::ALLOW_IN_GPU_RDD_VR_AND_SOCKET_PROCESS);
+ gProcessMatchTable[size_t(
+ ProcessSelector::ALLOW_IN_GPU_RDD_VR_SOCKET_AND_UTILITY_PROCESS)] =
+ ProcessSelectorMatches(
+ ProcessSelector::ALLOW_IN_GPU_RDD_VR_SOCKET_AND_UTILITY_PROCESS);
+ }
+
+ MOZ_ASSERT(NOT_INITIALIZED == mStatus);
+
+ nsCOMPtr<nsIFile> greDir = GetLocationFromDirectoryService(NS_GRE_DIR);
+ nsCOMPtr<nsIFile> appDir =
+ GetLocationFromDirectoryService(NS_XPCOM_CURRENT_PROCESS_DIR);
+
+ nsCategoryManager::GetSingleton()->SuppressNotifications(true);
+
+ auto* catMan = nsCategoryManager::GetSingleton();
+ for (const auto& cat : gStaticCategories) {
+ for (const auto& entry : cat) {
+ if (entry.Active()) {
+ catMan->AddCategoryEntry(cat.Name(), entry.Entry(), entry.Value());
+ }
+ }
+ }
+
+ bool loadChromeManifests;
+ switch (XRE_GetProcessType()) {
+ // We are going to assume that only a select few (see below) process types
+ // want to load chrome manifests, and that any new process types will not
+ // want to load them, because they're not going to be executing JS.
+ case GeckoProcessType_RemoteSandboxBroker:
+ default:
+ loadChromeManifests = false;
+ break;
+
+ // XXX The check this code replaced implicitly let through all of these
+ // process types, but presumably only the default (parent) and content
+ // processes really need chrome manifests...?
+ case GeckoProcessType_Default:
+ case GeckoProcessType_Content:
+ case GeckoProcessType_GMPlugin:
+ loadChromeManifests = true;
+ break;
+ }
+
+ if (loadChromeManifests) {
+ // This needs to be called very early, before anything in nsLayoutModule is
+ // used, and before any calls are made into the JS engine.
+ nsLayoutModuleInitialize();
+
+ mJSLoaderReady = true;
+
+ // The overall order in which chrome.manifests are expected to be treated
+ // is the following:
+ // - greDir's omni.ja or greDir
+ // - appDir's omni.ja or appDir
+
+ InitializeModuleLocations();
+ ComponentLocation* cl = sModuleLocations->AppendElement();
+ cl->type = NS_APP_LOCATION;
+ RefPtr<nsZipArchive> greOmnijar =
+ mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
+ if (greOmnijar) {
+ cl->location.Init(greOmnijar, "chrome.manifest");
+ } else {
+ nsCOMPtr<nsIFile> lf = CloneAndAppend(greDir, "chrome.manifest"_ns);
+ 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");
+ } else {
+ bool equals = false;
+ appDir->Equals(greDir, &equals);
+ if (!equals) {
+ cl = sModuleLocations->AppendElement();
+ cl->type = NS_APP_LOCATION;
+ nsCOMPtr<nsIFile> lf = CloneAndAppend(appDir, "chrome.manifest"_ns);
+ cl->location.Init(lf);
+ }
+ }
+
+ 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;
+
+ MOZ_ASSERT(!XRE_IsContentProcess() ||
+ CONTRACTID_HASHTABLE_INITIAL_LENGTH <= 8 ||
+ mFactories.Count() > CONTRACTID_HASHTABLE_INITIAL_LENGTH / 3,
+ "Initial component hashtable size is too large");
+
+ return NS_OK;
+}
+
+template <typename T>
+static void AssertNotMallocAllocated(T* aPtr) {
+#if defined(DEBUG) && defined(MOZ_MEMORY)
+ jemalloc_ptr_info_t info;
+ jemalloc_ptr_info((void*)aPtr, &info);
+ MOZ_ASSERT(info.tag == TagUnknown);
+#endif
+}
+
+template <typename T>
+static void AssertNotStackAllocated(T* aPtr) {
+ // On all of our supported platforms, the stack grows down. Any address
+ // located below the address of our argument is therefore guaranteed not to be
+ // stack-allocated by the caller.
+ //
+ // For addresses above our argument, things get trickier. The main thread
+ // stack is traditionally placed at the top of the program's address space,
+ // but that is becoming less reliable as more and more systems adopt address
+ // space layout randomization strategies, so we have to guess how much space
+ // above our argument pointer we need to care about.
+ //
+ // On most systems, we're guaranteed at least several KiB at the top of each
+ // stack for TLS. We'd probably be safe assuming at least 4KiB in the stack
+ // segment above our argument address, but safer is... well, safer.
+ //
+ // For threads with huge stacks, it's theoretically possible that we could
+ // wind up being passed a stack-allocated string from farther up the stack,
+ // but this is a best-effort thing, so we'll assume we only care about the
+ // immediate caller. For that case, max 2KiB per stack frame is probably a
+ // reasonable guess most of the time, and is less than the ~4KiB that we
+ // expect for TLS, so go with that to avoid the risk of bumping into heap
+ // data just above the stack.
+#ifdef DEBUG
+ static constexpr size_t kFuzz = 2048;
+
+ MOZ_ASSERT(uintptr_t(aPtr) < uintptr_t(&aPtr) ||
+ uintptr_t(aPtr) > uintptr_t(&aPtr) + kFuzz);
+#endif
+}
+
+static void DoRegisterManifest(NSLocationType aType, FileLocation& aFile,
+ bool aChromeOnly) {
+ auto result = URLPreloader::Read(aFile);
+ if (result.isOk()) {
+ nsCString buf(result.unwrap());
+ ParseManifest(aType, aFile, buf.BeginWriting(), aChromeOnly);
+ } 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);
+}
+
+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::ManifestCategory(ManifestProcessingContext& aCx,
+ int aLineNo, char* const* aArgv) {
+ char* category = aArgv[0];
+ char* key = aArgv[1];
+ char* value = aArgv[2];
+
+ nsCategoryManager::GetSingleton()->AddCategoryEntry(
+ nsDependentCString(category), nsDependentCString(key),
+ nsDependentCString(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);
+ }
+
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(nullptr, "chrome-manifests-loaded", nullptr);
+ }
+}
+
+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
+
+ StaticComponents::Shutdown();
+
+ delete sModuleLocations;
+
+ 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);
+}
+
+Maybe<EntryWrapper> nsComponentManagerImpl::LookupByCID(const nsID& aCID) {
+ return LookupByCID(MonitorAutoLock(mLock), aCID);
+}
+
+Maybe<EntryWrapper> nsComponentManagerImpl::LookupByCID(const MonitorAutoLock&,
+ const nsID& aCID) {
+ if (const StaticModule* module = StaticComponents::LookupByCID(aCID)) {
+ return Some(EntryWrapper(module));
+ }
+ if (nsFactoryEntry* entry = mFactories.Get(&aCID)) {
+ return Some(EntryWrapper(entry));
+ }
+ return Nothing();
+}
+
+Maybe<EntryWrapper> nsComponentManagerImpl::LookupByContractID(
+ const nsACString& aContractID) {
+ return LookupByContractID(MonitorAutoLock(mLock), aContractID);
+}
+
+Maybe<EntryWrapper> nsComponentManagerImpl::LookupByContractID(
+ const MonitorAutoLock&, const nsACString& aContractID) {
+ if (const StaticModule* module =
+ StaticComponents::LookupByContractID(aContractID)) {
+ return Some(EntryWrapper(module));
+ }
+ if (nsFactoryEntry* entry = mContractIDs.Get(aContractID)) {
+ // UnregisterFactory might have left a stale nsFactoryEntry in
+ // mContractIDs, so we should check to see whether this entry has
+ // anything useful.
+ if (entry->mFactory || entry->mServiceObject) {
+ return Some(EntryWrapper(entry));
+ }
+ }
+ return Nothing();
+}
+
+already_AddRefed<nsIFactory> nsComponentManagerImpl::FindFactory(
+ const nsCID& aClass) {
+ Maybe<EntryWrapper> e = LookupByCID(aClass);
+ if (!e) {
+ return nullptr;
+ }
+
+ return e->GetFactory();
+}
+
+already_AddRefed<nsIFactory> nsComponentManagerImpl::FindFactory(
+ const char* aContractID, uint32_t aContractIDLen) {
+ Maybe<EntryWrapper> entry =
+ LookupByContractID(nsDependentCString(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[NSID_LENGTH];
+ aClass.ToProvidedString(buf);
+ PR_LogPrint("nsComponentManager: GetClassObject(%s)", 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: GetClassObjectByContractID(%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\tGetClassObjectByContractID() %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, 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
+ fprintf(stderr,
+ "Creating new instance on shutdown. Denied.\n"
+ " CID: %s\n IID: %s\n",
+ AutoIDString(aClass).get(), AutoIDString(aIID).get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ *aResult = nullptr;
+
+ Maybe<EntryWrapper> entry = LookupByCID(aClass);
+
+ if (!entry) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+#ifdef SHOW_CI_ON_EXISTING_SERVICE
+ if (entry->ServiceInstance()) {
+ nsAutoCString message;
+ message = "You are calling CreateInstance \""_ns + AutoIDString(aClass) +
+ "\" when a service for this CID already exists!"_ns;
+ NS_ERROR(message.get());
+ }
+#endif
+
+ nsresult rv;
+ nsCOMPtr<nsIFactory> factory = entry->GetFactory();
+ if (factory) {
+ rv = factory->CreateInstance(aIID, aResult);
+ if (NS_SUCCEEDED(rv) && !*aResult) {
+ NS_ERROR("Factory did not return an object but returned success!");
+ rv = NS_ERROR_SERVICE_NOT_AVAILABLE;
+ }
+ } else {
+ // Translate error values
+ rv = NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ if (MOZ_LOG_TEST(nsComponentManagerLog, LogLevel::Warning)) {
+ char buf[NSID_LENGTH];
+ aClass.ToProvidedString(buf);
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Warning,
+ ("nsComponentManager: CreateInstance(%s) %s", buf,
+ NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"));
+ }
+
+ 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,
+ 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
+ fprintf(stderr,
+ "Creating new instance on shutdown. Denied.\n"
+ " ContractID: %s\n IID: %s\n",
+ aContractID, AutoIDString(aIID).get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ *aResult = nullptr;
+
+ Maybe<EntryWrapper> entry =
+ LookupByContractID(nsDependentCString(aContractID));
+
+ if (!entry) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+#ifdef SHOW_CI_ON_EXISTING_SERVICE
+ if (entry->ServiceInstance()) {
+ nsAutoCString message;
+ message =
+ "You are calling CreateInstance \""_ns +
+ nsDependentCString(aContractID) +
+ nsLiteralCString(
+ "\" 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(aIID, aResult);
+ if (NS_SUCCEEDED(rv) && !*aResult) {
+ NS_ERROR("Factory did not return an object but returned success!");
+ rv = NS_ERROR_SERVICE_NOT_AVAILABLE;
+ }
+ } 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 (nsFactoryEntry* entry : mFactories.Values()) {
+ entry->mFactory = nullptr;
+ entry->mServiceObject = nullptr;
+ }
+
+ for (const auto& module : gStaticModules) {
+ module.SetServiceInstance(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(MonitorAutoLock& aLock,
+ 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);
+ aLock.NotifyAll();
+ 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;
+}
+
+nsresult nsComponentManagerImpl::GetServiceLocked(Maybe<MonitorAutoLock>& aLock,
+ EntryWrapper& aEntry,
+ const nsIID& aIID,
+ void** aResult) {
+ MOZ_ASSERT(aLock.isSome());
+ if (!aLock.isSome()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (auto* service = aEntry.ServiceInstance()) {
+ aLock.reset();
+ return service->QueryInterface(aIID, aResult);
+ }
+
+ PRThread* currentPRThread = PR_GetCurrentThread();
+ MOZ_ASSERT(currentPRThread, "This should never be null!");
+
+ PRThread* pendingPRThread;
+ while ((pendingPRThread = GetPendingServiceThread(aEntry.CID()))) {
+ if (pendingPRThread == currentPRThread) {
+ NS_ERROR("Recursive GetService!");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ aLock->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 (auto* service = aEntry.ServiceInstance()) {
+ aLock.reset();
+ return service->QueryInterface(aIID, aResult);
+ }
+
+ DebugOnly<PendingServiceInfo*> newInfo =
+ AddPendingService(aEntry.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:
+
+ nsCOMPtr<nsISupports> service;
+ auto cleanup = MakeScopeExit([&]() {
+ // `service` must be released after the lock is released, so if we fail and
+ // still have a reference, release the lock before releasing it.
+ if (service) {
+ MOZ_ASSERT(aLock.isSome());
+ aLock.reset();
+ service = nullptr;
+ }
+ });
+ nsresult rv;
+ mLock.AssertCurrentThreadOwns();
+ {
+ MonitorAutoUnlock unlock(mLock);
+ AUTO_PROFILER_MARKER_TEXT(
+ "GetService", OTHER, MarkerStack::Capture(),
+ nsDependentCString(nsIDToCString(aEntry.CID()).get()));
+ rv = aEntry.CreateInstance(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_AVAILABLE;
+ }
+
+#ifdef DEBUG
+ pendingPRThread = GetPendingServiceThread(aEntry.CID());
+ MOZ_ASSERT(pendingPRThread == currentPRThread,
+ "Pending service array has been changed!");
+#endif
+ MOZ_ASSERT(aLock.isSome());
+ RemovePendingService(*aLock, aEntry.CID());
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ NS_ASSERTION(!aEntry.ServiceInstance(),
+ "Created two instances of a service!");
+
+ aEntry.SetServiceInstance(service.forget());
+
+ aLock.reset();
+
+ *aResult = do_AddRef(aEntry.ServiceInstance()).take();
+ return NS_OK;
+}
+
+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
+ fprintf(stderr,
+ "Getting service on shutdown. Denied.\n"
+ " CID: %s\n IID: %s\n",
+ AutoIDString(aClass).get(), AutoIDString(aIID).get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ Maybe<MonitorAutoLock> lock(std::in_place, mLock);
+
+ Maybe<EntryWrapper> entry = LookupByCID(*lock, aClass);
+ if (!entry) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ return GetServiceLocked(lock, *entry, aIID, aResult);
+}
+
+nsresult nsComponentManagerImpl::GetService(ModuleID aId, const nsIID& aIID,
+ void** aResult) {
+ const auto& entry = gStaticModules[size_t(aId)];
+
+ // 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
+ fprintf(stderr,
+ "Getting service on shutdown. Denied.\n"
+ " CID: %s\n IID: %s\n",
+ AutoIDString(entry.CID()).get(), AutoIDString(aIID).get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ Maybe<MonitorAutoLock> lock(std::in_place, mLock);
+
+ Maybe<EntryWrapper> wrapper;
+ if (entry.Overridable()) {
+ // If we expect this service to be overridden by test code, we need to look
+ // it up by contract ID every time.
+ wrapper = LookupByContractID(*lock, entry.ContractID());
+ if (!wrapper) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+ } else if (!entry.Active()) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ } else {
+ wrapper.emplace(&entry);
+ }
+ return GetServiceLocked(lock, *wrapper, aIID, aResult);
+}
+
+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
+ fprintf(stderr,
+ "Checking for service on shutdown. Denied.\n"
+ " CID: %s\n IID: %s\n",
+ AutoIDString(aClass).get(), AutoIDString(aIID).get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (Maybe<EntryWrapper> entry = LookupByCID(aClass)) {
+ if (auto* service = entry->ServiceInstance()) {
+ nsCOMPtr<nsISupports> instance;
+ nsresult rv = service->QueryInterface(aIID, getter_AddRefs(instance));
+ *aResult = (instance != nullptr);
+ return rv;
+ }
+ }
+
+ *aResult = false;
+ return NS_OK;
+}
+
+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
+ fprintf(stderr,
+ "Checking for service on shutdown. Denied.\n"
+ " ContractID: %s\n IID: %s\n",
+ aContractID, AutoIDString(aIID).get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (Maybe<EntryWrapper> entry =
+ LookupByContractID(nsDependentCString(aContractID))) {
+ if (auto* service = entry->ServiceInstance()) {
+ nsCOMPtr<nsISupports> instance;
+ nsresult rv = service->QueryInterface(aIID, getter_AddRefs(instance));
+ *aResult = (instance != nullptr);
+ return rv;
+ }
+ }
+
+ *aResult = false;
+ return NS_OK;
+}
+
+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
+ fprintf(stderr,
+ "Getting service on shutdown. Denied.\n"
+ " ContractID: %s\n IID: %s\n",
+ aContractID, AutoIDString(aIID).get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ AUTO_PROFILER_LABEL_DYNAMIC_CSTR_NONSENSITIVE("GetServiceByContractID", OTHER,
+ aContractID);
+ Maybe<MonitorAutoLock> lock(std::in_place, mLock);
+
+ Maybe<EntryWrapper> entry =
+ LookupByContractID(*lock, nsDependentCString(aContractID));
+ if (!entry) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ return GetServiceLocked(lock, *entry, aIID, aResult);
+}
+
+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;
+ }
+
+ nsDependentCString contractID(aContractID);
+
+ MonitorAutoLock lock(mLock);
+ nsFactoryEntry* oldf = mFactories.Get(&aClass);
+ if (oldf) {
+ StaticComponents::InvalidateContractID(contractID);
+ mContractIDs.InsertOrUpdate(contractID, oldf);
+ return NS_OK;
+ }
+
+ if (StaticComponents::LookupByCID(aClass)) {
+ // If this is the CID of a static module, just reset the invalid bit of
+ // the static entry for this contract ID, and assume it points to the
+ // correct class.
+ if (StaticComponents::InvalidateContractID(contractID, false)) {
+ mContractIDs.Remove(contractID);
+ return NS_OK;
+ }
+ }
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ auto f = MakeUnique<nsFactoryEntry>(aClass, aFactory);
+
+ MonitorAutoLock lock(mLock);
+ return mFactories.WithEntryHandle(&f->mCID, [&](auto&& entry) {
+ if (entry) {
+ return NS_ERROR_FACTORY_EXISTS;
+ }
+ if (StaticComponents::LookupByCID(f->mCID)) {
+ return NS_ERROR_FACTORY_EXISTS;
+ }
+ if (aContractID) {
+ nsDependentCString contractID(aContractID);
+ mContractIDs.InsertOrUpdate(contractID, f.get());
+ // We allow dynamically-registered contract IDs to override static
+ // entries, so invalidate any static entry for this contract ID.
+ StaticComponents::InvalidateContractID(contractID);
+ }
+ entry.Insert(f.release());
+
+ 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;
+
+ {
+ MonitorAutoLock lock(mLock);
+ auto entry = mFactories.Lookup(&aClass);
+ nsFactoryEntry* f = entry ? entry.Data() : nullptr;
+ if (!f || f->mFactory != aFactory) {
+ // Note: We do not support unregistering static factories.
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ entry.Remove();
+
+ // 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::IsCIDRegistered(const nsCID& aClass, bool* aResult) {
+ *aResult = LookupByCID(aClass).isSome();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::IsContractIDRegistered(const char* aClass,
+ bool* aResult) {
+ if (NS_WARN_IF(!aClass)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ Maybe<EntryWrapper> entry = LookupByContractID(nsDependentCString(aClass));
+
+ *aResult = entry.isSome();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::GetContractIDs(nsTArray<nsCString>& aResult) {
+ aResult = ToTArray<nsTArray<nsCString>>(mContractIDs.Keys());
+
+ for (const auto& entry : gContractEntries) {
+ if (!entry.Invalid()) {
+ aResult.AppendElement(entry.ContractID());
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::ContractIDToCID(const char* aContractID,
+ nsCID** aResult) {
+ {
+ MonitorAutoLock lock(mLock);
+ Maybe<EntryWrapper> entry =
+ LookupByContractID(lock, nsDependentCString(aContractID));
+ if (entry) {
+ *aResult = (nsCID*)moz_xmalloc(sizeof(nsCID));
+ **aResult = entry->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 += mFactories.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (const auto& data : mFactories.Values()) {
+ n += data->SizeOfIncludingThis(aMallocSizeOf);
+ }
+
+ n += mContractIDs.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (const auto& key : mContractIDs.Keys()) {
+ // We don't measure the nsFactoryEntry data because it's owned by
+ // mFactories (which is measured above).
+ n += key.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+
+ if (sModuleLocations) {
+ n += sModuleLocations->ShallowSizeOfIncludingThis(aMallocSizeOf);
+ }
+
+ n += mPendingServices.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ // Measurement of the following members may be added later if DMD finds it is
+ // worthwhile:
+ // - mMon
+ // - sModuleLocations' entries
+
+ return n;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsFactoryEntry
+////////////////////////////////////////////////////////////////////////////////
+
+nsFactoryEntry::nsFactoryEntry(const nsCID& aCID, nsIFactory* aFactory)
+ : mCID(aCID), mFactory(aFactory) {}
+
+already_AddRefed<nsIFactory> nsFactoryEntry::GetFactory() {
+ nsComponentManagerImpl::gComponentManager->mLock.AssertNotCurrentThreadOwns();
+
+ nsCOMPtr<nsIFactory> factory = mFactory;
+ return factory.forget();
+}
+
+nsresult nsFactoryEntry::CreateInstance(const nsIID& aIID, void** aResult) {
+ nsCOMPtr<nsIFactory> factory = GetFactory();
+ NS_ENSURE_TRUE(factory, NS_ERROR_FAILURE);
+ return factory->CreateInstance(aIID, aResult);
+}
+
+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:
+ // - mCID;
+ // - 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;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::AddBootstrappedManifestLocation(nsIFile* aLocation) {
+ NS_ENSURE_ARG_POINTER(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, "chrome.manifest"_ns);
+ return XRE_AddManifestLocation(NS_BOOTSTRAPPED_LOCATION, manifest);
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::RemoveBootstrappedManifestLocation(nsIFile* aLocation) {
+ NS_ENSURE_ARG_POINTER(aLocation);
+
+ nsCOMPtr<nsIChromeRegistry> cr = mozilla::services::GetChromeRegistry();
+ 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, "chrome.manifest"_ns);
+ elem.location.Init(lf);
+ }
+
+ // Remove reference.
+ nsComponentManagerImpl::sModuleLocations->RemoveElement(
+ elem, ComponentLocationComparator());
+
+ rv = cr->CheckForNewChrome();
+ return rv;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::GetComponentJSMs(nsIUTF8StringEnumerator** aJSMs) {
+ nsCOMPtr<nsIUTF8StringEnumerator> result =
+ StaticComponents::GetComponentJSMs();
+ result.forget(aJSMs);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::GetComponentESModules(
+ nsIUTF8StringEnumerator** aESModules) {
+ nsCOMPtr<nsIUTF8StringEnumerator> result =
+ StaticComponents::GetComponentESModules();
+ result.forget(aESModules);
+ return NS_OK;
+}
+
+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);
+ }
+ }
+
+ 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;
+}
+
+// Expose some important global interfaces to rust for the rust xpcom API. These
+// methods return a non-owning reference to the component manager, which should
+// live for the lifetime of XPCOM.
+extern "C" {
+
+const nsIComponentManager* Gecko_GetComponentManager() {
+ return nsComponentManagerImpl::gComponentManager;
+}
+
+const nsIServiceManager* Gecko_GetServiceManager() {
+ return nsComponentManagerImpl::gComponentManager;
+}
+
+const nsIComponentRegistrar* Gecko_GetComponentRegistrar() {
+ return nsComponentManagerImpl::gComponentManager;
+}
+
+// FFI-compatible version of `GetServiceHelper::operator()`.
+nsresult Gecko_GetServiceByModuleID(ModuleID aId, const nsIID* aIID,
+ void** aResult) {
+ return nsComponentManagerImpl::gComponentManager->GetService(aId, *aIID,
+ aResult);
+}
+
+// FFI-compatible version of `CreateInstanceHelper::operator()`.
+nsresult Gecko_CreateInstanceByModuleID(ModuleID aId, const nsIID* aIID,
+ void** aResult) {
+ const auto& entry = gStaticModules[size_t(aId)];
+ if (!entry.Active()) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ nsresult rv = entry.CreateInstance(*aIID, aResult);
+ return rv;
+}
+}