import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,792 @@
/* -*- 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 "mozilla/ArrayUtils.h"
#include "ManifestParser.h"
#include <string.h>
#include "prio.h"
#include "prprf.h"
#if defined(XP_WIN)
#include <windows.h>
#elif defined(MOZ_WIDGET_COCOA)
#include <CoreServices/CoreServices.h>
#include "nsCocoaFeatures.h"
#elif defined(MOZ_WIDGET_GTK)
#include <gtk/gtk.h>
#endif
#ifdef MOZ_WIDGET_ANDROID
#include "AndroidBridge.h"
#endif
#include "mozilla/Services.h"
#include "nsCRT.h"
#include "nsConsoleMessage.h"
#include "nsTextFormatter.h"
#include "nsVersionComparator.h"
#include "nsXPCOMCIDInternal.h"
#include "nsIConsoleService.h"
#include "nsIScriptError.h"
#include "nsIXULAppInfo.h"
#include "nsIXULRuntime.h"
using namespace mozilla;
struct ManifestDirective
{
const char* directive;
int argc;
// Binary components are only allowed for APP locations.
bool apponly;
// Some directives should only be delivered for APP or EXTENSION locations.
bool componentonly;
bool ischrome;
bool allowbootstrap;
// The platform/contentaccessible flags only apply to content directives.
bool contentflags;
// Function to handle this directive. This isn't a union because C++ still
// hasn't learned how to initialize unions in a sane way.
void (nsComponentManagerImpl::*mgrfunc)(
nsComponentManagerImpl::ManifestProcessingContext& aCx,
int aLineNo, char* const* aArgv);
void (nsChromeRegistry::*regfunc)(
nsChromeRegistry::ManifestProcessingContext& aCx,
int aLineNo, char* const* aArgv, int aFlags);
void* xptonlyfunc;
bool isContract;
};
static const ManifestDirective kParsingTable[] = {
{
"manifest", 1, false, false, true, true, false,
&nsComponentManagerImpl::ManifestManifest, nullptr, nullptr
},
{
"binary-component", 1, true, true, false, false, false,
&nsComponentManagerImpl::ManifestBinaryComponent, nullptr, nullptr
},
{
"interfaces", 1, false, true, false, false, false,
&nsComponentManagerImpl::ManifestXPT, nullptr, nullptr
},
{
"component", 2, false, true, false, false, false,
&nsComponentManagerImpl::ManifestComponent, nullptr, nullptr
},
{
"contract", 2, false, true, false, false, false,
&nsComponentManagerImpl::ManifestContract, nullptr, nullptr, true
},
{
"category", 3, false, true, false, false, false,
&nsComponentManagerImpl::ManifestCategory, nullptr, nullptr
},
{
"content", 2, false, true, true, true, true,
nullptr, &nsChromeRegistry::ManifestContent, nullptr
},
{
"locale", 3, false, true, true, true, false,
nullptr, &nsChromeRegistry::ManifestLocale, nullptr
},
{
"skin", 3, false, false, true, true, false,
nullptr, &nsChromeRegistry::ManifestSkin, nullptr
},
{
"overlay", 2, false, true, true, false, false,
nullptr, &nsChromeRegistry::ManifestOverlay, nullptr
},
{
"style", 2, false, false, true, false, false,
nullptr, &nsChromeRegistry::ManifestStyle, nullptr
},
{
// NB: note that while skin manifests can use this, they are only allowed
// to use it for chrome://../skin/ URLs
"override", 2, false, false, true, true, false,
nullptr, &nsChromeRegistry::ManifestOverride, nullptr
},
{
"resource", 2, false, true, true, true, false,
nullptr, &nsChromeRegistry::ManifestResource, nullptr
}
};
static const char kWhitespace[] = "\t ";
static bool
IsNewline(char aChar)
{
return aChar == '\n' || aChar == '\r';
}
namespace {
struct AutoPR_smprintf_free
{
explicit AutoPR_smprintf_free(char* aBuf) : mBuf(aBuf) {}
~AutoPR_smprintf_free()
{
if (mBuf) {
PR_smprintf_free(mBuf);
}
}
operator char*() const { return mBuf; }
char* mBuf;
};
} // namespace
/**
* If we are pre-loading XPTs, this method may do nothing because the
* console service is not initialized.
*/
void
LogMessage(const char* aMsg, ...)
{
if (!nsComponentManagerImpl::gComponentManager) {
return;
}
nsCOMPtr<nsIConsoleService> console =
do_GetService(NS_CONSOLESERVICE_CONTRACTID);
if (!console) {
return;
}
va_list args;
va_start(args, aMsg);
AutoPR_smprintf_free formatted(PR_vsmprintf(aMsg, args));
va_end(args);
nsCOMPtr<nsIConsoleMessage> error =
new nsConsoleMessage(NS_ConvertUTF8toUTF16(formatted).get());
console->LogMessage(error);
}
/**
* If we are pre-loading XPTs, this method may do nothing because the
* console service is not initialized.
*/
void
LogMessageWithContext(FileLocation& aFile,
uint32_t aLineNumber, const char* aMsg, ...)
{
va_list args;
va_start(args, aMsg);
AutoPR_smprintf_free formatted(PR_vsmprintf(aMsg, args));
va_end(args);
if (!formatted) {
return;
}
if (!nsComponentManagerImpl::gComponentManager) {
return;
}
nsCString file;
aFile.GetURIString(file);
nsCOMPtr<nsIScriptError> error =
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
if (!error) {
// This can happen early in component registration. Fall back to a
// generic console message.
LogMessage("Warning: in '%s', line %i: %s", file.get(),
aLineNumber, (char*)formatted);
return;
}
nsCOMPtr<nsIConsoleService> console =
do_GetService(NS_CONSOLESERVICE_CONTRACTID);
if (!console) {
return;
}
nsresult rv = error->Init(NS_ConvertUTF8toUTF16(formatted),
NS_ConvertUTF8toUTF16(file), EmptyString(),
aLineNumber, 0, nsIScriptError::warningFlag,
"chrome registration");
if (NS_FAILED(rv)) {
return;
}
console->LogMessage(error);
}
/**
* Check for a modifier flag of the following forms:
* "flag" (same as "true")
* "flag=yes|true|1"
* "flag="no|false|0"
* @param aFlag The flag to compare.
* @param aData The tokenized data to check; this is lowercased
* before being passed in.
* @param aResult If the flag is found, the value is assigned here.
* @return Whether the flag was handled.
*/
static bool
CheckFlag(const nsSubstring& aFlag, const nsSubstring& aData, bool& aResult)
{
if (!StringBeginsWith(aData, aFlag)) {
return false;
}
if (aFlag.Length() == aData.Length()) {
// the data is simply "flag", which is the same as "flag=yes"
aResult = true;
return true;
}
if (aData.CharAt(aFlag.Length()) != '=') {
// the data is "flag2=", which is not anything we care about
return false;
}
if (aData.Length() == aFlag.Length() + 1) {
aResult = false;
return true;
}
switch (aData.CharAt(aFlag.Length() + 1)) {
case '1':
case 't': //true
case 'y': //yes
aResult = true;
return true;
case '0':
case 'f': //false
case 'n': //no
aResult = false;
return true;
}
return false;
}
enum TriState
{
eUnspecified,
eBad,
eOK
};
/**
* Check for a modifier flag of the following form:
* "flag=string"
* "flag!=string"
* @param aFlag The flag to compare.
* @param aData The tokenized data to check; this is lowercased
* before being passed in.
* @param aValue The value that is expected.
* @param aResult If this is "ok" when passed in, this is left alone.
* Otherwise if the flag is found it is set to eBad or eOK.
* @return Whether the flag was handled.
*/
static bool
CheckStringFlag(const nsSubstring& aFlag, const nsSubstring& aData,
const nsSubstring& aValue, TriState& aResult)
{
if (aData.Length() < aFlag.Length() + 1) {
return false;
}
if (!StringBeginsWith(aData, aFlag)) {
return false;
}
bool comparison = true;
if (aData[aFlag.Length()] != '=') {
if (aData[aFlag.Length()] == '!' &&
aData.Length() >= aFlag.Length() + 2 &&
aData[aFlag.Length() + 1] == '=') {
comparison = false;
} else {
return false;
}
}
if (aResult != eOK) {
nsDependentSubstring testdata =
Substring(aData, aFlag.Length() + (comparison ? 1 : 2));
if (testdata.Equals(aValue)) {
aResult = comparison ? eOK : eBad;
} else {
aResult = comparison ? eBad : eOK;
}
}
return true;
}
/**
* Check for a modifier flag of the following form:
* "flag=version"
* "flag<=version"
* "flag<version"
* "flag>=version"
* "flag>version"
* @param aFlag The flag to compare.
* @param aData The tokenized data to check; this is lowercased
* before being passed in.
* @param aValue The value that is expected. If this is empty then no
* comparison will match.
* @param aResult If this is eOK when passed in, this is left alone.
* Otherwise if the flag is found it is set to eBad or eOK.
* @return Whether the flag was handled.
*/
#define COMPARE_EQ 1 << 0
#define COMPARE_LT 1 << 1
#define COMPARE_GT 1 << 2
static bool
CheckVersionFlag(const nsString& aFlag, const nsString& aData,
const nsString& aValue, TriState& aResult)
{
if (aData.Length() < aFlag.Length() + 2) {
return false;
}
if (!StringBeginsWith(aData, aFlag)) {
return false;
}
if (aValue.Length() == 0) {
if (aResult != eOK) {
aResult = eBad;
}
return true;
}
uint32_t comparison;
nsAutoString testdata;
switch (aData[aFlag.Length()]) {
case '=':
comparison = COMPARE_EQ;
testdata = Substring(aData, aFlag.Length() + 1);
break;
case '<':
if (aData[aFlag.Length() + 1] == '=') {
comparison = COMPARE_EQ | COMPARE_LT;
testdata = Substring(aData, aFlag.Length() + 2);
} else {
comparison = COMPARE_LT;
testdata = Substring(aData, aFlag.Length() + 1);
}
break;
case '>':
if (aData[aFlag.Length() + 1] == '=') {
comparison = COMPARE_EQ | COMPARE_GT;
testdata = Substring(aData, aFlag.Length() + 2);
} else {
comparison = COMPARE_GT;
testdata = Substring(aData, aFlag.Length() + 1);
}
break;
default:
return false;
}
if (testdata.Length() == 0) {
return false;
}
if (aResult != eOK) {
int32_t c = mozilla::CompareVersions(NS_ConvertUTF16toUTF8(aValue).get(),
NS_ConvertUTF16toUTF8(testdata).get());
if ((c == 0 && comparison & COMPARE_EQ) ||
(c < 0 && comparison & COMPARE_LT) ||
(c > 0 && comparison & COMPARE_GT)) {
aResult = eOK;
} else {
aResult = eBad;
}
}
return true;
}
// In-place conversion of ascii characters to lower case
static void
ToLowerCase(char* aToken)
{
for (; *aToken; ++aToken) {
*aToken = NS_ToLower(*aToken);
}
}
namespace {
struct CachedDirective
{
int lineno;
char* argv[4];
};
} // namespace
/**
* For XPT-Only mode, the parser handles only directives of "manifest"
* and "interfaces", and always call the function given by |xptonlyfunc|
* variable of struct |ManifestDirective|.
*
* This function is safe to be called before the component manager is
* ready if aXPTOnly is true for it don't invoke any component during
* parsing.
*/
void
ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
bool aChromeOnly, bool aXPTOnly)
{
nsComponentManagerImpl::ManifestProcessingContext mgrcx(aType, aFile,
aChromeOnly);
nsChromeRegistry::ManifestProcessingContext chromecx(aType, aFile);
nsresult rv;
NS_NAMED_LITERAL_STRING(kPlatform, "platform");
NS_NAMED_LITERAL_STRING(kContentAccessible, "contentaccessible");
NS_NAMED_LITERAL_STRING(kRemoteEnabled, "remoteenabled");
NS_NAMED_LITERAL_STRING(kRemoteRequired, "remoterequired");
NS_NAMED_LITERAL_STRING(kApplication, "application");
NS_NAMED_LITERAL_STRING(kAppVersion, "appversion");
NS_NAMED_LITERAL_STRING(kGeckoVersion, "platformversion");
NS_NAMED_LITERAL_STRING(kOs, "os");
NS_NAMED_LITERAL_STRING(kOsVersion, "osversion");
NS_NAMED_LITERAL_STRING(kABI, "abi");
NS_NAMED_LITERAL_STRING(kProcess, "process");
#if defined(MOZ_WIDGET_ANDROID)
NS_NAMED_LITERAL_STRING(kTablet, "tablet");
#endif
NS_NAMED_LITERAL_STRING(kMain, "main");
NS_NAMED_LITERAL_STRING(kContent, "content");
// Obsolete
NS_NAMED_LITERAL_STRING(kXPCNativeWrappers, "xpcnativewrappers");
nsAutoString appID;
nsAutoString appVersion;
nsAutoString geckoVersion;
nsAutoString osTarget;
nsAutoString abi;
nsAutoString process;
nsCOMPtr<nsIXULAppInfo> xapp;
if (!aXPTOnly) {
// Avoid to create any component for XPT only mode.
// No xapp means no ID, version, ..., modifiers checking.
xapp = do_GetService(XULAPPINFO_SERVICE_CONTRACTID);
}
if (xapp) {
nsAutoCString s;
rv = xapp->GetID(s);
if (NS_SUCCEEDED(rv)) {
CopyUTF8toUTF16(s, appID);
}
rv = xapp->GetVersion(s);
if (NS_SUCCEEDED(rv)) {
CopyUTF8toUTF16(s, appVersion);
}
rv = xapp->GetPlatformVersion(s);
if (NS_SUCCEEDED(rv)) {
CopyUTF8toUTF16(s, geckoVersion);
}
nsCOMPtr<nsIXULRuntime> xruntime(do_QueryInterface(xapp));
if (xruntime) {
rv = xruntime->GetOS(s);
if (NS_SUCCEEDED(rv)) {
ToLowerCase(s);
CopyUTF8toUTF16(s, osTarget);
}
rv = xruntime->GetXPCOMABI(s);
if (NS_SUCCEEDED(rv) && osTarget.Length()) {
ToLowerCase(s);
CopyUTF8toUTF16(s, abi);
abi.Insert(char16_t('_'), 0);
abi.Insert(osTarget, 0);
}
}
}
nsAutoString osVersion;
#if defined(XP_WIN)
#pragma warning(push)
#pragma warning(disable:4996) // VC12+ deprecates GetVersionEx
OSVERSIONINFO info = { sizeof(OSVERSIONINFO) };
if (GetVersionEx(&info)) {
nsTextFormatter::ssprintf(osVersion, u"%ld.%ld",
info.dwMajorVersion,
info.dwMinorVersion);
}
#pragma warning(pop)
#elif defined(MOZ_WIDGET_COCOA)
SInt32 majorVersion = nsCocoaFeatures::OSXVersionMajor();
SInt32 minorVersion = nsCocoaFeatures::OSXVersionMinor();
nsTextFormatter::ssprintf(osVersion, u"%ld.%ld",
majorVersion,
minorVersion);
#elif defined(MOZ_WIDGET_GTK)
nsTextFormatter::ssprintf(osVersion, u"%ld.%ld",
gtk_major_version,
gtk_minor_version);
#elif defined(MOZ_WIDGET_ANDROID)
bool isTablet = false;
if (mozilla::AndroidBridge::Bridge()) {
mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build$VERSION",
"RELEASE",
osVersion);
isTablet = java::GeckoAppShell::IsTablet();
}
#endif
if (XRE_IsContentProcess()) {
process = kContent;
} else {
process = kMain;
}
// Because contracts must be registered after CIDs, we save and process them
// at the end.
nsTArray<CachedDirective> contracts;
char* token;
char* newline = aBuf;
uint32_t line = 0;
// outer loop tokenizes by newline
while (*newline) {
while (*newline && IsNewline(*newline)) {
++newline;
++line;
}
if (!*newline) {
break;
}
token = newline;
while (*newline && !IsNewline(*newline)) {
++newline;
}
if (*newline) {
*newline = '\0';
++newline;
}
++line;
if (*token == '#') { // ignore lines that begin with # as comments
continue;
}
char* whitespace = token;
token = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
if (!token) {
continue;
}
const ManifestDirective* directive = nullptr;
for (const ManifestDirective* d = kParsingTable;
d < ArrayEnd(kParsingTable);
++d) {
if (!strcmp(d->directive, token) &&
(!aXPTOnly || d->xptonlyfunc)) {
directive = d;
break;
}
}
if (!directive) {
LogMessageWithContext(aFile, line,
"Ignoring unrecognized chrome manifest directive '%s'.",
token);
continue;
}
if (!directive->allowbootstrap && NS_BOOTSTRAPPED_LOCATION == aType) {
LogMessageWithContext(aFile, line,
"Bootstrapped manifest not allowed to use '%s' directive.",
token);
continue;
}
#ifndef MOZ_BINARY_EXTENSIONS
if (directive->apponly && NS_APP_LOCATION != aType) {
LogMessageWithContext(aFile, line,
"Only application manifests may use the '%s' directive.", token);
continue;
}
#endif
if (directive->componentonly && NS_SKIN_LOCATION == aType) {
LogMessageWithContext(aFile, line,
"Skin manifest not allowed to use '%s' directive.",
token);
continue;
}
NS_ASSERTION(directive->argc < 4, "Need to reset argv array length");
char* argv[4];
for (int i = 0; i < directive->argc; ++i) {
argv[i] = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
}
if (!argv[directive->argc - 1]) {
LogMessageWithContext(aFile, line,
"Not enough arguments for chrome manifest directive '%s', expected %i.",
token, directive->argc);
continue;
}
bool ok = true;
TriState stAppVersion = eUnspecified;
TriState stGeckoVersion = eUnspecified;
TriState stApp = eUnspecified;
TriState stOsVersion = eUnspecified;
TriState stOs = eUnspecified;
TriState stABI = eUnspecified;
TriState stProcess = eUnspecified;
#if defined(MOZ_WIDGET_ANDROID)
TriState stTablet = eUnspecified;
#endif
int flags = 0;
while ((token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) &&
ok) {
ToLowerCase(token);
NS_ConvertASCIItoUTF16 wtoken(token);
if (CheckStringFlag(kApplication, wtoken, appID, stApp) ||
CheckStringFlag(kOs, wtoken, osTarget, stOs) ||
CheckStringFlag(kABI, wtoken, abi, stABI) ||
CheckStringFlag(kProcess, wtoken, process, stProcess) ||
CheckVersionFlag(kOsVersion, wtoken, osVersion, stOsVersion) ||
CheckVersionFlag(kAppVersion, wtoken, appVersion, stAppVersion) ||
CheckVersionFlag(kGeckoVersion, wtoken, geckoVersion, stGeckoVersion)) {
continue;
}
#if defined(MOZ_WIDGET_ANDROID)
bool tablet = false;
if (CheckFlag(kTablet, wtoken, tablet)) {
stTablet = (tablet == isTablet) ? eOK : eBad;
continue;
}
#endif
if (directive->contentflags) {
bool flag;
if (CheckFlag(kPlatform, wtoken, flag)) {
if (flag)
flags |= nsChromeRegistry::PLATFORM_PACKAGE;
continue;
}
if (CheckFlag(kContentAccessible, wtoken, flag)) {
if (flag)
flags |= nsChromeRegistry::CONTENT_ACCESSIBLE;
continue;
}
if (CheckFlag(kRemoteEnabled, wtoken, flag)) {
if (flag)
flags |= nsChromeRegistry::REMOTE_ALLOWED;
continue;
}
if (CheckFlag(kRemoteRequired, wtoken, flag)) {
if (flag)
flags |= nsChromeRegistry::REMOTE_REQUIRED;
continue;
}
}
bool xpcNativeWrappers = true; // Dummy for CheckFlag.
if (CheckFlag(kXPCNativeWrappers, wtoken, xpcNativeWrappers)) {
LogMessageWithContext(aFile, line,
"Ignoring obsolete chrome registration modifier '%s'.",
token);
continue;
}
LogMessageWithContext(aFile, line,
"Unrecognized chrome manifest modifier '%s'.",
token);
ok = false;
}
if (!ok ||
stApp == eBad ||
stAppVersion == eBad ||
stGeckoVersion == eBad ||
stOs == eBad ||
stOsVersion == eBad ||
#ifdef MOZ_WIDGET_ANDROID
stTablet == eBad ||
#endif
stABI == eBad ||
stProcess == eBad) {
continue;
}
if (directive->regfunc) {
if (GeckoProcessType_Default != XRE_GetProcessType()) {
continue;
}
if (!nsChromeRegistry::gChromeRegistry) {
nsCOMPtr<nsIChromeRegistry> cr =
mozilla::services::GetChromeRegistryService();
if (!nsChromeRegistry::gChromeRegistry) {
LogMessageWithContext(aFile, line,
"Chrome registry isn't available yet.");
continue;
}
}
(nsChromeRegistry::gChromeRegistry->*(directive->regfunc))(
chromecx, line, argv, flags);
} else if (directive->ischrome || !aChromeOnly) {
if (directive->isContract) {
CachedDirective* cd = contracts.AppendElement();
cd->lineno = line;
cd->argv[0] = argv[0];
cd->argv[1] = argv[1];
} else {
(nsComponentManagerImpl::gComponentManager->*(directive->mgrfunc))(
mgrcx, line, argv);
}
}
}
for (uint32_t i = 0; i < contracts.Length(); ++i) {
CachedDirective& d = contracts[i];
nsComponentManagerImpl::gComponentManager->ManifestContract(mgrcx,
d.lineno,
d.argv);
}
}

