gecko/xpcom/components/nsComponentManager.cpp
Mike Hommey 2cfeef8160 Bug 620931 part 3 - Allow GRE and XUL application to use omni.jar independently. r=bsmedberg
We now store two independent locations for an omni.jar, allowing GRE/XRE and
XUL application to each have their own omni.jar. And since xulrunner setups
are very independent from the XUL applications, we implement support for both
omni.jar and non omni.jar cases in the same runtime, with the side effect of
allowing to switch from one to the other manually without rebuilding the
binaries.

We let the mozilla::Omnijar API handle both cases, so that callers don't need
too much work to support them.

We also make the preferences service load the same set of preferences in all
the various cases (unified vs. separate, omni.jar vs. no omni.jar).

The child process launcher for IPC is modified to pass the base directories
needed for the mozilla::Omnijar API initialization in the child process.

Finally, the startupcache file name canonicalization is modified to separate
APP and GRE resources.
2011-03-24 15:28:14 +01:00

2048 lines
61 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK *****
*
* This Original Code has been modified by IBM Corporation.
* Modifications made by IBM described herein are
* Copyright (c) International Business Machines
* Corporation, 2000
*
* Modifications to Mozilla code or documentation
* identified per MPL Section 3.3
*
* Date Modified by Description of modification
* 04/20/2000 IBM Corp. Added PR_CALLBACK for Optlink use in OS2
*/
#include <stdlib.h>
#include "nscore.h"
#include "nsISupports.h"
#include "nspr.h"
#include "nsCRT.h" // for atoll
// Arena used by component manager for storing contractid string, dll
// location strings and small objects
// CAUTION: Arena align mask needs to be defined before including plarena.h
// currently from nsComponentManager.h
#define PL_ARENA_CONST_ALIGN_MASK 7
#define NS_CM_BLOCK_SIZE (1024 * 8)
#include "nsAutoLock.h"
#include "nsCategoryManager.h"
#include "nsCOMPtr.h"
#include "nsComponentManager.h"
#include "nsDirectoryService.h"
#include "nsDirectoryServiceDefs.h"
#include "nsCategoryManager.h"
#include "nsCategoryManagerUtils.h"
#include "nsIEnumerator.h"
#include "xptiprivate.h"
#include "nsIConsoleService.h"
#include "nsIObserverService.h"
#include "nsISimpleEnumerator.h"
#include "nsIStringEnumerator.h"
#include "nsXPCOM.h"
#include "nsXPCOMPrivate.h"
#include "nsISupportsPrimitives.h"
#include "nsIClassInfo.h"
#include "nsLocalFile.h"
#include "nsReadableUtils.h"
#include "nsStaticComponents.h"
#include "nsString.h"
#include "nsXPIDLString.h"
#include "prcmon.h"
#include "xptinfo.h" // this after nsISupports, to pick up IID so that xpt stuff doesn't try to define it itself...
#include "nsThreadUtils.h"
#include "prthread.h"
#include "private/pprthred.h"
#include "nsTArray.h"
#include "prio.h"
#include "mozilla/FunctionTimer.h"
#include "ManifestParser.h"
#include "nsInt64.h"
#include "nsManifestLineReader.h"
#include "mozilla/GenericFactory.h"
#include "nsSupportsPrimitives.h"
#include "nsArrayEnumerator.h"
#include "nsStringEnumerator.h"
#include NEW_H // for placement new
#include "mozilla/Omnijar.h"
#include "nsJAR.h"
static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
#include "prlog.h"
NS_COM PRLogModuleInfo* nsComponentManagerLog = nsnull;
#if 0 || defined (DEBUG_timeless)
#define SHOW_DENIED_ON_SHUTDOWN
#define SHOW_CI_ON_EXISTING_SERVICE
#endif
// Bloated registry buffer size to improve startup performance -- needs to
// be big enough to fit the entire file into memory or it'll thrash.
// 512K is big enough to allow for some future growth in the registry.
#define BIG_REGISTRY_BUFLEN (512*1024)
// Common Key Names
const char classIDKeyName[]="classID";
const char classesKeyName[]="contractID";
const char componentsKeyName[]="components";
const char xpcomComponentsKeyName[]="software/mozilla/XPCOM/components";
const char xpcomKeyName[]="software/mozilla/XPCOM";
// Common Value Names
const char classIDValueName[]="ClassID";
const char classNameValueName[]="ClassName";
const char componentCountValueName[]="ComponentsCount";
const char componentTypeValueName[]="ComponentType";
const char contractIDValueName[]="ContractID";
const char fileSizeValueName[]="FileSize";
const char inprocServerValueName[]="InprocServer";
const char lastModValueName[]="LastModTimeStamp";
const char nativeComponentType[]="application/x-mozilla-native";
const char staticComponentType[]="application/x-mozilla-static";
const char jarComponentType[]="application/x-mozilla-jarjs";
const char versionValueName[]="VersionString";
const static char XPCOM_ABSCOMPONENT_PREFIX[] = "abs:";
const static char XPCOM_RELCOMPONENT_PREFIX[] = "rel:";
const static char XPCOM_GRECOMPONENT_PREFIX[] = "gre:";
static const char gIDFormat[] =
"{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}";
#define NS_EMPTY_IID \
{ \
0x00000000, \
0x0000, \
0x0000, \
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} \
}
NS_DEFINE_CID(kEmptyCID, NS_EMPTY_IID);
NS_DEFINE_CID(kCategoryManagerCID, NS_CATEGORYMANAGER_CID);
#define UID_STRING_LENGTH 39
#ifdef NS_FUNCTION_TIMER
#define COMPMGR_TIME_FUNCTION_CID(cid) \
char cid_buf__[NSID_LENGTH] = { '\0' }; \
cid.ToProvidedString(cid_buf__); \
NS_TIME_FUNCTION_MIN_FMT(5, "%s (line %d) (cid: %s)", MOZ_FUNCTION_NAME, \
__LINE__, cid_buf__)
#define COMPMGR_TIME_FUNCTION_CONTRACTID(cid) \
NS_TIME_FUNCTION_MIN_FMT(5, "%s (line %d) (contractid: %s)", MOZ_FUNCTION_NAME, \
__LINE__, (cid))
#else
#define COMPMGR_TIME_FUNCTION_CID(cid) do {} while (0)
#define COMPMGR_TIME_FUNCTION_CONTRACTID(cid) do {} while (0)
#endif
nsresult
nsGetServiceFromCategory::operator()(const nsIID& aIID, void** aInstancePtr) const
{
nsresult rv;
nsXPIDLCString value;
nsCOMPtr<nsICategoryManager> catman;
nsComponentManagerImpl *compMgr = nsComponentManagerImpl::gComponentManager;
if (!compMgr) {
rv = NS_ERROR_NOT_INITIALIZED;
goto error;
}
if (!mCategory || !mEntry) {
// when categories have defaults, use that for null mEntry
rv = NS_ERROR_NULL_POINTER;
goto error;
}
rv = compMgr->nsComponentManagerImpl::GetService(kCategoryManagerCID,
NS_GET_IID(nsICategoryManager),
getter_AddRefs(catman));
if (NS_FAILED(rv)) goto error;
/* find the contractID for category.entry */
rv = catman->GetCategoryEntry(mCategory, mEntry,
getter_Copies(value));
if (NS_FAILED(rv)) goto error;
if (!value) {
rv = NS_ERROR_SERVICE_NOT_AVAILABLE;
goto error;
}
rv = compMgr->
nsComponentManagerImpl::GetServiceByContractID(value,
aIID, aInstancePtr);
if (NS_FAILED(rv)) {
error:
*aInstancePtr = 0;
}
if (mErrorPtr)
*mErrorPtr = rv;
return rv;
}
////////////////////////////////////////////////////////////////////////////////
// Arena helper functions
////////////////////////////////////////////////////////////////////////////////
char *
ArenaStrndup(const char *s, PRUint32 len, PLArenaPool *arena)
{
void *mem;
// Include trailing null in the len
PL_ARENA_ALLOCATE(mem, arena, len+1);
if (mem)
memcpy(mem, s, len+1);
return static_cast<char *>(mem);
}
char*
ArenaStrdup(const char *s, PLArenaPool *arena)
{
return ArenaStrndup(s, strlen(s), arena);
}
// this is safe to call during InitXPCOM
static already_AddRefed<nsILocalFile>
GetLocationFromDirectoryService(const char* prop)
{
nsCOMPtr<nsIProperties> directoryService;
nsDirectoryService::Create(nsnull,
NS_GET_IID(nsIProperties),
getter_AddRefs(directoryService));
if (!directoryService)
return NULL;
nsCOMPtr<nsILocalFile> file;
nsresult rv = directoryService->Get(prop,
NS_GET_IID(nsILocalFile),
getter_AddRefs(file));
if (NS_FAILED(rv))
return NULL;
return file.forget();
}
static already_AddRefed<nsILocalFile>
CloneAndAppend(nsILocalFile* aBase, const nsACString& append)
{
nsCOMPtr<nsIFile> f;
aBase->Clone(getter_AddRefs(f));
if (!f)
return NULL;
nsCOMPtr<nsILocalFile> lf = do_QueryInterface(f);
f->AppendNative(append);
return lf.forget();
}
////////////////////////////////////////////////////////////////////////////////
// nsComponentManagerImpl
////////////////////////////////////////////////////////////////////////////////
nsresult
nsComponentManagerImpl::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
{
if (aOuter)
return NS_ERROR_NO_AGGREGATION;
if (!gComponentManager)
return NS_ERROR_FAILURE;
return gComponentManager->QueryInterface(aIID, aResult);
}
nsComponentManagerImpl::nsComponentManagerImpl()
: mMon(NULL)
, mStatus(NOT_INITIALIZED)
{
}
#define CONTRACTID_HASHTABLE_INITIAL_SIZE 2048
nsTArray<const mozilla::Module*>* nsComponentManagerImpl::sStaticModules;
/* static */ void
nsComponentManagerImpl::InitializeStaticModules()
{
if (sStaticModules)
return;
sStaticModules = new nsTArray<const mozilla::Module*>;
#ifdef MOZ_ENABLE_LIBXUL
for (const mozilla::Module *const *const *staticModules = kPStaticModules;
*staticModules; ++staticModules)
sStaticModules->AppendElement(**staticModules);
#endif
}
nsTArray<nsComponentManagerImpl::ComponentLocation>*
nsComponentManagerImpl::sModuleLocations;
/* static */ void
nsComponentManagerImpl::InitializeModuleLocations()
{
if (sModuleLocations)
return;
sModuleLocations = new nsTArray<ComponentLocation>;
}
nsresult nsComponentManagerImpl::Init()
{
NS_TIME_FUNCTION;
PR_ASSERT(NOT_INITIALIZED == mStatus);
if (nsComponentManagerLog == nsnull)
{
nsComponentManagerLog = PR_NewLogModule("nsComponentManager");
}
// Initialize our arena
NS_TIME_FUNCTION_MARK("Next: init component manager arena");
PL_INIT_ARENA_POOL(&mArena, "ComponentManagerArena", NS_CM_BLOCK_SIZE);
mFactories.Init(CONTRACTID_HASHTABLE_INITIAL_SIZE);
mContractIDs.Init(CONTRACTID_HASHTABLE_INITIAL_SIZE);
mLoaderMap.Init();
mKnownFileModules.Init();
mKnownJARModules.Init();
mMon = nsAutoMonitor::NewMonitor("nsComponentManagerImpl");
if (mMon == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
nsCOMPtr<nsILocalFile> greDir =
GetLocationFromDirectoryService(NS_GRE_DIR);
nsCOMPtr<nsILocalFile> appDir =
GetLocationFromDirectoryService(NS_XPCOM_CURRENT_PROCESS_DIR);
InitializeStaticModules();
InitializeModuleLocations();
ComponentLocation* cl = sModuleLocations->InsertElementAt(0);
cl->type = NS_COMPONENT_LOCATION;
cl->location = CloneAndAppend(appDir, NS_LITERAL_CSTRING("chrome.manifest"));
cl->jar = false;
PRBool equals = PR_FALSE;
appDir->Equals(greDir, &equals);
if (!equals) {
cl = sModuleLocations->InsertElementAt(0);
cl->type = NS_COMPONENT_LOCATION;
cl->location = CloneAndAppend(greDir, NS_LITERAL_CSTRING("chrome.manifest"));
cl->jar = false;
}
PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG,
("nsComponentManager: Initialized."));
NS_TIME_FUNCTION_MARK("Next: init native module loader");
nsresult rv = mNativeModuleLoader.Init();
if (NS_FAILED(rv))
return rv;
nsCategoryManager::GetSingleton()->SuppressNotifications(true);
RegisterModule(&kXPCOMModule, NULL);
for (PRUint32 i = 0; i < sStaticModules->Length(); ++i)
RegisterModule((*sStaticModules)[i], NULL);
nsCOMPtr<nsIFile> appOmnijar = mozilla::Omnijar::GetPath(mozilla::Omnijar::APP);
if (appOmnijar) {
cl = sModuleLocations->InsertElementAt(1); // Insert after greDir
cl->type = NS_COMPONENT_LOCATION;
cl->location = do_QueryInterface(appOmnijar);
cl->jar = true;
}
nsCOMPtr<nsIFile> greOmnijar = mozilla::Omnijar::GetPath(mozilla::Omnijar::GRE);
if (greOmnijar) {
cl = sModuleLocations->InsertElementAt(0);
cl->type = NS_COMPONENT_LOCATION;
cl->location = do_QueryInterface(greOmnijar);
cl->jar = true;
}
for (PRUint32 i = 0; i < sModuleLocations->Length(); ++i) {
ComponentLocation& l = sModuleLocations->ElementAt(i);
if (!l.jar) {
RegisterManifestFile(l.type, l.location, false);
continue;
}
nsCOMPtr<nsIZipReader> reader = do_CreateInstance(kZipReaderCID, &rv);
rv = reader->Open(l.location);
if (NS_SUCCEEDED(rv))
RegisterJarManifest(reader, "chrome.manifest", false);
}
nsCategoryManager::GetSingleton()->SuppressNotifications(false);
mStatus = NORMAL;
return NS_OK;
}
void
nsComponentManagerImpl::RegisterModule(const mozilla::Module* aModule,
nsILocalFile* aFile)
{
nsAutoMonitor mon(mMon);
KnownModule* m = new KnownModule(aModule, aFile);
if (aFile) {
nsCOMPtr<nsIHashable> h = do_QueryInterface(aFile);
NS_ASSERTION(!mKnownFileModules.Get(h),
"Must not register a binary module twice.");
mKnownFileModules.Put(h, m);
}
else
mKnownStaticModules.AppendElement(m);
if (aModule->mCIDs) {
const mozilla::Module::CIDEntry* entry;
for (entry = aModule->mCIDs; entry->cid; ++entry)
RegisterCIDEntry(entry, m);
}
if (aModule->mContractIDs) {
const mozilla::Module::ContractIDEntry* entry;
for (entry = aModule->mContractIDs; entry->contractid; ++entry)
RegisterContractID(entry);
NS_ASSERTION(!entry->cid, "Incorrectly terminated contract list");
}
if (aModule->mCategoryEntries) {
const mozilla::Module::CategoryEntry* entry;
for (entry = aModule->mCategoryEntries; entry->category; ++entry)
nsCategoryManager::GetSingleton()->
AddCategoryEntry(entry->category,
entry->entry,
entry->value);
}
}
void
nsComponentManagerImpl::RegisterCIDEntry(const mozilla::Module::CIDEntry* aEntry,
KnownModule* aModule)
{
PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMon);
nsFactoryEntry* f = mFactories.Get(*aEntry->cid);
if (f) {
NS_WARNING("Re-registering a CID?");
char idstr[NSID_LENGTH];
aEntry->cid->ToProvidedString(idstr);
nsCString existing;
if (f->mModule)
existing = f->mModule->Description();
else
existing = "<unknown module>";
LogMessage("While registering XPCOM module %s, trying to re-register CID '%s' already registered by %s.",
aModule->Description().get(),
idstr,
existing.get());
return;
}
f = new nsFactoryEntry(aEntry, aModule);
mFactories.Put(*aEntry->cid, f);
}
void
nsComponentManagerImpl::RegisterContractID(const mozilla::Module::ContractIDEntry* aEntry)
{
PR_ASSERT_CURRENT_THREAD_IN_MONITOR(mMon);
nsFactoryEntry* f = mFactories.Get(*aEntry->cid);
if (!f) {
NS_ERROR("No CID found when attempting to map contract ID");
char idstr[NSID_LENGTH];
aEntry->cid->ToProvidedString(idstr);
LogMessage("Could not map contract ID '%s' to CID %s because no implementation of the CID is registered.",
aEntry->contractid,
idstr);
return;
}
mContractIDs.Put(nsDependentCString(aEntry->contractid), f);
}
static void
CutExtension(nsCString& path)
{
PRInt32 dotPos = path.RFindChar('.');
if (kNotFound == dotPos)
path.Truncate();
else
path.Cut(0, dotPos + 1);
}
static nsCString
GetExtension(nsILocalFile* file)
{
nsCString extension;
file->GetNativePath(extension);
CutExtension(extension);
return extension;
}
static already_AddRefed<nsIInputStream>
LoadEntry(nsIZipReader* aReader, const char* aName)
{
if (!aReader)
return NULL;
nsCOMPtr<nsIInputStream> is;
nsresult rv = aReader->GetInputStream(aName, getter_AddRefs(is));
if (NS_FAILED(rv))
return NULL;
return is.forget();
}
void
nsComponentManagerImpl::RegisterJarManifest(nsIZipReader* aReader,
const char* aPath, bool aChromeOnly)
{
nsCOMPtr<nsIInputStream> is = LoadEntry(aReader, aPath);
if (!is) {
LogMessage("Could not find jar manifest entry '%s'.", aPath);
return;
}
PRUint32 flen;
is->Available(&flen);
nsAutoArrayPtr<char> whole(new char[flen + 1]);
if (!whole)
return;
for (PRUint32 totalRead = 0; totalRead < flen; ) {
PRUint32 avail;
PRUint32 read;
if (NS_FAILED(is->Available(&avail)))
return;
if (avail > flen)
return;
if (NS_FAILED(is->Read(whole + totalRead, avail, &read)))
return;
totalRead += read;
}
whole[flen] = '\0';
ParseManifest(NS_COMPONENT_LOCATION, aReader, aPath,
whole, aChromeOnly);
}
namespace {
struct AutoCloseFD
{
AutoCloseFD()
: mFD(NULL)
{ }
~AutoCloseFD() {
if (mFD)
PR_Close(mFD);
}
operator PRFileDesc*() {
return mFD;
}
PRFileDesc** operator&() {
NS_ASSERTION(!mFD, "Re-opening a file");
return &mFD;
}
PRFileDesc* mFD;
};
} // anonymous namespace
void
nsComponentManagerImpl::RegisterManifestFile(NSLocationType aType,
nsILocalFile* aFile,
bool aChromeOnly)
{
nsresult rv;
AutoCloseFD fd;
rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0444, &fd);
if (NS_FAILED(rv)) {
nsCAutoString path;
aFile->GetNativePath(path);
LogMessage("Could not read chrome manifest file '%s'.", path.get());
return;
}
PRFileInfo64 fileInfo;
if (PR_SUCCESS != PR_GetOpenFileInfo64(fd, &fileInfo))
return;
if (fileInfo.size > PRInt64(PR_INT32_MAX))
return;
nsAutoArrayPtr<char> data(new char[PRInt32(fileInfo.size + 1)]);
for (PRInt32 totalRead = 0; totalRead < fileInfo.size; ) {
PRInt32 read = PR_Read(fd, data + totalRead, PRInt32(fileInfo.size));
if (read < 0)
return;
totalRead += read;
}
data[fileInfo.size] = '\0';
ParseManifest(aType, aFile, data, aChromeOnly);
}
#if defined(XP_WIN) || defined(XP_OS2)
#define TRANSLATE_SLASHES
static void
TranslateSlashes(char* path)
{
for (; *path; ++path) {
if ('/' == *path)
*path = '\\';
}
}
#endif
static void
AppendFileToManifestPath(nsCString& path,
const char* file)
{
PRInt32 i = path.RFindChar('/');
if (kNotFound == i)
path.Truncate(0);
else
path.Truncate(i + 1);
path.Append(file);
}
void
nsComponentManagerImpl::ManifestManifest(ManifestProcessingContext& cx, int lineno, char *const * argv)
{
char* file = argv[0];
if (cx.mPath) {
nsCAutoString manifest(cx.mPath);
AppendFileToManifestPath(manifest, file);
RegisterJarManifest(cx.mReader, manifest.get(), cx.mChromeOnly);
}
else {
#ifdef TRANSLATE_SLASHES
TranslateSlashes(file);
#endif
nsCOMPtr<nsIFile> cfile;
cx.mFile->GetParent(getter_AddRefs(cfile));
nsCOMPtr<nsILocalFile> clfile = do_QueryInterface(cfile);
nsresult rv = clfile->AppendRelativeNativePath(nsDependentCString(file));
if (NS_FAILED(rv)) {
NS_WARNING("Couldn't append relative path?");
return;
}
RegisterManifestFile(cx.mType, clfile, cx.mChromeOnly);
}
}
void
nsComponentManagerImpl::ManifestBinaryComponent(ManifestProcessingContext& cx, int lineno, char *const * argv)
{
if (cx.mPath) {
NS_WARNING("Cannot load binary components from a jar.");
LogMessageWithContext(cx.mFile, cx.mPath, lineno,
"Cannot load binary components from a jar.");
return;
}
char* file = argv[0];
#ifdef TRANSLATE_SLASHES
TranslateSlashes(file);
#endif
nsCOMPtr<nsIFile> cfile;
cx.mFile->GetParent(getter_AddRefs(cfile));
nsCOMPtr<nsILocalFile> clfile = do_QueryInterface(cfile);
nsresult rv = clfile->AppendRelativeNativePath(nsDependentCString(file));
if (NS_FAILED(rv)) {
NS_WARNING("Couldn't append relative path?");
return;
}
nsCOMPtr<nsIHashable> h = do_QueryInterface(clfile);
NS_ASSERTION(h, "nsILocalFile doesn't implement nsIHashable");
if (mKnownFileModules.Get(h)) {
NS_WARNING("Attempting to register a binary component twice.");
LogMessageWithContext(cx.mFile, cx.mPath, lineno,
"Attempting to register a binary component twice.");
return;
}
const mozilla::Module* m = mNativeModuleLoader.LoadModule(clfile);
if (!m)
return;
RegisterModule(m, clfile);
}
void
nsComponentManagerImpl::ManifestXPT(ManifestProcessingContext& cx, int lineno, char *const * argv)
{
char* file = argv[0];
if (cx.mPath) {
nsCAutoString manifest(cx.mPath);
AppendFileToManifestPath(manifest, file);
nsCOMPtr<nsIInputStream> stream =
LoadEntry(cx.mReader, manifest.get());
if (!stream) {
NS_WARNING("Failed to load XPT file in a jar.");
return;
}
xptiInterfaceInfoManager::GetSingleton()
->RegisterInputStream(stream);
}
else {
#ifdef TRANSLATE_SLASHES
TranslateSlashes(file);
#endif
nsCOMPtr<nsIFile> cfile;
cx.mFile->GetParent(getter_AddRefs(cfile));
nsCOMPtr<nsILocalFile> clfile = do_QueryInterface(cfile);
nsresult rv = clfile->AppendRelativeNativePath(nsDependentCString(file));
if (NS_FAILED(rv)) {
NS_WARNING("Couldn't append relative path?");
return;
}
xptiInterfaceInfoManager::GetSingleton()
->RegisterFile(clfile);
}
}
void
nsComponentManagerImpl::ManifestComponent(ManifestProcessingContext& cx, int lineno, char *const * argv)
{
char* id = argv[0];
char* file = argv[1];
nsID cid;
if (!cid.Parse(id)) {
LogMessageWithContext(cx.mFile, cx.mPath, lineno,
"Malformed CID: '%s'.", id);
return;
}
nsAutoMonitor mon(mMon);
nsFactoryEntry* f = mFactories.Get(cid);
if (f) {
char idstr[NSID_LENGTH];
cid.ToProvidedString(idstr);
nsCString existing;
if (f->mModule)
existing = f->mModule->Description();
else
existing = "<unknown module>";
LogMessageWithContext(cx.mFile, cx.mPath, lineno,
"Trying to re-register CID '%s' already registered by %s.",
idstr,
existing.get());
return;
}
KnownModule* km;
if (cx.mPath) {
nsCAutoString manifest(cx.mPath);
AppendFileToManifestPath(manifest, file);
nsCAutoString hash;
cx.mFile->GetNativePath(hash);
hash.AppendLiteral("|");
hash.Append(manifest);
km = mKnownJARModules.Get(hash);
if (!km) {
km = new KnownModule(cx.mFile, manifest);
mKnownJARModules.Put(hash, km);
}
}
else {
#ifdef TRANSLATE_SLASHES
TranslateSlashes(file);
#endif
nsCOMPtr<nsIFile> cfile;
cx.mFile->GetParent(getter_AddRefs(cfile));
nsCOMPtr<nsILocalFile> clfile = do_QueryInterface(cfile);
nsresult rv = clfile->AppendRelativeNativePath(nsDependentCString(file));
if (NS_FAILED(rv)) {
NS_WARNING("Couldn't append relative path?");
return;
}
nsCOMPtr<nsIHashable> h = do_QueryInterface(clfile);
km = mKnownFileModules.Get(h);
if (!km) {
km = new KnownModule(clfile);
mKnownFileModules.Put(h, km);
}
}
void* place;
PL_ARENA_ALLOCATE(place, &mArena, sizeof(nsCID));
nsID* permanentCID = static_cast<nsID*>(place);
*permanentCID = cid;
PL_ARENA_ALLOCATE(place, &mArena, sizeof(mozilla::Module::CIDEntry));
mozilla::Module::CIDEntry* e = new (place) mozilla::Module::CIDEntry();
e->cid = permanentCID;
f = new nsFactoryEntry(e, km);
mFactories.Put(cid, f);
}
void
nsComponentManagerImpl::ManifestContract(ManifestProcessingContext& cx, int lineno, char *const * argv)
{
char* contract = argv[0];
char* id = argv[1];
nsID cid;
if (!cid.Parse(id)) {
LogMessageWithContext(cx.mFile, cx.mPath, lineno,
"Malformed CID: '%s'.", id);
return;
}
nsAutoMonitor mon(mMon);
nsFactoryEntry* f = mFactories.Get(cid);
if (!f) {
LogMessageWithContext(cx.mFile, cx.mPath, lineno,
"Could not map contract ID '%s' to CID %s because no implementation of the CID is registered.",
contract, id);
return;
}
mContractIDs.Put(nsDependentCString(contract), f);
}
void
nsComponentManagerImpl::ManifestCategory(ManifestProcessingContext& cx, int lineno, char *const * argv)
{
char* category = argv[0];
char* key = argv[1];
char* value = argv[2];
nsCategoryManager::GetSingleton()->
AddCategoryEntry(category, key, value);
}
void
nsComponentManagerImpl::RereadChromeManifests()
{
for (PRUint32 i = 0; i < sModuleLocations->Length(); ++i) {
ComponentLocation& l = sModuleLocations->ElementAt(i);
if (!l.jar) {
RegisterManifestFile(l.type, l.location, true);
continue;
}
nsresult rv;
nsCOMPtr<nsIZipReader> reader = do_CreateInstance(kZipReaderCID, &rv);
if (NS_SUCCEEDED(rv))
rv = reader->Open(l.location);
if (NS_SUCCEEDED(rv))
RegisterJarManifest(reader, "chrome.manifest", true);
}
}
bool
nsComponentManagerImpl::KnownModule::EnsureLoader()
{
if (!mLoader) {
nsCString extension;
if (!mPath.IsEmpty()) {
extension = mPath;
CutExtension(extension);
}
else {
extension = GetExtension(mFile);
}
mLoader = nsComponentManagerImpl::gComponentManager->LoaderForExtension(extension);
}
return !!mLoader;
}
bool
nsComponentManagerImpl::KnownModule::Load()
{
if (mFailed)
return false;
if (!mModule) {
if (!EnsureLoader())
return false;
if (!mPath.IsEmpty())
mModule = mLoader->LoadModuleFromJAR(mFile, mPath);
else
mModule = mLoader->LoadModule(mFile);
if (!mModule) {
mFailed = true;
return false;
}
}
if (!mLoaded) {
if (mModule->loadProc) {
nsresult rv = mModule->loadProc();
if (NS_FAILED(rv)) {
mFailed = true;
return rv;
}
}
mLoaded = true;
}
return true;
}
nsCString
nsComponentManagerImpl::KnownModule::Description() const
{
nsCString s;
if (!mPath.IsEmpty()) {
mFile->GetNativePath(s);
s.Insert(NS_LITERAL_CSTRING("jar:"), 0);
s.AppendLiteral("!/");
s.Append(mPath);
}
else if (mFile)
mFile->GetNativePath(s);
else
s = "<static module>";
return s;
}
nsresult nsComponentManagerImpl::Shutdown(void)
{
NS_TIME_FUNCTION;
PR_ASSERT(NORMAL == mStatus);
mStatus = SHUTDOWN_IN_PROGRESS;
// Shutdown the component manager
PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG, ("nsComponentManager: Beginning Shutdown."));
// Release all cached factories
mContractIDs.Clear();
mFactories.Clear(); // XXX release the objects, don't just clear
mLoaderMap.Clear();
mKnownJARModules.Clear();
mKnownFileModules.Clear();
mKnownStaticModules.Clear();
mLoaderData.Clear();
delete sStaticModules;
delete sModuleLocations;
// Unload libraries
mNativeModuleLoader.UnloadLibraries();
// delete arena for strings and small objects
PL_FinishArenaPool(&mArena);
mStatus = SHUTDOWN_COMPLETE;
PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG, ("nsComponentManager: Shutdown complete."));
return NS_OK;
}
nsComponentManagerImpl::~nsComponentManagerImpl()
{
PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG, ("nsComponentManager: Beginning destruction."));
if (SHUTDOWN_COMPLETE != mStatus)
Shutdown();
if (mMon) {
nsAutoMonitor::DestroyMonitor(mMon);
}
PR_LOG(nsComponentManagerLog, PR_LOG_DEBUG, ("nsComponentManager: Destroyed."));
}
NS_IMPL_THREADSAFE_ISUPPORTS5(nsComponentManagerImpl,
nsIComponentManager,
nsIServiceManager,
nsIComponentRegistrar,
nsISupportsWeakReference,
nsIInterfaceRequestor)
nsresult
nsComponentManagerImpl::GetInterface(const nsIID & uuid, void **result)
{
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(uuid, result);
}
nsFactoryEntry *
nsComponentManagerImpl::GetFactoryEntry(const char *aContractID,
PRUint32 aContractIDLen)
{
nsAutoMonitor mon(mMon);
return mContractIDs.Get(nsDependentCString(aContractID, aContractIDLen));
}
nsFactoryEntry *
nsComponentManagerImpl::GetFactoryEntry(const nsCID &aClass)
{
nsAutoMonitor mon(mMon);
return mFactories.Get(aClass);
}
already_AddRefed<nsIFactory>
nsComponentManagerImpl::FindFactory(const nsCID& aClass)
{
nsFactoryEntry* e = GetFactoryEntry(aClass);
if (!e)
return NULL;
return e->GetFactory();
}
already_AddRefed<nsIFactory>
nsComponentManagerImpl::FindFactory(const char *contractID,
PRUint32 aContractIDLen)
{
nsFactoryEntry *entry = GetFactoryEntry(contractID, aContractIDLen);
if (!entry)
return NULL;
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;
#ifdef PR_LOGGING
if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_DEBUG))
{
char *buf = aClass.ToString();
PR_LogPrint("nsComponentManager: GetClassObject(%s)", buf);
if (buf)
NS_Free(buf);
}
#endif
PR_ASSERT(aResult != nsnull);
nsCOMPtr<nsIFactory> factory = FindFactory(aClass);
if (!factory)
return NS_ERROR_FACTORY_NOT_REGISTERED;
rv = factory->QueryInterface(aIID, aResult);
PR_LOG(nsComponentManagerLog, PR_LOG_WARNING,
("\t\tGetClassObject() %s", NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"));
return rv;
}
NS_IMETHODIMP
nsComponentManagerImpl::GetClassObjectByContractID(const char *contractID,
const nsIID &aIID,
void **aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
NS_ENSURE_ARG_POINTER(contractID);
nsresult rv;
#ifdef PR_LOGGING
if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_DEBUG))
{
PR_LogPrint("nsComponentManager: GetClassObject(%s)", contractID);
}
#endif
nsCOMPtr<nsIFactory> factory = FindFactory(contractID, strlen(contractID));
if (!factory)
return NS_ERROR_FACTORY_NOT_REGISTERED;
rv = factory->QueryInterface(aIID, aResult);
PR_LOG(nsComponentManagerLog, PR_LOG_WARNING,
("\t\tGetClassObject() %s", NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"));
return rv;
}
/**
* CreateInstance()
*
* Create an instance of an object that implements an interface and belongs
* to the implementation aClass using the factory. The factory is immediately
* released and not held onto for any longer.
*/
NS_IMETHODIMP
nsComponentManagerImpl::CreateInstance(const nsCID &aClass,
nsISupports *aDelegate,
const nsIID &aIID,
void **aResult)
{
COMPMGR_TIME_FUNCTION_CID(aClass);
// test this first, since there's no point in creating a component during
// shutdown -- whether it's available or not would depend on the order it
// occurs in the list
if (gXPCOMShuttingDown) {
// When processing shutdown, don't process new GetService() requests
#ifdef SHOW_DENIED_ON_SHUTDOWN
nsXPIDLCString cid, iid;
cid.Adopt(aClass.ToString());
iid.Adopt(aIID.ToString());
fprintf(stderr, "Creating new instance on shutdown. Denied.\n"
" CID: %s\n IID: %s\n", cid.get(), iid.get());
#endif /* SHOW_DENIED_ON_SHUTDOWN */
return NS_ERROR_UNEXPECTED;
}
if (aResult == nsnull)
{
return NS_ERROR_NULL_POINTER;
}
*aResult = nsnull;
nsFactoryEntry *entry = GetFactoryEntry(aClass);
if (!entry)
return NS_ERROR_FACTORY_NOT_REGISTERED;
#ifdef SHOW_CI_ON_EXISTING_SERVICE
if (entry->mServiceObject) {
nsXPIDLCString cid;
cid.Adopt(aClass.ToString());
nsCAutoString message;
message = NS_LITERAL_CSTRING("You are calling CreateInstance \"") +
cid + NS_LITERAL_CSTRING("\" when a service for this CID already exists!");
NS_ERROR(message.get());
}
#endif
nsresult rv;
nsCOMPtr<nsIFactory> factory = entry->GetFactory();
if (factory)
{
rv = factory->CreateInstance(aDelegate, aIID, aResult);
if (NS_SUCCEEDED(rv) && !*aResult) {
NS_ERROR("Factory did not return an object but returned success!");
rv = NS_ERROR_SERVICE_NOT_FOUND;
}
}
else {
// Translate error values
rv = NS_ERROR_FACTORY_NOT_REGISTERED;
}
#ifdef PR_LOGGING
if (PR_LOG_TEST(nsComponentManagerLog, PR_LOG_WARNING))
{
char *buf = aClass.ToString();
PR_LOG(nsComponentManagerLog, PR_LOG_WARNING,
("nsComponentManager: CreateInstance(%s) %s", buf,
NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"));
if (buf)
NS_Free(buf);
}
#endif
return rv;
}
/**
* CreateInstanceByContractID()
*
* A variant of CreateInstance() that creates an instance of the object that
* implements the interface aIID and whose implementation has a contractID aContractID.
*
* This is only a convenience routine that turns around can calls the
* CreateInstance() with classid and iid.
*/
NS_IMETHODIMP
nsComponentManagerImpl::CreateInstanceByContractID(const char *aContractID,
nsISupports *aDelegate,
const nsIID &aIID,
void **aResult)
{
COMPMGR_TIME_FUNCTION_CONTRACTID(aContractID);
NS_ENSURE_ARG_POINTER(aContractID);
// test this first, since there's no point in creating a component during
// shutdown -- whether it's available or not would depend on the order it
// occurs in the list
if (gXPCOMShuttingDown) {
// When processing shutdown, don't process new GetService() requests
#ifdef SHOW_DENIED_ON_SHUTDOWN
nsXPIDLCString iid;
iid.Adopt(aIID.ToString());
fprintf(stderr, "Creating new instance on shutdown. Denied.\n"
" ContractID: %s\n IID: %s\n", aContractID, iid.get());
#endif /* SHOW_DENIED_ON_SHUTDOWN */
return NS_ERROR_UNEXPECTED;
}
if (aResult == nsnull)
{
return NS_ERROR_NULL_POINTER;
}
*aResult = nsnull;
nsFactoryEntry *entry = GetFactoryEntry(aContractID, strlen(aContractID));
if (!entry)
return NS_ERROR_FACTORY_NOT_REGISTERED;
#ifdef SHOW_CI_ON_EXISTING_SERVICE
if (entry->mServiceObject) {
nsCAutoString message;
message =
NS_LITERAL_CSTRING("You are calling CreateInstance \"") +
nsDependentCString(aContractID) +
NS_LITERAL_CSTRING("\" when a service for this CID already exists! "
"Add it to abusedContracts to track down the service consumer.");
NS_ERROR(message.get());
}
#endif
nsresult rv;
nsCOMPtr<nsIFactory> factory = entry->GetFactory();
if (factory)
{
rv = factory->CreateInstance(aDelegate, aIID, aResult);
if (NS_SUCCEEDED(rv) && !*aResult) {
NS_ERROR("Factory did not return an object but returned success!");
rv = NS_ERROR_SERVICE_NOT_FOUND;
}
}
else {
// Translate error values
rv = NS_ERROR_FACTORY_NOT_REGISTERED;
}
PR_LOG(nsComponentManagerLog, PR_LOG_WARNING,
("nsComponentManager: CreateInstanceByContractID(%s) %s", aContractID,
NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"));
return rv;
}
static PLDHashOperator
FreeFactoryEntries(const nsID& aCID,
nsFactoryEntry* aEntry,
void* arg)
{
aEntry->mFactory = NULL;
aEntry->mServiceObject = NULL;
return PL_DHASH_NEXT;
}
nsresult
nsComponentManagerImpl::FreeServices()
{
NS_ASSERTION(gXPCOMShuttingDown, "Must be shutting down in order to free all services");
if (!gXPCOMShuttingDown)
return NS_ERROR_FAILURE;
mFactories.EnumerateRead(FreeFactoryEntries, NULL);
return NS_OK;
}
// This should only ever be called within the monitor!
nsComponentManagerImpl::PendingServiceInfo*
nsComponentManagerImpl::AddPendingService(const nsCID& aServiceCID,
PRThread* aThread)
{
PendingServiceInfo* newInfo = mPendingServices.AppendElement();
if (newInfo) {
newInfo->cid = &aServiceCID;
newInfo->thread = aThread;
}
return newInfo;
}
// This should only ever be called within the monitor!
void
nsComponentManagerImpl::RemovePendingService(const nsCID& aServiceCID)
{
PRUint32 pendingCount = mPendingServices.Length();
for (PRUint32 index = 0; index < pendingCount; ++index) {
const PendingServiceInfo& info = mPendingServices.ElementAt(index);
if (info.cid->Equals(aServiceCID)) {
mPendingServices.RemoveElementAt(index);
return;
}
}
}
// This should only ever be called within the monitor!
PRThread*
nsComponentManagerImpl::GetPendingServiceThread(const nsCID& aServiceCID) const
{
PRUint32 pendingCount = mPendingServices.Length();
for (PRUint32 index = 0; index < pendingCount; ++index) {
const PendingServiceInfo& info = mPendingServices.ElementAt(index);
if (info.cid->Equals(aServiceCID)) {
return info.thread;
}
}
return nsnull;
}
NS_IMETHODIMP
nsComponentManagerImpl::GetService(const nsCID& aClass,
const nsIID& aIID,
void* *result)
{
// test this first, since there's no point in returning a service during
// shutdown -- whether it's available or not would depend on the order it
// occurs in the list
if (gXPCOMShuttingDown) {
// When processing shutdown, don't process new GetService() requests
#ifdef SHOW_DENIED_ON_SHUTDOWN
nsXPIDLCString cid, iid;
cid.Adopt(aClass.ToString());
iid.Adopt(aIID.ToString());
fprintf(stderr, "Getting service on shutdown. Denied.\n"
" CID: %s\n IID: %s\n", cid.get(), iid.get());
#endif /* SHOW_DENIED_ON_SHUTDOWN */
return NS_ERROR_UNEXPECTED;
}
nsAutoMonitor mon(mMon);
nsFactoryEntry* entry = mFactories.Get(aClass);
if (!entry)
return NS_ERROR_FACTORY_NOT_REGISTERED;
if (entry->mServiceObject) {
nsCOMPtr<nsISupports> supports = entry->mServiceObject;
mon.Exit();
return supports->QueryInterface(aIID, result);
}
// We only care about time when we create the service.
COMPMGR_TIME_FUNCTION_CID(aClass);
PRThread* currentPRThread = PR_GetCurrentThread();
NS_ASSERTION(currentPRThread, "This should never be null!");
// Needed to optimize the event loop below.
nsIThread* currentThread = nsnull;
PRThread* pendingPRThread;
while ((pendingPRThread = GetPendingServiceThread(aClass))) {
if (pendingPRThread == currentPRThread) {
NS_ERROR("Recursive GetService!");
return NS_ERROR_NOT_AVAILABLE;
}
mon.Exit();
if (!currentThread) {
currentThread = NS_GetCurrentThread();
NS_ASSERTION(currentThread, "This should never be null!");
}
// This will process a single event or yield the thread if no event is
// pending.
if (!NS_ProcessNextEvent(currentThread, PR_FALSE)) {
PR_Sleep(PR_INTERVAL_NO_WAIT);
}
mon.Enter();
}
// It's still possible that the other thread failed to create the
// service so we're not guaranteed to have an entry or service yet.
if (entry->mServiceObject) {
nsCOMPtr<nsISupports> supports = entry->mServiceObject;
mon.Exit();
return supports->QueryInterface(aIID, result);
}
#ifdef DEBUG
PendingServiceInfo* newInfo =
#endif
AddPendingService(aClass, currentPRThread);
NS_ASSERTION(newInfo, "Failed to add info to the array!");
nsCOMPtr<nsISupports> service;
// We need to not be holding the service manager's monitor while calling
// CreateInstance, because it invokes user code which could try to re-enter
// the service manager:
mon.Exit();
nsresult rv = CreateInstance(aClass, nsnull, aIID, getter_AddRefs(service));
mon.Enter();
#ifdef DEBUG
pendingPRThread = GetPendingServiceThread(aClass);
NS_ASSERTION(pendingPRThread == currentPRThread,
"Pending service array has been changed!");
#endif
RemovePendingService(aClass);
if (NS_FAILED(rv))
return rv;
NS_ASSERTION(!entry->mServiceObject, "Created two instances of a service!");
entry->mServiceObject = service;
*result = service.get();
if (!*result) {
NS_ERROR("Factory did not return an object but returned success!");
return NS_ERROR_SERVICE_NOT_FOUND;
}
NS_ADDREF(static_cast<nsISupports*>((*result)));
return rv;
}
NS_IMETHODIMP
nsComponentManagerImpl::IsServiceInstantiated(const nsCID & aClass,
const nsIID& aIID,
PRBool *result)
{
COMPMGR_TIME_FUNCTION_CID(aClass);
// Now we want to get the service if we already got it. If not, we don't want
// to create an instance of it. mmh!
// test this first, since there's no point in returning a service during
// shutdown -- whether it's available or not would depend on the order it
// occurs in the list
if (gXPCOMShuttingDown) {
// When processing shutdown, don't process new GetService() requests
#ifdef SHOW_DENIED_ON_SHUTDOWN
nsXPIDLCString cid, iid;
cid.Adopt(aClass.ToString());
iid.Adopt(aIID.ToString());
fprintf(stderr, "Checking for service on shutdown. Denied.\n"
" CID: %s\n IID: %s\n", cid.get(), iid.get());
#endif /* SHOW_DENIED_ON_SHUTDOWN */
return NS_ERROR_UNEXPECTED;
}
nsresult rv = NS_ERROR_SERVICE_NOT_AVAILABLE;
nsFactoryEntry* entry;
{
nsAutoMonitor mon(mMon);
entry = mFactories.Get(aClass);
}
if (entry && entry->mServiceObject) {
nsCOMPtr<nsISupports> service;
rv = entry->mServiceObject->QueryInterface(aIID, getter_AddRefs(service));
*result = (service!=nsnull);
}
return rv;
}
NS_IMETHODIMP nsComponentManagerImpl::IsServiceInstantiatedByContractID(const char *aContractID,
const nsIID& aIID,
PRBool *result)
{
COMPMGR_TIME_FUNCTION_CONTRACTID(aContractID);
// Now we want to get the service if we already got it. If not, we don't want
// to create an instance of it. mmh!
// test this first, since there's no point in returning a service during
// shutdown -- whether it's available or not would depend on the order it
// occurs in the list
if (gXPCOMShuttingDown) {
// When processing shutdown, don't process new GetService() requests
#ifdef SHOW_DENIED_ON_SHUTDOWN
nsXPIDLCString iid;
iid.Adopt(aIID.ToString());
fprintf(stderr, "Checking for service on shutdown. Denied.\n"
" ContractID: %s\n IID: %s\n", aContractID, iid.get());
#endif /* SHOW_DENIED_ON_SHUTDOWN */
return NS_ERROR_UNEXPECTED;
}
nsresult rv = NS_ERROR_SERVICE_NOT_AVAILABLE;
nsFactoryEntry *entry;
{
nsAutoMonitor mon(mMon);
entry = mContractIDs.Get(nsDependentCString(aContractID));
}
if (entry && entry->mServiceObject) {
nsCOMPtr<nsISupports> service;
rv = entry->mServiceObject->QueryInterface(aIID, getter_AddRefs(service));
*result = (service!=nsnull);
}
return rv;
}
NS_IMETHODIMP
nsComponentManagerImpl::GetServiceByContractID(const char* aContractID,
const nsIID& aIID,
void* *result)
{
// test this first, since there's no point in returning a service during
// shutdown -- whether it's available or not would depend on the order it
// occurs in the list
if (gXPCOMShuttingDown) {
// When processing shutdown, don't process new GetService() requests
#ifdef SHOW_DENIED_ON_SHUTDOWN
nsXPIDLCString iid;
iid.Adopt(aIID.ToString());
fprintf(stderr, "Getting service on shutdown. Denied.\n"
" ContractID: %s\n IID: %s\n", aContractID, iid.get());
#endif /* SHOW_DENIED_ON_SHUTDOWN */
return NS_ERROR_UNEXPECTED;
}
nsAutoMonitor mon(mMon);
nsFactoryEntry *entry = mContractIDs.Get(nsDependentCString(aContractID));
if (!entry)
return NS_ERROR_FACTORY_NOT_REGISTERED;
if (entry->mServiceObject) {
nsCOMPtr<nsISupports> serviceObject = entry->mServiceObject;
// We need to not be holding the service manager's monitor while calling
// QueryInterface, because it invokes user code which could try to re-enter
// the service manager, or try to grab some other lock/monitor/condvar
// and deadlock, e.g. bug 282743.
mon.Exit();
return serviceObject->QueryInterface(aIID, result);
}
// We only care about time when we create the service.
COMPMGR_TIME_FUNCTION_CONTRACTID(aContractID);
PRThread* currentPRThread = PR_GetCurrentThread();
NS_ASSERTION(currentPRThread, "This should never be null!");
// Needed to optimize the event loop below.
nsIThread* currentThread = nsnull;
PRThread* pendingPRThread;
while ((pendingPRThread = GetPendingServiceThread(*entry->mCIDEntry->cid))) {
if (pendingPRThread == currentPRThread) {
NS_ERROR("Recursive GetService!");
return NS_ERROR_NOT_AVAILABLE;
}
mon.Exit();
if (!currentThread) {
currentThread = NS_GetCurrentThread();
NS_ASSERTION(currentThread, "This should never be null!");
}
// This will process a single event or yield the thread if no event is
// pending.
if (!NS_ProcessNextEvent(currentThread, PR_FALSE)) {
PR_Sleep(PR_INTERVAL_NO_WAIT);
}
mon.Enter();
}
if (currentThread && entry->mServiceObject) {
// If we have a currentThread then we must have waited on another thread
// to create the service. Grab it now if that succeeded.
nsCOMPtr<nsISupports> serviceObject = entry->mServiceObject;
mon.Exit();
return serviceObject->QueryInterface(aIID, result);
}
#ifdef DEBUG
PendingServiceInfo* newInfo =
#endif
AddPendingService(*entry->mCIDEntry->cid, currentPRThread);
NS_ASSERTION(newInfo, "Failed to add info to the array!");
nsCOMPtr<nsISupports> service;
// We need to not be holding the service manager's monitor while calling
// CreateInstance, because it invokes user code which could try to re-enter
// the service manager:
mon.Exit();
nsresult rv = CreateInstanceByContractID(aContractID, nsnull, aIID,
getter_AddRefs(service));
mon.Enter();
#ifdef DEBUG
pendingPRThread = GetPendingServiceThread(*entry->mCIDEntry->cid);
NS_ASSERTION(pendingPRThread == currentPRThread,
"Pending service array has been changed!");
#endif
RemovePendingService(*entry->mCIDEntry->cid);
if (NS_FAILED(rv))
return rv;
NS_ASSERTION(!entry->mServiceObject, "Created two instances of a service!");
entry->mServiceObject = service;
*result = service.get();
NS_ADDREF(static_cast<nsISupports*>(*result));
return rv;
}
already_AddRefed<mozilla::ModuleLoader>
nsComponentManagerImpl::LoaderForExtension(const nsACString& aExt)
{
nsCOMPtr<mozilla::ModuleLoader> loader = mLoaderMap.Get(aExt);
if (!loader) {
loader = do_GetServiceFromCategory("module-loader",
PromiseFlatCString(aExt).get());
if (!loader)
return NULL;
mLoaderMap.Put(aExt, loader);
}
return loader.forget();
}
NS_IMETHODIMP
nsComponentManagerImpl::RegisterFactory(const nsCID& aClass,
const char* aName,
const char* aContractID,
nsIFactory* aFactory)
{
if (!aFactory) {
// If a null factory is passed in, this call just wants to reset
// the contract ID to point to an existing CID entry.
if (!aContractID)
return NS_ERROR_INVALID_ARG;
nsAutoMonitor mon(mMon);
nsFactoryEntry* oldf = mFactories.Get(aClass);
if (!oldf)
return NS_ERROR_FACTORY_NOT_REGISTERED;
mContractIDs.Put(nsDependentCString(aContractID), oldf);
return NS_OK;
}
nsAutoPtr<nsFactoryEntry> f(new nsFactoryEntry(aClass, aFactory));
nsAutoMonitor mon(mMon);
nsFactoryEntry* oldf = mFactories.Get(aClass);
if (oldf)
return NS_ERROR_FACTORY_EXISTS;
if (aContractID)
mContractIDs.Put(nsDependentCString(aContractID), f);
mFactories.Put(aClass, f.forget());
return NS_OK;
}
NS_IMETHODIMP
nsComponentManagerImpl::UnregisterFactory(const nsCID& aClass,
nsIFactory* aFactory)
{
// Don't release the dying factory or service object until releasing
// the component manager monitor.
nsCOMPtr<nsIFactory> dyingFactory;
nsCOMPtr<nsISupports> dyingServiceObject;
{
nsAutoMonitor mon(mMon);
nsFactoryEntry* f = mFactories.Get(aClass);
if (!f || f->mFactory != aFactory)
return NS_ERROR_FACTORY_NOT_REGISTERED;
mFactories.Remove(aClass);
// This might leave a stale contractid -> factory mapping in
// place, so null out the factory entry (see
// nsFactoryEntry::GetFactory)
f->mFactory.swap(dyingFactory);
f->mServiceObject.swap(dyingServiceObject);
}
return NS_OK;
}
NS_IMETHODIMP
nsComponentManagerImpl::AutoRegister(nsIFile* aLocation)
{
nsCOMPtr<nsILocalFile> lf = do_QueryInterface(aLocation);
if (!lf)
return NS_ERROR_INVALID_ARG;
XRE_AddManifestLocation(NS_COMPONENT_LOCATION, lf);
return NS_OK;
}
NS_IMETHODIMP
nsComponentManagerImpl::AutoUnregister(nsIFile* aLocation)
{
NS_ERROR("AutoUnregister not implemented.");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsComponentManagerImpl::RegisterFactoryLocation(const nsCID& aCID,
const char* aClassName,
const char* aContractID,
nsIFile* aFile,
const char* aLoaderStr,
const char* aType)
{
NS_ERROR("RegisterFactoryLocation not implemented.");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsComponentManagerImpl::UnregisterFactoryLocation(const nsCID& aCID,
nsIFile* aFile)
{
NS_ERROR("UnregisterFactoryLocation not implemented.");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsComponentManagerImpl::IsCIDRegistered(const nsCID & aClass,
PRBool *_retval)
{
*_retval = (nsnull != GetFactoryEntry(aClass));
return NS_OK;
}
NS_IMETHODIMP
nsComponentManagerImpl::IsContractIDRegistered(const char *aClass,
PRBool *_retval)
{
NS_ENSURE_ARG_POINTER(aClass);
nsFactoryEntry *entry = GetFactoryEntry(aClass, strlen(aClass));
if (entry)
*_retval = PR_TRUE;
else
*_retval = PR_FALSE;
return NS_OK;
}
static PLDHashOperator
EnumerateCIDHelper(const nsID& id, nsFactoryEntry* entry, void* closure)
{
nsCOMArray<nsISupports> *array = static_cast<nsCOMArray<nsISupports>*>(closure);
nsCOMPtr<nsISupportsID> wrapper = new nsSupportsIDImpl();
wrapper->SetData(&id);
array->AppendObject(wrapper);
return PL_DHASH_NEXT;
}
NS_IMETHODIMP
nsComponentManagerImpl::EnumerateCIDs(nsISimpleEnumerator **aEnumerator)
{
NS_TIME_FUNCTION;
nsCOMArray<nsISupports> array;
mFactories.EnumerateRead(EnumerateCIDHelper, &array);
return NS_NewArrayEnumerator(aEnumerator, array);
}
static PLDHashOperator
EnumerateContractsHelper(const nsACString& contract, nsFactoryEntry* entry, void* closure)
{
nsTArray<nsCString>* array = static_cast<nsTArray<nsCString>*>(closure);
array->AppendElement(contract);
return PL_DHASH_NEXT;
}
NS_IMETHODIMP
nsComponentManagerImpl::EnumerateContractIDs(nsISimpleEnumerator **aEnumerator)
{
NS_TIME_FUNCTION;
nsTArray<nsCString>* array = new nsTArray<nsCString>;
mContractIDs.EnumerateRead(EnumerateContractsHelper, array);
nsCOMPtr<nsIUTF8StringEnumerator> e;
nsresult rv = NS_NewAdoptingUTF8StringEnumerator(getter_AddRefs(e), array);
if (NS_FAILED(rv))
return rv;
return CallQueryInterface(e, aEnumerator);
}
NS_IMETHODIMP
nsComponentManagerImpl::CIDToContractID(const nsCID & aClass,
char **_retval)
{
NS_ERROR("CIDTOContractID not implemented");
return NS_ERROR_FACTORY_NOT_REGISTERED;
}
NS_IMETHODIMP
nsComponentManagerImpl::ContractIDToCID(const char *aContractID,
nsCID * *_retval)
{
{
nsAutoMonitor mon(mMon);
nsFactoryEntry* entry = mContractIDs.Get(nsDependentCString(aContractID));
if (entry) {
*_retval = (nsCID*) NS_Alloc(sizeof(nsCID));
**_retval = *entry->mCIDEntry->cid;
return NS_OK;
}
}
*_retval = NULL;
return NS_ERROR_FACTORY_NOT_REGISTERED;
}
////////////////////////////////////////////////////////////////////////////////
// nsFactoryEntry
////////////////////////////////////////////////////////////////////////////////
nsFactoryEntry::nsFactoryEntry(const mozilla::Module::CIDEntry* entry,
nsComponentManagerImpl::KnownModule* module)
: mCIDEntry(entry)
, mModule(module)
{
}
nsFactoryEntry::nsFactoryEntry(const nsCID& aCID, nsIFactory* factory)
: mCIDEntry(NULL)
, mModule(NULL)
, mFactory(factory)
{
mozilla::Module::CIDEntry* e = new mozilla::Module::CIDEntry();
nsCID* cid = new nsCID;
*cid = aCID;
e->cid = cid;
mCIDEntry = e;
}
nsFactoryEntry::~nsFactoryEntry()
{
// If this was a RegisterFactory entry, we own the CIDEntry/CID
if (!mModule) {
delete mCIDEntry->cid;
delete mCIDEntry;
}
}
already_AddRefed<nsIFactory>
nsFactoryEntry::GetFactory()
{
if (!mFactory) {
// RegisterFactory then UnregisterFactory can leave an entry in mContractIDs
// pointing to an unusable nsFactoryEntry.
if (!mModule)
return NULL;
if (!mModule->Load())
return NULL;
if (mModule->Module()->getFactoryProc) {
mFactory = mModule->Module()->getFactoryProc(*mModule->Module(),
*mCIDEntry);
}
else if (mCIDEntry->getFactoryProc) {
mFactory = mCIDEntry->getFactoryProc(*mModule->Module(), *mCIDEntry);
}
else {
NS_ASSERTION(mCIDEntry->constructorProc, "no getfactory or constructor");
mFactory = new mozilla::GenericFactory(mCIDEntry->constructorProc);
}
if (!mFactory)
return NULL;
}
NS_ADDREF(mFactory);
return mFactory.get();
}
////////////////////////////////////////////////////////////////////////////////
// Static Access Functions
////////////////////////////////////////////////////////////////////////////////
NS_COM nsresult
NS_GetComponentManager(nsIComponentManager* *result)
{
if (!nsComponentManagerImpl::gComponentManager)
return NS_ERROR_NOT_INITIALIZED;
NS_ADDREF(*result = nsComponentManagerImpl::gComponentManager);
return NS_OK;
}
NS_COM nsresult
NS_GetServiceManager(nsIServiceManager* *result)
{
if (!nsComponentManagerImpl::gComponentManager)
return NS_ERROR_NOT_INITIALIZED;
NS_ADDREF(*result = nsComponentManagerImpl::gComponentManager);
return NS_OK;
}
NS_COM nsresult
NS_GetComponentRegistrar(nsIComponentRegistrar* *result)
{
if (!nsComponentManagerImpl::gComponentManager)
return NS_ERROR_NOT_INITIALIZED;
NS_ADDREF(*result = nsComponentManagerImpl::gComponentManager);
return NS_OK;
}
EXPORT_XPCOM_API(nsresult)
XRE_AddStaticComponent(const mozilla::Module* aComponent)
{
nsComponentManagerImpl::InitializeStaticModules();
nsComponentManagerImpl::sStaticModules->AppendElement(aComponent);
if (nsComponentManagerImpl::gComponentManager &&
nsComponentManagerImpl::NORMAL == nsComponentManagerImpl::gComponentManager->mStatus)
nsComponentManagerImpl::gComponentManager->RegisterModule(aComponent, NULL);
return NS_OK;
}
EXPORT_XPCOM_API(nsresult)
XRE_AddManifestLocation(NSLocationType aType, nsILocalFile* aLocation)
{
nsComponentManagerImpl::InitializeModuleLocations();
nsComponentManagerImpl::ComponentLocation* c =
nsComponentManagerImpl::sModuleLocations->AppendElement();
c->type = aType;
c->location = aLocation;
c->jar = false;
if (nsComponentManagerImpl::gComponentManager &&
nsComponentManagerImpl::NORMAL == nsComponentManagerImpl::gComponentManager->mStatus)
nsComponentManagerImpl::gComponentManager->RegisterManifestFile(aType, aLocation, false);
return NS_OK;
}
EXPORT_XPCOM_API(nsresult)
XRE_AddJarManifestLocation(NSLocationType aType, nsILocalFile* aLocation)
{
nsComponentManagerImpl::InitializeModuleLocations();
nsComponentManagerImpl::ComponentLocation* c =
nsComponentManagerImpl::sModuleLocations->AppendElement();
c->type = aType;
c->location = aLocation;
c->jar = true;
if (!nsComponentManagerImpl::gComponentManager ||
nsComponentManagerImpl::NORMAL != nsComponentManagerImpl::gComponentManager->mStatus)
return NS_OK;
nsresult rv;
nsCOMPtr<nsIZipReader> reader = do_CreateInstance(kZipReaderCID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = reader->Open(c->location);
if (NS_SUCCEEDED(rv))
nsComponentManagerImpl::gComponentManager->RegisterJarManifest(reader, "chrome.manifest", false);
return NS_OK;
}