зеркало из https://github.com/mozilla/gecko-dev.git
1576 строки
50 KiB
C++
1576 строки
50 KiB
C++
/* -*- 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;
|
|
}
|
|
}
|