View file

@ -0,0 +1,22 @@
/* -*- 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/. */
#ifndef ManifestParser_h
#define ManifestParser_h
#include "nsComponentManager.h"
#include "nsChromeRegistry.h"
#include "mozilla/FileLocation.h"
void ParseManifest(NSLocationType aType, mozilla::FileLocation& aFile,
char* aBuf, bool aChromeOnly, bool aXPTOnly = false);
void LogMessage(const char* aMsg, ...);
void LogMessageWithContext(mozilla::FileLocation& aFile,
uint32_t aLineNumber, const char* aMsg, ...);
#endif // ManifestParser_h

157
xpcom/components/Module.h Normal file
View file

@ -0,0 +1,157 @@
/* -*- 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/. */
#ifndef mozilla_Module_h
#define mozilla_Module_h
#include "nscore.h"
#include "nsID.h"
#include "nsIFactory.h"
#include "nsCOMPtr.h" // for already_AddRefed
namespace mozilla {
/**
* A module implements one or more XPCOM components. This structure is used
* for both binary and script modules, but the registration members
* (cids/contractids/categoryentries) are unused for modules which are loaded
* via a module loader.
*/
struct Module
{
static const unsigned int kVersion = 52;
struct CIDEntry;
typedef already_AddRefed<nsIFactory> (*GetFactoryProcPtr)(
const Module& module, const CIDEntry& entry);
typedef nsresult (*ConstructorProcPtr)(nsISupports* aOuter,
const nsIID& aIID,
void** aResult);
typedef nsresult (*LoadFuncPtr)();
typedef void (*UnloadFuncPtr)();
/**
* This selector allows CIDEntrys to be marked so that they're only loaded
* into certain kinds of processes. Selectors can be combined.
*/
enum ProcessSelector
{
ANY_PROCESS = 0x0,
MAIN_PROCESS_ONLY = 0x1,
CONTENT_PROCESS_ONLY = 0x2,
/**
* By default, modules are not loaded in the GPU process, even if
* ANY_PROCESS is specified. This flag enables a module in the
* GPU process.
*/
ALLOW_IN_GPU_PROCESS = 0x4
};
/**
* The constructor callback is an implementation detail of the default binary
* loader and may be null.
*/
struct CIDEntry
{
const nsCID* cid;
bool service;
GetFactoryProcPtr getFactoryProc;
ConstructorProcPtr constructorProc;
ProcessSelector processSelector;
};
struct ContractIDEntry
{
const char* contractid;
nsID const* cid;
ProcessSelector processSelector;
};
struct CategoryEntry
{
const char* category;
const char* entry;
const char* value;
};
/**
* Binary compatibility check, should be kModuleVersion.
*/
unsigned int mVersion;
/**
* An array of CIDs (class IDs) implemented by this module. The final entry
* should be { nullptr }.
*/
const CIDEntry* mCIDs;
/**
* An array of mappings from contractid to CID. The final entry should
* be { nullptr }.
*/
const ContractIDEntry* mContractIDs;
/**
* An array of category manager entries. The final entry should be
* { nullptr }.
*/
const CategoryEntry* mCategoryEntries;
/**
* When the component manager tries to get the factory for a CID, it first
* checks for this module-level getfactory callback. If this function is
* not implemented, it checks the CIDEntry getfactory callback. If that is
* also nullptr, a generic factory is generated using the CIDEntry
* constructor callback which must be non-nullptr.
*/
GetFactoryProcPtr getFactoryProc;
/**
* Optional Function which are called when this module is loaded and
* at shutdown. These are not C++ constructor/destructors to avoid
* calling them too early in startup or too late in shutdown.
*/
LoadFuncPtr loadProc;
UnloadFuncPtr unloadProc;
/**
* Optional flags which control whether the module loads on a process-type
* basis.
*/
ProcessSelector selector;
};
} // namespace mozilla
#if defined(MOZILLA_INTERNAL_API)
# define NSMODULE_NAME(_name) _name##_NSModule
# if defined(_MSC_VER)
# pragma section(".kPStaticModules$M", read)
# pragma comment(linker, "/merge:.kPStaticModules=.rdata")
# define NSMODULE_SECTION __declspec(allocate(".kPStaticModules$M"), dllexport)
# elif defined(__GNUC__)
# if defined(__ELF__)
# define NSMODULE_SECTION __attribute__((section(".kPStaticModules"), visibility("default")))
# elif defined(__MACH__)
# define NSMODULE_SECTION __attribute__((section("__DATA, .kPStaticModules"), visibility("default")))
# elif defined (_WIN32)
# define NSMODULE_SECTION __attribute__((section(".kPStaticModules"), dllexport))
# endif
# endif
# if !defined(NSMODULE_SECTION)
# error Do not know how to define sections.
# endif
# define NSMODULE_DEFN(_name) extern NSMODULE_SECTION mozilla::Module const *const NSMODULE_NAME(_name)
#else
# define NSMODULE_NAME(_name) NSModule
# define NSMODULE_DEFN(_name) extern "C" NS_EXPORT mozilla::Module const *const NSModule
#endif
#endif // mozilla_Module_h

