summaryrefslogtreecommitdiffstats
path: root/xpcom/components
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /xpcom/components
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'xpcom/components')
-rw-r--r--xpcom/components/ManifestParser.cpp792
-rw-r--r--xpcom/components/ManifestParser.h22
-rw-r--r--xpcom/components/Module.h157
-rw-r--r--xpcom/components/ModuleLoader.h44
-rw-r--r--xpcom/components/ModuleUtils.h98
-rw-r--r--xpcom/components/moz.build55
-rw-r--r--xpcom/components/nsCategoryManager.cpp832
-rw-r--r--xpcom/components/nsCategoryManager.h146
-rw-r--r--xpcom/components/nsCategoryManagerUtils.h18
-rw-r--r--xpcom/components/nsComponentManager.cpp2083
-rw-r--r--xpcom/components/nsComponentManager.h363
-rw-r--r--xpcom/components/nsICategoryManager.idl69
-rw-r--r--xpcom/components/nsIClassInfo.idl87
-rw-r--r--xpcom/components/nsIComponentManager.idl106
-rw-r--r--xpcom/components/nsIComponentRegistrar.idl163
-rw-r--r--xpcom/components/nsIFactory.idl42
-rw-r--r--xpcom/components/nsIModule.idl82
-rw-r--r--xpcom/components/nsIServiceManager.idl72
-rw-r--r--xpcom/components/nsNativeModuleLoader.cpp220
-rw-r--r--xpcom/components/nsNativeModuleLoader.h39
20 files changed, 5490 insertions, 0 deletions
diff --git a/xpcom/components/ManifestParser.cpp b/xpcom/components/ManifestParser.cpp
new file mode 100644
index 000000000..4dcfc8402
--- /dev/null
+++ b/xpcom/components/ManifestParser.cpp
@@ -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);
+ }
+}
diff --git a/xpcom/components/ManifestParser.h b/xpcom/components/ManifestParser.h
new file mode 100644
index 000000000..019ec326c
--- /dev/null
+++ b/xpcom/components/ManifestParser.h
@@ -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
diff --git a/xpcom/components/Module.h b/xpcom/components/Module.h
new file mode 100644
index 000000000..21d07e82e
--- /dev/null
+++ b/xpcom/components/Module.h
@@ -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
diff --git a/xpcom/components/ModuleLoader.h b/xpcom/components/ModuleLoader.h
new file mode 100644
index 000000000..c2c920d4d
--- /dev/null
+++ b/xpcom/components/ModuleLoader.h
@@ -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
diff --git a/xpcom/components/ModuleUtils.h b/xpcom/components/ModuleUtils.h
new file mode 100644
index 000000000..9c31ed76b
--- /dev/null
+++ b/xpcom/components/ModuleUtils.h
@@ -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
diff --git a/xpcom/components/moz.build b/xpcom/components/moz.build
new file mode 100644
index 000000000..73a85cb3b
--- /dev/null
+++ b/xpcom/components/moz.build
@@ -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']
diff --git a/xpcom/components/nsCategoryManager.cpp b/xpcom/components/nsCategoryManager.cpp
new file mode 100644
index 000000000..527c78719
--- /dev/null
+++ b/xpcom/components/nsCategoryManager.cpp
@@ -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());
+ }
+ }
+ }
+}
diff --git a/xpcom/components/nsCategoryManager.h b/xpcom/components/nsCategoryManager.h
new file mode 100644
index 000000000..3a4faed72
--- /dev/null
+++ b/xpcom/components/nsCategoryManager.h
@@ -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
diff --git a/xpcom/components/nsCategoryManagerUtils.h b/xpcom/components/nsCategoryManagerUtils.h
new file mode 100644
index 000000000..a83d097c2
--- /dev/null
+++ b/xpcom/components/nsCategoryManagerUtils.h
@@ -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
diff --git a/xpcom/components/nsComponentManager.cpp b/xpcom/components/nsComponentManager.cpp
new file mode 100644
index 000000000..b9eb8e275
--- /dev/null
+++ b/xpcom/components/nsComponentManager.cpp
@@ -0,0 +1,2083 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This Original Code has been modified by IBM Corporation.
+ * Modifications made by IBM described herein are
+ * Copyright (c) International Business Machines
+ * Corporation, 2000
+ *
+ * Modifications to Mozilla code or documentation
+ * identified per MPL Section 3.3
+ *
+ * Date Modified by Description of modification
+ * 04/20/2000 IBM Corp. Added PR_CALLBACK for Optlink use in OS2
+ */
+
+#include <stdlib.h>
+#include "nscore.h"
+#include "nsISupports.h"
+#include "nspr.h"
+#include "nsCRT.h" // for atoll
+
+// Arena used by component manager for storing contractid string, dll
+// location strings and small objects
+// CAUTION: Arena align mask needs to be defined before including plarena.h
+// currently from nsComponentManager.h
+#define PL_ARENA_CONST_ALIGN_MASK 7
+#define NS_CM_BLOCK_SIZE (1024 * 8)
+
+#include "nsCategoryManager.h"
+#include "nsCOMPtr.h"
+#include "nsComponentManager.h"
+#include "nsDirectoryService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsCategoryManager.h"
+#include "nsCategoryManagerUtils.h"
+#include "xptiprivate.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/XPTInterfaceInfoManager.h"
+#include "nsIConsoleService.h"
+#include "nsIObserverService.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIStringEnumerator.h"
+#include "nsXPCOM.h"
+#include "nsXPCOMPrivate.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIClassInfo.h"
+#include "nsLocalFile.h"
+#include "nsReadableUtils.h"
+#include "nsString.h"
+#include "nsXPIDLString.h"
+#include "prcmon.h"
+#include "xptinfo.h" // this after nsISupports, to pick up IID so that xpt stuff doesn't try to define it itself...
+#include "nsThreadUtils.h"
+#include "prthread.h"
+#include "private/pprthred.h"
+#include "nsTArray.h"
+#include "prio.h"
+#include "ManifestParser.h"
+#include "nsNetUtil.h"
+#include "mozilla/Services.h"
+
+#include "mozilla/GenericFactory.h"
+#include "nsSupportsPrimitives.h"
+#include "nsArray.h"
+#include "nsIMutableArray.h"
+#include "nsArrayEnumerator.h"
+#include "nsStringEnumerator.h"
+#include "mozilla/FileUtils.h"
+#include "mozilla/UniquePtr.h"
+#include "nsDataHashtable.h"
+
+#include <new> // for placement new
+
+#include "mozilla/Omnijar.h"
+
+#include "mozilla/Logging.h"
+#include "LogModulePrefWatcher.h"
+
+using namespace mozilla;
+
+static LazyLogModule nsComponentManagerLog("nsComponentManager");
+
+#if 0 || defined (DEBUG_timeless)
+ #define SHOW_DENIED_ON_SHUTDOWN
+ #define SHOW_CI_ON_EXISTING_SERVICE
+#endif
+
+// Bloated registry buffer size to improve startup performance -- needs to
+// be big enough to fit the entire file into memory or it'll thrash.
+// 512K is big enough to allow for some future growth in the registry.
+#define BIG_REGISTRY_BUFLEN (512*1024)
+
+// Common Key Names
+const char xpcomComponentsKeyName[] = "software/mozilla/XPCOM/components";
+const char xpcomKeyName[] = "software/mozilla/XPCOM";
+
+// Common Value Names
+const char fileSizeValueName[] = "FileSize";
+const char lastModValueName[] = "LastModTimeStamp";
+const char nativeComponentType[] = "application/x-mozilla-native";
+const char staticComponentType[] = "application/x-mozilla-static";
+
+NS_DEFINE_CID(kCategoryManagerCID, NS_CATEGORYMANAGER_CID);
+
+#define UID_STRING_LENGTH 39
+
+nsresult
+nsGetServiceFromCategory::operator()(const nsIID& aIID,
+ void** aInstancePtr) const
+{
+ nsresult rv;
+ nsXPIDLCString value;
+ nsCOMPtr<nsICategoryManager> catman;
+ nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
+ if (!compMgr) {
+ rv = NS_ERROR_NOT_INITIALIZED;
+ goto error;
+ }
+
+ if (!mCategory || !mEntry) {
+ // when categories have defaults, use that for null mEntry
+ rv = NS_ERROR_NULL_POINTER;
+ goto error;
+ }
+
+ rv = compMgr->nsComponentManagerImpl::GetService(kCategoryManagerCID,
+ NS_GET_IID(nsICategoryManager),
+ getter_AddRefs(catman));
+ if (NS_FAILED(rv)) {
+ goto error;
+ }
+
+ /* find the contractID for category.entry */
+ rv = catman->GetCategoryEntry(mCategory, mEntry,
+ getter_Copies(value));
+ if (NS_FAILED(rv)) {
+ goto error;
+ }
+ if (!value) {
+ rv = NS_ERROR_SERVICE_NOT_AVAILABLE;
+ goto error;
+ }
+
+ rv = compMgr->nsComponentManagerImpl::GetServiceByContractID(value,
+ aIID,
+ aInstancePtr);
+ if (NS_FAILED(rv)) {
+error:
+ *aInstancePtr = 0;
+ }
+ if (mErrorPtr) {
+ *mErrorPtr = rv;
+ }
+ return rv;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Arena helper functions
+////////////////////////////////////////////////////////////////////////////////
+char*
+ArenaStrndup(const char* aStr, uint32_t aLen, PLArenaPool* aArena)
+{
+ void* mem;
+ // Include trailing null in the aLen
+ PL_ARENA_ALLOCATE(mem, aArena, aLen + 1);
+ if (mem) {
+ memcpy(mem, aStr, aLen + 1);
+ }
+ return static_cast<char*>(mem);
+}
+
+char*
+ArenaStrdup(const char* aStr, PLArenaPool* aArena)
+{
+ return ArenaStrndup(aStr, strlen(aStr), aArena);
+}
+
+// GetService and a few other functions need to exit their mutex mid-function
+// without reentering it later in the block. This class supports that
+// style of early-exit that MutexAutoUnlock doesn't.
+
+namespace {
+
+class MOZ_STACK_CLASS MutexLock
+{
+public:
+ explicit MutexLock(SafeMutex& aMutex)
+ : mMutex(aMutex)
+ , mLocked(false)
+ {
+ Lock();
+ }
+
+ ~MutexLock()
+ {
+ if (mLocked) {
+ Unlock();
+ }
+ }
+
+ void Lock()
+ {
+ NS_ASSERTION(!mLocked, "Re-entering a mutex");
+ mMutex.Lock();
+ mLocked = true;
+ }
+
+ void Unlock()
+ {
+ NS_ASSERTION(mLocked, "Exiting a mutex that isn't held!");
+ mMutex.Unlock();
+ mLocked = false;
+ }
+
+private:
+ SafeMutex& mMutex;
+ bool mLocked;
+};
+
+} // namespace
+
+// this is safe to call during InitXPCOM
+static already_AddRefed<nsIFile>
+GetLocationFromDirectoryService(const char* aProp)
+{
+ nsCOMPtr<nsIProperties> directoryService;
+ nsDirectoryService::Create(nullptr,
+ NS_GET_IID(nsIProperties),
+ getter_AddRefs(directoryService));
+
+ if (!directoryService) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIFile> file;
+ nsresult rv = directoryService->Get(aProp,
+ NS_GET_IID(nsIFile),
+ getter_AddRefs(file));
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ return file.forget();
+}
+
+static already_AddRefed<nsIFile>
+CloneAndAppend(nsIFile* aBase, const nsACString& aAppend)
+{
+ nsCOMPtr<nsIFile> f;
+ aBase->Clone(getter_AddRefs(f));
+ if (!f) {
+ return nullptr;
+ }
+
+ f->AppendNative(aAppend);
+ return f.forget();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsComponentManagerImpl
+////////////////////////////////////////////////////////////////////////////////
+
+nsresult
+nsComponentManagerImpl::Create(nsISupports* aOuter, REFNSIID aIID,
+ void** aResult)
+{
+ if (aOuter) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ if (!gComponentManager) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return gComponentManager->QueryInterface(aIID, aResult);
+}
+
+static const int CONTRACTID_HASHTABLE_INITIAL_LENGTH = 1024;
+
+nsComponentManagerImpl::nsComponentManagerImpl()
+ : mFactories(CONTRACTID_HASHTABLE_INITIAL_LENGTH)
+ , mContractIDs(CONTRACTID_HASHTABLE_INITIAL_LENGTH)
+ , mLock("nsComponentManagerImpl.mLock")
+ , mStatus(NOT_INITIALIZED)
+{
+}
+
+nsTArray<const mozilla::Module*>* nsComponentManagerImpl::sStaticModules;
+
+NSMODULE_DEFN(start_kPStaticModules);
+NSMODULE_DEFN(end_kPStaticModules);
+
+/* The content between start_kPStaticModules and end_kPStaticModules is gathered
+ * by the linker from various objects containing symbols in a specific section.
+ * ASAN considers (rightfully) the use of this content as a global buffer
+ * overflow. But this is a deliberate and well-considered choice, with no proper
+ * way to make ASAN happy. */
+MOZ_ASAN_BLACKLIST
+/* static */ void
+nsComponentManagerImpl::InitializeStaticModules()
+{
+ if (sStaticModules) {
+ return;
+ }
+
+ sStaticModules = new nsTArray<const mozilla::Module*>;
+ for (const mozilla::Module * const* staticModules =
+ &NSMODULE_NAME(start_kPStaticModules) + 1;
+ staticModules < &NSMODULE_NAME(end_kPStaticModules); ++staticModules)
+ if (*staticModules) { // ASAN adds padding
+ sStaticModules->AppendElement(*staticModules);
+ }
+}
+
+nsTArray<nsComponentManagerImpl::ComponentLocation>*
+nsComponentManagerImpl::sModuleLocations;
+
+/* static */ void
+nsComponentManagerImpl::InitializeModuleLocations()
+{
+ if (sModuleLocations) {
+ return;
+ }
+
+ sModuleLocations = new nsTArray<ComponentLocation>;
+}
+
+nsresult
+nsComponentManagerImpl::Init()
+{
+ MOZ_ASSERT(NOT_INITIALIZED == mStatus);
+
+ // Initialize our arena
+ PL_INIT_ARENA_POOL(&mArena, "ComponentManagerArena", NS_CM_BLOCK_SIZE);
+
+ nsCOMPtr<nsIFile> greDir =
+ GetLocationFromDirectoryService(NS_GRE_DIR);
+ nsCOMPtr<nsIFile> appDir =
+ GetLocationFromDirectoryService(NS_XPCOM_CURRENT_PROCESS_DIR);
+
+ InitializeStaticModules();
+
+ nsresult rv = mNativeModuleLoader.Init();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsCategoryManager::GetSingleton()->SuppressNotifications(true);
+
+ RegisterModule(&kXPCOMModule, nullptr);
+
+ for (uint32_t i = 0; i < sStaticModules->Length(); ++i) {
+ RegisterModule((*sStaticModules)[i], nullptr);
+ }
+
+ bool loadChromeManifests = (XRE_GetProcessType() != GeckoProcessType_GPU);
+ if (loadChromeManifests) {
+ // The overall order in which chrome.manifests are expected to be treated
+ // is the following:
+ // - greDir
+ // - greDir's omni.ja
+ // - appDir
+ // - appDir's omni.ja
+
+ InitializeModuleLocations();
+ ComponentLocation* cl = sModuleLocations->AppendElement();
+ nsCOMPtr<nsIFile> lf = CloneAndAppend(greDir,
+ NS_LITERAL_CSTRING("chrome.manifest"));
+ cl->type = NS_APP_LOCATION;
+ cl->location.Init(lf);
+
+ RefPtr<nsZipArchive> greOmnijar =
+ mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
+ if (greOmnijar) {
+ cl = sModuleLocations->AppendElement();
+ cl->type = NS_APP_LOCATION;
+ cl->location.Init(greOmnijar, "chrome.manifest");
+ }
+
+ bool equals = false;
+ appDir->Equals(greDir, &equals);
+ if (!equals) {
+ cl = sModuleLocations->AppendElement();
+ cl->type = NS_APP_LOCATION;
+ lf = CloneAndAppend(appDir, NS_LITERAL_CSTRING("chrome.manifest"));
+ cl->location.Init(lf);
+ }
+
+ RefPtr<nsZipArchive> appOmnijar =
+ mozilla::Omnijar::GetReader(mozilla::Omnijar::APP);
+ if (appOmnijar) {
+ cl = sModuleLocations->AppendElement();
+ cl->type = NS_APP_LOCATION;
+ cl->location.Init(appOmnijar, "chrome.manifest");
+ }
+
+ RereadChromeManifests(false);
+ }
+
+ nsCategoryManager::GetSingleton()->SuppressNotifications(false);
+
+ RegisterWeakMemoryReporter(this);
+
+ // NB: The logging preference watcher needs to be registered late enough in
+ // startup that it's okay to use the preference system, but also as soon as
+ // possible so that log modules enabled via preferences are turned on as
+ // early as possible.
+ //
+ // We can't initialize the preference watcher when the log module manager is
+ // initialized, as a number of things attempt to start logging before the
+ // preference system is initialized.
+ //
+ // The preference system is registered as a component so at this point during
+ // component manager initialization we know it is setup and we can register
+ // for notifications.
+ LogModulePrefWatcher::RegisterPrefWatcher();
+
+ // Unfortunately, we can't register the nsCategoryManager memory reporter
+ // in its constructor (which is triggered by the GetSingleton() call
+ // above) because the memory reporter manager isn't initialized at that
+ // point. So we wait until now.
+ nsCategoryManager::GetSingleton()->InitMemoryReporter();
+
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
+ ("nsComponentManager: Initialized."));
+
+ mStatus = NORMAL;
+
+ return NS_OK;
+}
+
+static bool
+ProcessSelectorMatches(Module::ProcessSelector aSelector)
+{
+ GeckoProcessType type = XRE_GetProcessType();
+ if (type == GeckoProcessType_GPU) {
+ return !!(aSelector & Module::ALLOW_IN_GPU_PROCESS);
+ }
+
+ if (aSelector & Module::MAIN_PROCESS_ONLY) {
+ return type == GeckoProcessType_Default;
+ }
+ if (aSelector & Module::CONTENT_PROCESS_ONLY) {
+ return type == GeckoProcessType_Content;
+ }
+ return true;
+}
+
+static const int kModuleVersionWithSelector = 51;
+
+void
+nsComponentManagerImpl::RegisterModule(const mozilla::Module* aModule,
+ FileLocation* aFile)
+{
+ mLock.AssertNotCurrentThreadOwns();
+
+ if (aModule->mVersion >= kModuleVersionWithSelector &&
+ !ProcessSelectorMatches(aModule->selector))
+ {
+ return;
+ }
+
+ {
+ // Scope the monitor so that we don't hold it while calling into the
+ // category manager.
+ MutexLock lock(mLock);
+
+ KnownModule* m;
+ if (aFile) {
+ nsCString uri;
+ aFile->GetURIString(uri);
+ NS_ASSERTION(!mKnownModules.Get(uri),
+ "Must not register a binary module twice.");
+
+ m = new KnownModule(aModule, *aFile);
+ mKnownModules.Put(uri, m);
+ } else {
+ m = new KnownModule(aModule);
+ mKnownStaticModules.AppendElement(m);
+ }
+
+ if (aModule->mCIDs) {
+ const mozilla::Module::CIDEntry* entry;
+ for (entry = aModule->mCIDs; entry->cid; ++entry) {
+ RegisterCIDEntryLocked(entry, m);
+ }
+ }
+
+ if (aModule->mContractIDs) {
+ const mozilla::Module::ContractIDEntry* entry;
+ for (entry = aModule->mContractIDs; entry->contractid; ++entry) {
+ RegisterContractIDLocked(entry);
+ }
+ MOZ_ASSERT(!entry->cid, "Incorrectly terminated contract list");
+ }
+ }
+
+ if (aModule->mCategoryEntries) {
+ const mozilla::Module::CategoryEntry* entry;
+ for (entry = aModule->mCategoryEntries; entry->category; ++entry)
+ nsCategoryManager::GetSingleton()->AddCategoryEntry(entry->category,
+ entry->entry,
+ entry->value);
+ }
+}
+
+void
+nsComponentManagerImpl::RegisterCIDEntryLocked(
+ const mozilla::Module::CIDEntry* aEntry,
+ KnownModule* aModule)
+{
+ mLock.AssertCurrentThreadOwns();
+
+ if (!ProcessSelectorMatches(aEntry->processSelector)) {
+ return;
+ }
+
+ nsFactoryEntry* f = mFactories.Get(*aEntry->cid);
+ if (f) {
+ NS_WARNING("Re-registering a CID?");
+
+ char idstr[NSID_LENGTH];
+ aEntry->cid->ToProvidedString(idstr);
+
+ nsCString existing;
+ if (f->mModule) {
+ existing = f->mModule->Description();
+ } else {
+ existing = "<unknown module>";
+ }
+ SafeMutexAutoUnlock unlock(mLock);
+ LogMessage("While registering XPCOM module %s, trying to re-register CID '%s' already registered by %s.",
+ aModule->Description().get(),
+ idstr,
+ existing.get());
+ return;
+ }
+
+ f = new nsFactoryEntry(aEntry, aModule);
+ mFactories.Put(*aEntry->cid, f);
+}
+
+void
+nsComponentManagerImpl::RegisterContractIDLocked(
+ const mozilla::Module::ContractIDEntry* aEntry)
+{
+ mLock.AssertCurrentThreadOwns();
+
+ if (!ProcessSelectorMatches(aEntry->processSelector)) {
+ return;
+ }
+
+ nsFactoryEntry* f = mFactories.Get(*aEntry->cid);
+ if (!f) {
+ NS_WARNING("No CID found when attempting to map contract ID");
+
+ char idstr[NSID_LENGTH];
+ aEntry->cid->ToProvidedString(idstr);
+
+ SafeMutexAutoUnlock unlock(mLock);
+ LogMessage("Could not map contract ID '%s' to CID %s because no implementation of the CID is registered.",
+ aEntry->contractid,
+ idstr);
+
+ return;
+ }
+
+ mContractIDs.Put(nsDependentCString(aEntry->contractid), f);
+}
+
+static void
+CutExtension(nsCString& aPath)
+{
+ int32_t dotPos = aPath.RFindChar('.');
+ if (kNotFound == dotPos) {
+ aPath.Truncate();
+ } else {
+ aPath.Cut(0, dotPos + 1);
+ }
+}
+
+static void
+DoRegisterManifest(NSLocationType aType,
+ FileLocation& aFile,
+ bool aChromeOnly,
+ bool aXPTOnly)
+{
+ MOZ_ASSERT(!aXPTOnly || !nsComponentManagerImpl::gComponentManager);
+ uint32_t len;
+ FileLocation::Data data;
+ UniquePtr<char[]> buf;
+ nsresult rv = aFile.GetData(data);
+ if (NS_SUCCEEDED(rv)) {
+ rv = data.GetSize(&len);
+ }
+ if (NS_SUCCEEDED(rv)) {
+ buf = MakeUnique<char[]>(len + 1);
+ rv = data.Copy(buf.get(), len);
+ }
+ if (NS_SUCCEEDED(rv)) {
+ buf[len] = '\0';
+ ParseManifest(aType, aFile, buf.get(), aChromeOnly, aXPTOnly);
+ } else if (NS_BOOTSTRAPPED_LOCATION != aType) {
+ nsCString uri;
+ aFile.GetURIString(uri);
+ LogMessage("Could not read chrome manifest '%s'.", uri.get());
+ }
+}
+
+void
+nsComponentManagerImpl::RegisterManifest(NSLocationType aType,
+ FileLocation& aFile,
+ bool aChromeOnly)
+{
+ DoRegisterManifest(aType, aFile, aChromeOnly, false);
+}
+
+void
+nsComponentManagerImpl::ManifestManifest(ManifestProcessingContext& aCx,
+ int aLineNo, char* const* aArgv)
+{
+ char* file = aArgv[0];
+ FileLocation f(aCx.mFile, file);
+ RegisterManifest(aCx.mType, f, aCx.mChromeOnly);
+}
+
+void
+nsComponentManagerImpl::ManifestBinaryComponent(ManifestProcessingContext& aCx,
+ int aLineNo,
+ char* const* aArgv)
+{
+ if (aCx.mFile.IsZip()) {
+ NS_WARNING("Cannot load binary components from a jar.");
+ LogMessageWithContext(aCx.mFile, aLineNo,
+ "Cannot load binary components from a jar.");
+ return;
+ }
+
+ FileLocation f(aCx.mFile, aArgv[0]);
+ nsCString uri;
+ f.GetURIString(uri);
+
+ if (mKnownModules.Get(uri)) {
+ NS_WARNING("Attempting to register a binary component twice.");
+ LogMessageWithContext(aCx.mFile, aLineNo,
+ "Attempting to register a binary component twice.");
+ return;
+ }
+
+ const mozilla::Module* m = mNativeModuleLoader.LoadModule(f);
+ // The native module loader should report an error here, we don't have to
+ if (!m) {
+ return;
+ }
+
+ RegisterModule(m, &f);
+}
+
+static void
+DoRegisterXPT(FileLocation& aFile)
+{
+ uint32_t len;
+ FileLocation::Data data;
+ UniquePtr<char[]> buf;
+ nsresult rv = aFile.GetData(data);
+ if (NS_SUCCEEDED(rv)) {
+ rv = data.GetSize(&len);
+ }
+ if (NS_SUCCEEDED(rv)) {
+ buf = MakeUnique<char[]>(len);
+ rv = data.Copy(buf.get(), len);
+ }
+ if (NS_SUCCEEDED(rv)) {
+ XPTInterfaceInfoManager::GetSingleton()->RegisterBuffer(buf.get(), len);
+ } else {
+ nsCString uri;
+ aFile.GetURIString(uri);
+ LogMessage("Could not read '%s'.", uri.get());
+ }
+}
+
+void
+nsComponentManagerImpl::ManifestXPT(ManifestProcessingContext& aCx,
+ int aLineNo, char* const* aArgv)
+{
+ FileLocation f(aCx.mFile, aArgv[0]);
+ DoRegisterXPT(f);
+}
+
+void
+nsComponentManagerImpl::ManifestComponent(ManifestProcessingContext& aCx,
+ int aLineNo, char* const* aArgv)
+{
+ mLock.AssertNotCurrentThreadOwns();
+
+ char* id = aArgv[0];
+ char* file = aArgv[1];
+
+ nsID cid;
+ if (!cid.Parse(id)) {
+ LogMessageWithContext(aCx.mFile, aLineNo,
+ "Malformed CID: '%s'.", id);
+ return;
+ }
+
+ // Precompute the hash/file data outside of the lock
+ FileLocation fl(aCx.mFile, file);
+ nsCString hash;
+ fl.GetURIString(hash);
+
+ MutexLock lock(mLock);
+ nsFactoryEntry* f = mFactories.Get(cid);
+ if (f) {
+ char idstr[NSID_LENGTH];
+ cid.ToProvidedString(idstr);
+
+ nsCString existing;
+ if (f->mModule) {
+ existing = f->mModule->Description();
+ } else {
+ existing = "<unknown module>";
+ }
+
+ lock.Unlock();
+
+ LogMessageWithContext(aCx.mFile, aLineNo,
+ "Trying to re-register CID '%s' already registered by %s.",
+ idstr,
+ existing.get());
+ return;
+ }
+
+ KnownModule* km;
+
+ km = mKnownModules.Get(hash);
+ if (!km) {
+ km = new KnownModule(fl);
+ mKnownModules.Put(hash, km);
+ }
+
+ void* place;
+
+ PL_ARENA_ALLOCATE(place, &mArena, sizeof(nsCID));
+ nsID* permanentCID = static_cast<nsID*>(place);
+ *permanentCID = cid;
+
+ PL_ARENA_ALLOCATE(place, &mArena, sizeof(mozilla::Module::CIDEntry));
+ mozilla::Module::CIDEntry* e = new (place) mozilla::Module::CIDEntry();
+ e->cid = permanentCID;
+
+ f = new nsFactoryEntry(e, km);
+ mFactories.Put(cid, f);
+}
+
+void
+nsComponentManagerImpl::ManifestContract(ManifestProcessingContext& aCx,
+ int aLineNo, char* const* aArgv)
+{
+ mLock.AssertNotCurrentThreadOwns();
+
+ char* contract = aArgv[0];
+ char* id = aArgv[1];
+
+ nsID cid;
+ if (!cid.Parse(id)) {
+ LogMessageWithContext(aCx.mFile, aLineNo,
+ "Malformed CID: '%s'.", id);
+ return;
+ }
+
+ MutexLock lock(mLock);
+ nsFactoryEntry* f = mFactories.Get(cid);
+ if (!f) {
+ lock.Unlock();
+ LogMessageWithContext(aCx.mFile, aLineNo,
+ "Could not map contract ID '%s' to CID %s because no implementation of the CID is registered.",
+ contract, id);
+ return;
+ }
+
+ mContractIDs.Put(nsDependentCString(contract), f);
+}
+
+void
+nsComponentManagerImpl::ManifestCategory(ManifestProcessingContext& aCx,
+ int aLineNo, char* const* aArgv)
+{
+ char* category = aArgv[0];
+ char* key = aArgv[1];
+ char* value = aArgv[2];
+
+ nsCategoryManager::GetSingleton()->
+ AddCategoryEntry(category, key, value);
+}
+
+void
+nsComponentManagerImpl::RereadChromeManifests(bool aChromeOnly)
+{
+ for (uint32_t i = 0; i < sModuleLocations->Length(); ++i) {
+ ComponentLocation& l = sModuleLocations->ElementAt(i);
+ RegisterManifest(l.type, l.location, aChromeOnly);
+ }
+}
+
+bool
+nsComponentManagerImpl::KnownModule::EnsureLoader()
+{
+ if (!mLoader) {
+ nsCString extension;
+ mFile.GetURIString(extension);
+ CutExtension(extension);
+ mLoader =
+ nsComponentManagerImpl::gComponentManager->LoaderForExtension(extension);
+ }
+ return !!mLoader;
+}
+
+bool
+nsComponentManagerImpl::KnownModule::Load()
+{
+ if (mFailed) {
+ return false;
+ }
+ if (!mModule) {
+ if (!EnsureLoader()) {
+ return false;
+ }
+
+ mModule = mLoader->LoadModule(mFile);
+
+ if (!mModule) {
+ mFailed = true;
+ return false;
+ }
+ }
+ if (!mLoaded) {
+ if (mModule->loadProc) {
+ nsresult rv = mModule->loadProc();
+ if (NS_FAILED(rv)) {
+ mFailed = true;
+ return false;
+ }
+ }
+ mLoaded = true;
+ }
+ return true;
+}
+
+nsCString
+nsComponentManagerImpl::KnownModule::Description() const
+{
+ nsCString s;
+ if (mFile) {
+ mFile.GetURIString(s);
+ } else {
+ s = "<static module>";
+ }
+ return s;
+}
+
+nsresult nsComponentManagerImpl::Shutdown(void)
+{
+ MOZ_ASSERT(NORMAL == mStatus);
+
+ mStatus = SHUTDOWN_IN_PROGRESS;
+
+ // Shutdown the component manager
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
+ ("nsComponentManager: Beginning Shutdown."));
+
+ UnregisterWeakMemoryReporter(this);
+
+ // Release all cached factories
+ mContractIDs.Clear();
+ mFactories.Clear(); // XXX release the objects, don't just clear
+ mLoaderMap.Clear();
+ mKnownModules.Clear();
+ mKnownStaticModules.Clear();
+
+ delete sStaticModules;
+ delete sModuleLocations;
+
+ // Unload libraries
+ mNativeModuleLoader.UnloadLibraries();
+
+ // delete arena for strings and small objects
+ PL_FinishArenaPool(&mArena);
+
+ mStatus = SHUTDOWN_COMPLETE;
+
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
+ ("nsComponentManager: Shutdown complete."));
+
+ return NS_OK;
+}
+
+nsComponentManagerImpl::~nsComponentManagerImpl()
+{
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
+ ("nsComponentManager: Beginning destruction."));
+
+ if (SHUTDOWN_COMPLETE != mStatus) {
+ Shutdown();
+ }
+
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
+ ("nsComponentManager: Destroyed."));
+}
+
+NS_IMPL_ISUPPORTS(nsComponentManagerImpl,
+ nsIComponentManager,
+ nsIServiceManager,
+ nsIComponentRegistrar,
+ nsISupportsWeakReference,
+ nsIInterfaceRequestor,
+ nsIMemoryReporter)
+
+nsresult
+nsComponentManagerImpl::GetInterface(const nsIID& aUuid, void** aResult)
+{
+ NS_WARNING("This isn't supported");
+ // fall through to QI as anything QIable is a superset of what can be
+ // got via the GetInterface()
+ return QueryInterface(aUuid, aResult);
+}
+
+nsFactoryEntry*
+nsComponentManagerImpl::GetFactoryEntry(const char* aContractID,
+ uint32_t aContractIDLen)
+{
+ SafeMutexAutoLock lock(mLock);
+ return mContractIDs.Get(nsDependentCString(aContractID, aContractIDLen));
+}
+
+
+nsFactoryEntry*
+nsComponentManagerImpl::GetFactoryEntry(const nsCID& aClass)
+{
+ SafeMutexAutoLock lock(mLock);
+ return mFactories.Get(aClass);
+}
+
+already_AddRefed<nsIFactory>
+nsComponentManagerImpl::FindFactory(const nsCID& aClass)
+{
+ nsFactoryEntry* e = GetFactoryEntry(aClass);
+ if (!e) {
+ return nullptr;
+ }
+
+ return e->GetFactory();
+}
+
+already_AddRefed<nsIFactory>
+nsComponentManagerImpl::FindFactory(const char* aContractID,
+ uint32_t aContractIDLen)
+{
+ nsFactoryEntry* entry = GetFactoryEntry(aContractID, aContractIDLen);
+ if (!entry) {
+ return nullptr;
+ }
+
+ return entry->GetFactory();
+}
+
+/**
+ * GetClassObject()
+ *
+ * Given a classID, this finds the singleton ClassObject that implements the CID.
+ * Returns an interface of type aIID off the singleton classobject.
+ */
+NS_IMETHODIMP
+nsComponentManagerImpl::GetClassObject(const nsCID& aClass, const nsIID& aIID,
+ void** aResult)
+{
+ nsresult rv;
+
+ if (MOZ_LOG_TEST(nsComponentManagerLog, LogLevel::Debug)) {
+ char* buf = aClass.ToString();
+ PR_LogPrint("nsComponentManager: GetClassObject(%s)", buf);
+ if (buf) {
+ free(buf);
+ }
+ }
+
+ MOZ_ASSERT(aResult != nullptr);
+
+ nsCOMPtr<nsIFactory> factory = FindFactory(aClass);
+ if (!factory) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ rv = factory->QueryInterface(aIID, aResult);
+
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Warning,
+ ("\t\tGetClassObject() %s", NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"));
+
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsComponentManagerImpl::GetClassObjectByContractID(const char* aContractID,
+ const nsIID& aIID,
+ void** aResult)
+{
+ if (NS_WARN_IF(!aResult) ||
+ NS_WARN_IF(!aContractID)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsresult rv;
+
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
+ ("nsComponentManager: GetClassObject(%s)", aContractID));
+
+ nsCOMPtr<nsIFactory> factory = FindFactory(aContractID, strlen(aContractID));
+ if (!factory) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ rv = factory->QueryInterface(aIID, aResult);
+
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Warning,
+ ("\t\tGetClassObject() %s", NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"));
+
+ return rv;
+}
+
+/**
+ * CreateInstance()
+ *
+ * Create an instance of an object that implements an interface and belongs
+ * to the implementation aClass using the factory. The factory is immediately
+ * released and not held onto for any longer.
+ */
+NS_IMETHODIMP
+nsComponentManagerImpl::CreateInstance(const nsCID& aClass,
+ nsISupports* aDelegate,
+ const nsIID& aIID,
+ void** aResult)
+{
+ // test this first, since there's no point in creating a component during
+ // shutdown -- whether it's available or not would depend on the order it
+ // occurs in the list
+ if (gXPCOMShuttingDown) {
+ // When processing shutdown, don't process new GetService() requests
+#ifdef SHOW_DENIED_ON_SHUTDOWN
+ nsXPIDLCString cid, iid;
+ cid.Adopt(aClass.ToString());
+ iid.Adopt(aIID.ToString());
+ fprintf(stderr, "Creating new instance on shutdown. Denied.\n"
+ " CID: %s\n IID: %s\n", cid.get(), iid.get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ *aResult = nullptr;
+
+ nsFactoryEntry* entry = GetFactoryEntry(aClass);
+
+ if (!entry) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+#ifdef SHOW_CI_ON_EXISTING_SERVICE
+ if (entry->mServiceObject) {
+ nsXPIDLCString cid;
+ cid.Adopt(aClass.ToString());
+ nsAutoCString message;
+ message = NS_LITERAL_CSTRING("You are calling CreateInstance \"") +
+ cid +
+ NS_LITERAL_CSTRING("\" when a service for this CID already exists!");
+ NS_ERROR(message.get());
+ }
+#endif
+
+ nsresult rv;
+ nsCOMPtr<nsIFactory> factory = entry->GetFactory();
+ if (factory) {
+ rv = factory->CreateInstance(aDelegate, aIID, aResult);
+ if (NS_SUCCEEDED(rv) && !*aResult) {
+ NS_ERROR("Factory did not return an object but returned success!");
+ rv = NS_ERROR_SERVICE_NOT_FOUND;
+ }
+ } else {
+ // Translate error values
+ rv = NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ if (MOZ_LOG_TEST(nsComponentManagerLog, LogLevel::Warning)) {
+ char* buf = aClass.ToString();
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Warning,
+ ("nsComponentManager: CreateInstance(%s) %s", buf,
+ NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"));
+ if (buf) {
+ free(buf);
+ }
+ }
+
+ return rv;
+}
+
+/**
+ * CreateInstanceByContractID()
+ *
+ * A variant of CreateInstance() that creates an instance of the object that
+ * implements the interface aIID and whose implementation has a contractID aContractID.
+ *
+ * This is only a convenience routine that turns around can calls the
+ * CreateInstance() with classid and iid.
+ */
+NS_IMETHODIMP
+nsComponentManagerImpl::CreateInstanceByContractID(const char* aContractID,
+ nsISupports* aDelegate,
+ const nsIID& aIID,
+ void** aResult)
+{
+ if (NS_WARN_IF(!aContractID)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // test this first, since there's no point in creating a component during
+ // shutdown -- whether it's available or not would depend on the order it
+ // occurs in the list
+ if (gXPCOMShuttingDown) {
+ // When processing shutdown, don't process new GetService() requests
+#ifdef SHOW_DENIED_ON_SHUTDOWN
+ nsXPIDLCString iid;
+ iid.Adopt(aIID.ToString());
+ fprintf(stderr, "Creating new instance on shutdown. Denied.\n"
+ " ContractID: %s\n IID: %s\n", aContractID, iid.get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ *aResult = nullptr;
+
+ nsFactoryEntry* entry = GetFactoryEntry(aContractID, strlen(aContractID));
+
+ if (!entry) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+#ifdef SHOW_CI_ON_EXISTING_SERVICE
+ if (entry->mServiceObject) {
+ nsAutoCString message;
+ message =
+ NS_LITERAL_CSTRING("You are calling CreateInstance \"") +
+ nsDependentCString(aContractID) +
+ NS_LITERAL_CSTRING("\" when a service for this CID already exists! "
+ "Add it to abusedContracts to track down the service consumer.");
+ NS_ERROR(message.get());
+ }
+#endif
+
+ nsresult rv;
+ nsCOMPtr<nsIFactory> factory = entry->GetFactory();
+ if (factory) {
+
+ rv = factory->CreateInstance(aDelegate, aIID, aResult);
+ if (NS_SUCCEEDED(rv) && !*aResult) {
+ NS_ERROR("Factory did not return an object but returned success!");
+ rv = NS_ERROR_SERVICE_NOT_FOUND;
+ }
+ } else {
+ // Translate error values
+ rv = NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Warning,
+ ("nsComponentManager: CreateInstanceByContractID(%s) %s", aContractID,
+ NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"));
+
+ return rv;
+}
+
+nsresult
+nsComponentManagerImpl::FreeServices()
+{
+ NS_ASSERTION(gXPCOMShuttingDown,
+ "Must be shutting down in order to free all services");
+
+ if (!gXPCOMShuttingDown) {
+ return NS_ERROR_FAILURE;
+ }
+
+ for (auto iter = mFactories.Iter(); !iter.Done(); iter.Next()) {
+ nsFactoryEntry* entry = iter.UserData();
+ entry->mFactory = nullptr;
+ entry->mServiceObject = nullptr;
+ }
+
+ return NS_OK;
+}
+
+// This should only ever be called within the monitor!
+nsComponentManagerImpl::PendingServiceInfo*
+nsComponentManagerImpl::AddPendingService(const nsCID& aServiceCID,
+ PRThread* aThread)
+{
+ PendingServiceInfo* newInfo = mPendingServices.AppendElement();
+ if (newInfo) {
+ newInfo->cid = &aServiceCID;
+ newInfo->thread = aThread;
+ }
+ return newInfo;
+}
+
+// This should only ever be called within the monitor!
+void
+nsComponentManagerImpl::RemovePendingService(const nsCID& aServiceCID)
+{
+ uint32_t pendingCount = mPendingServices.Length();
+ for (uint32_t index = 0; index < pendingCount; ++index) {
+ const PendingServiceInfo& info = mPendingServices.ElementAt(index);
+ if (info.cid->Equals(aServiceCID)) {
+ mPendingServices.RemoveElementAt(index);
+ return;
+ }
+ }
+}
+
+// This should only ever be called within the monitor!
+PRThread*
+nsComponentManagerImpl::GetPendingServiceThread(const nsCID& aServiceCID) const
+{
+ uint32_t pendingCount = mPendingServices.Length();
+ for (uint32_t index = 0; index < pendingCount; ++index) {
+ const PendingServiceInfo& info = mPendingServices.ElementAt(index);
+ if (info.cid->Equals(aServiceCID)) {
+ return info.thread;
+ }
+ }
+ return nullptr;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::GetService(const nsCID& aClass,
+ const nsIID& aIID,
+ void** aResult)
+{
+ // test this first, since there's no point in returning a service during
+ // shutdown -- whether it's available or not would depend on the order it
+ // occurs in the list
+ if (gXPCOMShuttingDown) {
+ // When processing shutdown, don't process new GetService() requests
+#ifdef SHOW_DENIED_ON_SHUTDOWN
+ nsXPIDLCString cid, iid;
+ cid.Adopt(aClass.ToString());
+ iid.Adopt(aIID.ToString());
+ fprintf(stderr, "Getting service on shutdown. Denied.\n"
+ " CID: %s\n IID: %s\n", cid.get(), iid.get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // `service` must be released after the lock is released, so it must be
+ // declared before the lock in this C++ block.
+ nsCOMPtr<nsISupports> service;
+ MutexLock lock(mLock);
+
+ nsFactoryEntry* entry = mFactories.Get(aClass);
+ if (!entry) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ if (entry->mServiceObject) {
+ lock.Unlock();
+ return entry->mServiceObject->QueryInterface(aIID, aResult);
+ }
+
+ PRThread* currentPRThread = PR_GetCurrentThread();
+ MOZ_ASSERT(currentPRThread, "This should never be null!");
+
+ // Needed to optimize the event loop below.
+ nsIThread* currentThread = nullptr;
+
+ PRThread* pendingPRThread;
+ while ((pendingPRThread = GetPendingServiceThread(aClass))) {
+ if (pendingPRThread == currentPRThread) {
+ NS_ERROR("Recursive GetService!");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+
+ SafeMutexAutoUnlock unlockPending(mLock);
+
+ if (!currentThread) {
+ currentThread = NS_GetCurrentThread();
+ MOZ_ASSERT(currentThread, "This should never be null!");
+ }
+
+ // This will process a single event or yield the thread if no event is
+ // pending.
+ if (!NS_ProcessNextEvent(currentThread, false)) {
+ PR_Sleep(PR_INTERVAL_NO_WAIT);
+ }
+ }
+
+ // It's still possible that the other thread failed to create the
+ // service so we're not guaranteed to have an entry or service yet.
+ if (entry->mServiceObject) {
+ lock.Unlock();
+ return entry->mServiceObject->QueryInterface(aIID, aResult);
+ }
+
+#ifdef DEBUG
+ PendingServiceInfo* newInfo =
+#endif
+ AddPendingService(aClass, currentPRThread);
+ NS_ASSERTION(newInfo, "Failed to add info to the array!");
+
+ // We need to not be holding the service manager's lock while calling
+ // CreateInstance, because it invokes user code which could try to re-enter
+ // the service manager:
+
+ nsresult rv;
+ {
+ SafeMutexAutoUnlock unlock(mLock);
+ rv = CreateInstance(aClass, nullptr, aIID, getter_AddRefs(service));
+ }
+ if (NS_SUCCEEDED(rv) && !service) {
+ NS_ERROR("Factory did not return an object but returned success");
+ return NS_ERROR_SERVICE_NOT_FOUND;
+ }
+
+#ifdef DEBUG
+ pendingPRThread = GetPendingServiceThread(aClass);
+ MOZ_ASSERT(pendingPRThread == currentPRThread,
+ "Pending service array has been changed!");
+#endif
+ RemovePendingService(aClass);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ NS_ASSERTION(!entry->mServiceObject, "Created two instances of a service!");
+
+ entry->mServiceObject = service.forget();
+
+ lock.Unlock();
+ nsISupports** sresult = reinterpret_cast<nsISupports**>(aResult);
+ *sresult = entry->mServiceObject;
+ (*sresult)->AddRef();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::IsServiceInstantiated(const nsCID& aClass,
+ const nsIID& aIID,
+ bool* aResult)
+{
+ // Now we want to get the service if we already got it. If not, we don't want
+ // to create an instance of it. mmh!
+
+ // test this first, since there's no point in returning a service during
+ // shutdown -- whether it's available or not would depend on the order it
+ // occurs in the list
+ if (gXPCOMShuttingDown) {
+ // When processing shutdown, don't process new GetService() requests
+#ifdef SHOW_DENIED_ON_SHUTDOWN
+ nsXPIDLCString cid, iid;
+ cid.Adopt(aClass.ToString());
+ iid.Adopt(aIID.ToString());
+ fprintf(stderr, "Checking for service on shutdown. Denied.\n"
+ " CID: %s\n IID: %s\n", cid.get(), iid.get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsresult rv = NS_ERROR_SERVICE_NOT_AVAILABLE;
+ nsFactoryEntry* entry;
+
+ {
+ SafeMutexAutoLock lock(mLock);
+ entry = mFactories.Get(aClass);
+ }
+
+ if (entry && entry->mServiceObject) {
+ nsCOMPtr<nsISupports> service;
+ rv = entry->mServiceObject->QueryInterface(aIID, getter_AddRefs(service));
+ *aResult = (service != nullptr);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::IsServiceInstantiatedByContractID(
+ const char* aContractID,
+ const nsIID& aIID,
+ bool* aResult)
+{
+ // Now we want to get the service if we already got it. If not, we don't want
+ // to create an instance of it. mmh!
+
+ // test this first, since there's no point in returning a service during
+ // shutdown -- whether it's available or not would depend on the order it
+ // occurs in the list
+ if (gXPCOMShuttingDown) {
+ // When processing shutdown, don't process new GetService() requests
+#ifdef SHOW_DENIED_ON_SHUTDOWN
+ nsXPIDLCString iid;
+ iid.Adopt(aIID.ToString());
+ fprintf(stderr, "Checking for service on shutdown. Denied.\n"
+ " ContractID: %s\n IID: %s\n", aContractID, iid.get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsresult rv = NS_ERROR_SERVICE_NOT_AVAILABLE;
+ nsFactoryEntry* entry;
+ {
+ SafeMutexAutoLock lock(mLock);
+ entry = mContractIDs.Get(nsDependentCString(aContractID));
+ }
+
+ if (entry && entry->mServiceObject) {
+ nsCOMPtr<nsISupports> service;
+ rv = entry->mServiceObject->QueryInterface(aIID, getter_AddRefs(service));
+ *aResult = (service != nullptr);
+ }
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsComponentManagerImpl::GetServiceByContractID(const char* aContractID,
+ const nsIID& aIID,
+ void** aResult)
+{
+ // test this first, since there's no point in returning a service during
+ // shutdown -- whether it's available or not would depend on the order it
+ // occurs in the list
+ if (gXPCOMShuttingDown) {
+ // When processing shutdown, don't process new GetService() requests
+#ifdef SHOW_DENIED_ON_SHUTDOWN
+ nsXPIDLCString iid;
+ iid.Adopt(aIID.ToString());
+ fprintf(stderr, "Getting service on shutdown. Denied.\n"
+ " ContractID: %s\n IID: %s\n", aContractID, iid.get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // `service` must be released after the lock is released, so it must be
+ // declared before the lock in this C++ block.
+ nsCOMPtr<nsISupports> service;
+ MutexLock lock(mLock);
+
+ nsFactoryEntry* entry = mContractIDs.Get(nsDependentCString(aContractID));
+ if (!entry) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ if (entry->mServiceObject) {
+ // We need to not be holding the service manager's monitor while calling
+ // QueryInterface, because it invokes user code which could try to re-enter
+ // the service manager, or try to grab some other lock/monitor/condvar
+ // and deadlock, e.g. bug 282743.
+ // `entry` is valid until XPCOM shutdown, so we can safely use it after
+ // exiting the lock.
+ lock.Unlock();
+ return entry->mServiceObject->QueryInterface(aIID, aResult);
+ }
+
+ PRThread* currentPRThread = PR_GetCurrentThread();
+ MOZ_ASSERT(currentPRThread, "This should never be null!");
+
+ // Needed to optimize the event loop below.
+ nsIThread* currentThread = nullptr;
+
+ PRThread* pendingPRThread;
+ while ((pendingPRThread = GetPendingServiceThread(*entry->mCIDEntry->cid))) {
+ if (pendingPRThread == currentPRThread) {
+ NS_ERROR("Recursive GetService!");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ SafeMutexAutoUnlock unlockPending(mLock);
+
+ if (!currentThread) {
+ currentThread = NS_GetCurrentThread();
+ MOZ_ASSERT(currentThread, "This should never be null!");
+ }
+
+ // This will process a single event or yield the thread if no event is
+ // pending.
+ if (!NS_ProcessNextEvent(currentThread, false)) {
+ PR_Sleep(PR_INTERVAL_NO_WAIT);
+ }
+ }
+
+ if (currentThread && entry->mServiceObject) {
+ // If we have a currentThread then we must have waited on another thread
+ // to create the service. Grab it now if that succeeded.
+ lock.Unlock();
+ return entry->mServiceObject->QueryInterface(aIID, aResult);
+ }
+
+#ifdef DEBUG
+ PendingServiceInfo* newInfo =
+#endif
+ AddPendingService(*entry->mCIDEntry->cid, currentPRThread);
+ NS_ASSERTION(newInfo, "Failed to add info to the array!");
+
+ // We need to not be holding the service manager's lock while calling
+ // CreateInstance, because it invokes user code which could try to re-enter
+ // the service manager:
+
+ nsresult rv;
+ {
+ SafeMutexAutoUnlock unlock(mLock);
+ rv = CreateInstanceByContractID(aContractID, nullptr, aIID,
+ getter_AddRefs(service));
+ }
+ if (NS_SUCCEEDED(rv) && !service) {
+ NS_ERROR("Factory did not return an object but returned success");
+ return NS_ERROR_SERVICE_NOT_FOUND;
+ }
+
+#ifdef DEBUG
+ pendingPRThread = GetPendingServiceThread(*entry->mCIDEntry->cid);
+ MOZ_ASSERT(pendingPRThread == currentPRThread,
+ "Pending service array has been changed!");
+#endif
+ RemovePendingService(*entry->mCIDEntry->cid);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ NS_ASSERTION(!entry->mServiceObject, "Created two instances of a service!");
+
+ entry->mServiceObject = service.forget();
+
+ lock.Unlock();
+
+ nsISupports** sresult = reinterpret_cast<nsISupports**>(aResult);
+ *sresult = entry->mServiceObject;
+ (*sresult)->AddRef();
+
+ return NS_OK;
+}
+
+already_AddRefed<mozilla::ModuleLoader>
+nsComponentManagerImpl::LoaderForExtension(const nsACString& aExt)
+{
+ nsCOMPtr<mozilla::ModuleLoader> loader = mLoaderMap.Get(aExt);
+ if (!loader) {
+ loader = do_GetServiceFromCategory("module-loader",
+ PromiseFlatCString(aExt).get());
+ if (!loader) {
+ return nullptr;
+ }
+
+ mLoaderMap.Put(aExt, loader);
+ }
+
+ return loader.forget();
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::RegisterFactory(const nsCID& aClass,
+ const char* aName,
+ const char* aContractID,
+ nsIFactory* aFactory)
+{
+ if (!aFactory) {
+ // If a null factory is passed in, this call just wants to reset
+ // the contract ID to point to an existing CID entry.
+ if (!aContractID) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ SafeMutexAutoLock lock(mLock);
+ nsFactoryEntry* oldf = mFactories.Get(aClass);
+ if (!oldf) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ mContractIDs.Put(nsDependentCString(aContractID), oldf);
+ return NS_OK;
+ }
+
+ nsAutoPtr<nsFactoryEntry> f(new nsFactoryEntry(aClass, aFactory));
+
+ SafeMutexAutoLock lock(mLock);
+ nsFactoryEntry* oldf = mFactories.Get(aClass);
+ if (oldf) {
+ return NS_ERROR_FACTORY_EXISTS;
+ }
+
+ if (aContractID) {
+ mContractIDs.Put(nsDependentCString(aContractID), f);
+ }
+
+ mFactories.Put(aClass, f.forget());
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::UnregisterFactory(const nsCID& aClass,
+ nsIFactory* aFactory)
+{
+ // Don't release the dying factory or service object until releasing
+ // the component manager monitor.
+ nsCOMPtr<nsIFactory> dyingFactory;
+ nsCOMPtr<nsISupports> dyingServiceObject;
+
+ {
+ SafeMutexAutoLock lock(mLock);
+ nsFactoryEntry* f = mFactories.Get(aClass);
+ if (!f || f->mFactory != aFactory) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ mFactories.Remove(aClass);
+
+ // This might leave a stale contractid -> factory mapping in
+ // place, so null out the factory entry (see
+ // nsFactoryEntry::GetFactory)
+ f->mFactory.swap(dyingFactory);
+ f->mServiceObject.swap(dyingServiceObject);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::AutoRegister(nsIFile* aLocation)
+{
+ XRE_AddManifestLocation(NS_EXTENSION_LOCATION, aLocation);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::AutoUnregister(nsIFile* aLocation)
+{
+ NS_ERROR("AutoUnregister not implemented.");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::RegisterFactoryLocation(const nsCID& aCID,
+ const char* aClassName,
+ const char* aContractID,
+ nsIFile* aFile,
+ const char* aLoaderStr,
+ const char* aType)
+{
+ NS_ERROR("RegisterFactoryLocation not implemented.");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::UnregisterFactoryLocation(const nsCID& aCID,
+ nsIFile* aFile)
+{
+ NS_ERROR("UnregisterFactoryLocation not implemented.");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::IsCIDRegistered(const nsCID& aClass,
+ bool* aResult)
+{
+ *aResult = (nullptr != GetFactoryEntry(aClass));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::IsContractIDRegistered(const char* aClass,
+ bool* aResult)
+{
+ if (NS_WARN_IF(!aClass)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsFactoryEntry* entry = GetFactoryEntry(aClass, strlen(aClass));
+
+ if (entry) {
+ // UnregisterFactory might have left a stale nsFactoryEntry in
+ // mContractIDs, so we should check to see whether this entry has
+ // anything useful.
+ *aResult = (bool(entry->mModule) ||
+ bool(entry->mFactory) ||
+ bool(entry->mServiceObject));
+ } else {
+ *aResult = false;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::EnumerateCIDs(nsISimpleEnumerator** aEnumerator)
+{
+ nsCOMArray<nsISupports> array;
+ for (auto iter = mFactories.Iter(); !iter.Done(); iter.Next()) {
+ const nsID& id = iter.Key();
+ nsCOMPtr<nsISupportsID> wrapper = new nsSupportsID();
+ wrapper->SetData(&id);
+ array.AppendObject(wrapper);
+ }
+ return NS_NewArrayEnumerator(aEnumerator, array);
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::EnumerateContractIDs(nsISimpleEnumerator** aEnumerator)
+{
+ nsTArray<nsCString>* array = new nsTArray<nsCString>;
+ for (auto iter = mContractIDs.Iter(); !iter.Done(); iter.Next()) {
+ const nsACString& contract = iter.Key();
+ array->AppendElement(contract);
+ }
+
+ nsCOMPtr<nsIUTF8StringEnumerator> e;
+ nsresult rv = NS_NewAdoptingUTF8StringEnumerator(getter_AddRefs(e), array);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return CallQueryInterface(e, aEnumerator);
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::CIDToContractID(const nsCID& aClass,
+ char** aResult)
+{
+ NS_ERROR("CIDTOContractID not implemented");
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::ContractIDToCID(const char* aContractID,
+ nsCID** aResult)
+{
+ {
+ SafeMutexAutoLock lock(mLock);
+ nsFactoryEntry* entry = mContractIDs.Get(nsDependentCString(aContractID));
+ if (entry) {
+ *aResult = (nsCID*)moz_xmalloc(sizeof(nsCID));
+ **aResult = *entry->mCIDEntry->cid;
+ return NS_OK;
+ }
+ }
+ *aResult = nullptr;
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+}
+
+MOZ_DEFINE_MALLOC_SIZE_OF(ComponentManagerMallocSizeOf)
+
+NS_IMETHODIMP
+nsComponentManagerImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize)
+{
+ MOZ_COLLECT_REPORT(
+ "explicit/xpcom/component-manager", KIND_HEAP, UNITS_BYTES,
+ SizeOfIncludingThis(ComponentManagerMallocSizeOf),
+ "Memory used for the XPCOM component manager.");
+
+ return NS_OK;
+}
+
+size_t
+nsComponentManagerImpl::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+ const
+{
+ size_t n = aMallocSizeOf(this);
+
+ n += mLoaderMap.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ n += mFactories.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = mFactories.ConstIter(); !iter.Done(); iter.Next()) {
+ n += iter.Data()->SizeOfIncludingThis(aMallocSizeOf);
+ }
+
+ n += mContractIDs.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (auto iter = mContractIDs.ConstIter(); !iter.Done(); iter.Next()) {
+ // We don't measure the nsFactoryEntry data because it's owned by
+ // mFactories (which is measured above).
+ n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+
+ n += sStaticModules->ShallowSizeOfIncludingThis(aMallocSizeOf);
+ n += sModuleLocations->ShallowSizeOfIncludingThis(aMallocSizeOf);
+
+ n += mKnownStaticModules.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ n += mKnownModules.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ n += PL_SizeOfArenaPoolExcludingPool(&mArena, aMallocSizeOf);
+
+ n += mPendingServices.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ // Measurement of the following members may be added later if DMD finds it is
+ // worthwhile:
+ // - mLoaderMap's keys and values
+ // - mMon
+ // - sStaticModules' entries
+ // - sModuleLocations' entries
+ // - mNativeModuleLoader
+ // - mKnownStaticModules' entries?
+ // - mKnownModules' keys and values?
+
+ return n;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsFactoryEntry
+////////////////////////////////////////////////////////////////////////////////
+
+nsFactoryEntry::nsFactoryEntry(const mozilla::Module::CIDEntry* aEntry,
+ nsComponentManagerImpl::KnownModule* aModule)
+ : mCIDEntry(aEntry)
+ , mModule(aModule)
+{
+}
+
+nsFactoryEntry::nsFactoryEntry(const nsCID& aCID, nsIFactory* aFactory)
+ : mCIDEntry(nullptr)
+ , mModule(nullptr)
+ , mFactory(aFactory)
+{
+ mozilla::Module::CIDEntry* e = new mozilla::Module::CIDEntry();
+ nsCID* cid = new nsCID;
+ *cid = aCID;
+ e->cid = cid;
+ mCIDEntry = e;
+}
+
+nsFactoryEntry::~nsFactoryEntry()
+{
+ // If this was a RegisterFactory entry, we own the CIDEntry/CID
+ if (!mModule) {
+ delete mCIDEntry->cid;
+ delete mCIDEntry;
+ }
+}
+
+already_AddRefed<nsIFactory>
+nsFactoryEntry::GetFactory()
+{
+ nsComponentManagerImpl::gComponentManager->mLock.AssertNotCurrentThreadOwns();
+
+ if (!mFactory) {
+ // RegisterFactory then UnregisterFactory can leave an entry in mContractIDs
+ // pointing to an unusable nsFactoryEntry.
+ if (!mModule) {
+ return nullptr;
+ }
+
+ if (!mModule->Load()) {
+ return nullptr;
+ }
+
+ // Don't set mFactory directly, it needs to be locked
+ nsCOMPtr<nsIFactory> factory;
+
+ if (mModule->Module()->getFactoryProc) {
+ factory = mModule->Module()->getFactoryProc(*mModule->Module(),
+ *mCIDEntry);
+ } else if (mCIDEntry->getFactoryProc) {
+ factory = mCIDEntry->getFactoryProc(*mModule->Module(), *mCIDEntry);
+ } else {
+ NS_ASSERTION(mCIDEntry->constructorProc, "no getfactory or constructor");
+ factory = new mozilla::GenericFactory(mCIDEntry->constructorProc);
+ }
+ if (!factory) {
+ return nullptr;
+ }
+
+ SafeMutexAutoLock lock(nsComponentManagerImpl::gComponentManager->mLock);
+ // Threads can race to set mFactory
+ if (!mFactory) {
+ factory.swap(mFactory);
+ }
+ }
+ nsCOMPtr<nsIFactory> factory = mFactory;
+ return factory.forget();
+}
+
+size_t
+nsFactoryEntry::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
+{
+ size_t n = aMallocSizeOf(this);
+
+ // Measurement of the following members may be added later if DMD finds it is
+ // worthwhile:
+ // - mCIDEntry;
+ // - mModule;
+ // - mFactory;
+ // - mServiceObject;
+
+ return n;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Static Access Functions
+////////////////////////////////////////////////////////////////////////////////
+
+nsresult
+NS_GetComponentManager(nsIComponentManager** aResult)
+{
+ if (!nsComponentManagerImpl::gComponentManager) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ NS_ADDREF(*aResult = nsComponentManagerImpl::gComponentManager);
+ return NS_OK;
+}
+
+nsresult
+NS_GetServiceManager(nsIServiceManager** aResult)
+{
+ if (!nsComponentManagerImpl::gComponentManager) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ NS_ADDREF(*aResult = nsComponentManagerImpl::gComponentManager);
+ return NS_OK;
+}
+
+
+nsresult
+NS_GetComponentRegistrar(nsIComponentRegistrar** aResult)
+{
+ if (!nsComponentManagerImpl::gComponentManager) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ NS_ADDREF(*aResult = nsComponentManagerImpl::gComponentManager);
+ return NS_OK;
+}
+
+EXPORT_XPCOM_API(nsresult)
+XRE_AddStaticComponent(const mozilla::Module* aComponent)
+{
+ nsComponentManagerImpl::InitializeStaticModules();
+ nsComponentManagerImpl::sStaticModules->AppendElement(aComponent);
+
+ if (nsComponentManagerImpl::gComponentManager &&
+ nsComponentManagerImpl::NORMAL ==
+ nsComponentManagerImpl::gComponentManager->mStatus) {
+ nsComponentManagerImpl::gComponentManager->RegisterModule(aComponent,
+ nullptr);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::AddBootstrappedManifestLocation(nsIFile* aLocation)
+{
+ nsString path;
+ nsresult rv = aLocation->GetPath(path);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (Substring(path, path.Length() - 4).EqualsLiteral(".xpi")) {
+ return XRE_AddJarManifestLocation(NS_BOOTSTRAPPED_LOCATION, aLocation);
+ }
+
+ nsCOMPtr<nsIFile> manifest =
+ CloneAndAppend(aLocation, NS_LITERAL_CSTRING("chrome.manifest"));
+ return XRE_AddManifestLocation(NS_BOOTSTRAPPED_LOCATION, manifest);
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::RemoveBootstrappedManifestLocation(nsIFile* aLocation)
+{
+ nsCOMPtr<nsIChromeRegistry> cr = mozilla::services::GetChromeRegistryService();
+ if (!cr) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsString path;
+ nsresult rv = aLocation->GetPath(path);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsComponentManagerImpl::ComponentLocation elem;
+ elem.type = NS_BOOTSTRAPPED_LOCATION;
+
+ if (Substring(path, path.Length() - 4).EqualsLiteral(".xpi")) {
+ elem.location.Init(aLocation, "chrome.manifest");
+ } else {
+ nsCOMPtr<nsIFile> lf =
+ CloneAndAppend(aLocation, NS_LITERAL_CSTRING("chrome.manifest"));
+ elem.location.Init(lf);
+ }
+
+ // Remove reference.
+ nsComponentManagerImpl::sModuleLocations->RemoveElement(elem,
+ ComponentLocationComparator());
+
+ rv = cr->CheckForNewChrome();
+ return rv;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::GetManifestLocations(nsIArray** aLocations)
+{
+ NS_ENSURE_ARG_POINTER(aLocations);
+ *aLocations = nullptr;
+
+ if (!sModuleLocations) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ nsCOMPtr<nsIMutableArray> locations = nsArray::Create();
+ nsresult rv;
+ for (uint32_t i = 0; i < sModuleLocations->Length(); ++i) {
+ ComponentLocation& l = sModuleLocations->ElementAt(i);
+ FileLocation loc = l.location;
+ nsCString uriString;
+ loc.GetURIString(uriString);
+ nsCOMPtr<nsIURI> uri;
+ rv = NS_NewURI(getter_AddRefs(uri), uriString);
+ if (NS_SUCCEEDED(rv)) {
+ locations->AppendElement(uri, false);
+ }
+ }
+
+ locations.forget(aLocations);
+ return NS_OK;
+}
+
+EXPORT_XPCOM_API(nsresult)
+XRE_AddManifestLocation(NSLocationType aType, nsIFile* aLocation)
+{
+ nsComponentManagerImpl::InitializeModuleLocations();
+ nsComponentManagerImpl::ComponentLocation* c =
+ nsComponentManagerImpl::sModuleLocations->AppendElement();
+ c->type = aType;
+ c->location.Init(aLocation);
+
+ if (nsComponentManagerImpl::gComponentManager &&
+ nsComponentManagerImpl::NORMAL ==
+ nsComponentManagerImpl::gComponentManager->mStatus) {
+ nsComponentManagerImpl::gComponentManager->RegisterManifest(aType,
+ c->location,
+ false);
+ }
+
+ return NS_OK;
+}
+
+EXPORT_XPCOM_API(nsresult)
+XRE_AddJarManifestLocation(NSLocationType aType, nsIFile* aLocation)
+{
+ nsComponentManagerImpl::InitializeModuleLocations();
+ nsComponentManagerImpl::ComponentLocation* c =
+ nsComponentManagerImpl::sModuleLocations->AppendElement();
+
+ c->type = aType;
+ c->location.Init(aLocation, "chrome.manifest");
+
+ if (nsComponentManagerImpl::gComponentManager &&
+ nsComponentManagerImpl::NORMAL ==
+ nsComponentManagerImpl::gComponentManager->mStatus) {
+ nsComponentManagerImpl::gComponentManager->RegisterManifest(aType,
+ c->location,
+ false);
+ }
+
+ return NS_OK;
+}
+
diff --git a/xpcom/components/nsComponentManager.h b/xpcom/components/nsComponentManager.h
new file mode 100644
index 000000000..f0e0f684a
--- /dev/null
+++ b/xpcom/components/nsComponentManager.h
@@ -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__
diff --git a/xpcom/components/nsICategoryManager.idl b/xpcom/components/nsICategoryManager.idl
new file mode 100644
index 000000000..e33d3ed8b
--- /dev/null
+++ b/xpcom/components/nsICategoryManager.idl
@@ -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();
+};
+
diff --git a/xpcom/components/nsIClassInfo.idl b/xpcom/components/nsIClassInfo.idl
new file mode 100644
index 000000000..639d12aa8
--- /dev/null
+++ b/xpcom/components/nsIClassInfo.idl
@@ -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;
+
+};
diff --git a/xpcom/components/nsIComponentManager.idl b/xpcom/components/nsIComponentManager.idl
new file mode 100644
index 000000000..1e5693a28
--- /dev/null
+++ b/xpcom/components/nsIComponentManager.idl
@@ -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++
diff --git a/xpcom/components/nsIComponentRegistrar.idl b/xpcom/components/nsIComponentRegistrar.idl
new file mode 100644
index 000000000..e12acdf25
--- /dev/null
+++ b/xpcom/components/nsIComponentRegistrar.idl
@@ -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);
+};
+
+
+
+
+
+
+
+
+
+
diff --git a/xpcom/components/nsIFactory.idl b/xpcom/components/nsIFactory.idl
new file mode 100644
index 000000000..54152976f
--- /dev/null
+++ b/xpcom/components/nsIFactory.idl
@@ -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);
+};
diff --git a/xpcom/components/nsIModule.idl b/xpcom/components/nsIModule.idl
new file mode 100644
index 000000000..4570a0199
--- /dev/null
+++ b/xpcom/components/nsIModule.idl
@@ -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);
+};
+
+
diff --git a/xpcom/components/nsIServiceManager.idl b/xpcom/components/nsIServiceManager.idl
new file mode 100644
index 000000000..45c1c3cc2
--- /dev/null
+++ b/xpcom/components/nsIServiceManager.idl
@@ -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
+%}
+
diff --git a/xpcom/components/nsNativeModuleLoader.cpp b/xpcom/components/nsNativeModuleLoader.cpp
new file mode 100644
index 000000000..6452f5d1b
--- /dev/null
+++ b/xpcom/components/nsNativeModuleLoader.cpp
@@ -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();
+ }
+}
diff --git a/xpcom/components/nsNativeModuleLoader.h b/xpcom/components/nsNativeModuleLoader.h
new file mode 100644
index 000000000..b05a4a7a2
--- /dev/null
+++ b/xpcom/components/nsNativeModuleLoader.h
@@ -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__ */