View file

@ -0,0 +1,44 @@
/* -*- 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/. */
#ifndef mozilla_ModuleLoader_h
#define mozilla_ModuleLoader_h
#include "nsISupports.h"
#include "mozilla/Module.h"
#include "mozilla/FileLocation.h"
#define MOZILLA_MODULELOADER_PSEUDO_IID \
{ 0xD951A8CE, 0x6E9F, 0x464F, \
{ 0x8A, 0xC8, 0x14, 0x61, 0xC0, 0xD3, 0x63, 0xC8 } }
namespace mozilla {
/**
* Module loaders are responsible for loading a component file. The static
* component loader is special and does not use this abstract interface.
*
* @note Implementations of this interface should be threadsafe,
* methods may be called from any thread.
*/
class ModuleLoader : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_MODULELOADER_PSEUDO_IID)
/**
* Return the module for a specified file. The caller should cache
* the module: the implementer should not expect for the same file
* to be loaded multiple times. The Module object should either be
* statically or permanently allocated; it will not be freed.
*/
virtual const Module* LoadModule(mozilla::FileLocation& aFile) = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(ModuleLoader, MOZILLA_MODULELOADER_PSEUDO_IID)
} // namespace mozilla
#endif // mozilla_ModuleLoader_h

View file

@ -0,0 +1,98 @@
/* -*- 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/. */
#ifndef mozilla_GenericModule_h
#define mozilla_GenericModule_h
#include "mozilla/Attributes.h"
#include "mozilla/Module.h"
#define NS_GENERIC_FACTORY_CONSTRUCTOR(_InstanceClass) \
static nsresult \
_InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \
void **aResult) \
{ \
RefPtr<_InstanceClass> inst; \
\
*aResult = nullptr; \
if (nullptr != aOuter) { \
return NS_ERROR_NO_AGGREGATION; \
} \
\
inst = new _InstanceClass(); \
return inst->QueryInterface(aIID, aResult); \
}
#define NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(_InstanceClass, _InitMethod) \
static nsresult \
_InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \
void **aResult) \
{ \
nsresult rv; \
\
RefPtr<_InstanceClass> inst; \
\
*aResult = nullptr; \
if (nullptr != aOuter) { \
return NS_ERROR_NO_AGGREGATION; \
} \
\
inst = new _InstanceClass(); \
rv = inst->_InitMethod(); \
if (NS_SUCCEEDED(rv)) { \
rv = inst->QueryInterface(aIID, aResult); \
} \
\
return rv; \
}
// 'Constructor' that uses an existing getter function that gets a singleton.
// NOTE: assumes that getter does an AddRef - so additional AddRef is not done.
#define NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(_InstanceClass, _GetterProc) \
static nsresult \
_InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \
void **aResult) \
{ \
RefPtr<_InstanceClass> inst; \
\
*aResult = nullptr; \
if (nullptr != aOuter) { \
return NS_ERROR_NO_AGGREGATION; \
} \
\
inst = already_AddRefed<_InstanceClass>(_GetterProc()); \
if (nullptr == inst) { \
return NS_ERROR_OUT_OF_MEMORY; \
} \
return inst->QueryInterface(aIID, aResult); \
}
#ifndef MOZILLA_INTERNAL_API
#include "nsIModule.h"
#include "nsISupportsUtils.h"
namespace mozilla {
class GenericModule final : public nsIModule
{
~GenericModule() {}
public:
explicit GenericModule(const mozilla::Module* aData) : mData(aData) {}
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIMODULE
private:
const mozilla::Module* mData;
};
} // namespace mozilla
#endif
#endif // mozilla_GenericModule_h

View file

@ -0,0 +1,55 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
XPIDL_SOURCES += [
'nsICategoryManager.idl',
'nsIClassInfo.idl',
'nsIComponentManager.idl',
'nsIComponentRegistrar.idl',
'nsIFactory.idl',
'nsIModule.idl',
'nsIServiceManager.idl',
]
XPIDL_MODULE = 'xpcom_components'
EXPORTS += [
'nsCategoryManagerUtils.h',
]
EXPORTS.mozilla += [
'Module.h',
'ModuleLoader.h',
'ModuleUtils.h',
]
# nsCategoryManager.cpp and nsComponentManager.cpp cannot be built in
# unified mode because they use thea PL_ARENA_CONST_ALIGN_MASK macro
# with plarena.h.
SOURCES += [
'nsCategoryManager.cpp',
'nsComponentManager.cpp',
]
UNIFIED_SOURCES += [
'ManifestParser.cpp',
'nsNativeModuleLoader.cpp',
]
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'!..',
'../base',
'../build',
'../ds',
'../reflect/xptinfo',
'/chrome',
'/modules/libjar',
]
if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
CXXFLAGS += CONFIG['TK_CFLAGS']

View file

@ -0,0 +1,832 @@
/* -*- 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/. */
#define PL_ARENA_CONST_ALIGN_MASK 7
#include "nsICategoryManager.h"
#include "nsCategoryManager.h"
#include "plarena.h"
#include "prio.h"
#include "prprf.h"
#include "prlock.h"
#include "nsCOMPtr.h"
#include "nsTHashtable.h"
#include "nsClassHashtable.h"
#include "nsIFactory.h"
#include "nsIStringEnumerator.h"
#include "nsSupportsPrimitives.h"
#include "nsComponentManagerUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsReadableUtils.h"
#include "nsCRT.h"
#include "nsQuickSort.h"
#include "nsEnumeratorUtils.h"
#include "nsThreadUtils.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Services.h"
#include "ManifestParser.h"
#include "nsISimpleEnumerator.h"
using namespace mozilla;
class nsIComponentLoaderManager;
/*
CategoryDatabase
contains 0 or more 1-1 mappings of string to Category
each Category contains 0 or more 1-1 mappings of string keys to string values
In other words, the CategoryDatabase is a tree, whose root is a hashtable.
Internal nodes (or Categories) are hashtables. Leaf nodes are strings.
The leaf strings are allocated in an arena, because we assume they're not
going to change much ;)
*/
#define NS_CATEGORYMANAGER_ARENA_SIZE (1024 * 8)
// pulled in from nsComponentManager.cpp
char* ArenaStrdup(const char* aStr, PLArenaPool* aArena);
//
// BaseStringEnumerator is subclassed by EntryEnumerator and
// CategoryEnumerator
//
class BaseStringEnumerator
: public nsISimpleEnumerator
, private nsIUTF8StringEnumerator
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISIMPLEENUMERATOR
NS_DECL_NSIUTF8STRINGENUMERATOR
protected:
// Callback function for NS_QuickSort to sort mArray
static int SortCallback(const void*, const void*, void*);
BaseStringEnumerator()
: mArray(nullptr)
, mCount(0)
, mSimpleCurItem(0)
, mStringCurItem(0)
{
}
// A virtual destructor is needed here because subclasses of
// BaseStringEnumerator do not implement their own Release() method.
virtual ~BaseStringEnumerator()
{
delete [] mArray;
}
void Sort();
const char** mArray;
uint32_t mCount;
uint32_t mSimpleCurItem;
uint32_t mStringCurItem;
};
NS_IMPL_ISUPPORTS(BaseStringEnumerator, nsISimpleEnumerator,
nsIUTF8StringEnumerator)
NS_IMETHODIMP
BaseStringEnumerator::HasMoreElements(bool* aResult)
{
*aResult = (mSimpleCurItem < mCount);
return NS_OK;
}
NS_IMETHODIMP
BaseStringEnumerator::GetNext(nsISupports** aResult)
{
if (mSimpleCurItem >= mCount) {
return NS_ERROR_FAILURE;
}
nsSupportsDependentCString* str =
new nsSupportsDependentCString(mArray[mSimpleCurItem++]);
if (!str) {
return NS_ERROR_OUT_OF_MEMORY;
}
*aResult = str;
NS_ADDREF(*aResult);
return NS_OK;
}
NS_IMETHODIMP
BaseStringEnumerator::HasMore(bool* aResult)
{
*aResult = (mStringCurItem < mCount);
return NS_OK;
}
NS_IMETHODIMP
BaseStringEnumerator::GetNext(nsACString& aResult)
{
if (mStringCurItem >= mCount) {
return NS_ERROR_FAILURE;
}
aResult = nsDependentCString(mArray[mStringCurItem++]);
return NS_OK;
}
int
BaseStringEnumerator::SortCallback(const void* aE1, const void* aE2,
void* /*unused*/)
{
char const* const* s1 = reinterpret_cast<char const* const*>(aE1);
char const* const* s2 = reinterpret_cast<char const* const*>(aE2);
return strcmp(*s1, *s2);
}
void
BaseStringEnumerator::Sort()
{
NS_QuickSort(mArray, mCount, sizeof(mArray[0]), SortCallback, nullptr);
}
//
// EntryEnumerator is the wrapper that allows nsICategoryManager::EnumerateCategory
//
class EntryEnumerator
: public BaseStringEnumerator
{
public:
static EntryEnumerator* Create(nsTHashtable<CategoryLeaf>& aTable);
};
EntryEnumerator*
EntryEnumerator::Create(nsTHashtable<CategoryLeaf>& aTable)
{
EntryEnumerator* enumObj = new EntryEnumerator();
if (!enumObj) {
return nullptr;
}
enumObj->mArray = new char const* [aTable.Count()];
if (!enumObj->mArray) {
delete enumObj;
return nullptr;
}
for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) {
CategoryLeaf* leaf = iter.Get();
if (leaf->value) {
enumObj->mArray[enumObj->mCount++] = leaf->GetKey();
}
}
enumObj->Sort();
return enumObj;
}
//
// CategoryNode implementations
//
CategoryNode*
CategoryNode::Create(PLArenaPool* aArena)
{
return new (aArena) CategoryNode();
}
CategoryNode::~CategoryNode()
{
}
void*
CategoryNode::operator new(size_t aSize, PLArenaPool* aArena)
{
void* p;
PL_ARENA_ALLOCATE(p, aArena, aSize);
return p;
}
nsresult
CategoryNode::GetLeaf(const char* aEntryName,
char** aResult)
{
MutexAutoLock lock(mLock);
nsresult rv = NS_ERROR_NOT_AVAILABLE;
CategoryLeaf* ent = mTable.GetEntry(aEntryName);
if (ent && ent->value) {
*aResult = NS_strdup(ent->value);
if (*aResult) {
rv = NS_OK;
}
}
return rv;
}
nsresult
CategoryNode::AddLeaf(const char* aEntryName,
const char* aValue,
bool aReplace,
char** aResult,
PLArenaPool* aArena)
{
if (aResult) {
*aResult = nullptr;
}
MutexAutoLock lock(mLock);
CategoryLeaf* leaf = mTable.GetEntry(aEntryName);
if (!leaf) {
const char* arenaEntryName = ArenaStrdup(aEntryName, aArena);
if (!arenaEntryName) {
return NS_ERROR_OUT_OF_MEMORY;
}
leaf = mTable.PutEntry(arenaEntryName);
if (!leaf) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
if (leaf->value && !aReplace) {
return NS_ERROR_INVALID_ARG;
}
const char* arenaValue = ArenaStrdup(aValue, aArena);
if (!arenaValue) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (aResult && leaf->value) {
*aResult = ToNewCString(nsDependentCString(leaf->value));
if (!*aResult) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
leaf->value = arenaValue;
return NS_OK;
}
void
CategoryNode::DeleteLeaf(const char* aEntryName)
{
// we don't throw any errors, because it normally doesn't matter
// and it makes JS a lot cleaner
MutexAutoLock lock(mLock);
// we can just remove the entire hash entry without introspection
mTable.RemoveEntry(aEntryName);
}
nsresult
CategoryNode::Enumerate(nsISimpleEnumerator** aResult)
{
if (NS_WARN_IF(!aResult)) {
return NS_ERROR_INVALID_ARG;
}
MutexAutoLock lock(mLock);
EntryEnumerator* enumObj = EntryEnumerator::Create(mTable);
if (!enumObj) {
return NS_ERROR_OUT_OF_MEMORY;
}
*aResult = enumObj;
NS_ADDREF(*aResult);
return NS_OK;
}
size_t
CategoryNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
{
// We don't measure the strings pointed to by the entries because the
// pointers are non-owning.
return mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
}
//
// CategoryEnumerator class
//
class CategoryEnumerator
: public BaseStringEnumerator
{
public:
static CategoryEnumerator* Create(nsClassHashtable<nsDepCharHashKey,
CategoryNode>& aTable);
};
CategoryEnumerator*
CategoryEnumerator::Create(nsClassHashtable<nsDepCharHashKey, CategoryNode>&
aTable)
{
CategoryEnumerator* enumObj = new CategoryEnumerator();
if (!enumObj) {
return nullptr;
}
enumObj->mArray = new const char* [aTable.Count()];
if (!enumObj->mArray) {
delete enumObj;
return nullptr;
}
for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) {
// if a category has no entries, we pretend it doesn't exist
CategoryNode* aNode = iter.UserData();
if (aNode->Count()) {
const char* str = iter.Key();
enumObj->mArray[enumObj->mCount++] = str;
}
}
return enumObj;
}
//
// nsCategoryManager implementations
//
NS_IMPL_QUERY_INTERFACE(nsCategoryManager, nsICategoryManager, nsIMemoryReporter)
NS_IMETHODIMP_(MozExternalRefCountType)
nsCategoryManager::AddRef()
{
return 2;
}
NS_IMETHODIMP_(MozExternalRefCountType)
nsCategoryManager::Release()
{
return 1;
}
nsCategoryManager* nsCategoryManager::gCategoryManager;
/* static */ nsCategoryManager*
nsCategoryManager::GetSingleton()
{
if (!gCategoryManager) {
gCategoryManager = new nsCategoryManager();
}
return gCategoryManager;
}
/* static */ void
nsCategoryManager::Destroy()
{
// The nsMemoryReporterManager gets destroyed before the nsCategoryManager,
// so we don't need to unregister the nsCategoryManager as a memory reporter.
// In debug builds we assert that unregistering fails, as a way (imperfect
// but better than nothing) of testing the "destroyed before" part.
MOZ_ASSERT(NS_FAILED(UnregisterWeakMemoryReporter(gCategoryManager)));
delete gCategoryManager;
gCategoryManager = nullptr;
}
nsresult
nsCategoryManager::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult)
{
if (aOuter) {
return NS_ERROR_NO_AGGREGATION;
}
return GetSingleton()->QueryInterface(aIID, aResult);
}
nsCategoryManager::nsCategoryManager()
: mLock("nsCategoryManager")
, mSuppressNotifications(false)
{
PL_INIT_ARENA_POOL(&mArena, "CategoryManagerArena",
NS_CATEGORYMANAGER_ARENA_SIZE);
}
void
nsCategoryManager::InitMemoryReporter()
{
RegisterWeakMemoryReporter(this);
}
nsCategoryManager::~nsCategoryManager()
{
// the hashtable contains entries that must be deleted before the arena is
// destroyed, or else you will have PRLocks undestroyed and other Really
// Bad Stuff (TM)
mTable.Clear();
PL_FinishArenaPool(&mArena);
}
inline CategoryNode*
nsCategoryManager::get_category(const char* aName)
{
CategoryNode* node;
if (!mTable.Get(aName, &node)) {
return nullptr;
}
return node;
}
MOZ_DEFINE_MALLOC_SIZE_OF(CategoryManagerMallocSizeOf)
NS_IMETHODIMP
nsCategoryManager::CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize)
{
MOZ_COLLECT_REPORT(
"explicit/xpcom/category-manager", KIND_HEAP, UNITS_BYTES,
SizeOfIncludingThis(CategoryManagerMallocSizeOf),
"Memory used for the XPCOM category manager.");
return NS_OK;
}
size_t
nsCategoryManager::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
{
size_t n = aMallocSizeOf(this);
n += PL_SizeOfArenaPoolExcludingPool(&mArena, aMallocSizeOf);
n += mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
for (auto iter = mTable.ConstIter(); !iter.Done(); iter.Next()) {
// We don't measure the key string because it's a non-owning pointer.
n += iter.Data()->SizeOfExcludingThis(aMallocSizeOf);
}
return n;
}
namespace {
class CategoryNotificationRunnable : public Runnable
{
public:
CategoryNotificationRunnable(nsISupports* aSubject,
const char* aTopic,
const char* aData)
: mSubject(aSubject)
, mTopic(aTopic)
, mData(aData)
{
}
NS_DECL_NSIRUNNABLE
private:
nsCOMPtr<nsISupports> mSubject;
const char* mTopic;
NS_ConvertUTF8toUTF16 mData;
};
NS_IMETHODIMP
CategoryNotificationRunnable::Run()
{
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
observerService->NotifyObservers(mSubject, mTopic, mData.get());
}
return NS_OK;
}
} // namespace
void
nsCategoryManager::NotifyObservers(const char* aTopic,
const char* aCategoryName,
const char* aEntryName)
{
if (mSuppressNotifications) {
return;
}
RefPtr<CategoryNotificationRunnable> r;
if (aEntryName) {
nsCOMPtr<nsISupportsCString> entry =
do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
if (!entry) {
return;
}
nsresult rv = entry->SetData(nsDependentCString(aEntryName));
if (NS_FAILED(rv)) {
return;
}
r = new CategoryNotificationRunnable(entry, aTopic, aCategoryName);
} else {
r = new CategoryNotificationRunnable(NS_ISUPPORTS_CAST(nsICategoryManager*,
this),
aTopic, aCategoryName);
}
NS_DispatchToMainThread(r);
}
NS_IMETHODIMP
nsCategoryManager::GetCategoryEntry(const char* aCategoryName,
const char* aEntryName,
char** aResult)
{
if (NS_WARN_IF(!aCategoryName) ||
NS_WARN_IF(!aEntryName) ||
NS_WARN_IF(!aResult)) {
return NS_ERROR_INVALID_ARG;
}
nsresult status = NS_ERROR_NOT_AVAILABLE;
CategoryNode* category;
{
MutexAutoLock lock(mLock);
category = get_category(aCategoryName);
}
if (category) {
status = category->GetLeaf(aEntryName, aResult);
}
return status;
}
NS_IMETHODIMP
nsCategoryManager::AddCategoryEntry(const char* aCategoryName,
const char* aEntryName,
const char* aValue,
bool aPersist,
bool aReplace,
char** aResult)
{
if (aPersist) {
NS_ERROR("Category manager doesn't support persistence.");
return NS_ERROR_INVALID_ARG;
}
AddCategoryEntry(aCategoryName, aEntryName, aValue, aReplace, aResult);
return NS_OK;
}
void
nsCategoryManager::AddCategoryEntry(const char* aCategoryName,
const char* aEntryName,
const char* aValue,
bool aReplace,
char** aOldValue)
{
if (aOldValue) {
*aOldValue = nullptr;
}
// Before we can insert a new entry, we'll need to
// find the |CategoryNode| to put it in...
CategoryNode* category;
{
MutexAutoLock lock(mLock);
category = get_category(aCategoryName);
if (!category) {
// That category doesn't exist yet; let's make it.
category = CategoryNode::Create(&mArena);
char* categoryName = ArenaStrdup(aCategoryName, &mArena);
mTable.Put(categoryName, category);
}
}
if (!category) {
return;
}
// We will need the return value of AddLeaf even if the called doesn't want it
char* oldEntry = nullptr;
nsresult rv = category->AddLeaf(aEntryName,
aValue,
aReplace,
&oldEntry,
&mArena);
if (NS_SUCCEEDED(rv)) {
if (oldEntry) {
NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID,
aCategoryName, aEntryName);
}
NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID,
aCategoryName, aEntryName);
if (aOldValue) {
*aOldValue = oldEntry;
} else {
free(oldEntry);
}
}
}
NS_IMETHODIMP
nsCategoryManager::DeleteCategoryEntry(const char* aCategoryName,
const char* aEntryName,
bool aDontPersist)
{
if (NS_WARN_IF(!aCategoryName) ||
NS_WARN_IF(!aEntryName)) {
return NS_ERROR_INVALID_ARG;
}
/*
Note: no errors are reported since failure to delete
probably won't hurt you, and returning errors seriously
inconveniences JS clients
*/
CategoryNode* category;
{
MutexAutoLock lock(mLock);
category = get_category(aCategoryName);
}
if (category) {
category->DeleteLeaf(aEntryName);
NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID,
aCategoryName, aEntryName);
}
return NS_OK;
}
NS_IMETHODIMP
nsCategoryManager::DeleteCategory(const char* aCategoryName)
{
if (NS_WARN_IF(!aCategoryName)) {
return NS_ERROR_INVALID_ARG;
}
// the categories are arena-allocated, so we don't
// actually delete them. We just remove all of the
// leaf nodes.
CategoryNode* category;
{
MutexAutoLock lock(mLock);
category = get_category(aCategoryName);
}
if (category) {
category->Clear();
NotifyObservers(NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID,
aCategoryName, nullptr);
}
return NS_OK;
}
NS_IMETHODIMP
nsCategoryManager::EnumerateCategory(const char* aCategoryName,
nsISimpleEnumerator** aResult)
{
if (NS_WARN_IF(!aCategoryName) ||
NS_WARN_IF(!aResult)) {
return NS_ERROR_INVALID_ARG;
}
CategoryNode* category;
{
MutexAutoLock lock(mLock);
category = get_category(aCategoryName);
}
if (!category) {
return NS_NewEmptyEnumerator(aResult);
}
return category->Enumerate(aResult);
}
NS_IMETHODIMP
nsCategoryManager::EnumerateCategories(nsISimpleEnumerator** aResult)
{
if (NS_WARN_IF(!aResult)) {
return NS_ERROR_INVALID_ARG;
}
MutexAutoLock lock(mLock);
CategoryEnumerator* enumObj = CategoryEnumerator::Create(mTable);
if (!enumObj) {
return NS_ERROR_OUT_OF_MEMORY;
}
*aResult = enumObj;
NS_ADDREF(*aResult);
return NS_OK;
}
struct writecat_struct
{
PRFileDesc* fd;
bool success;
};
nsresult
nsCategoryManager::SuppressNotifications(bool aSuppress)
{
mSuppressNotifications = aSuppress;
return NS_OK;
}
/*
* CreateServicesFromCategory()
*
* Given a category, this convenience functions enumerates the category and
* creates a service of every CID or ContractID registered under the category.
* If observerTopic is non null and the service implements nsIObserver,
* this will attempt to notify the observer with the origin, observerTopic string
* as parameter.
*/
void
NS_CreateServicesFromCategory(const char* aCategory,
nsISupports* aOrigin,
const char* aObserverTopic,
const char16_t* aObserverData)
{
nsresult rv;
nsCOMPtr<nsICategoryManager> categoryManager =
do_GetService("@mozilla.org/categorymanager;1");
if (!categoryManager) {
return;
}
nsCOMPtr<nsISimpleEnumerator> enumerator;
rv = categoryManager->EnumerateCategory(aCategory,
getter_AddRefs(enumerator));
if (NS_FAILED(rv)) {
return;
}
nsCOMPtr<nsIUTF8StringEnumerator> senumerator =
do_QueryInterface(enumerator);
if (!senumerator) {
NS_WARNING("Category enumerator doesn't support nsIUTF8StringEnumerator.");
return;
}
bool hasMore;
while (NS_SUCCEEDED(senumerator->HasMore(&hasMore)) && hasMore) {
// From here on just skip any error we get.
nsAutoCString entryString;
if (NS_FAILED(senumerator->GetNext(entryString))) {
continue;
}
nsXPIDLCString contractID;
rv = categoryManager->GetCategoryEntry(aCategory, entryString.get(),
getter_Copies(contractID));
if (NS_FAILED(rv)) {
continue;
}
nsCOMPtr<nsISupports> instance = do_GetService(contractID);
if (!instance) {
LogMessage("While creating services from category '%s', could not create service for entry '%s', contract ID '%s'",
aCategory, entryString.get(), contractID.get());
continue;
}
if (aObserverTopic) {
// try an observer, if it implements it.
nsCOMPtr<nsIObserver> observer = do_QueryInterface(instance);
if (observer) {
observer->Observe(aOrigin, aObserverTopic,
aObserverData ? aObserverData : u"");
} else {
LogMessage("While creating services from category '%s', service for entry '%s', contract ID '%s' does not implement nsIObserver.",
aCategory, entryString.get(), contractID.get());
}
}
}
}

View file

@ -0,0 +1,146 @@
/* -*- 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/. */
#ifndef NSCATEGORYMANAGER_H
#define NSCATEGORYMANAGER_H
#include "prio.h"
#include "plarena.h"
#include "nsClassHashtable.h"
#include "nsICategoryManager.h"
#include "nsIMemoryReporter.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Mutex.h"
#include "mozilla/Attributes.h"
class nsIMemoryReporter;
/* 16d222a6-1dd2-11b2-b693-f38b02c021b2 */
#define NS_CATEGORYMANAGER_CID \
{ 0x16d222a6, 0x1dd2, 0x11b2, \
{0xb6, 0x93, 0xf3, 0x8b, 0x02, 0xc0, 0x21, 0xb2} }
/**
* a "leaf-node", managed by the nsCategoryNode hashtable.
*
* we need to keep a "persistent value" (which will be written to the registry)
* and a non-persistent value (for the current runtime): these are usually
* the same, except when aPersist==false. The strings are permanently arena-
* allocated, and will never go away.
*/
class CategoryLeaf : public nsDepCharHashKey
{
public:
explicit CategoryLeaf(const char* aKey) : nsDepCharHashKey(aKey), value(nullptr) {}
const char* value;
};
/**
* CategoryNode keeps a hashtable of its entries.
* the CategoryNode itself is permanently allocated in
* the arena.
*/
class CategoryNode
{
public:
nsresult GetLeaf(const char* aEntryName,
char** aResult);
nsresult AddLeaf(const char* aEntryName,
const char* aValue,
bool aReplace,
char** aResult,
PLArenaPool* aArena);
void DeleteLeaf(const char* aEntryName);
void Clear()
{
mozilla::MutexAutoLock lock(mLock);
mTable.Clear();
}
uint32_t Count()
{
mozilla::MutexAutoLock lock(mLock);
uint32_t tCount = mTable.Count();
return tCount;
}
nsresult Enumerate(nsISimpleEnumerator** aResult);
// CategoryNode is arena-allocated, with the strings
static CategoryNode* Create(PLArenaPool* aArena);
~CategoryNode();
void operator delete(void*) {}
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
private:
CategoryNode() : mLock("CategoryLeaf") {}
void* operator new(size_t aSize, PLArenaPool* aArena);
nsTHashtable<CategoryLeaf> mTable;
mozilla::Mutex mLock;
};
/**
* The main implementation of nsICategoryManager.
*
* This implementation is thread-safe.
*/
class nsCategoryManager final
: public nsICategoryManager
, public nsIMemoryReporter
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSICATEGORYMANAGER
NS_DECL_NSIMEMORYREPORTER
/**
* Suppress or unsuppress notifications of category changes to the
* observer service. This is to be used by nsComponentManagerImpl
* on startup while reading the stored category list.
*/
nsresult SuppressNotifications(bool aSuppress);
void AddCategoryEntry(const char* aCategory,
const char* aKey,
const char* aValue,
bool aReplace = true,
char** aOldValue = nullptr);
static nsresult Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
void InitMemoryReporter();
static nsCategoryManager* GetSingleton();
static void Destroy();
private:
static nsCategoryManager* gCategoryManager;
nsCategoryManager();
~nsCategoryManager();
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
CategoryNode* get_category(const char* aName);
void NotifyObservers(const char* aTopic,
const char* aCategoryName, // must be a static string
const char* aEntryName);
PLArenaPool mArena;
nsClassHashtable<nsDepCharHashKey, CategoryNode> mTable;
mozilla::Mutex mLock;
bool mSuppressNotifications;
};
#endif

View file

@ -0,0 +1,18 @@
/* -*- 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/. */
#ifndef nsCategoryManagerUtils_h__
#define nsCategoryManagerUtils_h__
#include "nsICategoryManager.h"
void
NS_CreateServicesFromCategory(const char* aCategory,
nsISupports* aOrigin,
const char* aObserverTopic,
const char16_t* aObserverData = nullptr);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,363 @@
/* -*- 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/. */
#ifndef nsComponentManager_h__
#define nsComponentManager_h__
#include "nsXPCOM.h"
#include "xpcom-private.h"
#include "nsIComponentManager.h"
#include "nsIComponentRegistrar.h"
#include "nsIMemoryReporter.h"
#include "nsIServiceManager.h"
#include "nsIFile.h"
#include "mozilla/Atomics.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Module.h"
#include "mozilla/ModuleLoader.h"
#include "mozilla/Mutex.h"
#include "nsXULAppAPI.h"
#include "nsNativeModuleLoader.h"
#include "nsIFactory.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "PLDHashTable.h"
#include "prtime.h"
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
#include "nsWeakReference.h"
#include "plarena.h"
#include "nsCOMArray.h"
#include "nsDataHashtable.h"
#include "nsInterfaceHashtable.h"
#include "nsClassHashtable.h"
#include "nsTArray.h"
#include "mozilla/Omnijar.h"
#include "mozilla/Attributes.h"
struct nsFactoryEntry;
class nsIServiceManager;
struct PRThread;
#define NS_COMPONENTMANAGER_CID \
{ /* 91775d60-d5dc-11d2-92fb-00e09805570f */ \
0x91775d60, \
0xd5dc, \
0x11d2, \
{0x92, 0xfb, 0x00, 0xe0, 0x98, 0x05, 0x57, 0x0f} \
}
/* keys for registry use */
extern const char xpcomKeyName[];
extern const char xpcomComponentsKeyName[];
extern const char lastModValueName[];
extern const char fileSizeValueName[];
extern const char nativeComponentType[];
extern const char staticComponentType[];
#ifdef DEBUG
#define XPCOM_CHECK_PENDING_CIDS
#endif
////////////////////////////////////////////////////////////////////////////////
extern const mozilla::Module kXPCOMModule;
/**
* This is a wrapper around mozilla::Mutex which provides runtime
* checking for a deadlock where the same thread tries to lock a mutex while
* it is already locked. This checking is present in both debug and release
* builds.
*/
class SafeMutex
{
public:
explicit SafeMutex(const char* aName)
: mMutex(aName)
, mOwnerThread(nullptr)
{
}
~SafeMutex() {}
void Lock()
{
AssertNotCurrentThreadOwns();
mMutex.Lock();
MOZ_ASSERT(mOwnerThread == nullptr);
mOwnerThread = PR_GetCurrentThread();
}
void Unlock()
{
MOZ_ASSERT(mOwnerThread == PR_GetCurrentThread());
mOwnerThread = nullptr;
mMutex.Unlock();
}
void AssertCurrentThreadOwns() const
{
// This method is a debug-only check
MOZ_ASSERT(mOwnerThread == PR_GetCurrentThread());
}
MOZ_NEVER_INLINE void AssertNotCurrentThreadOwns() const
{
// This method is a release-mode check
if (PR_GetCurrentThread() == mOwnerThread) {
MOZ_CRASH();
}
}
private:
mozilla::Mutex mMutex;
mozilla::Atomic<PRThread*, mozilla::Relaxed> mOwnerThread;
};
typedef mozilla::BaseAutoLock<SafeMutex> SafeMutexAutoLock;
typedef mozilla::BaseAutoUnlock<SafeMutex> SafeMutexAutoUnlock;
class nsComponentManagerImpl final
: public nsIComponentManager
, public nsIServiceManager
, public nsSupportsWeakReference
, public nsIComponentRegistrar
, public nsIInterfaceRequestor
, public nsIMemoryReporter
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSICOMPONENTMANAGER
NS_DECL_NSICOMPONENTREGISTRAR
NS_DECL_NSIMEMORYREPORTER
static nsresult Create(nsISupports* aOuter, REFNSIID aIID, void** aResult);
nsresult RegistryLocationForFile(nsIFile* aFile,
nsCString& aResult);
nsresult FileForRegistryLocation(const nsCString& aLocation,
nsIFile** aSpec);
NS_DECL_NSISERVICEMANAGER
// nsComponentManagerImpl methods:
nsComponentManagerImpl();
static nsComponentManagerImpl* gComponentManager;
nsresult Init();
nsresult Shutdown(void);
nsresult FreeServices();
already_AddRefed<mozilla::ModuleLoader> LoaderForExtension(const nsACString& aExt);
nsInterfaceHashtable<nsCStringHashKey, mozilla::ModuleLoader> mLoaderMap;
already_AddRefed<nsIFactory> FindFactory(const nsCID& aClass);
already_AddRefed<nsIFactory> FindFactory(const char* aContractID,
uint32_t aContractIDLen);
already_AddRefed<nsIFactory> LoadFactory(nsFactoryEntry* aEntry);
nsFactoryEntry* GetFactoryEntry(const char* aContractID,
uint32_t aContractIDLen);
nsFactoryEntry* GetFactoryEntry(const nsCID& aClass);
nsDataHashtable<nsIDHashKey, nsFactoryEntry*> mFactories;
nsDataHashtable<nsCStringHashKey, nsFactoryEntry*> mContractIDs;
SafeMutex mLock;
static void InitializeStaticModules();
static void InitializeModuleLocations();
struct ComponentLocation
{
NSLocationType type;
mozilla::FileLocation location;
};
class ComponentLocationComparator
{
public:
bool Equals(const ComponentLocation& aA, const ComponentLocation& aB) const
{
return (aA.type == aB.type && aA.location.Equals(aB.location));
}
};
static nsTArray<const mozilla::Module*>* sStaticModules;
static nsTArray<ComponentLocation>* sModuleLocations;
nsNativeModuleLoader mNativeModuleLoader;
class KnownModule
{
public:
/**
* Static or binary module.
*/
KnownModule(const mozilla::Module* aModule, mozilla::FileLocation& aFile)
: mModule(aModule)
, mFile(aFile)
, mLoaded(false)
, mFailed(false)
{
}
explicit KnownModule(const mozilla::Module* aModule)
: mModule(aModule)
, mLoaded(false)
, mFailed(false)
{
}
explicit KnownModule(mozilla::FileLocation& aFile)
: mModule(nullptr)
, mFile(aFile)
, mLoader(nullptr)
, mLoaded(false)
, mFailed(false)
{
}
~KnownModule()
{
if (mLoaded && mModule->unloadProc) {
mModule->unloadProc();
}
}
bool EnsureLoader();
bool Load();
const mozilla::Module* Module() const { return mModule; }
/**
* For error logging, get a description of this module, either the
* file path, or <static module>.
*/
nsCString Description() const;
private:
const mozilla::Module* mModule;
mozilla::FileLocation mFile;
nsCOMPtr<mozilla::ModuleLoader> mLoader;
bool mLoaded;
bool mFailed;
};
// The KnownModule is kept alive by these members, it is
// referenced by pointer from the factory entries.
nsTArray<nsAutoPtr<KnownModule>> mKnownStaticModules;
// The key is the URI string of the module
nsClassHashtable<nsCStringHashKey, KnownModule> mKnownModules;
// Mutex not held
void RegisterModule(const mozilla::Module* aModule,
mozilla::FileLocation* aFile);
// Mutex held
void RegisterCIDEntryLocked(const mozilla::Module::CIDEntry* aEntry,
KnownModule* aModule);
void RegisterContractIDLocked(const mozilla::Module::ContractIDEntry* aEntry);
// Mutex not held
void RegisterManifest(NSLocationType aType, mozilla::FileLocation& aFile,
bool aChromeOnly);
struct ManifestProcessingContext
{
ManifestProcessingContext(NSLocationType aType,
mozilla::FileLocation& aFile, bool aChromeOnly)
: mType(aType)
, mFile(aFile)
, mChromeOnly(aChromeOnly)
{
}
~ManifestProcessingContext() {}
NSLocationType mType;
mozilla::FileLocation mFile;
bool mChromeOnly;
};
void ManifestManifest(ManifestProcessingContext& aCx, int aLineNo,
char* const* aArgv);
void ManifestBinaryComponent(ManifestProcessingContext& aCx, int aLineNo,
char* const* aArgv);
void ManifestXPT(ManifestProcessingContext& aCx, int aLineNo,
char* const* aArgv);
void ManifestComponent(ManifestProcessingContext& aCx, int aLineNo,
char* const* aArgv);
void ManifestContract(ManifestProcessingContext& aCx, int aLineNo,
char* const* aArgv);
void ManifestCategory(ManifestProcessingContext& aCx, int aLineNo,
char* const* aArgv);
void RereadChromeManifests(bool aChromeOnly = true);
// Shutdown
enum
{
NOT_INITIALIZED,
NORMAL,
SHUTDOWN_IN_PROGRESS,
SHUTDOWN_COMPLETE
} mStatus;
PLArenaPool mArena;
struct PendingServiceInfo
{
const nsCID* cid;
PRThread* thread;
};
inline PendingServiceInfo* AddPendingService(const nsCID& aServiceCID,
PRThread* aThread);
inline void RemovePendingService(const nsCID& aServiceCID);
inline PRThread* GetPendingServiceThread(const nsCID& aServiceCID) const;
nsTArray<PendingServiceInfo> mPendingServices;
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
private:
~nsComponentManagerImpl();
};
#define NS_MAX_FILENAME_LEN 1024
#define NS_ERROR_IS_DIR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_XPCOM, 24)
struct nsFactoryEntry
{
nsFactoryEntry(const mozilla::Module::CIDEntry* aEntry,
nsComponentManagerImpl::KnownModule* aModule);
// nsIComponentRegistrar.registerFactory support
nsFactoryEntry(const nsCID& aClass, nsIFactory* aFactory);
~nsFactoryEntry();
already_AddRefed<nsIFactory> GetFactory();
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
const mozilla::Module::CIDEntry* mCIDEntry;
nsComponentManagerImpl::KnownModule* mModule;
nsCOMPtr<nsIFactory> mFactory;
nsCOMPtr<nsISupports> mServiceObject;
};
#endif // nsComponentManager_h__

View file

@ -0,0 +1,69 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 "nsISupports.idl"
interface nsISimpleEnumerator;
/*
* nsICategoryManager
*/
[scriptable, uuid(3275b2cd-af6d-429a-80d7-f0c5120342ac)]
interface nsICategoryManager : nsISupports
{
/**
* Get the value for the given category's entry.
* @param aCategory The name of the category ("protocol")
* @param aEntry The entry you're looking for ("http")
* @return The value.
*/
string getCategoryEntry(in string aCategory, in string aEntry);
/**
* Add an entry to a category.
* @param aCategory The name of the category ("protocol")
* @param aEntry The entry to be added ("http")
* @param aValue The value for the entry ("moz.httprulez.1")
* @param aPersist Should this data persist between invocations?
* @param aReplace Should we replace an existing entry?
* @return Previous entry, if any
*/
string addCategoryEntry(in string aCategory, in string aEntry,
in string aValue, in boolean aPersist,
in boolean aReplace);
/**
* Delete an entry from the category.
* @param aCategory The name of the category ("protocol")
* @param aEntry The entry to be added ("http")
* @param aPersist Delete persistent data from registry, if present?
*/
void deleteCategoryEntry(in string aCategory, in string aEntry,
in boolean aPersist);
/**
* Delete a category and all entries.
* @param aCategory The category to be deleted.
*/
void deleteCategory(in string aCategory);
/**
* Enumerate the entries in a category.
* @param aCategory The category to be enumerated.
* @return a simple enumerator, each result QIs to
* nsISupportsCString.
*/
nsISimpleEnumerator enumerateCategory(in string aCategory);
/**
* Enumerate all existing categories
* @param aCategory The category to be enumerated.
* @return a simple enumerator, each result QIs to
* nsISupportsCString.
*/
nsISimpleEnumerator enumerateCategories();
};

View file

@ -0,0 +1,87 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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 "nsISupports.idl"
interface nsIXPCScriptable;
/**
* Provides information about a specific implementation class. If you want
* your class to implement nsIClassInfo, see nsIClassInfoImpl.h for
* instructions--you most likely do not want to inherit from nsIClassInfo.
*/
[scriptable, uuid(a60569d7-d401-4677-ba63-2aa5971af25d)]
interface nsIClassInfo : nsISupports
{
/**
* Get an ordered list of the interface ids that instances of the class
* promise to implement. Note that nsISupports is an implicit member
* of any such list and need not be included.
*
* Should set *count = 0 and *array = null and return NS_OK if getting the
* list is not supported.
*/
void getInterfaces(out uint32_t count,
[array, size_is(count), retval] out nsIIDPtr array);
/**
* Return an object to assist XPConnect in supplying JavaScript-specific
* behavior to callers of the instance object, or null if not needed.
*/
nsIXPCScriptable getScriptableHelper();
/**
* A contract ID through which an instance of this class can be created
* (or accessed as a service, if |flags & SINGLETON|), or null.
*/
readonly attribute string contractID;
/**
* A human readable string naming the class, or null.
*/
readonly attribute string classDescription;
/**
* A class ID through which an instance of this class can be created
* (or accessed as a service, if |flags & SINGLETON|), or null.
*/
readonly attribute nsCIDPtr classID;
/**
* Bitflags for 'flags' attribute.
*/
const uint32_t SINGLETON = 1 << 0;
const uint32_t THREADSAFE = 1 << 1;
const uint32_t MAIN_THREAD_ONLY = 1 << 2;
const uint32_t DOM_OBJECT = 1 << 3;
const uint32_t PLUGIN_OBJECT = 1 << 4;
const uint32_t SINGLETON_CLASSINFO = 1 << 5;
/**
* 'flags' attribute bitflag: whether objects of this type implement
* nsIContent.
*/
const uint32_t CONTENT_NODE = 1 << 6;
// The high order bit is RESERVED for consumers of these flags.
// No implementor of this interface should ever return flags
// with this bit set.
const uint32_t RESERVED = 1 << 31;
readonly attribute uint32_t flags;
/**
* Also a class ID through which an instance of this class can be created
* (or accessed as a service, if |flags & SINGLETON|). If the class does
* not have a CID, it should return NS_ERROR_NOT_AVAILABLE. This attribute
* exists so C++ callers can avoid allocating and freeing a CID, as would
* happen if they used classID.
*/
[noscript] readonly attribute nsCID classIDNoAlloc;
};

View file

@ -0,0 +1,106 @@
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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/. */
/**
* The nsIComponentManager interface.
*/
#include "nsISupports.idl"
interface nsIFile;
interface nsIFactory;
interface nsIArray;
[scriptable, uuid(d604ffc3-1ba3-4f6c-b65f-1ed4199364c3)]
interface nsIComponentManager : nsISupports
{
/**
* getClassObject
*
* Returns the factory object that can be used to create instances of
* CID aClass
*
* @param aClass The classid of the factory that is being requested
*/
void getClassObject(in nsCIDRef aClass,
in nsIIDRef aIID,
[iid_is(aIID),retval] out nsQIResult result);
/**
* getClassObjectByContractID
*
* Returns the factory object that can be used to create instances of
* CID aClass
*
* @param aClass The classid of the factory that is being requested
*/
void getClassObjectByContractID(in string aContractID,
in nsIIDRef aIID,
[iid_is(aIID),retval] out nsQIResult result);
/**
* createInstance
*
* Create an instance of the CID aClass and return the interface aIID.
*
* @param aClass : ClassID of object instance requested
* @param aDelegate : Used for aggregation
* @param aIID : IID of interface requested
*/
void createInstance(in nsCIDRef aClass,
in nsISupports aDelegate,
in nsIIDRef aIID,
[iid_is(aIID),retval] out nsQIResult result);
/**
* createInstanceByContractID
*
* Create an instance of the CID that implements aContractID and return the
* interface aIID.
*
* @param aContractID : aContractID of object instance requested
* @param aDelegate : Used for aggregation
* @param aIID : IID of interface requested
*/
void createInstanceByContractID(in string aContractID,
in nsISupports aDelegate,
in nsIIDRef aIID,
[iid_is(aIID),retval] out nsQIResult result);
/**
* addBootstrappedManifestLocation
*
* Adds a bootstrapped manifest location on runtime.
*
* @param aLocation : A directory where chrome.manifest resides,
* or an XPI with it on the root.
*/
void addBootstrappedManifestLocation(in nsIFile aLocation);
/**
* removeBootstrappedManifestLocation
*
* Removes a bootstrapped manifest location on runtime.
*
* @param aLocation : A directory where chrome.manifest resides,
* or an XPI with it on the root.
*/
void removeBootstrappedManifestLocation(in nsIFile aLocation);
/**
* getManifestLocations
*
* Get an array of nsIURIs of all registered and builtin manifest locations.
*/
nsIArray getManifestLocations();
};
%{ C++
#ifdef MOZILLA_INTERNAL_API
#include "nsComponentManagerUtils.h"
#endif
%} C++

View file

@ -0,0 +1,163 @@
/* 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/. */
/**
* The nsIComponentRegistrar interface.
*/
#include "nsISupports.idl"
interface nsIFile;
interface nsIFactory;
interface nsISimpleEnumerator;
[scriptable, uuid(2417cbfe-65ad-48a6-b4b6-eb84db174392)]
interface nsIComponentRegistrar : nsISupports
{
/**
* autoRegister
*
* Register a .manifest file, or an entire directory containing
* these files. Registration lasts for this run only, and is not cached.
*
* @note Formerly this method would register component files directly. This
* is no longer supported.
*/
void autoRegister(in nsIFile aSpec);
/**
* autoUnregister
* @status OBSOLETE: This method is no longer implemented, but preserved
* in this interface for binary compatibility with
* Mozilla 1.9.2.
*/
void autoUnregister(in nsIFile aSpec);
/**
* registerFactory
*
* Register a factory with a given ContractID, CID and Class Name.
*
* @param aClass : CID of object
* @param aClassName : Class Name of CID (unused)
* @param aContractID : ContractID associated with CID aClass. May be null
* if no contract ID is needed.
* @param aFactory : Factory that will be registered for CID aClass.
* If aFactory is null, the contract will be associated
* with a previously registered CID.
*/
void registerFactory(in nsCIDRef aClass,
in string aClassName,
in string aContractID,
in nsIFactory aFactory);
/**
* unregisterFactory
*
* Unregister a factory associated with CID aClass.
*
* @param aClass : CID being unregistered
* @param aFactory : Factory previously registered to create instances of
* CID aClass.
*
* @throws NS_ERROR* Method failure.
*/
void unregisterFactory(in nsCIDRef aClass,
in nsIFactory aFactory);
/**
* registerFactoryLocation
* @status OBSOLETE: This method is no longer implemented, but preserved
* in this interface for binary compatibility with
* Mozilla 1.9.2.
*/
void registerFactoryLocation(in nsCIDRef aClass,
in string aClassName,
in string aContractID,
in nsIFile aFile,
in string aLoaderStr,
in string aType);
/**
* unregisterFactoryLocation
* @status OBSOLETE: This method is no longer implemented, but preserved
* in this interface for binary compatibility with
* Mozilla 1.9.2.
*/
void unregisterFactoryLocation(in nsCIDRef aClass,
in nsIFile aFile);
/**
* isCIDRegistered
*
* Returns true if a factory is registered for the CID.
*
* @param aClass : CID queried for registeration
* @return : true if a factory is registered for CID
* false otherwise.
*/
boolean isCIDRegistered(in nsCIDRef aClass);
/**
* isContractIDRegistered
*
* Returns true if a factory is registered for the contract id.
*
* @param aClass : contract id queried for registeration
* @return : true if a factory is registered for contract id
* false otherwise.
*/
boolean isContractIDRegistered(in string aContractID);
/**
* enumerateCIDs
*
* Enumerate the list of all registered CIDs.
*
* @return : enumerator for CIDs. Elements of the enumeration can be QI'ed
* for the nsISupportsID interface. From the nsISupportsID, you
* can obtain the actual CID.
*/
nsISimpleEnumerator enumerateCIDs();
/**
* enumerateContractIDs
*
* Enumerate the list of all registered ContractIDs.
*
* @return : enumerator for ContractIDs. Elements of the enumeration can be
* QI'ed for the nsISupportsCString interface. From the
* nsISupportsCString interface, you can obtain the actual
* Contract ID string.
*/
nsISimpleEnumerator enumerateContractIDs();
/**
* CIDToContractID
* @status OBSOLETE: This method is no longer implemented, but preserved
* in this interface for binary compatibility with
* Mozilla 1.9.2.
*/
string CIDToContractID(in nsCIDRef aClass);
/**
* contractIDToCID
*
* Returns the CID for a given Contract ID, if one exists and is registered.
*
* @return : Contract ID.
*/
nsCIDPtr contractIDToCID(in string aContractID);
};

View file

@ -0,0 +1,42 @@
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 "nsISupports.idl"
/**
* A class factory allows the creation of nsISupports derived
* components without specifying a concrete base class.
*/
[scriptable, object, uuid(00000001-0000-0000-c000-000000000046)]
interface nsIFactory : nsISupports {
/**
* Creates an instance of a component.
*
* @param aOuter Pointer to a component that wishes to be aggregated
* in the resulting instance. This will be nullptr if no
* aggregation is requested.
* @param iid The IID of the interface being requested in
* the component which is being currently created.
* @param result [out] Pointer to the newly created instance, if successful.
* @throws NS_NOINTERFACE - Interface not accessible.
* @throws NS_ERROR_NO_AGGREGATION - if an 'outer' object is supplied, but the
* component is not aggregatable.
* NS_ERROR* - Method failure.
*/
void createInstance(in nsISupports aOuter, in nsIIDRef iid,
[retval, iid_is(iid)] out nsQIResult result);
/**
* LockFactory provides the client a way to keep the component
* in memory until it is finished with it. The client can call
* LockFactory(PR_TRUE) to lock the factory and LockFactory(PR_FALSE)
* to release the factory.
*
* @param lock - Must be PR_TRUE or PR_FALSE
* @throws NS_ERROR* - Method failure.
*/
void lockFactory(in boolean lock);
};

View file

@ -0,0 +1,82 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 "nsISupports.idl"
interface nsIFile;
interface nsIComponentManager;
/**
* The nsIModule interface.
*/
[scriptable, uuid(7392D032-5371-11d3-994E-00805FD26FEE)]
interface nsIModule : nsISupports
{
/**
* Object Instance Creation
*
* Obtains a Class Object from a nsIModule for a given CID and IID pair.
* This class object can either be query to a nsIFactory or a may be
* query to a nsIClassInfo.
*
* @param aCompMgr : The global component manager
* @param aClass : ClassID of object instance requested
* @param aIID : IID of interface requested
*
*/
void getClassObject(in nsIComponentManager aCompMgr,
in nsCIDRef aClass,
in nsIIDRef aIID,
[retval, iid_is(aIID)] out nsQIResult aResult);
/**
* One time registration callback
*
* When the nsIModule is discovered, this method will be
* called so that any setup registration can be preformed.
*
* @param aCompMgr : The global component manager
* @param aLocation : The location of the nsIModule on disk
* @param aLoaderStr: Opaque loader specific string
* @param aType : Loader Type being used to load this module
*/
void registerSelf(in nsIComponentManager aCompMgr,
in nsIFile aLocation,
in string aLoaderStr,
in string aType);
/**
* One time unregistration callback
*
* When the nsIModule is being unregistered, this method will be
* called so that any unregistration can be preformed
*
* @param aCompMgr : The global component manager
* @param aLocation : The location of the nsIModule on disk
* @param aLoaderStr : Opaque loader specific string
*
*/
void unregisterSelf(in nsIComponentManager aCompMgr,
in nsIFile aLocation,
in string aLoaderStr);
/**
* Module load management
*
* @param aCompMgr : The global component manager
*
* @return indicates to the caller if the module can be unloaded.
* Returning PR_TRUE isn't a guarantee that the module will be
* unloaded. It constitues only willingness of the module to be
* unloaded. It is very important to ensure that no outstanding
* references to the module's code/data exist before returning
* PR_TRUE.
* Returning PR_FALSE guaratees that the module won't be unloaded.
*/
boolean canUnload(in nsIComponentManager aCompMgr);
};

View file

@ -0,0 +1,72 @@
/* 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 "nsISupports.idl"
/**
* The nsIServiceManager manager interface provides a means to obtain
* global services in an application. The service manager depends on the
* repository to find and instantiate factories to obtain services.
*
* Users of the service manager must first obtain a pointer to the global
* service manager by calling NS_GetServiceManager. After that,
* they can request specific services by calling GetService. When they are
* finished they can NS_RELEASE() the service as usual.
*
* A user of a service may keep references to particular services indefinitely
* and only must call Release when it shuts down.
*/
[scriptable, uuid(8bb35ed9-e332-462d-9155-4a002ab5c958)]
interface nsIServiceManager : nsISupports
{
/**
* getServiceByContractID
*
* Returns the instance that implements aClass or aContractID and the
* interface aIID. This may result in the instance being created.
*
* @param aClass or aContractID : aClass or aContractID of object
* instance requested
* @param aIID : IID of interface requested
* @param result : resulting service
*/
void getService(in nsCIDRef aClass,
in nsIIDRef aIID,
[iid_is(aIID),retval] out nsQIResult result);
void getServiceByContractID(in string aContractID,
in nsIIDRef aIID,
[iid_is(aIID),retval] out nsQIResult result);
/**
* isServiceInstantiated
*
* isServiceInstantiated will return a true if the service has already
* been created, or throw otherwise
*
* @param aClass or aContractID : aClass or aContractID of object
* instance requested
* @param aIID : IID of interface requested
* @throws NS_ERROR_SERVICE_NOT_AVAILABLE if the service hasn't been
* instantiated
* @throws NS_NOINTERFACE if the IID given isn't supported by the object
*/
boolean isServiceInstantiated(in nsCIDRef aClass, in nsIIDRef aIID);
boolean isServiceInstantiatedByContractID(in string aContractID, in nsIIDRef aIID);
};
%{C++
// Observing xpcom autoregistration. Topics will be 'start' and 'stop'.
#define NS_XPCOM_AUTOREGISTRATION_OBSERVER_ID "xpcom-autoregistration"
#ifdef MOZILLA_INTERNAL_API
#include "nsXPCOM.h"
#include "nsComponentManagerUtils.h"
#include "nsServiceManagerUtils.h"
#endif
%}

View file

@ -0,0 +1,220 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* This Original Code has been modified by IBM Corporation.
* Modifications made by IBM described herein are
* Copyright (c) International Business Machines
* Corporation, 2000
*
* Modifications to Mozilla code or documentation
* identified per MPL Section 3.3
*
* Date Modified by Description of modification
* 04/20/2000 IBM Corp. Added PR_CALLBACK for Optlink use in OS2
*/
#include "nsNativeModuleLoader.h"
#include "mozilla/Logging.h"
#include "prinit.h"
#include "prerror.h"
#include "nsComponentManager.h"
#include "ManifestParser.h" // for LogMessage
#include "nsCRTGlue.h"
#include "nsThreadUtils.h"
#include "nsTraceRefcnt.h"
#include "nsIFile.h"
#include "mozilla/WindowsDllBlocklist.h"
#ifdef XP_WIN
#include <windows.h>
#endif
#ifdef XP_MACOSX
#include <signal.h>
#endif
#ifdef DEBUG
#define IMPLEMENT_BREAK_AFTER_LOAD
#endif
using namespace mozilla;
static LazyLogModule sNativeModuleLoaderLog("nsNativeModuleLoader");
#define LOG(level, args) MOZ_LOG(sNativeModuleLoaderLog, level, args)
nsresult
nsNativeModuleLoader::Init()
{
MOZ_ASSERT(NS_IsMainThread(), "Startup not on main thread?");
LOG(LogLevel::Debug, ("nsNativeModuleLoader::Init()"));
return NS_OK;
}
class LoadModuleMainThreadRunnable : public Runnable
{
public:
LoadModuleMainThreadRunnable(nsNativeModuleLoader* aLoader,
FileLocation& aFile)
: mManager(nsComponentManagerImpl::gComponentManager)
, mLoader(aLoader)
, mFile(aFile)
, mResult(nullptr)
{
}
NS_IMETHOD Run() override
{
mResult = mLoader->LoadModule(mFile);
return NS_OK;
}
RefPtr<nsComponentManagerImpl> mManager;
nsNativeModuleLoader* mLoader;
FileLocation mFile;
const mozilla::Module* mResult;
};
const mozilla::Module*
nsNativeModuleLoader::LoadModule(FileLocation& aFile)
{
if (aFile.IsZip()) {
NS_ERROR("Binary components cannot be loaded from JARs");
return nullptr;
}
nsCOMPtr<nsIFile> file = aFile.GetBaseFile();
nsresult rv;
if (!NS_IsMainThread()) {
// If this call is off the main thread, synchronously proxy it
// to the main thread.
RefPtr<LoadModuleMainThreadRunnable> r =
new LoadModuleMainThreadRunnable(this, aFile);
NS_DispatchToMainThread(r, NS_DISPATCH_SYNC);
return r->mResult;
}
nsCOMPtr<nsIHashable> hashedFile(do_QueryInterface(file));
if (!hashedFile) {
NS_ERROR("nsIFile is not nsIHashable");
return nullptr;
}
nsAutoCString filePath;
file->GetNativePath(filePath);
NativeLoadData data;
if (mLibraries.Get(hashedFile, &data)) {
NS_ASSERTION(data.mModule, "Corrupt mLibraries hash");
LOG(LogLevel::Debug,
("nsNativeModuleLoader::LoadModule(\"%s\") - found in cache",
filePath.get()));
return data.mModule;
}
// We haven't loaded this module before
{
#ifdef HAS_DLL_BLOCKLIST
AutoSetXPCOMLoadOnMainThread guard;
#endif
rv = file->Load(&data.mLibrary);
}
if (NS_FAILED(rv)) {
char errorMsg[1024] = "<unknown; can't get error from NSPR>";
if (PR_GetErrorTextLength() < (int)sizeof(errorMsg)) {
PR_GetErrorText(errorMsg);
}
LogMessage("Failed to load native module at path '%s': (%lx) %s",
filePath.get(), rv, errorMsg);
return nullptr;
}
#ifdef IMPLEMENT_BREAK_AFTER_LOAD
nsAutoCString leafName;
file->GetNativeLeafName(leafName);
char* env = getenv("XPCOM_BREAK_ON_LOAD");
char* blist;
if (env && *env && (blist = strdup(env))) {
char* nextTok = blist;
while (char* token = NS_strtok(":", &nextTok)) {
if (leafName.Find(token, true) != kNotFound) {
NS_BREAK();
}
}
free(blist);
}
#endif
void* module = PR_FindSymbol(data.mLibrary, "NSModule");
if (!module) {
LogMessage("Native module at path '%s' doesn't export symbol `NSModule`.",
filePath.get());
PR_UnloadLibrary(data.mLibrary);
return nullptr;
}
data.mModule = *(mozilla::Module const* const*)module;
if (mozilla::Module::kVersion != data.mModule->mVersion) {
LogMessage("Native module at path '%s' is incompatible with this version of Firefox, has version %i, expected %i.",
filePath.get(), data.mModule->mVersion,
mozilla::Module::kVersion);
PR_UnloadLibrary(data.mLibrary);
return nullptr;
}
mLibraries.Put(hashedFile, data); // infallible
return data.mModule;
}
void
nsNativeModuleLoader::UnloadLibraries()
{
MOZ_ASSERT(NS_IsMainThread(), "Shutdown not on main thread?");
for (auto iter = mLibraries.Iter(); !iter.Done(); iter.Next()) {
NativeLoadData& loadData = iter.Data();
loadData.mModule = nullptr;
}
for (auto iter = mLibraries.Iter(); !iter.Done(); iter.Next()) {
if (MOZ_LOG_TEST(sNativeModuleLoaderLog, LogLevel::Debug)) {
nsIHashable* hashedFile = iter.Key();
nsCOMPtr<nsIFile> file(do_QueryInterface(hashedFile));
nsAutoCString filePath;
file->GetNativePath(filePath);
LOG(LogLevel::Debug,
("nsNativeModuleLoader::UnloaderFunc(\"%s\")", filePath.get()));
}
#ifdef NS_BUILD_REFCNT_LOGGING
nsTraceRefcnt::SetActivityIsLegal(false);
#endif
#if 0
// XXXbsmedberg: do this as soon as the static-destructor crash(es)
// are fixed
NativeLoadData& loadData = iter.Data();
PRStatus ret = PR_UnloadLibrary(loadData.mLibrary);
NS_ASSERTION(ret == PR_SUCCESS, "Failed to unload library");
#endif
#ifdef NS_BUILD_REFCNT_LOGGING
nsTraceRefcnt::SetActivityIsLegal(true);
#endif
iter.Remove();
}
}

View file

@ -0,0 +1,39 @@
/* -*- 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/. */
#ifndef nsNativeModuleLoader_h__
#define nsNativeModuleLoader_h__
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
#include "prlink.h"
namespace mozilla {
class FileLocation;
} // namespace mozilla
class nsNativeModuleLoader final
{
public:
const mozilla::Module* LoadModule(mozilla::FileLocation& aFile);
nsresult Init();
void UnloadLibraries();
private:
struct NativeLoadData
{
NativeLoadData() : mModule(nullptr), mLibrary(nullptr) {}
const mozilla::Module* mModule;
PRLibrary* mLibrary;
};
nsDataHashtable<nsHashableHashKey, NativeLoadData> mLibraries;
};
#endif /* nsNativeModuleLoader_h__ */