summaryrefslogtreecommitdiffstats
path: root/xpcom/components
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/components')
-rw-r--r--xpcom/components/GenericFactory.cpp18
-rw-r--r--xpcom/components/GenericFactory.h38
-rw-r--r--xpcom/components/ManifestParser.cpp678
-rw-r--r--xpcom/components/ManifestParser.h23
-rw-r--r--xpcom/components/Module.h81
-rw-r--r--xpcom/components/ModuleUtils.h81
-rw-r--r--xpcom/components/StaticComponents.cpp.in410
-rw-r--r--xpcom/components/StaticComponents.h284
-rw-r--r--xpcom/components/components.conf23
-rw-r--r--xpcom/components/gen_static_components.py1216
-rw-r--r--xpcom/components/moz.build84
-rw-r--r--xpcom/components/nsCategoryCache.cpp162
-rw-r--r--xpcom/components/nsCategoryCache.h123
-rw-r--r--xpcom/components/nsCategoryManager.cpp690
-rw-r--r--xpcom/components/nsCategoryManager.h145
-rw-r--r--xpcom/components/nsCategoryManagerUtils.h14
-rw-r--r--xpcom/components/nsComponentManager.cpp1591
-rw-r--r--xpcom/components/nsComponentManager.h218
-rw-r--r--xpcom/components/nsComponentManagerUtils.cpp259
-rw-r--r--xpcom/components/nsComponentManagerUtils.h170
-rw-r--r--xpcom/components/nsICategoryManager.idl137
-rw-r--r--xpcom/components/nsIClassInfo.idl74
-rw-r--r--xpcom/components/nsIComponentManager.idl117
-rw-r--r--xpcom/components/nsIComponentRegistrar.idl100
-rw-r--r--xpcom/components/nsIFactory.idl27
-rw-r--r--xpcom/components/nsIServiceManager.idl70
-rw-r--r--xpcom/components/nsServiceManagerUtils.h56
-rw-r--r--xpcom/components/test/python.toml4
-rw-r--r--xpcom/components/test/test_gen_static_components.py150
29 files changed, 7043 insertions, 0 deletions
diff --git a/xpcom/components/GenericFactory.cpp b/xpcom/components/GenericFactory.cpp
new file mode 100644
index 0000000000..5eb88e8ba4
--- /dev/null
+++ b/xpcom/components/GenericFactory.cpp
@@ -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/. */
+
+#include "mozilla/GenericFactory.h"
+
+namespace mozilla {
+
+NS_IMPL_ISUPPORTS(GenericFactory, nsIFactory)
+
+NS_IMETHODIMP
+GenericFactory::CreateInstance(REFNSIID aIID, void** aResult) {
+ return mCtor(aIID, aResult);
+}
+
+} // namespace mozilla
diff --git a/xpcom/components/GenericFactory.h b/xpcom/components/GenericFactory.h
new file mode 100644
index 0000000000..3fb2186e12
--- /dev/null
+++ b/xpcom/components/GenericFactory.h
@@ -0,0 +1,38 @@
+/* -*- 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_GenericFactory_h
+#define mozilla_GenericFactory_h
+
+#include "nsIFactory.h"
+
+namespace mozilla {
+
+/**
+ * A generic factory which uses a constructor function to create instances.
+ * This class is intended for use by the component manager and the generic
+ * module.
+ */
+class GenericFactory final : public nsIFactory {
+ ~GenericFactory() = default;
+
+ public:
+ typedef nsresult (*ConstructorProcPtr)(const nsIID& aIID, void** aResult);
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIFACTORY
+
+ explicit GenericFactory(ConstructorProcPtr aCtor) : mCtor(aCtor) {
+ NS_ASSERTION(mCtor, "GenericFactory with no constructor");
+ }
+
+ private:
+ ConstructorProcPtr mCtor;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_GenericFactory_h
diff --git a/xpcom/components/ManifestParser.cpp b/xpcom/components/ManifestParser.cpp
new file mode 100644
index 0000000000..88ee06d78d
--- /dev/null
+++ b/xpcom/components/ManifestParser.cpp
@@ -0,0 +1,678 @@
+/* -*- 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 "mozilla/Printf.h"
+#include "mozilla/UniquePtr.h"
+
+#include "ManifestParser.h"
+
+#include <string.h>
+
+#include "prio.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 "AndroidBuild.h"
+# include "mozilla/java/GeckoAppShellWrappers.h"
+#endif
+
+#ifdef MOZ_BACKGROUNDTASKS
+# include "mozilla/BackgroundTasks.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;
+
+ bool ischrome;
+
+ // The contentaccessible flags only apply to content/resource 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);
+};
+static const ManifestDirective kParsingTable[] = {
+ // clang-format off
+ {
+ "manifest", 1, true, false,
+ &nsComponentManagerImpl::ManifestManifest, nullptr,
+ },
+ {
+ "category", 3, false, false,
+ &nsComponentManagerImpl::ManifestCategory, nullptr,
+ },
+ {
+ "content", 2, true, true,
+ nullptr, &nsChromeRegistry::ManifestContent,
+ },
+ {
+ "locale", 3, true, false,
+ nullptr, &nsChromeRegistry::ManifestLocale,
+ },
+ {
+ "skin", 3, true, false,
+ nullptr, &nsChromeRegistry::ManifestSkin,
+ },
+ {
+ // NB: note that while skin manifests can use this, they are only allowed
+ // to use it for chrome://../skin/ URLs
+ "override", 2, true, false,
+ nullptr, &nsChromeRegistry::ManifestOverride,
+ },
+ {
+ "resource", 2, false, true,
+ nullptr, &nsChromeRegistry::ManifestResource,
+ }
+ // clang-format on
+};
+
+static const char kWhitespace[] = "\t ";
+
+static bool IsNewline(char aChar) { return aChar == '\n' || aChar == '\r'; }
+
+void LogMessage(const char* aMsg, ...) {
+ MOZ_ASSERT(nsComponentManagerImpl::gComponentManager);
+
+ nsCOMPtr<nsIConsoleService> console =
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+ if (!console) {
+ return;
+ }
+
+ va_list args;
+ va_start(args, aMsg);
+ SmprintfPointer formatted(mozilla::Vsmprintf(aMsg, args));
+ va_end(args);
+
+ nsCOMPtr<nsIConsoleMessage> error =
+ new nsConsoleMessage(NS_ConvertUTF8toUTF16(formatted.get()));
+ console->LogMessage(error);
+}
+
+void LogMessageWithContext(FileLocation& aFile, uint32_t aLineNumber,
+ const char* aMsg, ...) {
+ va_list args;
+ va_start(args, aMsg);
+ SmprintfPointer formatted(mozilla::Vsmprintf(aMsg, args));
+ va_end(args);
+ if (!formatted) {
+ return;
+ }
+
+ MOZ_ASSERT(nsComponentManagerImpl::gComponentManager);
+
+ 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,
+ formatted.get());
+ return;
+ }
+
+ nsCOMPtr<nsIConsoleService> console =
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+ if (!console) {
+ return;
+ }
+
+ nsresult rv = error->Init(
+ NS_ConvertUTF8toUTF16(formatted.get()), NS_ConvertUTF8toUTF16(file),
+ u""_ns, aLineNumber, 0, nsIScriptError::warningFlag,
+ "chrome registration"_ns, false /* from private window */,
+ true /* from chrome context */);
+ 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 nsAString& aFlag, const nsAString& 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 nsAString& aFlag, const nsAString& aData,
+ const nsAString& 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;
+}
+
+static bool CheckOsFlag(const nsAString& aFlag, const nsAString& aData,
+ const nsAString& aValue, TriState& aResult) {
+ bool result = CheckStringFlag(aFlag, aData, aValue, aResult);
+#if defined(XP_UNIX) && !defined(XP_DARWIN) && !defined(ANDROID)
+ if (result && aResult == eBad) {
+ result = CheckStringFlag(aFlag, aData, u"likeunix"_ns, aResult);
+ }
+#endif
+ return result;
+}
+
+/**
+ * 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
+
+void ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
+ bool aChromeOnly) {
+ nsComponentManagerImpl::ManifestProcessingContext mgrcx(aType, aFile,
+ aChromeOnly);
+ nsChromeRegistry::ManifestProcessingContext chromecx(aType, aFile);
+ nsresult rv;
+
+ constexpr auto kContentAccessible = u"contentaccessible"_ns;
+ constexpr auto kRemoteEnabled = u"remoteenabled"_ns;
+ constexpr auto kRemoteRequired = u"remoterequired"_ns;
+ constexpr auto kApplication = u"application"_ns;
+ constexpr auto kAppVersion = u"appversion"_ns;
+ constexpr auto kGeckoVersion = u"platformversion"_ns;
+ constexpr auto kOs = u"os"_ns;
+ constexpr auto kOsVersion = u"osversion"_ns;
+ constexpr auto kABI = u"abi"_ns;
+ constexpr auto kProcess = u"process"_ns;
+#if defined(MOZ_WIDGET_ANDROID)
+ constexpr auto kTablet = u"tablet"_ns;
+#endif
+ // You might expect this to be guarded by MOZ_BACKGROUNDTASKS, but it's not
+ // possible to have conditional manifest contents, so we need to recognize and
+ // discard these tokens even when MOZ_BACKGROUNDTASKS is not set.
+ constexpr auto kBackgroundTask = u"backgroundtask"_ns;
+
+ constexpr auto kMain = u"main"_ns;
+ constexpr auto kContent = u"content"_ns;
+
+ // Obsolete
+ constexpr auto kXPCNativeWrappers = u"xpcnativewrappers"_ns;
+
+ nsAutoString appID;
+ nsAutoString appVersion;
+ nsAutoString geckoVersion;
+ nsAutoString osTarget;
+ nsAutoString abi;
+ nsAutoString process;
+
+ nsCOMPtr<nsIXULAppInfo> 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::macOSVersionMajor();
+ SInt32 minorVersion = nsCocoaFeatures::macOSVersionMinor();
+ 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 (jni::IsAvailable()) {
+ jni::String::LocalRef release = java::sdk::Build::VERSION::RELEASE();
+ osVersion.Assign(release->ToString());
+ isTablet = java::GeckoAppShell::IsTablet();
+ }
+#endif
+
+ if (XRE_IsContentProcess()) {
+ process = kContent;
+ } else {
+ process = kMain;
+ }
+
+ 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)) {
+ directive = d;
+ break;
+ }
+ }
+
+ if (!directive) {
+ LogMessageWithContext(
+ aFile, line, "Ignoring unrecognized chrome manifest directive '%s'.",
+ token);
+ continue;
+ }
+
+ if (!directive->ischrome && NS_BOOTSTRAPPED_LOCATION == aType) {
+ LogMessageWithContext(
+ aFile, line,
+ "Bootstrapped 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
+#ifdef MOZ_BACKGROUNDTASKS
+ // When in background task mode, default to not registering
+ // category directivies unless backgroundtask=1 is specified.
+ TriState stBackgroundTask = (BackgroundTasks::IsBackgroundTaskMode() &&
+ strcmp("category", directive->directive) == 0)
+ ? eBad
+ : 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) ||
+ CheckOsFlag(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
+
+ // You might expect this to be guarded by MOZ_BACKGROUNDTASKS, it's not
+ // possible to have conditional manifest contents.
+ bool flag;
+ if (CheckFlag(kBackgroundTask, wtoken, flag)) {
+#if defined(MOZ_BACKGROUNDTASKS)
+ // Background task mode is active: filter.
+ stBackgroundTask =
+ (flag == BackgroundTasks::IsBackgroundTaskMode()) ? eOK : eBad;
+#endif /* defined(MOZ_BACKGROUNDTASKS) */
+ continue;
+ }
+
+ if (directive->contentflags) {
+ bool flag;
+ 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
+#ifdef MOZ_BACKGROUNDTASKS
+ stBackgroundTask == eBad ||
+#endif
+ stABI == eBad || stProcess == eBad) {
+ continue;
+ }
+
+ if (directive->regfunc) {
+ if (GeckoProcessType_Default != XRE_GetProcessType()) {
+ continue;
+ }
+
+ if (!nsChromeRegistry::gChromeRegistry) {
+ nsCOMPtr<nsIChromeRegistry> cr = mozilla::services::GetChromeRegistry();
+ 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) {
+ (nsComponentManagerImpl::gComponentManager->*(directive->mgrfunc))(
+ mgrcx, line, argv);
+ }
+ }
+}
diff --git a/xpcom/components/ManifestParser.h b/xpcom/components/ManifestParser.h
new file mode 100644
index 0000000000..e66cb58bcb
--- /dev/null
+++ b/xpcom/components/ManifestParser.h
@@ -0,0 +1,23 @@
+/* -*- 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/Attributes.h"
+#include "mozilla/FileLocation.h"
+
+void ParseManifest(NSLocationType aType, mozilla::FileLocation& aFile,
+ char* aBuf, bool aChromeOnly);
+
+void LogMessage(const char* aMsg, ...) MOZ_FORMAT_PRINTF(1, 2);
+
+void LogMessageWithContext(mozilla::FileLocation& aFile, uint32_t aLineNumber,
+ const char* aMsg, ...) MOZ_FORMAT_PRINTF(3, 4);
+
+#endif // ManifestParser_h
diff --git a/xpcom/components/Module.h b/xpcom/components/Module.h
new file mode 100644
index 0000000000..b8fae64356
--- /dev/null
+++ b/xpcom/components/Module.h
@@ -0,0 +1,81 @@
+/* -*- 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"
+
+namespace mozilla {
+
+namespace Module {
+/**
+ * This selector allows components to be marked so that they're only loaded
+ * into certain kinds of processes. Selectors can be combined.
+ */
+// Note: This must be kept in sync with the selector matching in
+// nsComponentManager.cpp.
+enum ProcessSelector {
+ ANY_PROCESS = 0,
+ MAIN_PROCESS_ONLY = 1 << 0,
+ CONTENT_PROCESS_ONLY = 1 << 1,
+
+ /**
+ * By default, modules are not loaded in the GPU, VR, Socket, RDD, Utility,
+ * GMPlugin and IPDLUnitTest processes, even if ANY_PROCESS is specified.
+ * This flag enables a module in the relevant process.
+ *
+ * NOTE: IPDLUnitTest does not have its own flag, and will only load a
+ * module if it is enabled in all processes.
+ */
+ ALLOW_IN_GPU_PROCESS = 1 << 2,
+ ALLOW_IN_VR_PROCESS = 1 << 3,
+ ALLOW_IN_SOCKET_PROCESS = 1 << 4,
+ ALLOW_IN_RDD_PROCESS = 1 << 5,
+ ALLOW_IN_UTILITY_PROCESS = 1 << 6,
+ ALLOW_IN_GMPLUGIN_PROCESS = 1 << 7,
+ ALLOW_IN_GPU_AND_MAIN_PROCESS = ALLOW_IN_GPU_PROCESS | MAIN_PROCESS_ONLY,
+ ALLOW_IN_GPU_AND_VR_PROCESS = ALLOW_IN_GPU_PROCESS | ALLOW_IN_VR_PROCESS,
+ ALLOW_IN_GPU_AND_SOCKET_PROCESS =
+ ALLOW_IN_GPU_PROCESS | ALLOW_IN_SOCKET_PROCESS,
+ ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS =
+ ALLOW_IN_GPU_PROCESS | ALLOW_IN_VR_PROCESS | ALLOW_IN_SOCKET_PROCESS,
+ ALLOW_IN_RDD_AND_SOCKET_PROCESS =
+ ALLOW_IN_RDD_PROCESS | ALLOW_IN_SOCKET_PROCESS,
+ ALLOW_IN_GPU_RDD_AND_SOCKET_PROCESS =
+ ALLOW_IN_GPU_PROCESS | ALLOW_IN_RDD_PROCESS | ALLOW_IN_SOCKET_PROCESS,
+ ALLOW_IN_GPU_RDD_SOCKET_AND_UTILITY_PROCESS =
+ ALLOW_IN_GPU_PROCESS | ALLOW_IN_RDD_PROCESS | ALLOW_IN_SOCKET_PROCESS,
+ ALLOW_IN_GPU_RDD_VR_AND_SOCKET_PROCESS =
+ ALLOW_IN_GPU_PROCESS | ALLOW_IN_RDD_PROCESS | ALLOW_IN_VR_PROCESS |
+ ALLOW_IN_SOCKET_PROCESS,
+ ALLOW_IN_GPU_RDD_VR_SOCKET_AND_UTILITY_PROCESS =
+ ALLOW_IN_GPU_PROCESS | ALLOW_IN_RDD_PROCESS | ALLOW_IN_VR_PROCESS |
+ ALLOW_IN_SOCKET_PROCESS | ALLOW_IN_UTILITY_PROCESS,
+ ALLOW_IN_GPU_RDD_VR_SOCKET_UTILITY_AND_GMPLUGIN_PROCESS =
+ ALLOW_IN_GPU_PROCESS | ALLOW_IN_RDD_PROCESS | ALLOW_IN_VR_PROCESS |
+ ALLOW_IN_SOCKET_PROCESS | ALLOW_IN_UTILITY_PROCESS |
+ ALLOW_IN_GMPLUGIN_PROCESS
+};
+
+static constexpr size_t kMaxProcessSelector = size_t(
+ ProcessSelector::ALLOW_IN_GPU_RDD_VR_SOCKET_UTILITY_AND_GMPLUGIN_PROCESS);
+
+/**
+ * This allows category entries to be marked so that they are or are
+ * not loaded when in backgroundtask mode.
+ */
+// Note: This must be kept in sync with the selector matching in
+// StaticComponents.cpp.in.
+enum BackgroundTasksSelector {
+ NO_TASKS = 0x0,
+ ALL_TASKS = 0xFFFF,
+};
+}; // namespace Module
+
+} // namespace mozilla
+
+#endif // mozilla_Module_h
diff --git a/xpcom/components/ModuleUtils.h b/xpcom/components/ModuleUtils.h
new file mode 100644
index 0000000000..92ec8aa173
--- /dev/null
+++ b/xpcom/components/ModuleUtils.h
@@ -0,0 +1,81 @@
+/* -*- 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 <type_traits>
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Module.h"
+
+#define NS_GENERIC_FACTORY_CONSTRUCTOR(_InstanceClass) \
+ static nsresult _InstanceClass##Constructor(REFNSIID aIID, void** aResult) { \
+ RefPtr<_InstanceClass> inst; \
+ \
+ *aResult = nullptr; \
+ \
+ inst = new _InstanceClass(); \
+ return inst->QueryInterface(aIID, aResult); \
+ }
+
+#define NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(_InstanceClass, _InitMethod) \
+ static nsresult _InstanceClass##Constructor(REFNSIID aIID, void** aResult) { \
+ nsresult rv; \
+ \
+ RefPtr<_InstanceClass> inst; \
+ \
+ *aResult = nullptr; \
+ \
+ inst = new _InstanceClass(); \
+ rv = inst->_InitMethod(); \
+ if (NS_SUCCEEDED(rv)) { \
+ rv = inst->QueryInterface(aIID, aResult); \
+ } \
+ \
+ return rv; \
+ }
+
+namespace mozilla {
+namespace detail {
+
+template <typename T>
+struct RemoveAlreadyAddRefed {
+ using Type = T;
+};
+
+template <typename T>
+struct RemoveAlreadyAddRefed<already_AddRefed<T>> {
+ using Type = T;
+};
+
+} // namespace detail
+} // namespace mozilla
+
+// 'Constructor' that uses an existing getter function that gets a singleton.
+#define NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(_InstanceClass, _GetterProc) \
+ static nsresult _InstanceClass##Constructor(REFNSIID aIID, void** aResult) { \
+ RefPtr<_InstanceClass> inst; \
+ \
+ *aResult = nullptr; \
+ \
+ using T = \
+ mozilla::detail::RemoveAlreadyAddRefed<decltype(_GetterProc())>::Type; \
+ static_assert( \
+ std::is_same_v<already_AddRefed<T>, decltype(_GetterProc())>, \
+ "Singleton constructor must return already_AddRefed"); \
+ static_assert( \
+ std::is_base_of<_InstanceClass, T>::value, \
+ "Singleton constructor must return correct already_AddRefed"); \
+ inst = _GetterProc(); \
+ if (nullptr == inst) { \
+ return NS_ERROR_OUT_OF_MEMORY; \
+ } \
+ return inst->QueryInterface(aIID, aResult); \
+ }
+
+#endif // mozilla_GenericModule_h
diff --git a/xpcom/components/StaticComponents.cpp.in b/xpcom/components/StaticComponents.cpp.in
new file mode 100644
index 0000000000..7f3fee6859
--- /dev/null
+++ b/xpcom/components/StaticComponents.cpp.in
@@ -0,0 +1,410 @@
+/* -*- 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 "StaticComponents.h"
+
+#include "mozilla/ArrayUtils.h"
+#ifdef MOZ_BACKGROUNDTASKS
+# include "mozilla/BackgroundTasks.h"
+#endif
+#include "mozilla/PerfectHash.h"
+#include "mozilla/ResultExtensions.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozJSModuleLoader.h"
+#include "nsCOMPtr.h"
+#include "nsComponentManager.h"
+#include "nsContentUtils.h"
+#include "nsIFactory.h"
+#include "nsISupports.h"
+#include "nsIXPConnect.h"
+#include "nsString.h"
+#include "nsStringEnumerator.h"
+#include "nsTArray.h"
+#include "xptdata.h"
+#include "xptinfo.h"
+#include "js/PropertyAndElement.h" // JS_GetProperty
+
+// Cleanup pollution from zipstruct.h
+#undef UNSUPPORTED
+
+// Public includes
+//# @includes@
+
+// Relative includes
+//# @relative_includes@
+
+//# @decls@
+
+namespace mozilla {
+
+using dom::AutoJSAPI;
+
+namespace xpcom {
+
+static constexpr uint32_t kNoContractID = 0xffffffff;
+
+namespace {
+// Template helpers for constructor function sanity checks.
+template <typename T>
+struct RemoveAlreadyAddRefed {
+ using Type = T;
+};
+
+template <typename T>
+struct RemoveAlreadyAddRefed<already_AddRefed<T>> {
+ using Type = T;
+};
+} // anonymous namespace
+
+
+uint8_t gInvalidContracts[kContractCount / 8 + 1];
+
+static StaticRefPtr<nsISupports> gServiceInstances[kStaticModuleCount];
+
+uint8_t gInitCalled[kModuleInitCount / 8 + 1];
+
+static const char gStrings[] =
+//# @strings@
+ "";
+
+const StaticCategory gStaticCategories[kStaticCategoryCount] = {
+//# @categories@
+};
+const StaticCategoryEntry gStaticCategoryEntries[] = {
+//# @category_entries@
+};
+
+const nsXPTInterface gInterfaces[] = {
+//# @interfaces@
+};
+
+const StringOffset gComponentJSMs[] = {
+//# @component_jsms@
+};
+
+const StringOffset gComponentESModules[] = {
+//# @component_esmodules@
+};
+
+/**
+ * Returns a nsCString corresponding to the given entry in the `gStrings` string
+ * table. The resulting nsCString points directly to static storage, and does
+ * not incur any memory allocation overhead.
+ */
+static inline nsCString GetString(const StringOffset& aOffset) {
+ const char* str = &gStrings[aOffset.mOffset];
+ nsCString result;
+ result.AssignLiteral(str, strlen(str));
+ return result;
+}
+
+nsCString ContractEntry::ContractID() const {
+ return GetString(mContractID);
+}
+
+bool ContractEntry::Matches(const nsACString& aContractID) const {
+ return aContractID == ContractID() && Module().Active();
+}
+
+enum class ComponentType { JSM, ESM };
+
+template <ComponentType type>
+static nsresult ConstructJSMOrESMComponent(const nsACString& aURI,
+ const char* aConstructor,
+ nsISupports** aResult) {
+ if (!nsComponentManagerImpl::JSLoaderReady()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ AutoJSAPI jsapi;
+ MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope()));
+ JSContext* cx = jsapi.cx();
+
+ JS::Rooted<JSObject*> exports(cx);
+ if constexpr (type == ComponentType::JSM) {
+ JS::Rooted<JSObject*> global(cx);
+ MOZ_TRY(mozJSModuleLoader::Get()->Import(cx, aURI, &global, &exports));
+ } else {
+ MOZ_TRY(mozJSModuleLoader::Get()->ImportESModule(cx, aURI, &exports));
+ }
+
+ JS::Rooted<JS::Value> ctor(cx);
+ if (!JS_GetProperty(cx, exports, aConstructor, &ctor) ||
+ !ctor.isObject()) {
+ return NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED;
+ }
+
+ JS::Rooted<JSObject*> inst(cx);
+ if (!JS::Construct(cx, ctor, JS::HandleValueArray::empty(), &inst)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return nsContentUtils::XPConnect()->WrapJS(cx, inst, NS_GET_IID(nsISupports),
+ (void**)aResult);
+}
+
+static nsresult ConstructJSMComponent(const nsACString& aURI,
+ const char* aConstructor,
+ nsISupports** aResult) {
+ return ConstructJSMOrESMComponent<ComponentType::JSM>(
+ aURI, aConstructor, aResult);
+}
+
+static nsresult ConstructESModuleComponent(const nsACString& aURI,
+ const char* aConstructor,
+ nsISupports** aResult) {
+ return ConstructJSMOrESMComponent<ComponentType::ESM>(
+ aURI, aConstructor, aResult);
+}
+
+//# @module_cid_table@
+
+//# @module_contract_id_table@
+
+//# @js_services_table@
+
+//# @protocol_handlers_table@
+
+static inline bool CalledInit(size_t aIdx) {
+ return GetBit(gInitCalled, aIdx);
+}
+
+static nsresult CallInitFunc(size_t aIdx) {
+ if (CalledInit(aIdx)) {
+ return NS_OK;
+ }
+
+ nsresult rv = NS_OK;
+ switch (aIdx) {
+//# @init_funcs@
+ }
+
+ SetBit(gInitCalled, aIdx);
+
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ return rv;
+}
+
+static void CallUnloadFuncs() {
+//# @unload_funcs@
+}
+
+nsresult CreateInstanceImpl(ModuleID aID, const nsIID& aIID, void** aResult) {
+ // The full set of constructors for all static modules.
+ // This switch statement will be compiled to a relative address jump table
+ // with no runtime relocations and a single indirect jump.
+ switch (aID) {
+//# @constructors@
+ }
+
+ MOZ_ASSERT_UNREACHABLE("Constructor didn't return");
+ return NS_ERROR_FAILURE;
+}
+
+
+namespace {
+
+class StaticModuleFactory final : public nsIFactory {
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIFACTORY
+
+ explicit StaticModuleFactory(ModuleID aID) : mID(aID) {}
+
+private:
+ ~StaticModuleFactory() = default;
+
+ const ModuleID mID;
+};
+
+NS_IMPL_ISUPPORTS(StaticModuleFactory, nsIFactory)
+
+NS_IMETHODIMP StaticModuleFactory::CreateInstance(const nsIID& aIID,
+ void** aResult) {
+ return CreateInstanceImpl(mID, aIID, aResult);
+}
+
+} // anonymous namespace
+
+
+already_AddRefed<nsIFactory> StaticModule::GetFactory() const {
+ return do_AddRef(new StaticModuleFactory(ID()));
+}
+
+bool StaticModule::Active() const {
+ return FastProcessSelectorMatches(mProcessSelector);
+}
+
+bool StaticModule::Overridable() const {
+ return mContractID.mOffset != kNoContractID;
+}
+
+nsCString StaticModule::ContractID() const {
+ MOZ_ASSERT(Overridable());
+ return GetString(mContractID);
+}
+
+nsresult StaticModule::CreateInstance(const nsIID& aIID, void** aResult) const {
+ return CreateInstanceImpl(ID(), aIID, aResult);
+}
+
+GetServiceHelper StaticModule::GetService() const {
+ return { ID(), nullptr };
+}
+
+GetServiceHelper StaticModule::GetService(nsresult* aRv) const {
+ return { ID(), aRv };
+}
+
+
+nsISupports* StaticModule::ServiceInstance() const {
+ return gServiceInstances[Idx()];
+}
+
+void StaticModule::SetServiceInstance(
+ already_AddRefed<nsISupports> aInst) const {
+ gServiceInstances[Idx()] = aInst;
+}
+
+
+nsCString StaticCategoryEntry::Entry() const {
+ return GetString(mEntry);
+}
+
+nsCString StaticCategoryEntry::Value() const {
+ return GetString(mValue);
+}
+
+bool StaticCategoryEntry::Active() const {
+ if (!FastProcessSelectorMatches(mProcessSelector)) {
+ return false;
+ }
+#ifdef MOZ_BACKGROUNDTASKS
+ if (MOZ_UNLIKELY(BackgroundTasks::IsBackgroundTaskMode())) {
+ return mBackgroundTasksSelector != Module::BackgroundTasksSelector::NO_TASKS;
+ }
+#endif /* MOZ_BACKGROUNDTASKS */
+ return true;
+}
+
+nsCString StaticCategory::Name() const {
+ return GetString(mName);
+}
+
+nsCString JSServiceEntry::Name() const {
+ return GetString(mName);
+}
+
+JSServiceEntry::InterfaceList JSServiceEntry::Interfaces() const {
+ InterfaceList iids;
+ iids.SetCapacity(mInterfaceCount);
+
+ for (size_t i = 0; i < mInterfaceCount; i++) {
+ nsXPTInterface ifaceID = gInterfaces[mInterfaceOffset.mOffset + i];
+ iids.AppendElement(&nsXPTInterfaceInfo::Get(ifaceID)->IID());
+ }
+ return iids;
+}
+
+
+/* static */
+const JSServiceEntry* JSServiceEntry::Lookup(const nsACString& aName) {
+ return LookupJSService(aName);
+}
+
+nsCString StaticProtocolHandler::Scheme() const {
+ return GetString(mScheme);
+}
+
+/* static */
+const StaticProtocolHandler* StaticProtocolHandler::Lookup(const nsACString& aScheme) {
+ return LookupProtocolHandler(aScheme);
+}
+
+/* static */ const StaticModule* StaticComponents::LookupByCID(
+ const nsID& aCID) {
+ return ModuleByCID(aCID);
+}
+
+/* static */ const StaticModule* StaticComponents::LookupByContractID(
+ const nsACString& aContractID) {
+ if (const ContractEntry* entry = LookupContractID(aContractID)) {
+ if (!entry->Invalid()) {
+ return &entry->Module();
+ }
+ }
+ return nullptr;
+}
+
+/* static */ bool StaticComponents::InvalidateContractID(
+ const nsACString& aContractID, bool aInvalid) {
+ if (const ContractEntry* entry = LookupContractID(aContractID)) {
+ entry->SetInvalid(aInvalid);
+ return true;
+ }
+ return false;
+}
+
+/* static */ already_AddRefed<nsIUTF8StringEnumerator>
+StaticComponents::GetComponentJSMs() {
+ auto jsms = MakeUnique<nsTArray<nsCString>>(MOZ_ARRAY_LENGTH(gComponentJSMs));
+
+ for (const auto& entry : gComponentJSMs) {
+ jsms->AppendElement(GetString(entry));
+ }
+
+ nsCOMPtr<nsIUTF8StringEnumerator> result;
+ MOZ_ALWAYS_SUCCEEDS(NS_NewAdoptingUTF8StringEnumerator(getter_AddRefs(result),
+ jsms.release()));
+ return result.forget();
+}
+
+/* static */ already_AddRefed<nsIUTF8StringEnumerator>
+StaticComponents::GetComponentESModules() {
+ auto esModules = MakeUnique<nsTArray<nsCString>>(MOZ_ARRAY_LENGTH(gComponentESModules));
+
+ for (const auto& entry : gComponentESModules) {
+ esModules->AppendElement(GetString(entry));
+ }
+
+ nsCOMPtr<nsIUTF8StringEnumerator> result;
+ MOZ_ALWAYS_SUCCEEDS(NS_NewAdoptingUTF8StringEnumerator(getter_AddRefs(result),
+ esModules.release()));
+ return result.forget();
+}
+
+/* static */ Span<const JSServiceEntry> StaticComponents::GetJSServices() {
+ return { gJSServices, ArrayLength(gJSServices) };
+}
+
+/* static */ void StaticComponents::Shutdown() {
+ CallUnloadFuncs();
+}
+
+/* static */ const nsID& Components::GetCID(ModuleID aID) {
+ return gStaticModules[size_t(aID)].CID();
+}
+
+nsresult GetServiceHelper::operator()(const nsIID& aIID, void** aResult) const {
+ nsresult rv =
+ nsComponentManagerImpl::gComponentManager->GetService(mId, aIID, aResult);
+ return SetResult(rv);
+}
+
+nsresult CreateInstanceHelper::operator()(const nsIID& aIID,
+ void** aResult) const {
+ const auto& entry = gStaticModules[size_t(mId)];
+ if (!entry.Active()) {
+ return SetResult(NS_ERROR_FACTORY_NOT_REGISTERED);
+ }
+
+ nsresult rv = entry.CreateInstance(aIID, aResult);
+ return SetResult(rv);
+}
+
+} // namespace xpcom
+} // namespace mozilla
diff --git a/xpcom/components/StaticComponents.h b/xpcom/components/StaticComponents.h
new file mode 100644
index 0000000000..ac4095f2f3
--- /dev/null
+++ b/xpcom/components/StaticComponents.h
@@ -0,0 +1,284 @@
+/* -*- 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 StaticComponents_h
+#define StaticComponents_h
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Module.h"
+#include "mozilla/Span.h"
+#include "nsID.h"
+#include "nsStringFwd.h"
+#include "nscore.h"
+
+#include "mozilla/Components.h"
+#include "StaticComponentData.h"
+
+class nsIFactory;
+class nsIUTF8StringEnumerator;
+class nsISupports;
+template <typename T, size_t N>
+class AutoTArray;
+
+namespace mozilla {
+namespace xpcom {
+
+struct ContractEntry;
+struct StaticModule;
+
+struct StaticCategoryEntry;
+struct StaticCategory;
+
+struct StaticProtocolHandler;
+
+extern const StaticModule gStaticModules[kStaticModuleCount];
+
+extern const ContractEntry gContractEntries[kContractCount];
+extern uint8_t gInvalidContracts[kContractCount / 8 + 1];
+
+extern const StaticCategory gStaticCategories[kStaticCategoryCount];
+extern const StaticCategoryEntry gStaticCategoryEntries[];
+
+extern const StaticProtocolHandler
+ gStaticProtocolHandlers[kStaticProtocolHandlerCount];
+
+template <size_t N>
+static inline bool GetBit(const uint8_t (&aBits)[N], size_t aBit) {
+ static constexpr size_t width = sizeof(aBits[0]) * 8;
+
+ size_t idx = aBit / width;
+ MOZ_ASSERT(idx < N);
+ return aBits[idx] & (1 << (aBit % width));
+}
+
+template <size_t N>
+static inline void SetBit(uint8_t (&aBits)[N], size_t aBit,
+ bool aValue = true) {
+ static constexpr size_t width = sizeof(aBits[0]) * 8;
+
+ size_t idx = aBit / width;
+ MOZ_ASSERT(idx < N);
+ if (aValue) {
+ aBits[idx] |= 1 << (aBit % width);
+ } else {
+ aBits[idx] &= ~(1 << (aBit % width));
+ }
+}
+
+/**
+ * Represents a string entry in the static string table. Can be converted to a
+ * nsCString using GetString() in StaticComponents.cpp.
+ *
+ * This is a struct rather than a pure offset primarily for the purposes of type
+ * safety, but also so that it can easily be extended to include a static length
+ * in the future, if efficiency concerns warrant it.
+ */
+struct StringOffset final {
+ uint32_t mOffset;
+};
+
+/**
+ * Represents an offset into the interfaces table.
+ */
+struct InterfaceOffset final {
+ uint16_t mOffset;
+};
+
+/**
+ * Represents a static component entry defined in a `Classes` list in an XPCOM
+ * manifest. Handles creating instances of and caching service instances for
+ * that class.
+ */
+struct StaticModule {
+ nsID mCID;
+ StringOffset mContractID;
+ Module::ProcessSelector mProcessSelector;
+
+ const nsID& CID() const { return mCID; }
+
+ ModuleID ID() const { return ModuleID(this - gStaticModules); }
+
+ /**
+ * Returns this entry's index in the gStaticModules array.
+ */
+ size_t Idx() const { return size_t(ID()); }
+
+ /**
+ * Returns true if this component's corresponding contract ID is expected to
+ * be overridden at runtime. If so, it should always be looked up by its
+ * ContractID() when retrieving its service instance.
+ */
+ bool Overridable() const;
+
+ /**
+ * If this entry is overridable, returns its associated contract ID string.
+ * The component should always be looked up by this contract ID when
+ * retrieving its service instance.
+ *
+ * Note: This may *only* be called if Overridable() returns true.
+ */
+ nsCString ContractID() const;
+
+ /**
+ * Returns true if this entry is active. Typically this will only return false
+ * if the entry's process selector does not match this process.
+ */
+ bool Active() const;
+
+ already_AddRefed<nsIFactory> GetFactory() const;
+
+ nsresult CreateInstance(const nsIID& aIID, void** aResult) const;
+
+ GetServiceHelper GetService() const;
+ GetServiceHelper GetService(nsresult*) const;
+
+ nsISupports* ServiceInstance() const;
+ void SetServiceInstance(already_AddRefed<nsISupports> aInst) const;
+};
+
+/**
+ * Represents a static mapping between a contract ID string and a StaticModule
+ * entry.
+ */
+struct ContractEntry final {
+ StringOffset mContractID;
+ ModuleID mModuleID;
+
+ size_t Idx() const { return this - gContractEntries; }
+
+ nsCString ContractID() const;
+
+ const StaticModule& Module() const {
+ return gStaticModules[size_t(mModuleID)];
+ }
+
+ /**
+ * Returns true if this entry's underlying module is active, and its contract
+ * ID matches the given contract ID string. This is used by the PerfectHash
+ * function to determine whether to return a result for this entry.
+ */
+ bool Matches(const nsACString& aContractID) const;
+
+ /**
+ * Returns true if this entry has been invalidated, and should be ignored.
+ *
+ * Contract IDs may be overwritten at runtime. When that happens for a static
+ * contract ID, we mark its entry invalid, and ignore it thereafter.
+ */
+ bool Invalid() const { return GetBit(gInvalidContracts, Idx()); }
+
+ /**
+ * Marks this entry invalid (or unsets the invalid bit if aInvalid is false),
+ * after which it will be ignored in contract ID lookup attempts. See
+ * `Invalid()` above.
+ */
+ void SetInvalid(bool aInvalid = true) const {
+ return SetBit(gInvalidContracts, Idx(), aInvalid);
+ }
+};
+
+/**
+ * Represents a declared category manager entry declared in an XPCOM manifest.
+ *
+ * The entire set of static category entries is read at startup and loaded into
+ * the category manager's dynamic hash tables, so there is memory and
+ * initialization overhead for each entry in these tables. This may be further
+ * optimized in the future to reduce some of that overhead.
+ */
+struct StaticCategoryEntry final {
+ StringOffset mEntry;
+ StringOffset mValue;
+ Module::BackgroundTasksSelector mBackgroundTasksSelector;
+ Module::ProcessSelector mProcessSelector;
+
+ nsCString Entry() const;
+ nsCString Value() const;
+ bool Active() const;
+};
+
+struct StaticCategory final {
+ StringOffset mName;
+ uint16_t mStart;
+ uint16_t mCount;
+
+ nsCString Name() const;
+
+ const StaticCategoryEntry* begin() const {
+ return &gStaticCategoryEntries[mStart];
+ }
+ const StaticCategoryEntry* end() const {
+ return &gStaticCategoryEntries[mStart + mCount];
+ }
+};
+
+struct JSServiceEntry final {
+ using InterfaceList = AutoTArray<const nsIID*, 4>;
+
+ static const JSServiceEntry* Lookup(const nsACString& aName);
+
+ StringOffset mName;
+ ModuleID mModuleID;
+
+ InterfaceOffset mInterfaceOffset;
+
+ uint8_t mInterfaceCount;
+
+ nsCString Name() const;
+
+ const StaticModule& Module() const {
+ return gStaticModules[size_t(mModuleID)];
+ }
+
+ InterfaceList Interfaces() const;
+};
+
+struct StaticProtocolHandler final {
+ static const StaticProtocolHandler* Lookup(const nsACString& aScheme);
+ static const StaticProtocolHandler& Default() {
+ return gStaticProtocolHandlers[kDefaultProtocolHandlerIndex];
+ }
+
+ StringOffset mScheme;
+ uint32_t mProtocolFlags;
+ int32_t mDefaultPort;
+ ModuleID mModuleID;
+ bool mHasDynamicFlags;
+
+ nsCString Scheme() const;
+
+ const StaticModule& Module() const {
+ return gStaticModules[size_t(mModuleID)];
+ }
+};
+
+class StaticComponents final {
+ public:
+ static const StaticModule* LookupByCID(const nsID& aCID);
+
+ static const StaticModule* LookupByContractID(const nsACString& aContractID);
+
+ /**
+ * Marks a static contract ID entry invalid (or unsets the invalid bit if
+ * aInvalid is false). See `CategoryEntry::Invalid()`.
+ */
+ static bool InvalidateContractID(const nsACString& aContractID,
+ bool aInvalid = true);
+
+ static already_AddRefed<nsIUTF8StringEnumerator> GetComponentJSMs();
+ static already_AddRefed<nsIUTF8StringEnumerator> GetComponentESModules();
+
+ static Span<const JSServiceEntry> GetJSServices();
+
+ /**
+ * Calls any module unload from manifests whose components have been loaded.
+ */
+ static void Shutdown();
+};
+
+} // namespace xpcom
+} // namespace mozilla
+
+#endif // defined StaticComponents_h
diff --git a/xpcom/components/components.conf b/xpcom/components/components.conf
new file mode 100644
index 0000000000..9938fb104f
--- /dev/null
+++ b/xpcom/components/components.conf
@@ -0,0 +1,23 @@
+# -*- 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/.
+
+Classes = [
+ {
+ 'js_name': 'catMan',
+ 'cid': '{16d222a6-1dd2-11b2-b693-f38b02c021b2}',
+ 'contract_ids': ['@mozilla.org/categorymanager;1'],
+ 'interfaces': ['nsICategoryManager'],
+ 'legacy_constructor': 'nsCategoryManager::Create',
+ 'headers': ['/xpcom/components/nsCategoryManager.h'],
+ 'processes': ProcessSelector.ALLOW_IN_GPU_RDD_SOCKET_AND_UTILITY_PROCESS,
+ },
+ {
+ 'cid': '{91775d60-d5dc-11d2-92fb-00e09805570f}',
+ 'legacy_constructor': 'nsComponentManagerImpl::Create',
+ 'headers': ['/xpcom/components/nsComponentManager.h'],
+ 'processes': ProcessSelector.ALLOW_IN_GPU_RDD_VR_SOCKET_UTILITY_AND_GMPLUGIN_PROCESS,
+ },
+]
diff --git a/xpcom/components/gen_static_components.py b/xpcom/components/gen_static_components.py
new file mode 100644
index 0000000000..f759dc3132
--- /dev/null
+++ b/xpcom/components/gen_static_components.py
@@ -0,0 +1,1216 @@
+import json
+import os
+import re
+import struct
+from collections import defaultdict
+from uuid import UUID
+
+import buildconfig
+from mozbuild.util import FileAvoidWrite
+from perfecthash import PerfectHash
+
+NO_CONTRACT_ID = 0xFFFFFFFF
+
+PHF_SIZE = 512
+
+TINY_PHF_SIZE = 16
+
+# In tests, we might not have a (complete) buildconfig.
+ENDIAN = (
+ "<" if buildconfig.substs.get("TARGET_ENDIANNESS", "little") == "little" else ">"
+)
+
+
+# Represents a UUID in the format used internally by Gecko, and supports
+# serializing it in that format to both C++ source and raw byte arrays.
+class UUIDRepr(object):
+ def __init__(self, uuid):
+ self.uuid = uuid
+
+ fields = uuid.fields
+
+ self.a = fields[0]
+ self.b = fields[1]
+ self.c = fields[2]
+
+ d = list(fields[3:5])
+ for i in range(0, 6):
+ d.append(fields[5] >> (8 * (5 - i)) & 0xFF)
+
+ self.d = tuple(d)
+
+ def __str__(self):
+ return str(self.uuid)
+
+ @property
+ def bytes(self):
+ return struct.pack(ENDIAN + "IHHBBBBBBBB", self.a, self.b, self.c, *self.d)
+
+ def to_cxx(self):
+ rest = ", ".join("0x%02x" % b for b in self.d)
+
+ return "{ 0x%x, 0x%x, 0x%x, { %s } }" % (self.a, self.b, self.c, rest)
+
+
+# Corresponds to the Module::ProcessSelector enum in Module.h. The actual
+# values don't matter, since the code generator emits symbolic constants for
+# these values, but we use the same values as the enum constants for clarity.
+class ProcessSelector:
+ ANY_PROCESS = 0
+ MAIN_PROCESS_ONLY = 1 << 0
+ CONTENT_PROCESS_ONLY = 1 << 1
+ ALLOW_IN_GPU_PROCESS = 1 << 2
+ ALLOW_IN_VR_PROCESS = 1 << 3
+ ALLOW_IN_SOCKET_PROCESS = 1 << 4
+ ALLOW_IN_RDD_PROCESS = 1 << 5
+ ALLOW_IN_UTILITY_PROCESS = 1 << 6
+ ALLOW_IN_GMPLUGIN_PROCESS = 1 << 7
+ ALLOW_IN_GPU_AND_MAIN_PROCESS = ALLOW_IN_GPU_PROCESS | MAIN_PROCESS_ONLY
+ ALLOW_IN_GPU_AND_SOCKET_PROCESS = ALLOW_IN_GPU_PROCESS | ALLOW_IN_SOCKET_PROCESS
+ ALLOW_IN_GPU_AND_VR_PROCESS = ALLOW_IN_GPU_PROCESS | ALLOW_IN_VR_PROCESS
+ ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS = (
+ ALLOW_IN_GPU_PROCESS | ALLOW_IN_VR_PROCESS | ALLOW_IN_SOCKET_PROCESS
+ )
+ ALLOW_IN_RDD_AND_SOCKET_PROCESS = ALLOW_IN_RDD_PROCESS | ALLOW_IN_SOCKET_PROCESS
+ ALLOW_IN_GPU_RDD_AND_SOCKET_PROCESS = (
+ ALLOW_IN_GPU_PROCESS | ALLOW_IN_RDD_PROCESS | ALLOW_IN_SOCKET_PROCESS
+ )
+ ALLOW_IN_GPU_RDD_SOCKET_AND_UTILITY_PROCESS = (
+ ALLOW_IN_GPU_PROCESS
+ | ALLOW_IN_RDD_PROCESS
+ | ALLOW_IN_SOCKET_PROCESS
+ | ALLOW_IN_UTILITY_PROCESS
+ )
+ ALLOW_IN_GPU_RDD_VR_AND_SOCKET_PROCESS = (
+ ALLOW_IN_GPU_PROCESS
+ | ALLOW_IN_RDD_PROCESS
+ | ALLOW_IN_VR_PROCESS
+ | ALLOW_IN_SOCKET_PROCESS
+ )
+ ALLOW_IN_GPU_RDD_VR_SOCKET_AND_UTILITY_PROCESS = (
+ ALLOW_IN_GPU_PROCESS
+ | ALLOW_IN_RDD_PROCESS
+ | ALLOW_IN_VR_PROCESS
+ | ALLOW_IN_SOCKET_PROCESS
+ | ALLOW_IN_UTILITY_PROCESS
+ )
+ ALLOW_IN_GPU_RDD_VR_SOCKET_UTILITY_AND_GMPLUGIN_PROCESS = (
+ ALLOW_IN_GPU_PROCESS
+ | ALLOW_IN_RDD_PROCESS
+ | ALLOW_IN_VR_PROCESS
+ | ALLOW_IN_SOCKET_PROCESS
+ | ALLOW_IN_UTILITY_PROCESS
+ | ALLOW_IN_GMPLUGIN_PROCESS
+ )
+
+
+# Maps ProcessSelector constants to the name of the corresponding
+# Module::ProcessSelector enum value.
+PROCESSES = {
+ ProcessSelector.ANY_PROCESS: "ANY_PROCESS",
+ ProcessSelector.MAIN_PROCESS_ONLY: "MAIN_PROCESS_ONLY",
+ ProcessSelector.CONTENT_PROCESS_ONLY: "CONTENT_PROCESS_ONLY",
+ ProcessSelector.ALLOW_IN_GPU_PROCESS: "ALLOW_IN_GPU_PROCESS",
+ ProcessSelector.ALLOW_IN_VR_PROCESS: "ALLOW_IN_VR_PROCESS",
+ ProcessSelector.ALLOW_IN_SOCKET_PROCESS: "ALLOW_IN_SOCKET_PROCESS",
+ ProcessSelector.ALLOW_IN_RDD_PROCESS: "ALLOW_IN_RDD_PROCESS",
+ ProcessSelector.ALLOW_IN_GPU_AND_MAIN_PROCESS: "ALLOW_IN_GPU_AND_MAIN_PROCESS",
+ ProcessSelector.ALLOW_IN_GPU_AND_SOCKET_PROCESS: "ALLOW_IN_GPU_AND_SOCKET_PROCESS",
+ ProcessSelector.ALLOW_IN_GPU_AND_VR_PROCESS: "ALLOW_IN_GPU_AND_VR_PROCESS",
+ ProcessSelector.ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS: "ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS",
+ ProcessSelector.ALLOW_IN_RDD_AND_SOCKET_PROCESS: "ALLOW_IN_RDD_AND_SOCKET_PROCESS",
+ ProcessSelector.ALLOW_IN_GPU_RDD_AND_SOCKET_PROCESS: "ALLOW_IN_GPU_RDD_AND_SOCKET_PROCESS",
+ ProcessSelector.ALLOW_IN_GPU_RDD_SOCKET_AND_UTILITY_PROCESS: "ALLOW_IN_GPU_RDD_SOCKET_AND_UTILITY_PROCESS", # NOQA: E501
+ ProcessSelector.ALLOW_IN_GPU_RDD_VR_AND_SOCKET_PROCESS: "ALLOW_IN_GPU_RDD_VR_AND_SOCKET_PROCESS", # NOQA: E501
+ ProcessSelector.ALLOW_IN_GPU_RDD_VR_SOCKET_AND_UTILITY_PROCESS: "ALLOW_IN_GPU_RDD_VR_SOCKET_AND_UTILITY_PROCESS", # NOQA: E501
+ ProcessSelector.ALLOW_IN_GPU_RDD_VR_SOCKET_UTILITY_AND_GMPLUGIN_PROCESS: "ALLOW_IN_GPU_RDD_VR_SOCKET_UTILITY_AND_GMPLUGIN_PROCESS", # NOQA: E501
+}
+
+
+# Emits the C++ symbolic constant corresponding to a ProcessSelector constant.
+def lower_processes(processes):
+ return "Module::ProcessSelector::%s" % PROCESSES[processes]
+
+
+# Emits the C++ symbolic constant for a ModuleEntry's ModuleID enum entry.
+def lower_module_id(module):
+ return "ModuleID::%s" % module.name
+
+
+# Corresponds to the Module::BackgroundTasksSelector enum in Module.h. The
+# actual values don't matter, since the code generator emits symbolic constants
+# for these values, but we use the same values as the enum constants for
+# clarity.
+class BackgroundTasksSelector:
+ NO_TASKS = 0x0
+ ALL_TASKS = 0xFFFF
+
+
+# Maps BackgroundTasksSelector constants to the name of the corresponding
+# Module::BackgroundTasksSelector enum value.
+BACKGROUNDTASKS = {
+ BackgroundTasksSelector.ALL_TASKS: "ALL_TASKS",
+ BackgroundTasksSelector.NO_TASKS: "NO_TASKS",
+}
+
+
+# Emits the C++ symbolic constant corresponding to a BackgroundTasks constant.
+def lower_backgroundtasks(backgroundtasks):
+ return "Module::BackgroundTasksSelector::%s" % BACKGROUNDTASKS[backgroundtasks]
+
+
+# Represents a static string table, indexed by offset. This allows us to
+# reference strings from static data structures without requiring runtime
+# relocations.
+class StringTable(object):
+ def __init__(self):
+ self.entries = {}
+ self.entry_list = []
+ self.size = 0
+
+ self._serialized = False
+
+ # Returns the index of the given string in the `entry_list` array. If
+ # no entry for the string exists, it first creates one.
+ def get_idx(self, string):
+ idx = self.entries.get(string, None)
+ if idx is not None:
+ return idx
+
+ assert not self._serialized
+
+ assert len(string) == len(string.encode("utf-8"))
+
+ idx = self.size
+ self.size += len(string) + 1
+
+ self.entries[string] = idx
+ self.entry_list.append(string)
+ return idx
+
+ # Returns the C++ code representing string data of this string table, as a
+ # single string literal. This must only be called after the last call to
+ # `get_idx()` or `entry_to_cxx()` for this instance.
+ def to_cxx(self):
+ self._serialized = True
+
+ lines = []
+
+ idx = 0
+ for entry in self.entry_list:
+ str_ = entry.replace("\\", "\\\\").replace('"', r"\"").replace("\n", r"\n")
+
+ lines.append(' /* 0x%x */ "%s\\0"\n' % (idx, str_))
+
+ idx += len(entry) + 1
+
+ return "".join(lines)
+
+ # Returns a `StringEntry` struct initializer for the string table entry
+ # corresponding to the given string. If no matching entry exists, it is
+ # first created.
+ def entry_to_cxx(self, string):
+ idx = self.get_idx(string)
+ return "{ 0x%x } /* %s */" % (idx, pretty_string(string))
+
+
+strings = StringTable()
+
+interfaces = []
+
+
+# Represents a C++ namespace, containing a set of classes and potentially
+# sub-namespaces. This is used to generate pre-declarations for incomplete
+# types referenced in XPCOM manifests.
+class Namespace(object):
+ def __init__(self, name=None):
+ self.name = name
+ self.classes = set()
+ self.namespaces = {}
+
+ # Returns a Namespace object for the sub-namespace with the given name.
+ def sub(self, name):
+ assert name not in self.classes
+
+ if name not in self.namespaces:
+ self.namespaces[name] = Namespace(name)
+ return self.namespaces[name]
+
+ # Generates C++ code to pre-declare all classes in this namespace and all
+ # of its sub-namespaces.
+ def to_cxx(self):
+ res = ""
+ if self.name:
+ res += "namespace %s {\n" % self.name
+
+ for clas in sorted(self.classes):
+ res += "class %s;\n" % clas
+
+ for ns in sorted(self.namespaces.keys()):
+ res += self.namespaces[ns].to_cxx()
+
+ if self.name:
+ res += "} // namespace %s\n" % self.name
+
+ return res
+
+
+# Represents a component defined in an XPCOM manifest's `Classes` array.
+class ModuleEntry(object):
+ next_anon_id = 0
+
+ def __init__(self, data, init_idx):
+ self.cid = UUIDRepr(UUID(data["cid"]))
+ self.contract_ids = data.get("contract_ids", [])
+ self.type = data.get("type", "nsISupports")
+ self.categories = data.get("categories", {})
+ self.processes = data.get("processes", 0)
+ self.headers = data.get("headers", [])
+
+ self.js_name = data.get("js_name", None)
+ self.interfaces = data.get("interfaces", [])
+
+ if len(self.interfaces) > 255:
+ raise Exception(
+ "JS service %s may not have more than 255 " "interfaces" % self.js_name
+ )
+
+ self.interfaces_offset = len(interfaces)
+ for iface in self.interfaces:
+ interfaces.append(iface)
+
+ # If the manifest declares Init or Unload functions, this contains its
+ # index, as understood by the `CallInitFunc()` function.
+ #
+ # If it contains any value other than `None`, a corresponding
+ # `CallInitFunc(init_idx)` call will be genrated before calling this
+ # module's constructor.
+ self.init_idx = init_idx
+
+ self.constructor = data.get("constructor", None)
+ self.legacy_constructor = data.get("legacy_constructor", None)
+ self.init_method = data.get("init_method", [])
+
+ self.jsm = data.get("jsm", None)
+ self.esModule = data.get("esModule", None)
+
+ self.external = data.get(
+ "external", not (self.headers or self.legacy_constructor)
+ )
+ self.singleton = data.get("singleton", False)
+ self.overridable = data.get("overridable", False)
+
+ self.protocol_config = data.get("protocol_config", None)
+
+ if "name" in data:
+ self.anonymous = False
+ self.name = data["name"]
+ else:
+ self.anonymous = True
+ self.name = "Anonymous%03d" % ModuleEntry.next_anon_id
+ ModuleEntry.next_anon_id += 1
+
+ def error(str_):
+ raise Exception(
+ "Error defining component %s (%s): %s"
+ % (str(self.cid), ", ".join(map(repr, self.contract_ids)), str_)
+ )
+
+ if self.jsm:
+ if not self.constructor:
+ error("JavaScript components must specify a constructor")
+
+ for prop in ("init_method", "legacy_constructor", "headers"):
+ if getattr(self, prop):
+ error(
+ "JavaScript components may not specify a '%s' "
+ "property" % prop
+ )
+ elif self.esModule:
+ if not self.constructor:
+ error("JavaScript components must specify a constructor")
+
+ for prop in ("init_method", "legacy_constructor", "headers"):
+ if getattr(self, prop):
+ error(
+ "JavaScript components may not specify a '%s' "
+ "property" % prop
+ )
+ elif self.external:
+ if self.constructor or self.legacy_constructor:
+ error(
+ "Externally-constructed components may not specify "
+ "'constructor' or 'legacy_constructor' properties"
+ )
+ if self.init_method:
+ error(
+ "Externally-constructed components may not specify "
+ "'init_method' properties"
+ )
+ if self.type == "nsISupports":
+ error(
+ "Externally-constructed components must specify a type "
+ "other than nsISupports"
+ )
+
+ if self.constructor and self.legacy_constructor:
+ error(
+ "The 'constructor' and 'legacy_constructor' properties "
+ "are mutually exclusive"
+ )
+
+ if self.overridable and not self.contract_ids:
+ error("Overridable components must specify at least one contract " "ID")
+
+ @property
+ def contract_id(self):
+ return self.contract_ids[0]
+
+ # Generates the C++ code for a StaticModule struct initializer
+ # representing this component.
+ def to_cxx(self):
+ contract_id = (
+ strings.entry_to_cxx(self.contract_id)
+ if self.overridable
+ else "{ 0x%x }" % NO_CONTRACT_ID
+ )
+
+ return """
+ /* {name} */ {{
+ /* {{{cid_string}}} */
+ {cid},
+ {contract_id},
+ {processes},
+ }}""".format(
+ name=self.name,
+ cid=self.cid.to_cxx(),
+ cid_string=str(self.cid),
+ contract_id=contract_id,
+ processes=lower_processes(self.processes),
+ )
+
+ # Generates the C++ code for a JSServiceEntry representing this module.
+ def lower_js_service(self):
+ return """
+ {{
+ {js_name},
+ ModuleID::{name},
+ {{ {iface_offset} }},
+ {iface_count}
+ }}""".format(
+ js_name=strings.entry_to_cxx(self.js_name),
+ name=self.name,
+ iface_offset=self.interfaces_offset,
+ iface_count=len(self.interfaces),
+ )
+
+ # Generates the C++ code necessary to construct an instance of this
+ # component.
+ #
+ # This code lives in a function with the following arguments:
+ #
+ # - aIID: The `const nsIID&` interface ID that the resulting instance
+ # will be queried to.
+ #
+ # - aResult: The `void**` pointer in which to store the result.
+ #
+ # And which returns an `nsresult` indicating success or failure.
+ def lower_constructor(self):
+ res = ""
+
+ if self.init_idx is not None:
+ res += " MOZ_TRY(CallInitFunc(%d));\n" % self.init_idx
+
+ if self.legacy_constructor:
+ res += (
+ " return /* legacy */ %s(aIID, aResult);\n"
+ % self.legacy_constructor
+ )
+ return res
+
+ if self.jsm:
+ res += (
+ " nsCOMPtr<nsISupports> inst;\n"
+ " MOZ_TRY(ConstructJSMComponent(nsLiteralCString(%s),\n"
+ " %s,\n"
+ " getter_AddRefs(inst)));"
+ "\n" % (json.dumps(self.jsm), json.dumps(self.constructor))
+ )
+ elif self.esModule:
+ res += (
+ " nsCOMPtr<nsISupports> inst;\n"
+ " MOZ_TRY(ConstructESModuleComponent(nsLiteralCString(%s),\n"
+ " %s,\n"
+ " getter_AddRefs(inst)));"
+ "\n" % (json.dumps(self.esModule), json.dumps(self.constructor))
+ )
+ elif self.external:
+ res += (
+ " nsCOMPtr<nsISupports> inst = "
+ "mozCreateComponent<%s>();\n" % self.type
+ )
+ # The custom constructor may return null, so check before calling
+ # any methods.
+ res += " NS_ENSURE_TRUE(inst, NS_ERROR_FAILURE);\n"
+ else:
+ res += " RefPtr<%s> inst = " % self.type
+
+ if not self.constructor:
+ res += "new %s();\n" % self.type
+ else:
+ res += "%s();\n" % self.constructor
+ # The `new` operator is infallible, so we don't need to worry
+ # about it returning null, but custom constructors may, so
+ # check before calling any methods.
+ res += " NS_ENSURE_TRUE(inst, NS_ERROR_OUT_OF_MEMORY);\n"
+
+ # Check that the constructor function returns an appropriate
+ # `already_AddRefed` value for our declared type.
+ res += """
+ using T =
+ RemoveAlreadyAddRefed<decltype(%(constructor)s())>::Type;
+ static_assert(
+ std::is_same_v<already_AddRefed<T>, decltype(%(constructor)s())>,
+ "Singleton constructor must return already_AddRefed");
+ static_assert(
+ std::is_base_of<%(type)s, T>::value,
+ "Singleton constructor must return correct already_AddRefed");
+
+""" % {
+ "type": self.type,
+ "constructor": self.constructor,
+ }
+
+ if self.init_method:
+ res += " MOZ_TRY(inst->%s());\n" % self.init_method
+
+ res += " return inst->QueryInterface(aIID, aResult);\n"
+
+ return res
+
+ # Generates the C++ code for the `mozilla::components::<name>` entry
+ # corresponding to this component. This may not be called for modules
+ # without an explicit `name` (in which cases, `self.anonymous` will be
+ # true).
+ def lower_getters(self):
+ assert not self.anonymous
+
+ substs = {
+ "name": self.name,
+ "id": "::mozilla::xpcom::ModuleID::%s" % self.name,
+ }
+
+ res = (
+ """
+namespace %(name)s {
+static inline const nsID& CID() {
+ return ::mozilla::xpcom::Components::GetCID(%(id)s);
+}
+
+static inline ::mozilla::xpcom::GetServiceHelper Service(nsresult* aRv = nullptr) {
+ return {%(id)s, aRv};
+}
+"""
+ % substs
+ )
+
+ if not self.singleton:
+ res += (
+ """
+static inline ::mozilla::xpcom::CreateInstanceHelper Create(nsresult* aRv = nullptr) {
+ return {%(id)s, aRv};
+}
+"""
+ % substs
+ )
+
+ res += (
+ """\
+} // namespace %(name)s
+"""
+ % substs
+ )
+
+ return res
+
+ # Generates the rust code for the `xpcom::components::<name>` entry
+ # corresponding to this component. This may not be called for modules
+ # without an explicit `name` (in which cases, `self.anonymous` will be
+ # true).
+ def lower_getters_rust(self):
+ assert not self.anonymous
+
+ substs = {
+ "name": self.name,
+ "id": "super::ModuleID::%s" % self.name,
+ }
+
+ res = (
+ """
+#[allow(non_snake_case)]
+pub mod %(name)s {
+ /// Get the singleton service instance for this component.
+ pub fn service<T: crate::XpCom>() -> Result<crate::RefPtr<T>, nserror::nsresult> {
+ let mut ga = crate::GetterAddrefs::<T>::new();
+ let rv = unsafe { super::Gecko_GetServiceByModuleID(%(id)s, &T::IID, ga.void_ptr()) };
+ if rv.failed() {
+ return Err(rv);
+ }
+ ga.refptr().ok_or(nserror::NS_ERROR_NO_INTERFACE)
+ }
+"""
+ % substs
+ )
+
+ if not self.singleton:
+ res += (
+ """
+ /// Create a new instance of this component.
+ pub fn create<T: crate::XpCom>() -> Result<crate::RefPtr<T>, nserror::nsresult> {
+ let mut ga = crate::GetterAddrefs::<T>::new();
+ let rv = unsafe { super::Gecko_CreateInstanceByModuleID(%(id)s, &T::IID, ga.void_ptr()) };
+ if rv.failed() {
+ return Err(rv);
+ }
+ ga.refptr().ok_or(nserror::NS_ERROR_NO_INTERFACE)
+ }
+"""
+ % substs
+ )
+
+ res += """\
+}
+"""
+
+ return res
+
+
+# Returns a quoted string literal representing the given raw string, with
+# certain special characters replaced so that it can be used in a C++-style
+# (/* ... */) comment.
+def pretty_string(string):
+ return json.dumps(string).replace("*/", r"*\/").replace("/*", r"/\*")
+
+
+# Represents a static contract ID entry, corresponding to a C++ ContractEntry
+# struct, mapping a contract ID to a static module entry.
+class ContractEntry(object):
+ def __init__(self, contract, module):
+ self.contract = contract
+ self.module = module
+
+ def to_cxx(self):
+ return """
+ {{
+ {contract},
+ {module_id},
+ }}""".format(
+ contract=strings.entry_to_cxx(self.contract),
+ module_id=lower_module_id(self.module),
+ )
+
+
+# Represents a static ProtocolHandler entry, corresponding to a C++
+# ProtocolEntry struct, mapping a scheme to a static module entry and metadata.
+class ProtocolHandler(object):
+ def __init__(self, config, module):
+ def error(str_):
+ raise Exception(
+ "Error defining protocol handler %s (%s): %s"
+ % (str(module.cid), ", ".join(map(repr, module.contract_ids)), str_)
+ )
+
+ self.module = module
+ self.scheme = config.get("scheme", None)
+ if self.scheme is None:
+ error("No scheme defined for protocol component")
+ self.flags = config.get("flags", None)
+ if self.flags is None:
+ error("No flags defined for protocol component")
+ self.default_port = config.get("default_port", -1)
+ self.has_dynamic_flags = config.get("has_dynamic_flags", False)
+
+ def to_cxx(self):
+ return """
+ {{
+ .mScheme = {scheme},
+ .mProtocolFlags = {flags},
+ .mDefaultPort = {default_port},
+ .mModuleID = {module_id},
+ .mHasDynamicFlags = {has_dynamic_flags},
+ }}
+ """.format(
+ scheme=strings.entry_to_cxx(self.scheme),
+ module_id=lower_module_id(self.module),
+ flags=" | ".join("nsIProtocolHandler::%s" % flag for flag in self.flags),
+ default_port=self.default_port,
+ has_dynamic_flags="true" if self.has_dynamic_flags else "false",
+ )
+
+
+# Generates the C++ code for the StaticCategoryEntry and StaticCategory
+# structs for all category entries declared in XPCOM manifests.
+def gen_categories(substs, categories):
+ cats = []
+ ents = []
+
+ count = 0
+ for category, entries in sorted(categories.items()):
+
+ def k(entry):
+ return tuple(entry[0]["name"]) + entry[1:]
+
+ entries.sort(key=k)
+
+ cats.append(
+ " { %s,\n"
+ " %d, %d },\n" % (strings.entry_to_cxx(category), count, len(entries))
+ )
+ count += len(entries)
+
+ ents.append(" /* %s */\n" % pretty_string(category))
+ for entry, value, processes in entries:
+ name = entry["name"]
+ backgroundtasks = entry.get(
+ "backgroundtasks", BackgroundTasksSelector.NO_TASKS
+ )
+
+ ents.append(
+ " { %s,\n"
+ " %s,\n"
+ " %s,\n"
+ " %s },\n"
+ % (
+ strings.entry_to_cxx(name),
+ strings.entry_to_cxx(value),
+ lower_backgroundtasks(backgroundtasks),
+ lower_processes(processes),
+ )
+ )
+ ents.append("\n")
+ ents.pop()
+
+ substs["category_count"] = len(cats)
+ substs["categories"] = "".join(cats)
+ substs["category_entries"] = "".join(ents)
+
+
+# Generates the C++ code for all Init and Unload functions declared in XPCOM
+# manifests. These form the bodies of the `CallInitFunc()` and `CallUnload`
+# functions in StaticComponents.cpp.
+def gen_module_funcs(substs, funcs):
+ inits = []
+ unloads = []
+
+ template = """\
+ case %d:
+ %s
+ break;
+"""
+
+ for i, (init, unload) in enumerate(funcs):
+ init_code = "%s();" % init if init else "/* empty */"
+ inits.append(template % (i, init_code))
+
+ if unload:
+ unloads.append(
+ """\
+ if (CalledInit(%d)) {
+ %s();
+ }
+"""
+ % (i, unload)
+ )
+
+ substs["init_funcs"] = "".join(inits)
+ substs["unload_funcs"] = "".join(unloads)
+ substs["init_count"] = len(funcs)
+
+
+def gen_interfaces(ifaces):
+ res = []
+ for iface in ifaces:
+ res.append(" nsXPTInterface::%s,\n" % iface)
+ return "".join(res)
+
+
+# Generates class pre-declarations for any types referenced in `Classes` array
+# entries which do not have corresponding `headers` entries to fully declare
+# their types.
+def gen_decls(types):
+ root_ns = Namespace()
+
+ for type_ in sorted(types):
+ parts = type_.split("::")
+
+ ns = root_ns
+ for part in parts[:-1]:
+ ns = ns.sub(part)
+ ns.classes.add(parts[-1])
+
+ return root_ns.to_cxx()
+
+
+# Generates the `switch` body for the `CreateInstanceImpl()` function, with a
+# `case` for each value in ModuleID to construct an instance of the
+# corresponding component.
+def gen_constructors(entries):
+ constructors = []
+ for entry in entries:
+ constructors.append(
+ """\
+ case {id}: {{
+{constructor}\
+ }}
+""".format(
+ id=lower_module_id(entry), constructor=entry.lower_constructor()
+ )
+ )
+
+ return "".join(constructors)
+
+
+# Generates the getter code for each named component entry in the
+# `mozilla::components::` namespace.
+def gen_getters(entries):
+ entries = list(entries)
+ entries.sort(key=lambda e: e.name)
+
+ return "".join(entry.lower_getters() for entry in entries if not entry.anonymous)
+
+
+# Generates the rust getter code for each named component entry in the
+# `xpcom::components::` module.
+def gen_getters_rust(entries):
+ entries = list(entries)
+ entries.sort(key=lambda e: e.name)
+
+ return "".join(
+ entry.lower_getters_rust() for entry in entries if not entry.anonymous
+ )
+
+
+def gen_includes(substs, all_headers):
+ headers = set()
+ absolute_headers = set()
+
+ for header in all_headers:
+ if header.startswith("/"):
+ absolute_headers.add(header)
+ else:
+ headers.add(header)
+
+ includes = ['#include "%s"' % header for header in sorted(headers)]
+ substs["includes"] = "\n".join(includes) + "\n"
+
+ relative_includes = [
+ '#include "../..%s"' % header for header in sorted(absolute_headers)
+ ]
+ substs["relative_includes"] = "\n".join(relative_includes) + "\n"
+
+
+def to_category_list(val):
+ # Entries can be bare strings (like `"m-browser"`), lists of bare strings,
+ # or dictionaries (like `{"name": "m-browser", "backgroundtasks":
+ # BackgroundTasksSelector.ALL_TASKS}`), somewhat recursively.
+
+ def ensure_dict(v):
+ # Turn `v` into `{"name": v}` if it's not already a dict.
+ if isinstance(v, dict):
+ return v
+ return {"name": v}
+
+ if isinstance(val, (list, tuple)):
+ return tuple(ensure_dict(v) for v in val)
+
+ if isinstance(val, dict):
+ # Explode `{"name": ["x", "y"], "backgroundtasks": ...}` into
+ # `[{"name": "x", "backgroundtasks": ...}, {"name": "y", "backgroundtasks": ...}]`.
+ names = val.pop("name")
+
+ vals = []
+ for entry in to_category_list(names):
+ d = dict(val)
+ d["name"] = entry["name"]
+ vals.append(d)
+
+ return tuple(vals)
+
+ return (ensure_dict(val),)
+
+
+def gen_substs(manifests):
+ module_funcs = []
+
+ headers = set()
+
+ modules = []
+ categories = defaultdict(list)
+
+ for manifest in manifests:
+ headers |= set(manifest.get("Headers", []))
+
+ init_idx = None
+ init = manifest.get("InitFunc")
+ unload = manifest.get("UnloadFunc")
+ if init or unload:
+ init_idx = len(module_funcs)
+ module_funcs.append((init, unload))
+
+ for clas in manifest["Classes"]:
+ modules.append(ModuleEntry(clas, init_idx))
+
+ for category, entries in manifest.get("Categories", {}).items():
+ for key, entry in entries.items():
+ if isinstance(entry, tuple):
+ value, process = entry
+ else:
+ value, process = entry, 0
+ categories[category].append(({"name": key}, value, process))
+
+ cids = set()
+ contracts = []
+ contract_map = {}
+ js_services = {}
+ protocol_handlers = {}
+
+ jsms = set()
+ esModules = set()
+
+ types = set()
+
+ for mod in modules:
+ headers |= set(mod.headers)
+
+ for contract_id in mod.contract_ids:
+ if contract_id in contract_map:
+ raise Exception("Duplicate contract ID: %s" % contract_id)
+
+ entry = ContractEntry(contract_id, mod)
+ contracts.append(entry)
+ contract_map[contract_id] = entry
+
+ for category, entries in mod.categories.items():
+ for entry in to_category_list(entries):
+ categories[category].append((entry, mod.contract_id, mod.processes))
+
+ if mod.type and not mod.headers:
+ types.add(mod.type)
+
+ if mod.jsm:
+ jsms.add(mod.jsm)
+
+ if mod.esModule:
+ esModules.add(mod.esModule)
+
+ if mod.js_name:
+ if mod.js_name in js_services:
+ raise Exception("Duplicate JS service name: %s" % mod.js_name)
+ js_services[mod.js_name] = mod
+
+ if mod.protocol_config:
+ handler = ProtocolHandler(mod.protocol_config, mod)
+ if handler.scheme in protocol_handlers:
+ raise Exception("Duplicate protocol handler: %s" % handler.scheme)
+ protocol_handlers[handler.scheme] = handler
+
+ if str(mod.cid) in cids:
+ raise Exception("Duplicate cid: %s" % str(mod.cid))
+ cids.add(str(mod.cid))
+
+ cid_phf = PerfectHash(modules, PHF_SIZE, key=lambda module: module.cid.bytes)
+
+ contract_phf = PerfectHash(contracts, PHF_SIZE, key=lambda entry: entry.contract)
+
+ js_services_phf = PerfectHash(
+ list(js_services.values()), PHF_SIZE, key=lambda entry: entry.js_name
+ )
+
+ protocol_handlers_phf = PerfectHash(
+ list(protocol_handlers.values()), TINY_PHF_SIZE, key=lambda entry: entry.scheme
+ )
+
+ js_services_json = {}
+ for entry in js_services.values():
+ for iface in entry.interfaces:
+ js_services_json[iface] = entry.js_name
+
+ substs = {}
+
+ gen_categories(substs, categories)
+
+ substs["module_ids"] = "".join(" %s,\n" % entry.name for entry in cid_phf.entries)
+
+ substs["module_count"] = len(modules)
+ substs["contract_count"] = len(contracts)
+ substs["protocol_handler_count"] = len(protocol_handlers)
+
+ substs["default_protocol_handler_idx"] = protocol_handlers_phf.get_index("default")
+
+ gen_module_funcs(substs, module_funcs)
+
+ gen_includes(substs, headers)
+
+ substs["component_jsms"] = (
+ "\n".join(" %s," % strings.entry_to_cxx(jsm) for jsm in sorted(jsms)) + "\n"
+ )
+ substs["component_esmodules"] = (
+ "\n".join(
+ " %s," % strings.entry_to_cxx(esModule) for esModule in sorted(esModules)
+ )
+ + "\n"
+ )
+
+ substs["interfaces"] = gen_interfaces(interfaces)
+
+ substs["decls"] = gen_decls(types)
+
+ substs["constructors"] = gen_constructors(cid_phf.entries)
+
+ substs["component_getters"] = gen_getters(cid_phf.entries)
+
+ substs["component_getters_rust"] = gen_getters_rust(cid_phf.entries)
+
+ substs["module_cid_table"] = cid_phf.cxx_codegen(
+ name="ModuleByCID",
+ entry_type="StaticModule",
+ entries_name="gStaticModules",
+ lower_entry=lambda entry: entry.to_cxx(),
+ return_type="const StaticModule*",
+ return_entry=(
+ "return entry.CID().Equals(aKey) && entry.Active()" " ? &entry : nullptr;"
+ ),
+ key_type="const nsID&",
+ key_bytes="reinterpret_cast<const char*>(&aKey)",
+ key_length="sizeof(nsID)",
+ )
+
+ substs["module_contract_id_table"] = contract_phf.cxx_codegen(
+ name="LookupContractID",
+ entry_type="ContractEntry",
+ entries_name="gContractEntries",
+ lower_entry=lambda entry: entry.to_cxx(),
+ return_type="const ContractEntry*",
+ return_entry="return entry.Matches(aKey) ? &entry : nullptr;",
+ key_type="const nsACString&",
+ key_bytes="aKey.BeginReading()",
+ key_length="aKey.Length()",
+ )
+
+ substs["js_services_table"] = js_services_phf.cxx_codegen(
+ name="LookupJSService",
+ entry_type="JSServiceEntry",
+ entries_name="gJSServices",
+ lower_entry=lambda entry: entry.lower_js_service(),
+ return_type="const JSServiceEntry*",
+ return_entry="return entry.Name() == aKey ? &entry : nullptr;",
+ key_type="const nsACString&",
+ key_bytes="aKey.BeginReading()",
+ key_length="aKey.Length()",
+ )
+
+ substs["protocol_handlers_table"] = protocol_handlers_phf.cxx_codegen(
+ name="LookupProtocolHandler",
+ entry_type="StaticProtocolHandler",
+ entries_name="gStaticProtocolHandlers",
+ lower_entry=lambda entry: entry.to_cxx(),
+ return_type="const StaticProtocolHandler*",
+ return_entry="return entry.Scheme() == aKey ? &entry : nullptr;",
+ key_type="const nsACString&",
+ key_bytes="aKey.BeginReading()",
+ key_length="aKey.Length()",
+ )
+
+ substs["js_services_json"] = json.dumps(js_services_json, sort_keys=True, indent=4)
+
+ # Do this only after everything else has been emitted so we're sure the
+ # string table is complete.
+ substs["strings"] = strings.to_cxx()
+ return substs
+
+
+# Returns true if the given build config substitution is defined and truthy.
+def defined(subst):
+ return bool(buildconfig.substs.get(subst))
+
+
+def read_manifest(filename):
+ glbl = {
+ "buildconfig": buildconfig,
+ "defined": defined,
+ "ProcessSelector": ProcessSelector,
+ "BackgroundTasksSelector": BackgroundTasksSelector,
+ }
+ code = compile(open(filename).read(), filename, "exec")
+ exec(code, glbl)
+ return glbl
+
+
+def main(fd, conf_file, template_file):
+ def open_output(filename):
+ return FileAvoidWrite(os.path.join(os.path.dirname(fd.name), filename))
+
+ conf = json.load(open(conf_file, "r"))
+
+ deps = set()
+
+ manifests = []
+ for filename in conf["manifests"]:
+ deps.add(filename)
+ manifest = read_manifest(filename)
+ manifests.append(manifest)
+ manifest.setdefault("Priority", 50)
+ manifest["__filename__"] = filename
+
+ manifests.sort(key=lambda man: (man["Priority"], man["__filename__"]))
+
+ substs = gen_substs(manifests)
+
+ def replacer(match):
+ return substs[match.group(1)]
+
+ with open_output("StaticComponents.cpp") as fh:
+ with open(template_file, "r") as tfh:
+ template = tfh.read()
+
+ fh.write(re.sub(r"//# @([a-zA-Z_]+)@\n", replacer, template))
+
+ with open_output("StaticComponentData.h") as fh:
+ fh.write(
+ """\
+/* -*- 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 StaticComponentData_h
+#define StaticComponentData_h
+
+#include <stddef.h>
+
+namespace mozilla {
+namespace xpcom {
+
+static constexpr size_t kStaticModuleCount = %(module_count)d;
+
+static constexpr size_t kContractCount = %(contract_count)d;
+
+static constexpr size_t kStaticCategoryCount = %(category_count)d;
+
+static constexpr size_t kModuleInitCount = %(init_count)d;
+
+static constexpr size_t kStaticProtocolHandlerCount = %(protocol_handler_count)d;
+
+static constexpr size_t kDefaultProtocolHandlerIndex = %(default_protocol_handler_idx)d;
+
+} // namespace xpcom
+} // namespace mozilla
+
+#endif
+"""
+ % substs
+ )
+
+ with open_output("components.rs") as fh:
+ fh.write(
+ """\
+/// Unique IDs for each statically-registered module.
+#[repr(u16)]
+pub enum ModuleID {
+%(module_ids)s
+}
+
+%(component_getters_rust)s
+"""
+ % substs
+ )
+
+ fd.write(
+ """\
+/* -*- 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_Components_h
+#define mozilla_Components_h
+
+#include "nsCOMPtr.h"
+
+struct nsID;
+
+#define NS_IMPL_COMPONENT_FACTORY(iface) \\
+ template <> \\
+ already_AddRefed<nsISupports> mozCreateComponent<iface>()
+
+template <typename T>
+already_AddRefed<nsISupports> mozCreateComponent();
+
+namespace mozilla {
+namespace xpcom {
+
+enum class ModuleID : uint16_t {
+%(module_ids)s
+};
+
+// May be added as a friend function to allow constructing services via
+// private constructors and init methods.
+nsresult CreateInstanceImpl(ModuleID aID, const nsIID& aIID, void** aResult);
+
+class MOZ_STACK_CLASS StaticModuleHelper : public nsCOMPtr_helper {
+ public:
+ StaticModuleHelper(ModuleID aId, nsresult* aErrorPtr)
+ : mId(aId), mErrorPtr(aErrorPtr) {}
+
+ protected:
+ nsresult SetResult(nsresult aRv) const {
+ if (mErrorPtr) {
+ *mErrorPtr = aRv;
+ }
+ return aRv;
+ }
+
+ ModuleID mId;
+ nsresult* mErrorPtr;
+};
+
+class MOZ_STACK_CLASS GetServiceHelper final : public StaticModuleHelper {
+ public:
+ using StaticModuleHelper::StaticModuleHelper;
+
+ nsresult NS_FASTCALL operator()(const nsIID& aIID,
+ void** aResult) const override;
+};
+
+class MOZ_STACK_CLASS CreateInstanceHelper final : public StaticModuleHelper {
+ public:
+ using StaticModuleHelper::StaticModuleHelper;
+
+ nsresult NS_FASTCALL operator()(const nsIID& aIID,
+ void** aResult) const override;
+};
+
+class Components final {
+ public:
+ static const nsID& GetCID(ModuleID aID);
+};
+
+} // namespace xpcom
+
+namespace components {
+%(component_getters)s
+} // namespace components
+
+} // namespace mozilla
+
+#endif
+"""
+ % substs
+ )
+
+ with open_output("services.json") as fh:
+ fh.write(substs["js_services_json"])
+
+ return deps
diff --git a/xpcom/components/moz.build b/xpcom/components/moz.build
new file mode 100644
index 0000000000..e62b56b44a
--- /dev/null
+++ b/xpcom/components/moz.build
@@ -0,0 +1,84 @@
+# -*- 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",
+ "nsIServiceManager.idl",
+]
+
+XPIDL_MODULE = "xpcom_components"
+
+EXPORTS += [
+ "nsCategoryCache.h",
+ "nsCategoryManagerUtils.h",
+ "nsComponentManagerUtils.h",
+ "nsServiceManagerUtils.h",
+]
+
+EXPORTS.mozilla += [
+ "GenericFactory.h",
+ "Module.h",
+ "ModuleUtils.h",
+]
+
+XPCOM_MANIFESTS += [
+ "components.conf",
+]
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ EXPORTS.mozilla += [
+ "!Components.h",
+ ]
+
+ GeneratedFile(
+ "Components.h",
+ "StaticComponentData.h",
+ "StaticComponents.cpp",
+ "services.json",
+ "components.rs",
+ script="gen_static_components.py",
+ inputs=["!manifest-lists.json", "StaticComponents.cpp.in"],
+ )
+
+UNIFIED_SOURCES += [
+ "GenericFactory.cpp",
+ "ManifestParser.cpp",
+ "nsCategoryCache.cpp",
+ "nsCategoryManager.cpp",
+ "nsComponentManager.cpp",
+ "nsComponentManagerUtils.cpp",
+]
+
+SOURCES += [
+ "!StaticComponents.cpp",
+]
+
+FINAL_LIBRARY = "xul"
+
+LOCAL_INCLUDES += [
+ "!..",
+ "../base",
+ "../build",
+ "../ds",
+ "/chrome",
+ "/js/xpconnect/loader",
+ "/js/xpconnect/src",
+ "/layout/build",
+ "/modules/libjar",
+]
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
+ CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+PYTHON_UNITTEST_MANIFESTS += [
+ "test/python.toml",
+]
diff --git a/xpcom/components/nsCategoryCache.cpp b/xpcom/components/nsCategoryCache.cpp
new file mode 100644
index 0000000000..35f555fa51
--- /dev/null
+++ b/xpcom/components/nsCategoryCache.cpp
@@ -0,0 +1,162 @@
+/* -*- 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 "nsIObserverService.h"
+#include "mozilla/Services.h"
+#include "mozilla/SimpleEnumerator.h"
+
+#include "nsICategoryManager.h"
+#include "nsISupportsPrimitives.h"
+
+#include "nsXPCOMCID.h"
+
+#include "nsCategoryCache.h"
+
+using mozilla::SimpleEnumerator;
+
+nsCategoryObserver::nsCategoryObserver(const nsACString& aCategory)
+ : mCategory(aCategory),
+ mCallback(nullptr),
+ mClosure(nullptr),
+ mObserversRemoved(false) {
+ MOZ_ASSERT(NS_IsMainThread());
+ // First, enumerate the currently existing entries
+ nsCOMPtr<nsICategoryManager> catMan =
+ do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
+ if (!catMan) {
+ return;
+ }
+
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ nsresult rv =
+ catMan->EnumerateCategory(aCategory, getter_AddRefs(enumerator));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ for (auto& categoryEntry : SimpleEnumerator<nsICategoryEntry>(enumerator)) {
+ nsAutoCString entryValue;
+ categoryEntry->GetValue(entryValue);
+
+ if (nsCOMPtr<nsISupports> service = do_GetService(entryValue.get())) {
+ nsAutoCString entryName;
+ categoryEntry->GetEntry(entryName);
+
+ mHash.InsertOrUpdate(entryName, service);
+ }
+ }
+
+ // Now, listen for changes
+ nsCOMPtr<nsIObserverService> serv = mozilla::services::GetObserverService();
+ if (serv) {
+ serv->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+ serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID, false);
+ serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID, false);
+ serv->AddObserver(this, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID, false);
+ }
+}
+
+nsCategoryObserver::~nsCategoryObserver() = default;
+
+NS_IMPL_ISUPPORTS(nsCategoryObserver, nsIObserver)
+
+void nsCategoryObserver::ListenerDied() {
+ MOZ_ASSERT(NS_IsMainThread());
+ RemoveObservers();
+ mCallback = nullptr;
+ mClosure = nullptr;
+}
+
+void nsCategoryObserver::SetListener(void(aCallback)(void*), void* aClosure) {
+ MOZ_ASSERT(NS_IsMainThread());
+ mCallback = aCallback;
+ mClosure = aClosure;
+}
+
+void nsCategoryObserver::RemoveObservers() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (mObserversRemoved) {
+ return;
+ }
+
+ if (mCallback) {
+ mCallback(mClosure);
+ }
+
+ mObserversRemoved = true;
+ nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+ if (obsSvc) {
+ obsSvc->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+ obsSvc->RemoveObserver(this, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID);
+ obsSvc->RemoveObserver(this, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID);
+ obsSvc->RemoveObserver(this, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID);
+ }
+}
+
+NS_IMETHODIMP
+nsCategoryObserver::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
+ mHash.Clear();
+ RemoveObservers();
+
+ return NS_OK;
+ }
+
+ if (!aData ||
+ !nsDependentString(aData).Equals(NS_ConvertASCIItoUTF16(mCategory))) {
+ return NS_OK;
+ }
+
+ nsAutoCString str;
+ nsCOMPtr<nsISupportsCString> strWrapper(do_QueryInterface(aSubject));
+ if (strWrapper) {
+ strWrapper->GetData(str);
+ }
+
+ if (strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID) == 0) {
+ // We may get an add notification even when we already have an entry. This
+ // is due to the notification happening asynchronously, so if the entry gets
+ // added and an nsCategoryObserver gets instantiated before events get
+ // processed, we'd get the notification for an existing entry.
+ // Do nothing in that case.
+ if (mHash.GetWeak(str)) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsICategoryManager> catMan =
+ do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
+ if (!catMan) {
+ return NS_OK;
+ }
+
+ nsCString entryValue;
+ catMan->GetCategoryEntry(mCategory, str, entryValue);
+
+ nsCOMPtr<nsISupports> service = do_GetService(entryValue.get());
+
+ if (service) {
+ mHash.InsertOrUpdate(str, service);
+ }
+ if (mCallback) {
+ mCallback(mClosure);
+ }
+ } else if (strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID) == 0) {
+ mHash.Remove(str);
+ if (mCallback) {
+ mCallback(mClosure);
+ }
+ } else if (strcmp(aTopic, NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID) == 0) {
+ mHash.Clear();
+ if (mCallback) {
+ mCallback(mClosure);
+ }
+ }
+ return NS_OK;
+}
diff --git a/xpcom/components/nsCategoryCache.h b/xpcom/components/nsCategoryCache.h
new file mode 100644
index 0000000000..f2d7ba32a3
--- /dev/null
+++ b/xpcom/components/nsCategoryCache.h
@@ -0,0 +1,123 @@
+/* -*- 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 nsCategoryCache_h_
+#define nsCategoryCache_h_
+
+#include "mozilla/Attributes.h"
+
+#include "nsIObserver.h"
+
+#include "nsServiceManagerUtils.h"
+
+#include "nsCOMArray.h"
+#include "nsInterfaceHashtable.h"
+
+#include "nsXPCOM.h"
+#include "MainThreadUtils.h"
+
+class nsCategoryObserver final : public nsIObserver {
+ ~nsCategoryObserver();
+
+ public:
+ explicit nsCategoryObserver(const nsACString& aCategory);
+
+ void ListenerDied();
+ void SetListener(void(aCallback)(void*), void* aClosure);
+ nsInterfaceHashtable<nsCStringHashKey, nsISupports>& GetHash() {
+ return mHash;
+ }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+ private:
+ void RemoveObservers();
+
+ nsInterfaceHashtable<nsCStringHashKey, nsISupports> mHash;
+ nsCString mCategory;
+ void (*mCallback)(void*);
+ void* mClosure;
+ bool mObserversRemoved;
+};
+
+/**
+ * This is a helper class that caches services that are registered in a certain
+ * category. The intended usage is that a service stores a variable of type
+ * nsCategoryCache<nsIFoo> in a member variable, where nsIFoo is the interface
+ * that these services should implement. The constructor of this class should
+ * then get the name of the category.
+ */
+template <class T>
+class nsCategoryCache final {
+ public:
+ explicit nsCategoryCache(const char* aCategory) : mCategoryName(aCategory) {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+ ~nsCategoryCache() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mObserver) {
+ mObserver->ListenerDied();
+ }
+ }
+
+ void GetEntries(nsCOMArray<T>& aResult) {
+ MOZ_ASSERT(NS_IsMainThread());
+ LazyInit();
+
+ AddEntries(aResult);
+ }
+
+ /**
+ * This function returns an nsCOMArray of interface pointers to the cached
+ * category enries for the requested category. This was added in order to be
+ * used in call sites where the overhead of excessive allocations can be
+ * unacceptable. See bug 1360971 for an example.
+ */
+ const nsCOMArray<T>& GetCachedEntries() {
+ MOZ_ASSERT(NS_IsMainThread());
+ LazyInit();
+
+ if (mCachedEntries.IsEmpty()) {
+ AddEntries(mCachedEntries);
+ }
+ return mCachedEntries;
+ }
+
+ private:
+ void LazyInit() {
+ // Lazy initialization, so that services in this category can't
+ // cause reentrant getService (bug 386376)
+ if (!mObserver) {
+ mObserver = new nsCategoryObserver(mCategoryName);
+ mObserver->SetListener(nsCategoryCache<T>::OnCategoryChanged, this);
+ }
+ }
+
+ void AddEntries(nsCOMArray<T>& aResult) {
+ for (nsISupports* entry : mObserver->GetHash().Values()) {
+ nsCOMPtr<T> service = do_QueryInterface(entry);
+ if (service) {
+ aResult.AppendElement(service.forget());
+ }
+ }
+ }
+
+ static void OnCategoryChanged(void* aClosure) {
+ MOZ_ASSERT(NS_IsMainThread());
+ auto self = static_cast<nsCategoryCache<T>*>(aClosure);
+ self->mCachedEntries.Clear();
+ }
+
+ private:
+ // Not to be implemented
+ nsCategoryCache(const nsCategoryCache<T>&);
+
+ nsCString mCategoryName;
+ RefPtr<nsCategoryObserver> mObserver;
+ nsCOMArray<T> mCachedEntries;
+};
+
+#endif
diff --git a/xpcom/components/nsCategoryManager.cpp b/xpcom/components/nsCategoryManager.cpp
new file mode 100644
index 0000000000..5602f0001f
--- /dev/null
+++ b/xpcom/components/nsCategoryManager.cpp
@@ -0,0 +1,690 @@
+/* -*- 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 "nsCategoryManager.h"
+#include "nsCategoryManagerUtils.h"
+
+#include "prio.h"
+#include "prlock.h"
+#include "nsArrayEnumerator.h"
+#include "nsCOMPtr.h"
+#include "nsTHashtable.h"
+#include "nsClassHashtable.h"
+#include "nsStringEnumerator.h"
+#include "nsSupportsPrimitives.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsReadableUtils.h"
+#include "nsCRT.h"
+#include "nsPrintfCString.h"
+#include "nsEnumeratorUtils.h"
+#include "nsThreadUtils.h"
+#include "mozilla/ArenaAllocatorExtensions.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/ProfilerLabels.h"
+#include "mozilla/ProfilerMarkers.h"
+#include "mozilla/Services.h"
+#include "mozilla/SimpleEnumerator.h"
+
+#include "ManifestParser.h"
+#include "nsSimpleEnumerator.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 ;)
+*/
+
+//
+// CategoryEnumerator class
+//
+
+class CategoryEnumerator : public nsSimpleEnumerator,
+ private nsStringEnumeratorBase {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSISIMPLEENUMERATOR
+ NS_DECL_NSIUTF8STRINGENUMERATOR
+
+ using nsStringEnumeratorBase::GetNext;
+
+ const nsID& DefaultInterface() override {
+ return NS_GET_IID(nsISupportsCString);
+ }
+
+ static CategoryEnumerator* Create(
+ nsClassHashtable<nsDepCharHashKey, CategoryNode>& aTable);
+
+ protected:
+ CategoryEnumerator()
+ : mArray(nullptr), mCount(0), mSimpleCurItem(0), mStringCurItem(0) {}
+
+ ~CategoryEnumerator() override { delete[] mArray; }
+
+ const char** mArray;
+ uint32_t mCount;
+ uint32_t mSimpleCurItem;
+ uint32_t mStringCurItem;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED(CategoryEnumerator, nsSimpleEnumerator,
+ nsIUTF8StringEnumerator, nsIStringEnumerator)
+
+NS_IMETHODIMP
+CategoryEnumerator::HasMoreElements(bool* aResult) {
+ *aResult = (mSimpleCurItem < mCount);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CategoryEnumerator::GetNext(nsISupports** aResult) {
+ if (mSimpleCurItem >= mCount) {
+ return NS_ERROR_FAILURE;
+ }
+
+ auto* str = new nsSupportsDependentCString(mArray[mSimpleCurItem++]);
+
+ *aResult = str;
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CategoryEnumerator::HasMore(bool* aResult) {
+ *aResult = (mStringCurItem < mCount);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+CategoryEnumerator::GetNext(nsACString& aResult) {
+ if (mStringCurItem >= mCount) {
+ return NS_ERROR_FAILURE;
+ }
+
+ aResult = nsDependentCString(mArray[mStringCurItem++]);
+ return NS_OK;
+}
+
+CategoryEnumerator* CategoryEnumerator::Create(
+ nsClassHashtable<nsDepCharHashKey, CategoryNode>& aTable) {
+ auto* enumObj = new CategoryEnumerator();
+ if (!enumObj) {
+ return nullptr;
+ }
+
+ enumObj->mArray = new const char*[aTable.Count()];
+ if (!enumObj->mArray) {
+ delete enumObj;
+ return nullptr;
+ }
+
+ for (const auto& entry : aTable) {
+ // if a category has no entries, we pretend it doesn't exist
+ CategoryNode* aNode = entry.GetWeak();
+ if (aNode->Count()) {
+ enumObj->mArray[enumObj->mCount++] = entry.GetKey();
+ }
+ }
+
+ return enumObj;
+}
+
+class CategoryEntry final : public nsICategoryEntry {
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICATEGORYENTRY
+ NS_DECL_NSISUPPORTSCSTRING
+ NS_DECL_NSISUPPORTSPRIMITIVE
+
+ CategoryEntry(const char* aKey, const char* aValue)
+ : mKey(aKey), mValue(aValue) {}
+
+ const char* Key() const { return mKey; }
+
+ static CategoryEntry* Cast(nsICategoryEntry* aEntry) {
+ return static_cast<CategoryEntry*>(aEntry);
+ }
+
+ private:
+ ~CategoryEntry() = default;
+
+ const char* mKey;
+ const char* mValue;
+};
+
+NS_IMPL_ISUPPORTS(CategoryEntry, nsICategoryEntry, nsISupportsCString)
+
+nsresult CategoryEntry::ToString(char** aResult) {
+ *aResult = moz_xstrdup(mKey);
+ return NS_OK;
+}
+
+nsresult CategoryEntry::GetType(uint16_t* aType) {
+ *aType = TYPE_CSTRING;
+ return NS_OK;
+}
+
+nsresult CategoryEntry::GetData(nsACString& aData) {
+ aData = mKey;
+ return NS_OK;
+}
+
+nsresult CategoryEntry::SetData(const nsACString& aData) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult CategoryEntry::GetEntry(nsACString& aEntry) {
+ aEntry = mKey;
+ return NS_OK;
+}
+
+nsresult CategoryEntry::GetValue(nsACString& aValue) {
+ aValue = mValue;
+ return NS_OK;
+}
+
+static nsresult CreateEntryEnumerator(nsTHashtable<CategoryLeaf>& aTable,
+ nsISimpleEnumerator** aResult) {
+ nsCOMArray<nsICategoryEntry> entries(aTable.Count());
+
+ for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) {
+ CategoryLeaf* leaf = iter.Get();
+ if (leaf->value) {
+ entries.AppendElement(new CategoryEntry(leaf->GetKey(), leaf->value));
+ }
+ }
+
+ entries.Sort([](nsICategoryEntry* aA, nsICategoryEntry* aB) {
+ return strcmp(CategoryEntry::Cast(aA)->Key(),
+ CategoryEntry::Cast(aB)->Key());
+ });
+
+ return NS_NewArrayEnumerator(aResult, entries, NS_GET_IID(nsICategoryEntry));
+}
+
+//
+// CategoryNode implementations
+//
+
+CategoryNode* CategoryNode::Create(CategoryAllocator* aArena) {
+ return new (aArena) CategoryNode();
+}
+
+CategoryNode::~CategoryNode() = default;
+
+void* CategoryNode::operator new(size_t aSize, CategoryAllocator* aArena) {
+ return aArena->Allocate(aSize, mozilla::fallible);
+}
+
+static inline const char* MaybeStrdup(const nsACString& aStr,
+ CategoryAllocator* aArena) {
+ if (aStr.IsLiteral()) {
+ return aStr.BeginReading();
+ }
+ return ArenaStrdup(PromiseFlatCString(aStr).get(), *aArena);
+}
+
+nsresult CategoryNode::GetLeaf(const nsACString& aEntryName,
+ nsACString& aResult) {
+ MutexAutoLock lock(mLock);
+ nsresult rv = NS_ERROR_NOT_AVAILABLE;
+ CategoryLeaf* ent = mTable.GetEntry(PromiseFlatCString(aEntryName).get());
+
+ if (ent && ent->value) {
+ aResult.Assign(ent->value);
+ return NS_OK;
+ }
+
+ return rv;
+}
+
+nsresult CategoryNode::AddLeaf(const nsACString& aEntryName,
+ const nsACString& aValue, bool aReplace,
+ nsACString& aResult, CategoryAllocator* aArena) {
+ aResult.SetIsVoid(true);
+
+ auto entryName = PromiseFlatCString(aEntryName);
+
+ MutexAutoLock lock(mLock);
+ CategoryLeaf* leaf = mTable.GetEntry(entryName.get());
+
+ if (!leaf) {
+ leaf = mTable.PutEntry(MaybeStrdup(aEntryName, aArena));
+ if (!leaf) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ if (leaf->value && !aReplace) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (leaf->value) {
+ aResult.AssignLiteral(leaf->value, strlen(leaf->value));
+ } else {
+ aResult.SetIsVoid(true);
+ }
+ leaf->value = MaybeStrdup(aValue, aArena);
+ return NS_OK;
+}
+
+void CategoryNode::DeleteLeaf(const nsACString& 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(PromiseFlatCString(aEntryName).get());
+}
+
+nsresult CategoryNode::Enumerate(nsISimpleEnumerator** aResult) {
+ MutexAutoLock lock(mLock);
+ return CreateEntryEnumerator(mTable, aResult);
+}
+
+size_t CategoryNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) {
+ // We don't measure the strings pointed to by the entries because the
+ // pointers are non-owning.
+ MutexAutoLock lock(mLock);
+ return mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
+}
+
+//
+// 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(REFNSIID aIID, void** aResult) {
+ return GetSingleton()->QueryInterface(aIID, aResult);
+}
+
+nsCategoryManager::nsCategoryManager()
+ : mLock("nsCategoryManager"), mSuppressNotifications(false) {}
+
+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();
+}
+
+inline CategoryNode* nsCategoryManager::get_category(const nsACString& aName) {
+ CategoryNode* node;
+ if (!mTable.Get(PromiseFlatCString(aName).get(), &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) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MutexAutoLock lock(mLock);
+ size_t n = aMallocSizeOf(this);
+
+ n += mArena.SizeOfExcludingThis(aMallocSizeOf);
+
+ n += mTable.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (const auto& data : mTable.Values()) {
+ // We don't measure the key string because it's a non-owning pointer.
+ n += data->SizeOfExcludingThis(aMallocSizeOf);
+ }
+
+ return n;
+}
+
+namespace {
+
+class CategoryNotificationRunnable : public Runnable {
+ public:
+ CategoryNotificationRunnable(nsISupports* aSubject, const char* aTopic,
+ const nsACString& aData)
+ : Runnable("CategoryNotificationRunnable"),
+ 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 nsACString& aCategoryName,
+ const nsACString& aEntryName) {
+ if (mSuppressNotifications) {
+ return;
+ }
+
+ RefPtr<CategoryNotificationRunnable> r;
+
+ if (aEntryName.Length()) {
+ nsCOMPtr<nsISupportsCString> entry =
+ do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID);
+ if (!entry) {
+ return;
+ }
+
+ nsresult rv = entry->SetData(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 nsACString& aCategoryName,
+ const nsACString& aEntryName,
+ nsACString& aResult) {
+ 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 nsACString& aCategoryName,
+ const nsACString& aEntryName,
+ const nsACString& aValue, bool aPersist,
+ bool aReplace, nsACString& 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 nsACString& aCategoryName,
+ const nsACString& aEntryName,
+ const nsACString& aValue,
+ bool aReplace, nsACString& aOldValue) {
+ MOZ_ASSERT(NS_IsMainThread());
+ aOldValue.SetIsVoid(true);
+
+ // 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 = mTable
+ .InsertOrUpdate(
+ MaybeStrdup(aCategoryName, &mArena),
+ UniquePtr<CategoryNode>{CategoryNode::Create(&mArena)})
+ .get();
+ }
+ }
+
+ if (!category) {
+ return;
+ }
+
+ nsresult rv =
+ category->AddLeaf(aEntryName, aValue, aReplace, aOldValue, &mArena);
+
+ if (NS_SUCCEEDED(rv)) {
+ if (!aOldValue.IsEmpty()) {
+ NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID,
+ aCategoryName, aEntryName);
+ }
+ NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID, aCategoryName,
+ aEntryName);
+ }
+}
+
+NS_IMETHODIMP
+nsCategoryManager::DeleteCategoryEntry(const nsACString& aCategoryName,
+ const nsACString& aEntryName,
+ bool aDontPersist) {
+ /*
+ 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 nsACString& aCategoryName) {
+ // 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,
+ VoidCString());
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsCategoryManager::EnumerateCategory(const nsACString& aCategoryName,
+ nsISimpleEnumerator** aResult) {
+ 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;
+ }
+
+ nsDependentCString category(aCategory);
+
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ rv = categoryManager->EnumerateCategory(category, getter_AddRefs(enumerator));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ for (auto& categoryEntry : SimpleEnumerator<nsICategoryEntry>(enumerator)) {
+ // From here on just skip any error we get.
+ nsAutoCString entryString;
+ categoryEntry->GetEntry(entryString);
+
+ nsAutoCString contractID;
+ categoryEntry->GetValue(contractID);
+
+ nsCOMPtr<nsISupports> instance = do_GetService(contractID.get());
+ 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) {
+ nsPrintfCString profilerStr("%s (%s)", aObserverTopic,
+ entryString.get());
+ AUTO_PROFILER_MARKER_TEXT("Category observer notification", OTHER,
+ MarkerStack::Capture(), profilerStr);
+ AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_NONSENSITIVE(
+ "Category observer notification -", OTHER, profilerStr);
+
+ 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 0000000000..765882fe47
--- /dev/null
+++ b/xpcom/components/nsCategoryManager.h
@@ -0,0 +1,145 @@
+/* -*- 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 "nsClassHashtable.h"
+#include "nsICategoryManager.h"
+#include "nsIMemoryReporter.h"
+#include "mozilla/ArenaAllocator.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Attributes.h"
+
+class nsIMemoryReporter;
+
+typedef mozilla::ArenaAllocator<1024 * 8, 8> CategoryAllocator;
+
+/* 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 nsACString& aEntryName, nsACString& aResult);
+
+ nsresult AddLeaf(const nsACString& aEntryName, const nsACString& aValue,
+ bool aReplace, nsACString& aResult,
+ CategoryAllocator* aArena);
+
+ void DeleteLeaf(const nsACString& 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(CategoryAllocator* aArena);
+ ~CategoryNode();
+ void operator delete(void*) {}
+
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
+
+ private:
+ CategoryNode() : mLock("CategoryLeaf") {}
+
+ void* operator new(size_t aSize, CategoryAllocator* aArena);
+
+ nsTHashtable<CategoryLeaf> mTable MOZ_GUARDED_BY(mLock);
+ 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 nsACString& aCategory, const nsACString& aKey,
+ const nsACString& aValue, bool aReplace,
+ nsACString& aOldValue);
+
+ void AddCategoryEntry(const nsACString& aCategory, const nsACString& aKey,
+ const nsACString& aValue, bool aReplace = true) {
+ nsCString oldValue;
+ return AddCategoryEntry(aCategory, aKey, aValue, aReplace, oldValue);
+ }
+
+ static nsresult Create(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 nsACString& aName) MOZ_REQUIRES(mLock);
+ void NotifyObservers(
+ const char* aTopic,
+ const nsACString& aCategoryName, // must be a static string
+ const nsACString& aEntryName);
+
+ CategoryAllocator mArena; // Mainthread only
+ nsClassHashtable<nsDepCharHashKey, CategoryNode> mTable MOZ_GUARDED_BY(mLock);
+ mozilla::Mutex mLock;
+ bool mSuppressNotifications; // Mainthread only
+};
+
+#endif
diff --git a/xpcom/components/nsCategoryManagerUtils.h b/xpcom/components/nsCategoryManagerUtils.h
new file mode 100644
index 0000000000..30db31bab6
--- /dev/null
+++ b/xpcom/components/nsCategoryManagerUtils.h
@@ -0,0 +1,14 @@
+/* -*- 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__
+
+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 0000000000..515fc82312
--- /dev/null
+++ b/xpcom/components/nsComponentManager.cpp
@@ -0,0 +1,1591 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdlib.h>
+#include "nscore.h"
+#include "nsISupports.h"
+#include "nspr.h"
+#include "nsCRT.h" // for atoll
+
+#include "StaticComponents.h"
+
+#include "nsCategoryManager.h"
+#include "nsCOMPtr.h"
+#include "nsComponentManager.h"
+#include "nsDirectoryService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsCategoryManager.h"
+#include "nsLayoutModule.h"
+#include "mozilla/MemoryReporting.h"
+#include "nsIObserverService.h"
+#include "nsIStringEnumerator.h"
+#include "nsXPCOM.h"
+#include "nsXPCOMPrivate.h"
+#include "nsISupportsPrimitives.h"
+#include "nsLocalFile.h"
+#include "nsReadableUtils.h"
+#include "nsString.h"
+#include "prcmon.h"
+#include "nsThreadManager.h"
+#include "nsThreadUtils.h"
+#include "prthread.h"
+#include "private/pprthred.h"
+#include "nsTArray.h"
+#include "prio.h"
+#include "ManifestParser.h"
+#include "nsNetUtil.h"
+#include "mozilla/Services.h"
+
+#include "mozilla/GenericFactory.h"
+#include "nsSupportsPrimitives.h"
+#include "nsArray.h"
+#include "nsIMutableArray.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/FileUtils.h"
+#include "mozilla/ProfilerLabels.h"
+#include "mozilla/ProfilerMarkers.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/URLPreloader.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Variant.h"
+
+#include <new> // for placement new
+
+#include "mozilla/Omnijar.h"
+
+#include "mozilla/Logging.h"
+#include "LogModulePrefWatcher.h"
+#include "xpcpublic.h"
+
+#ifdef MOZ_MEMORY
+# include "mozmemory.h"
+#endif
+
+using namespace mozilla;
+using namespace mozilla::xpcom;
+
+static LazyLogModule nsComponentManagerLog("nsComponentManager");
+
+#if 0
+# define SHOW_DENIED_ON_SHUTDOWN
+# define SHOW_CI_ON_EXISTING_SERVICE
+#endif
+
+namespace {
+
+class AutoIDString : public nsAutoCStringN<NSID_LENGTH> {
+ public:
+ explicit AutoIDString(const nsID& aID) {
+ SetLength(NSID_LENGTH - 1);
+ aID.ToProvidedString(
+ *reinterpret_cast<char(*)[NSID_LENGTH]>(BeginWriting()));
+ }
+};
+
+} // namespace
+
+namespace mozilla {
+namespace xpcom {
+
+using ProcessSelector = Module::ProcessSelector;
+
+// Note: These must be kept in sync with the ProcessSelector definition in
+// Module.h.
+bool ProcessSelectorMatches(ProcessSelector aSelector) {
+ GeckoProcessType type = XRE_GetProcessType();
+ if (type == GeckoProcessType_GPU) {
+ return !!(aSelector & Module::ALLOW_IN_GPU_PROCESS);
+ }
+
+ if (type == GeckoProcessType_RDD) {
+ return !!(aSelector & Module::ALLOW_IN_RDD_PROCESS);
+ }
+
+ if (type == GeckoProcessType_Socket) {
+ return !!(aSelector & (Module::ALLOW_IN_SOCKET_PROCESS));
+ }
+
+ if (type == GeckoProcessType_VR) {
+ return !!(aSelector & Module::ALLOW_IN_VR_PROCESS);
+ }
+
+ if (type == GeckoProcessType_Utility) {
+ return !!(aSelector & Module::ALLOW_IN_UTILITY_PROCESS);
+ }
+
+ if (type == GeckoProcessType_GMPlugin) {
+ return !!(aSelector & Module::ALLOW_IN_GMPLUGIN_PROCESS);
+ }
+
+ // Only allow XPCOM modules which can be loaded in all processes to be loaded
+ // in the IPDLUnitTest process.
+ if (type == GeckoProcessType_IPDLUnitTest) {
+ return size_t(aSelector) == Module::kMaxProcessSelector;
+ }
+
+ if (aSelector & Module::MAIN_PROCESS_ONLY) {
+ return type == GeckoProcessType_Default;
+ }
+ if (aSelector & Module::CONTENT_PROCESS_ONLY) {
+ return type == GeckoProcessType_Content;
+ }
+ return true;
+}
+
+static bool gProcessMatchTable[Module::kMaxProcessSelector + 1];
+
+bool FastProcessSelectorMatches(ProcessSelector aSelector) {
+ return gProcessMatchTable[size_t(aSelector)];
+}
+
+} // namespace xpcom
+} // namespace mozilla
+
+namespace {
+
+/**
+ * A wrapper simple wrapper class, which can hold either a dynamic
+ * nsFactoryEntry instance, or a static StaticModule entry, and transparently
+ * forwards method calls to the wrapped object.
+ *
+ * This allows the same code to work with either static or dynamic modules
+ * without caring about the difference.
+ */
+class MOZ_STACK_CLASS EntryWrapper final {
+ public:
+ explicit EntryWrapper(nsFactoryEntry* aEntry) : mEntry(aEntry) {}
+
+ explicit EntryWrapper(const StaticModule* aEntry) : mEntry(aEntry) {}
+
+#define MATCH(type, ifFactory, ifStatic) \
+ struct Matcher { \
+ type operator()(nsFactoryEntry* entry) { ifFactory; } \
+ type operator()(const StaticModule* entry) { ifStatic; } \
+ }; \
+ return mEntry.match((Matcher()))
+
+ const nsID& CID() {
+ MATCH(const nsID&, return entry->mCID, return entry->CID());
+ }
+
+ already_AddRefed<nsIFactory> GetFactory() {
+ MATCH(already_AddRefed<nsIFactory>, return entry->GetFactory(),
+ return entry->GetFactory());
+ }
+
+ /**
+ * Creates an instance of the underlying component. This should be used in
+ * preference to GetFactory()->CreateInstance() where appropriate, since it
+ * side-steps the necessity of creating a nsIFactory instance for static
+ * modules.
+ */
+ nsresult CreateInstance(const nsIID& aIID, void** aResult) {
+ if (mEntry.is<nsFactoryEntry*>()) {
+ return mEntry.as<nsFactoryEntry*>()->CreateInstance(aIID, aResult);
+ }
+ return mEntry.as<const StaticModule*>()->CreateInstance(aIID, aResult);
+ }
+
+ /**
+ * Returns the cached service instance for this entry, if any. This should
+ * only be accessed while mLock is held.
+ */
+ nsISupports* ServiceInstance() {
+ MATCH(nsISupports*, return entry->mServiceObject,
+ return entry->ServiceInstance());
+ }
+ void SetServiceInstance(already_AddRefed<nsISupports> aInst) {
+ if (mEntry.is<nsFactoryEntry*>()) {
+ mEntry.as<nsFactoryEntry*>()->mServiceObject = aInst;
+ } else {
+ return mEntry.as<const StaticModule*>()->SetServiceInstance(
+ std::move(aInst));
+ }
+ }
+
+ /**
+ * Returns the description string for the module this entry belongs to.
+ * Currently always returns "<unknown module>".
+ */
+ nsCString ModuleDescription() { return "<unknown module>"_ns; }
+
+ private:
+ Variant<nsFactoryEntry*, const StaticModule*> mEntry;
+};
+
+} // namespace
+
+// this is safe to call during InitXPCOM
+static already_AddRefed<nsIFile> GetLocationFromDirectoryService(
+ const char* aProp) {
+ nsCOMPtr<nsIProperties> directoryService;
+ nsDirectoryService::Create(NS_GET_IID(nsIProperties),
+ getter_AddRefs(directoryService));
+
+ if (!directoryService) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIFile> file;
+ nsresult rv =
+ directoryService->Get(aProp, NS_GET_IID(nsIFile), getter_AddRefs(file));
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+
+ return file.forget();
+}
+
+static already_AddRefed<nsIFile> CloneAndAppend(nsIFile* aBase,
+ const nsACString& aAppend) {
+ nsCOMPtr<nsIFile> f;
+ aBase->Clone(getter_AddRefs(f));
+ if (!f) {
+ return nullptr;
+ }
+
+ f->AppendNative(aAppend);
+ return f.forget();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsComponentManagerImpl
+////////////////////////////////////////////////////////////////////////////////
+
+nsresult nsComponentManagerImpl::Create(REFNSIID aIID, void** aResult) {
+ if (!gComponentManager) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return gComponentManager->QueryInterface(aIID, aResult);
+}
+
+static const int CONTRACTID_HASHTABLE_INITIAL_LENGTH = 8;
+
+nsComponentManagerImpl::nsComponentManagerImpl()
+ : mFactories(CONTRACTID_HASHTABLE_INITIAL_LENGTH),
+ mContractIDs(CONTRACTID_HASHTABLE_INITIAL_LENGTH),
+ mLock("nsComponentManagerImpl.mLock"),
+ mStatus(NOT_INITIALIZED) {}
+
+nsTArray<nsComponentManagerImpl::ComponentLocation>*
+ nsComponentManagerImpl::sModuleLocations;
+
+/* static */
+void nsComponentManagerImpl::InitializeModuleLocations() {
+ if (sModuleLocations) {
+ return;
+ }
+
+ sModuleLocations = new nsTArray<ComponentLocation>;
+}
+
+nsresult nsComponentManagerImpl::Init() {
+ {
+ gProcessMatchTable[size_t(ProcessSelector::ANY_PROCESS)] =
+ ProcessSelectorMatches(ProcessSelector::ANY_PROCESS);
+ gProcessMatchTable[size_t(ProcessSelector::MAIN_PROCESS_ONLY)] =
+ ProcessSelectorMatches(ProcessSelector::MAIN_PROCESS_ONLY);
+ gProcessMatchTable[size_t(ProcessSelector::CONTENT_PROCESS_ONLY)] =
+ ProcessSelectorMatches(ProcessSelector::CONTENT_PROCESS_ONLY);
+ gProcessMatchTable[size_t(ProcessSelector::ALLOW_IN_GPU_PROCESS)] =
+ ProcessSelectorMatches(ProcessSelector::ALLOW_IN_GPU_PROCESS);
+ gProcessMatchTable[size_t(ProcessSelector::ALLOW_IN_VR_PROCESS)] =
+ ProcessSelectorMatches(ProcessSelector::ALLOW_IN_VR_PROCESS);
+ gProcessMatchTable[size_t(ProcessSelector::ALLOW_IN_SOCKET_PROCESS)] =
+ ProcessSelectorMatches(ProcessSelector::ALLOW_IN_SOCKET_PROCESS);
+ gProcessMatchTable[size_t(ProcessSelector::ALLOW_IN_RDD_PROCESS)] =
+ ProcessSelectorMatches(ProcessSelector::ALLOW_IN_RDD_PROCESS);
+ gProcessMatchTable[size_t(ProcessSelector::ALLOW_IN_GMPLUGIN_PROCESS)] =
+ ProcessSelectorMatches(ProcessSelector::ALLOW_IN_GMPLUGIN_PROCESS);
+ gProcessMatchTable[size_t(ProcessSelector::ALLOW_IN_GPU_AND_MAIN_PROCESS)] =
+ ProcessSelectorMatches(ProcessSelector::ALLOW_IN_GPU_AND_MAIN_PROCESS);
+ gProcessMatchTable[size_t(ProcessSelector::ALLOW_IN_GPU_AND_VR_PROCESS)] =
+ ProcessSelectorMatches(ProcessSelector::ALLOW_IN_GPU_AND_VR_PROCESS);
+ gProcessMatchTable[size_t(
+ ProcessSelector::ALLOW_IN_GPU_AND_SOCKET_PROCESS)] =
+ ProcessSelectorMatches(
+ ProcessSelector::ALLOW_IN_GPU_AND_SOCKET_PROCESS);
+ gProcessMatchTable[size_t(
+ ProcessSelector::ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS)] =
+ ProcessSelectorMatches(
+ ProcessSelector::ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS);
+ gProcessMatchTable[size_t(
+ ProcessSelector::ALLOW_IN_RDD_AND_SOCKET_PROCESS)] =
+ ProcessSelectorMatches(
+ ProcessSelector::ALLOW_IN_RDD_AND_SOCKET_PROCESS);
+ gProcessMatchTable[size_t(
+ ProcessSelector::ALLOW_IN_GPU_RDD_AND_SOCKET_PROCESS)] =
+ ProcessSelectorMatches(
+ ProcessSelector::ALLOW_IN_GPU_RDD_AND_SOCKET_PROCESS);
+ gProcessMatchTable[size_t(
+ ProcessSelector::ALLOW_IN_GPU_RDD_SOCKET_AND_UTILITY_PROCESS)] =
+ ProcessSelectorMatches(
+ ProcessSelector::ALLOW_IN_GPU_RDD_SOCKET_AND_UTILITY_PROCESS);
+ gProcessMatchTable[size_t(
+ ProcessSelector::ALLOW_IN_GPU_RDD_VR_AND_SOCKET_PROCESS)] =
+ ProcessSelectorMatches(
+ ProcessSelector::ALLOW_IN_GPU_RDD_VR_AND_SOCKET_PROCESS);
+ gProcessMatchTable[size_t(
+ ProcessSelector::ALLOW_IN_GPU_RDD_VR_SOCKET_AND_UTILITY_PROCESS)] =
+ ProcessSelectorMatches(
+ ProcessSelector::ALLOW_IN_GPU_RDD_VR_SOCKET_AND_UTILITY_PROCESS);
+ gProcessMatchTable[size_t(
+ ProcessSelector::
+ ALLOW_IN_GPU_RDD_VR_SOCKET_UTILITY_AND_GMPLUGIN_PROCESS)] =
+ ProcessSelectorMatches(
+ ProcessSelector::
+ ALLOW_IN_GPU_RDD_VR_SOCKET_UTILITY_AND_GMPLUGIN_PROCESS);
+ }
+
+ MOZ_ASSERT(NOT_INITIALIZED == mStatus);
+
+ nsCOMPtr<nsIFile> greDir = GetLocationFromDirectoryService(NS_GRE_DIR);
+ nsCOMPtr<nsIFile> appDir =
+ GetLocationFromDirectoryService(NS_XPCOM_CURRENT_PROCESS_DIR);
+
+ nsCategoryManager::GetSingleton()->SuppressNotifications(true);
+
+ auto* catMan = nsCategoryManager::GetSingleton();
+ for (const auto& cat : gStaticCategories) {
+ for (const auto& entry : cat) {
+ if (entry.Active()) {
+ catMan->AddCategoryEntry(cat.Name(), entry.Entry(), entry.Value());
+ }
+ }
+ }
+
+ // This needs to be initialized late enough, so that preferences service can
+ // be accessed but before the IO service, and we want it in all process types.
+ xpc::ReadOnlyPage::Init();
+
+ bool loadChromeManifests;
+ switch (XRE_GetProcessType()) {
+ // We are going to assume that only a select few (see below) process types
+ // want to load chrome manifests, and that any new process types will not
+ // want to load them, because they're not going to be executing JS.
+ case GeckoProcessType_RemoteSandboxBroker:
+ default:
+ loadChromeManifests = false;
+ break;
+
+ // XXX The check this code replaced implicitly let through all of these
+ // process types, but presumably only the default (parent) and content
+ // processes really need chrome manifests...?
+ case GeckoProcessType_Default:
+ case GeckoProcessType_Content:
+ loadChromeManifests = true;
+ break;
+ }
+
+ if (loadChromeManifests) {
+ // This needs to be called very early, before anything in nsLayoutModule is
+ // used, and before any calls are made into the JS engine.
+ nsLayoutModuleInitialize();
+
+ mJSLoaderReady = true;
+
+ // The overall order in which chrome.manifests are expected to be treated
+ // is the following:
+ // - greDir's omni.ja or greDir
+ // - appDir's omni.ja or appDir
+
+ InitializeModuleLocations();
+ ComponentLocation* cl = sModuleLocations->AppendElement();
+ cl->type = NS_APP_LOCATION;
+ RefPtr<nsZipArchive> greOmnijar =
+ mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
+ if (greOmnijar) {
+ cl->location.Init(greOmnijar, "chrome.manifest");
+ } else {
+ nsCOMPtr<nsIFile> lf = CloneAndAppend(greDir, "chrome.manifest"_ns);
+ cl->location.Init(lf);
+ }
+
+ RefPtr<nsZipArchive> appOmnijar =
+ mozilla::Omnijar::GetReader(mozilla::Omnijar::APP);
+ if (appOmnijar) {
+ cl = sModuleLocations->AppendElement();
+ cl->type = NS_APP_LOCATION;
+ cl->location.Init(appOmnijar, "chrome.manifest");
+ } else {
+ bool equals = false;
+ appDir->Equals(greDir, &equals);
+ if (!equals) {
+ cl = sModuleLocations->AppendElement();
+ cl->type = NS_APP_LOCATION;
+ nsCOMPtr<nsIFile> lf = CloneAndAppend(appDir, "chrome.manifest"_ns);
+ cl->location.Init(lf);
+ }
+ }
+
+ RereadChromeManifests(false);
+ }
+
+ nsCategoryManager::GetSingleton()->SuppressNotifications(false);
+
+ RegisterWeakMemoryReporter(this);
+
+ // NB: The logging preference watcher needs to be registered late enough in
+ // startup that it's okay to use the preference system, but also as soon as
+ // possible so that log modules enabled via preferences are turned on as
+ // early as possible.
+ //
+ // We can't initialize the preference watcher when the log module manager is
+ // initialized, as a number of things attempt to start logging before the
+ // preference system is initialized.
+ //
+ // The preference system is registered as a component so at this point during
+ // component manager initialization we know it is setup and we can register
+ // for notifications.
+ LogModulePrefWatcher::RegisterPrefWatcher();
+
+ // Unfortunately, we can't register the nsCategoryManager memory reporter
+ // in its constructor (which is triggered by the GetSingleton() call
+ // above) because the memory reporter manager isn't initialized at that
+ // point. So we wait until now.
+ nsCategoryManager::GetSingleton()->InitMemoryReporter();
+
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
+ ("nsComponentManager: Initialized."));
+
+ mStatus = NORMAL;
+
+ MOZ_ASSERT(!XRE_IsContentProcess() ||
+ CONTRACTID_HASHTABLE_INITIAL_LENGTH <= 8 ||
+ mFactories.Count() > CONTRACTID_HASHTABLE_INITIAL_LENGTH / 3,
+ "Initial component hashtable size is too large");
+
+ return NS_OK;
+}
+
+template <typename T>
+static void AssertNotMallocAllocated(T* aPtr) {
+#if defined(DEBUG) && defined(MOZ_MEMORY)
+ jemalloc_ptr_info_t info;
+ jemalloc_ptr_info((void*)aPtr, &info);
+ MOZ_ASSERT(info.tag == TagUnknown);
+#endif
+}
+
+template <typename T>
+static void AssertNotStackAllocated(T* aPtr) {
+ // On all of our supported platforms, the stack grows down. Any address
+ // located below the address of our argument is therefore guaranteed not to be
+ // stack-allocated by the caller.
+ //
+ // For addresses above our argument, things get trickier. The main thread
+ // stack is traditionally placed at the top of the program's address space,
+ // but that is becoming less reliable as more and more systems adopt address
+ // space layout randomization strategies, so we have to guess how much space
+ // above our argument pointer we need to care about.
+ //
+ // On most systems, we're guaranteed at least several KiB at the top of each
+ // stack for TLS. We'd probably be safe assuming at least 4KiB in the stack
+ // segment above our argument address, but safer is... well, safer.
+ //
+ // For threads with huge stacks, it's theoretically possible that we could
+ // wind up being passed a stack-allocated string from farther up the stack,
+ // but this is a best-effort thing, so we'll assume we only care about the
+ // immediate caller. For that case, max 2KiB per stack frame is probably a
+ // reasonable guess most of the time, and is less than the ~4KiB that we
+ // expect for TLS, so go with that to avoid the risk of bumping into heap
+ // data just above the stack.
+#ifdef DEBUG
+ static constexpr size_t kFuzz = 2048;
+
+ MOZ_ASSERT(uintptr_t(aPtr) < uintptr_t(&aPtr) ||
+ uintptr_t(aPtr) > uintptr_t(&aPtr) + kFuzz);
+#endif
+}
+
+static void DoRegisterManifest(NSLocationType aType, FileLocation& aFile,
+ bool aChromeOnly) {
+ auto result = URLPreloader::Read(aFile);
+ if (result.isOk()) {
+ nsCString buf(result.unwrap());
+ ParseManifest(aType, aFile, buf.BeginWriting(), aChromeOnly);
+ } else if (NS_BOOTSTRAPPED_LOCATION != aType) {
+ nsCString uri;
+ aFile.GetURIString(uri);
+ LogMessage("Could not read chrome manifest '%s'.", uri.get());
+ }
+}
+
+void nsComponentManagerImpl::RegisterManifest(NSLocationType aType,
+ FileLocation& aFile,
+ bool aChromeOnly) {
+ DoRegisterManifest(aType, aFile, aChromeOnly);
+}
+
+void nsComponentManagerImpl::ManifestManifest(ManifestProcessingContext& aCx,
+ int aLineNo, char* const* aArgv) {
+ char* file = aArgv[0];
+ FileLocation f(aCx.mFile, file);
+ RegisterManifest(aCx.mType, f, aCx.mChromeOnly);
+}
+
+void nsComponentManagerImpl::ManifestCategory(ManifestProcessingContext& aCx,
+ int aLineNo, char* const* aArgv) {
+ char* category = aArgv[0];
+ char* key = aArgv[1];
+ char* value = aArgv[2];
+
+ nsCategoryManager::GetSingleton()->AddCategoryEntry(
+ nsDependentCString(category), nsDependentCString(key),
+ nsDependentCString(value));
+}
+
+void nsComponentManagerImpl::RereadChromeManifests(bool aChromeOnly) {
+ for (uint32_t i = 0; i < sModuleLocations->Length(); ++i) {
+ ComponentLocation& l = sModuleLocations->ElementAt(i);
+ RegisterManifest(l.type, l.location, aChromeOnly);
+ }
+
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(nullptr, "chrome-manifests-loaded", nullptr);
+ }
+}
+
+nsresult nsComponentManagerImpl::Shutdown(void) {
+ MOZ_ASSERT(NORMAL == mStatus);
+
+ mStatus = SHUTDOWN_IN_PROGRESS;
+
+ // Shutdown the component manager
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
+ ("nsComponentManager: Beginning Shutdown."));
+
+ UnregisterWeakMemoryReporter(this);
+
+ // Release all cached factories
+ mContractIDs.Clear();
+ mFactories.Clear(); // XXX release the objects, don't just clear
+
+ StaticComponents::Shutdown();
+
+ delete sModuleLocations;
+
+ mStatus = SHUTDOWN_COMPLETE;
+
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
+ ("nsComponentManager: Shutdown complete."));
+
+ return NS_OK;
+}
+
+nsComponentManagerImpl::~nsComponentManagerImpl() {
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
+ ("nsComponentManager: Beginning destruction."));
+
+ if (SHUTDOWN_COMPLETE != mStatus) {
+ Shutdown();
+ }
+
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
+ ("nsComponentManager: Destroyed."));
+}
+
+NS_IMPL_ISUPPORTS(nsComponentManagerImpl, nsIComponentManager,
+ nsIServiceManager, nsIComponentRegistrar,
+ nsISupportsWeakReference, nsIInterfaceRequestor,
+ nsIMemoryReporter)
+
+nsresult nsComponentManagerImpl::GetInterface(const nsIID& aUuid,
+ void** aResult) {
+ NS_WARNING("This isn't supported");
+ // fall through to QI as anything QIable is a superset of what can be
+ // got via the GetInterface()
+ return QueryInterface(aUuid, aResult);
+}
+
+Maybe<EntryWrapper> nsComponentManagerImpl::LookupByCID(const nsID& aCID) {
+ return LookupByCID(MonitorAutoLock(mLock), aCID);
+}
+
+Maybe<EntryWrapper> nsComponentManagerImpl::LookupByCID(const MonitorAutoLock&,
+ const nsID& aCID) {
+ if (const StaticModule* module = StaticComponents::LookupByCID(aCID)) {
+ return Some(EntryWrapper(module));
+ }
+ if (nsFactoryEntry* entry = mFactories.Get(&aCID)) {
+ return Some(EntryWrapper(entry));
+ }
+ return Nothing();
+}
+
+Maybe<EntryWrapper> nsComponentManagerImpl::LookupByContractID(
+ const nsACString& aContractID) {
+ return LookupByContractID(MonitorAutoLock(mLock), aContractID);
+}
+
+Maybe<EntryWrapper> nsComponentManagerImpl::LookupByContractID(
+ const MonitorAutoLock&, const nsACString& aContractID) {
+ if (const StaticModule* module =
+ StaticComponents::LookupByContractID(aContractID)) {
+ return Some(EntryWrapper(module));
+ }
+ if (nsFactoryEntry* entry = mContractIDs.Get(aContractID)) {
+ // UnregisterFactory might have left a stale nsFactoryEntry in
+ // mContractIDs, so we should check to see whether this entry has
+ // anything useful.
+ if (entry->mFactory || entry->mServiceObject) {
+ return Some(EntryWrapper(entry));
+ }
+ }
+ return Nothing();
+}
+
+already_AddRefed<nsIFactory> nsComponentManagerImpl::FindFactory(
+ const nsCID& aClass) {
+ Maybe<EntryWrapper> e = LookupByCID(aClass);
+ if (!e) {
+ return nullptr;
+ }
+
+ return e->GetFactory();
+}
+
+already_AddRefed<nsIFactory> nsComponentManagerImpl::FindFactory(
+ const char* aContractID, uint32_t aContractIDLen) {
+ Maybe<EntryWrapper> entry =
+ LookupByContractID(nsDependentCString(aContractID, aContractIDLen));
+ if (!entry) {
+ return nullptr;
+ }
+
+ return entry->GetFactory();
+}
+
+/**
+ * GetClassObject()
+ *
+ * Given a classID, this finds the singleton ClassObject that implements the
+ * CID. Returns an interface of type aIID off the singleton classobject.
+ */
+NS_IMETHODIMP
+nsComponentManagerImpl::GetClassObject(const nsCID& aClass, const nsIID& aIID,
+ void** aResult) {
+ nsresult rv;
+
+ if (MOZ_LOG_TEST(nsComponentManagerLog, LogLevel::Debug)) {
+ char buf[NSID_LENGTH];
+ aClass.ToProvidedString(buf);
+ PR_LogPrint("nsComponentManager: GetClassObject(%s)", buf);
+ }
+
+ MOZ_ASSERT(aResult != nullptr);
+
+ nsCOMPtr<nsIFactory> factory = FindFactory(aClass);
+ if (!factory) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ rv = factory->QueryInterface(aIID, aResult);
+
+ MOZ_LOG(
+ nsComponentManagerLog, LogLevel::Warning,
+ ("\t\tGetClassObject() %s", NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"));
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::GetClassObjectByContractID(const char* aContractID,
+ const nsIID& aIID,
+ void** aResult) {
+ if (NS_WARN_IF(!aResult) || NS_WARN_IF(!aContractID)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsresult rv;
+
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Debug,
+ ("nsComponentManager: GetClassObjectByContractID(%s)", aContractID));
+
+ nsCOMPtr<nsIFactory> factory = FindFactory(aContractID, strlen(aContractID));
+ if (!factory) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ rv = factory->QueryInterface(aIID, aResult);
+
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Warning,
+ ("\t\tGetClassObjectByContractID() %s",
+ NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"));
+
+ return rv;
+}
+
+/**
+ * CreateInstance()
+ *
+ * Create an instance of an object that implements an interface and belongs
+ * to the implementation aClass using the factory. The factory is immediately
+ * released and not held onto for any longer.
+ */
+NS_IMETHODIMP
+nsComponentManagerImpl::CreateInstance(const nsCID& aClass, const nsIID& aIID,
+ void** aResult) {
+ // test this first, since there's no point in creating a component during
+ // shutdown -- whether it's available or not would depend on the order it
+ // occurs in the list
+ if (gXPCOMShuttingDown) {
+ // When processing shutdown, don't process new GetService() requests
+#ifdef SHOW_DENIED_ON_SHUTDOWN
+ fprintf(stderr,
+ "Creating new instance on shutdown. Denied.\n"
+ " CID: %s\n IID: %s\n",
+ AutoIDString(aClass).get(), AutoIDString(aIID).get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ *aResult = nullptr;
+
+ Maybe<EntryWrapper> entry = LookupByCID(aClass);
+
+ if (!entry) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+#ifdef SHOW_CI_ON_EXISTING_SERVICE
+ if (entry->ServiceInstance()) {
+ nsAutoCString message;
+ message = "You are calling CreateInstance \""_ns + AutoIDString(aClass) +
+ "\" when a service for this CID already exists!"_ns;
+ NS_ERROR(message.get());
+ }
+#endif
+
+ nsresult rv;
+ nsCOMPtr<nsIFactory> factory = entry->GetFactory();
+ if (factory) {
+ rv = factory->CreateInstance(aIID, aResult);
+ if (NS_SUCCEEDED(rv) && !*aResult) {
+ NS_ERROR("Factory did not return an object but returned success!");
+ rv = NS_ERROR_SERVICE_NOT_AVAILABLE;
+ }
+ } else {
+ // Translate error values
+ rv = NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ if (MOZ_LOG_TEST(nsComponentManagerLog, LogLevel::Warning)) {
+ char buf[NSID_LENGTH];
+ aClass.ToProvidedString(buf);
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Warning,
+ ("nsComponentManager: CreateInstance(%s) %s", buf,
+ NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"));
+ }
+
+ return rv;
+}
+
+/**
+ * CreateInstanceByContractID()
+ *
+ * A variant of CreateInstance() that creates an instance of the object that
+ * implements the interface aIID and whose implementation has a contractID
+ * aContractID.
+ *
+ * This is only a convenience routine that turns around can calls the
+ * CreateInstance() with classid and iid.
+ */
+NS_IMETHODIMP
+nsComponentManagerImpl::CreateInstanceByContractID(const char* aContractID,
+ const nsIID& aIID,
+ void** aResult) {
+ if (NS_WARN_IF(!aContractID)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // test this first, since there's no point in creating a component during
+ // shutdown -- whether it's available or not would depend on the order it
+ // occurs in the list
+ if (gXPCOMShuttingDown) {
+ // When processing shutdown, don't process new GetService() requests
+#ifdef SHOW_DENIED_ON_SHUTDOWN
+ fprintf(stderr,
+ "Creating new instance on shutdown. Denied.\n"
+ " ContractID: %s\n IID: %s\n",
+ aContractID, AutoIDString(aIID).get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (!aResult) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ *aResult = nullptr;
+
+ Maybe<EntryWrapper> entry =
+ LookupByContractID(nsDependentCString(aContractID));
+
+ if (!entry) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+#ifdef SHOW_CI_ON_EXISTING_SERVICE
+ if (entry->ServiceInstance()) {
+ nsAutoCString message;
+ message =
+ "You are calling CreateInstance \""_ns +
+ nsDependentCString(aContractID) +
+ nsLiteralCString(
+ "\" when a service for this CID already exists! "
+ "Add it to abusedContracts to track down the service consumer.");
+ NS_ERROR(message.get());
+ }
+#endif
+
+ nsresult rv;
+ nsCOMPtr<nsIFactory> factory = entry->GetFactory();
+ if (factory) {
+ rv = factory->CreateInstance(aIID, aResult);
+ if (NS_SUCCEEDED(rv) && !*aResult) {
+ NS_ERROR("Factory did not return an object but returned success!");
+ rv = NS_ERROR_SERVICE_NOT_AVAILABLE;
+ }
+ } else {
+ // Translate error values
+ rv = NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ MOZ_LOG(nsComponentManagerLog, LogLevel::Warning,
+ ("nsComponentManager: CreateInstanceByContractID(%s) %s", aContractID,
+ NS_SUCCEEDED(rv) ? "succeeded" : "FAILED"));
+
+ return rv;
+}
+
+nsresult nsComponentManagerImpl::FreeServices() {
+ NS_ASSERTION(gXPCOMShuttingDown,
+ "Must be shutting down in order to free all services");
+
+ if (!gXPCOMShuttingDown) {
+ return NS_ERROR_FAILURE;
+ }
+
+ for (nsFactoryEntry* entry : mFactories.Values()) {
+ entry->mFactory = nullptr;
+ entry->mServiceObject = nullptr;
+ }
+
+ for (const auto& module : gStaticModules) {
+ module.SetServiceInstance(nullptr);
+ }
+
+ return NS_OK;
+}
+
+// This should only ever be called within the monitor!
+nsComponentManagerImpl::PendingServiceInfo*
+nsComponentManagerImpl::AddPendingService(const nsCID& aServiceCID,
+ PRThread* aThread) {
+ PendingServiceInfo* newInfo = mPendingServices.AppendElement();
+ if (newInfo) {
+ newInfo->cid = &aServiceCID;
+ newInfo->thread = aThread;
+ }
+ return newInfo;
+}
+
+// This should only ever be called within the monitor!
+void nsComponentManagerImpl::RemovePendingService(MonitorAutoLock& aLock,
+ const nsCID& aServiceCID) {
+ uint32_t pendingCount = mPendingServices.Length();
+ for (uint32_t index = 0; index < pendingCount; ++index) {
+ const PendingServiceInfo& info = mPendingServices.ElementAt(index);
+ if (info.cid->Equals(aServiceCID)) {
+ mPendingServices.RemoveElementAt(index);
+ aLock.NotifyAll();
+ return;
+ }
+ }
+}
+
+// This should only ever be called within the monitor!
+PRThread* nsComponentManagerImpl::GetPendingServiceThread(
+ const nsCID& aServiceCID) const {
+ uint32_t pendingCount = mPendingServices.Length();
+ for (uint32_t index = 0; index < pendingCount; ++index) {
+ const PendingServiceInfo& info = mPendingServices.ElementAt(index);
+ if (info.cid->Equals(aServiceCID)) {
+ return info.thread;
+ }
+ }
+ return nullptr;
+}
+
+nsresult nsComponentManagerImpl::GetServiceLocked(Maybe<MonitorAutoLock>& aLock,
+ EntryWrapper& aEntry,
+ const nsIID& aIID,
+ void** aResult) {
+ MOZ_ASSERT(aLock.isSome());
+ if (!aLock.isSome()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (auto* service = aEntry.ServiceInstance()) {
+ aLock.reset();
+ return service->QueryInterface(aIID, aResult);
+ }
+
+ PRThread* currentPRThread = PR_GetCurrentThread();
+ MOZ_ASSERT(currentPRThread, "This should never be null!");
+
+ PRThread* pendingPRThread;
+ while ((pendingPRThread = GetPendingServiceThread(aEntry.CID()))) {
+ if (pendingPRThread == currentPRThread) {
+ NS_ERROR("Recursive GetService!");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ aLock->Wait();
+ }
+
+ // It's still possible that the other thread failed to create the
+ // service so we're not guaranteed to have an entry or service yet.
+ if (auto* service = aEntry.ServiceInstance()) {
+ aLock.reset();
+ return service->QueryInterface(aIID, aResult);
+ }
+
+ DebugOnly<PendingServiceInfo*> newInfo =
+ AddPendingService(aEntry.CID(), currentPRThread);
+ NS_ASSERTION(newInfo, "Failed to add info to the array!");
+
+ // We need to not be holding the service manager's lock while calling
+ // CreateInstance, because it invokes user code which could try to re-enter
+ // the service manager:
+
+ nsCOMPtr<nsISupports> service;
+ auto cleanup = MakeScopeExit([&]() {
+ // `service` must be released after the lock is released, so if we fail and
+ // still have a reference, release the lock before releasing it.
+ if (service) {
+ MOZ_ASSERT(aLock.isSome());
+ aLock.reset();
+ service = nullptr;
+ }
+ });
+ nsresult rv;
+ mLock.AssertCurrentThreadOwns();
+ {
+ MonitorAutoUnlock unlock(mLock);
+ AUTO_PROFILER_MARKER_TEXT(
+ "GetService", OTHER, MarkerStack::Capture(),
+ nsDependentCString(nsIDToCString(aEntry.CID()).get()));
+ rv = aEntry.CreateInstance(aIID, getter_AddRefs(service));
+ }
+ if (NS_SUCCEEDED(rv) && !service) {
+ NS_ERROR("Factory did not return an object but returned success");
+ return NS_ERROR_SERVICE_NOT_AVAILABLE;
+ }
+
+#ifdef DEBUG
+ pendingPRThread = GetPendingServiceThread(aEntry.CID());
+ MOZ_ASSERT(pendingPRThread == currentPRThread,
+ "Pending service array has been changed!");
+#endif
+ MOZ_ASSERT(aLock.isSome());
+ RemovePendingService(*aLock, aEntry.CID());
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ NS_ASSERTION(!aEntry.ServiceInstance(),
+ "Created two instances of a service!");
+
+ aEntry.SetServiceInstance(service.forget());
+
+ aLock.reset();
+
+ *aResult = do_AddRef(aEntry.ServiceInstance()).take();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::GetService(const nsCID& aClass, const nsIID& aIID,
+ void** aResult) {
+ // test this first, since there's no point in returning a service during
+ // shutdown -- whether it's available or not would depend on the order it
+ // occurs in the list
+ if (gXPCOMShuttingDown) {
+ // When processing shutdown, don't process new GetService() requests
+#ifdef SHOW_DENIED_ON_SHUTDOWN
+ fprintf(stderr,
+ "Getting service on shutdown. Denied.\n"
+ " CID: %s\n IID: %s\n",
+ AutoIDString(aClass).get(), AutoIDString(aIID).get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ Maybe<MonitorAutoLock> lock(std::in_place, mLock);
+
+ Maybe<EntryWrapper> entry = LookupByCID(*lock, aClass);
+ if (!entry) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ return GetServiceLocked(lock, *entry, aIID, aResult);
+}
+
+nsresult nsComponentManagerImpl::GetService(ModuleID aId, const nsIID& aIID,
+ void** aResult) {
+ const auto& entry = gStaticModules[size_t(aId)];
+
+ // test this first, since there's no point in returning a service during
+ // shutdown -- whether it's available or not would depend on the order it
+ // occurs in the list
+ if (gXPCOMShuttingDown) {
+ // When processing shutdown, don't process new GetService() requests
+#ifdef SHOW_DENIED_ON_SHUTDOWN
+ fprintf(stderr,
+ "Getting service on shutdown. Denied.\n"
+ " CID: %s\n IID: %s\n",
+ AutoIDString(entry.CID()).get(), AutoIDString(aIID).get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ Maybe<MonitorAutoLock> lock(std::in_place, mLock);
+
+ Maybe<EntryWrapper> wrapper;
+ if (entry.Overridable()) {
+ // If we expect this service to be overridden by test code, we need to look
+ // it up by contract ID every time.
+ wrapper = LookupByContractID(*lock, entry.ContractID());
+ if (!wrapper) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+ } else if (!entry.Active()) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ } else {
+ wrapper.emplace(&entry);
+ }
+ return GetServiceLocked(lock, *wrapper, aIID, aResult);
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::IsServiceInstantiated(const nsCID& aClass,
+ const nsIID& aIID,
+ bool* aResult) {
+ // Now we want to get the service if we already got it. If not, we don't want
+ // to create an instance of it. mmh!
+
+ // test this first, since there's no point in returning a service during
+ // shutdown -- whether it's available or not would depend on the order it
+ // occurs in the list
+ if (gXPCOMShuttingDown) {
+ // When processing shutdown, don't process new GetService() requests
+#ifdef SHOW_DENIED_ON_SHUTDOWN
+ fprintf(stderr,
+ "Checking for service on shutdown. Denied.\n"
+ " CID: %s\n IID: %s\n",
+ AutoIDString(aClass).get(), AutoIDString(aIID).get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (Maybe<EntryWrapper> entry = LookupByCID(aClass)) {
+ if (auto* service = entry->ServiceInstance()) {
+ nsCOMPtr<nsISupports> instance;
+ nsresult rv = service->QueryInterface(aIID, getter_AddRefs(instance));
+ *aResult = (instance != nullptr);
+ return rv;
+ }
+ }
+
+ *aResult = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::IsServiceInstantiatedByContractID(
+ const char* aContractID, const nsIID& aIID, bool* aResult) {
+ // Now we want to get the service if we already got it. If not, we don't want
+ // to create an instance of it. mmh!
+
+ // test this first, since there's no point in returning a service during
+ // shutdown -- whether it's available or not would depend on the order it
+ // occurs in the list
+ if (gXPCOMShuttingDown) {
+ // When processing shutdown, don't process new GetService() requests
+#ifdef SHOW_DENIED_ON_SHUTDOWN
+ fprintf(stderr,
+ "Checking for service on shutdown. Denied.\n"
+ " ContractID: %s\n IID: %s\n",
+ aContractID, AutoIDString(aIID).get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (Maybe<EntryWrapper> entry =
+ LookupByContractID(nsDependentCString(aContractID))) {
+ if (auto* service = entry->ServiceInstance()) {
+ nsCOMPtr<nsISupports> instance;
+ nsresult rv = service->QueryInterface(aIID, getter_AddRefs(instance));
+ *aResult = (instance != nullptr);
+ return rv;
+ }
+ }
+
+ *aResult = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::GetServiceByContractID(const char* aContractID,
+ const nsIID& aIID,
+ void** aResult) {
+ // test this first, since there's no point in returning a service during
+ // shutdown -- whether it's available or not would depend on the order it
+ // occurs in the list
+ if (gXPCOMShuttingDown) {
+ // When processing shutdown, don't process new GetService() requests
+#ifdef SHOW_DENIED_ON_SHUTDOWN
+ fprintf(stderr,
+ "Getting service on shutdown. Denied.\n"
+ " ContractID: %s\n IID: %s\n",
+ aContractID, AutoIDString(aIID).get());
+#endif /* SHOW_DENIED_ON_SHUTDOWN */
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ AUTO_PROFILER_LABEL_DYNAMIC_CSTR_NONSENSITIVE("GetServiceByContractID", OTHER,
+ aContractID);
+ Maybe<MonitorAutoLock> lock(std::in_place, mLock);
+
+ Maybe<EntryWrapper> entry =
+ LookupByContractID(*lock, nsDependentCString(aContractID));
+ if (!entry) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ return GetServiceLocked(lock, *entry, aIID, aResult);
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::RegisterFactory(const nsCID& aClass, const char* aName,
+ const char* aContractID,
+ nsIFactory* aFactory) {
+ if (!aFactory) {
+ // If a null factory is passed in, this call just wants to reset
+ // the contract ID to point to an existing CID entry.
+ if (!aContractID) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsDependentCString contractID(aContractID);
+
+ MonitorAutoLock lock(mLock);
+ nsFactoryEntry* oldf = mFactories.Get(&aClass);
+ if (oldf) {
+ StaticComponents::InvalidateContractID(contractID);
+ mContractIDs.InsertOrUpdate(contractID, oldf);
+ return NS_OK;
+ }
+
+ if (StaticComponents::LookupByCID(aClass)) {
+ // If this is the CID of a static module, just reset the invalid bit of
+ // the static entry for this contract ID, and assume it points to the
+ // correct class.
+ if (StaticComponents::InvalidateContractID(contractID, false)) {
+ mContractIDs.Remove(contractID);
+ return NS_OK;
+ }
+ }
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ auto f = MakeUnique<nsFactoryEntry>(aClass, aFactory);
+
+ MonitorAutoLock lock(mLock);
+ return mFactories.WithEntryHandle(&f->mCID, [&](auto&& entry) {
+ if (entry) {
+ return NS_ERROR_FACTORY_EXISTS;
+ }
+ if (StaticComponents::LookupByCID(f->mCID)) {
+ return NS_ERROR_FACTORY_EXISTS;
+ }
+ if (aContractID) {
+ nsDependentCString contractID(aContractID);
+ mContractIDs.InsertOrUpdate(contractID, f.get());
+ // We allow dynamically-registered contract IDs to override static
+ // entries, so invalidate any static entry for this contract ID.
+ StaticComponents::InvalidateContractID(contractID);
+ }
+ entry.Insert(f.release());
+
+ return NS_OK;
+ });
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::UnregisterFactory(const nsCID& aClass,
+ nsIFactory* aFactory) {
+ // Don't release the dying factory or service object until releasing
+ // the component manager monitor.
+ nsCOMPtr<nsIFactory> dyingFactory;
+ nsCOMPtr<nsISupports> dyingServiceObject;
+
+ {
+ MonitorAutoLock lock(mLock);
+ auto entry = mFactories.Lookup(&aClass);
+ nsFactoryEntry* f = entry ? entry.Data() : nullptr;
+ if (!f || f->mFactory != aFactory) {
+ // Note: We do not support unregistering static factories.
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ entry.Remove();
+
+ // This might leave a stale contractid -> factory mapping in
+ // place, so null out the factory entry (see
+ // nsFactoryEntry::GetFactory)
+ f->mFactory.swap(dyingFactory);
+ f->mServiceObject.swap(dyingServiceObject);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::AutoRegister(nsIFile* aLocation) {
+ XRE_AddManifestLocation(NS_EXTENSION_LOCATION, aLocation);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::IsCIDRegistered(const nsCID& aClass, bool* aResult) {
+ *aResult = LookupByCID(aClass).isSome();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::IsContractIDRegistered(const char* aClass,
+ bool* aResult) {
+ if (NS_WARN_IF(!aClass)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ Maybe<EntryWrapper> entry = LookupByContractID(nsDependentCString(aClass));
+
+ *aResult = entry.isSome();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::GetContractIDs(nsTArray<nsCString>& aResult) {
+ aResult = ToTArray<nsTArray<nsCString>>(mContractIDs.Keys());
+
+ for (const auto& entry : gContractEntries) {
+ if (!entry.Invalid()) {
+ aResult.AppendElement(entry.ContractID());
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::ContractIDToCID(const char* aContractID,
+ nsCID** aResult) {
+ {
+ MonitorAutoLock lock(mLock);
+ Maybe<EntryWrapper> entry =
+ LookupByContractID(lock, nsDependentCString(aContractID));
+ if (entry) {
+ *aResult = (nsCID*)moz_xmalloc(sizeof(nsCID));
+ **aResult = entry->CID();
+ return NS_OK;
+ }
+ }
+ *aResult = nullptr;
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+}
+
+MOZ_DEFINE_MALLOC_SIZE_OF(ComponentManagerMallocSizeOf)
+
+NS_IMETHODIMP
+nsComponentManagerImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) {
+ MOZ_COLLECT_REPORT("explicit/xpcom/component-manager", KIND_HEAP, UNITS_BYTES,
+ SizeOfIncludingThis(ComponentManagerMallocSizeOf),
+ "Memory used for the XPCOM component manager.");
+
+ return NS_OK;
+}
+
+size_t nsComponentManagerImpl::SizeOfIncludingThis(
+ mozilla::MallocSizeOf aMallocSizeOf) const {
+ size_t n = aMallocSizeOf(this);
+
+ n += mFactories.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (const auto& data : mFactories.Values()) {
+ n += data->SizeOfIncludingThis(aMallocSizeOf);
+ }
+
+ n += mContractIDs.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (const auto& key : mContractIDs.Keys()) {
+ // We don't measure the nsFactoryEntry data because it's owned by
+ // mFactories (which is measured above).
+ n += key.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+
+ if (sModuleLocations) {
+ n += sModuleLocations->ShallowSizeOfIncludingThis(aMallocSizeOf);
+ }
+
+ n += mPendingServices.ShallowSizeOfExcludingThis(aMallocSizeOf);
+
+ // Measurement of the following members may be added later if DMD finds it is
+ // worthwhile:
+ // - mMon
+ // - sModuleLocations' entries
+
+ return n;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsFactoryEntry
+////////////////////////////////////////////////////////////////////////////////
+
+nsFactoryEntry::nsFactoryEntry(const nsCID& aCID, nsIFactory* aFactory)
+ : mCID(aCID), mFactory(aFactory) {}
+
+already_AddRefed<nsIFactory> nsFactoryEntry::GetFactory() {
+ nsComponentManagerImpl::gComponentManager->mLock.AssertNotCurrentThreadOwns();
+
+ nsCOMPtr<nsIFactory> factory = mFactory;
+ return factory.forget();
+}
+
+nsresult nsFactoryEntry::CreateInstance(const nsIID& aIID, void** aResult) {
+ nsCOMPtr<nsIFactory> factory = GetFactory();
+ NS_ENSURE_TRUE(factory, NS_ERROR_FAILURE);
+ return factory->CreateInstance(aIID, aResult);
+}
+
+size_t nsFactoryEntry::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
+ size_t n = aMallocSizeOf(this);
+
+ // Measurement of the following members may be added later if DMD finds it is
+ // worthwhile:
+ // - mCID;
+ // - mFactory;
+ // - mServiceObject;
+
+ return n;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Static Access Functions
+////////////////////////////////////////////////////////////////////////////////
+
+nsresult NS_GetComponentManager(nsIComponentManager** aResult) {
+ if (!nsComponentManagerImpl::gComponentManager) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ NS_ADDREF(*aResult = nsComponentManagerImpl::gComponentManager);
+ return NS_OK;
+}
+
+nsresult NS_GetServiceManager(nsIServiceManager** aResult) {
+ if (!nsComponentManagerImpl::gComponentManager) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ NS_ADDREF(*aResult = nsComponentManagerImpl::gComponentManager);
+ return NS_OK;
+}
+
+nsresult NS_GetComponentRegistrar(nsIComponentRegistrar** aResult) {
+ if (!nsComponentManagerImpl::gComponentManager) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ NS_ADDREF(*aResult = nsComponentManagerImpl::gComponentManager);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::AddBootstrappedManifestLocation(nsIFile* aLocation) {
+ NS_ENSURE_ARG_POINTER(aLocation);
+
+ nsString path;
+ nsresult rv = aLocation->GetPath(path);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (Substring(path, path.Length() - 4).EqualsLiteral(".xpi")) {
+ return XRE_AddJarManifestLocation(NS_BOOTSTRAPPED_LOCATION, aLocation);
+ }
+
+ nsCOMPtr<nsIFile> manifest = CloneAndAppend(aLocation, "chrome.manifest"_ns);
+ return XRE_AddManifestLocation(NS_BOOTSTRAPPED_LOCATION, manifest);
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::RemoveBootstrappedManifestLocation(nsIFile* aLocation) {
+ NS_ENSURE_ARG_POINTER(aLocation);
+
+ nsCOMPtr<nsIChromeRegistry> cr = mozilla::services::GetChromeRegistry();
+ if (!cr) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsString path;
+ nsresult rv = aLocation->GetPath(path);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsComponentManagerImpl::ComponentLocation elem;
+ elem.type = NS_BOOTSTRAPPED_LOCATION;
+
+ if (Substring(path, path.Length() - 4).EqualsLiteral(".xpi")) {
+ elem.location.Init(aLocation, "chrome.manifest");
+ } else {
+ nsCOMPtr<nsIFile> lf = CloneAndAppend(aLocation, "chrome.manifest"_ns);
+ elem.location.Init(lf);
+ }
+
+ // Remove reference.
+ nsComponentManagerImpl::sModuleLocations->RemoveElement(
+ elem, ComponentLocationComparator());
+
+ rv = cr->CheckForNewChrome();
+ return rv;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::GetComponentJSMs(nsIUTF8StringEnumerator** aJSMs) {
+ nsCOMPtr<nsIUTF8StringEnumerator> result =
+ StaticComponents::GetComponentJSMs();
+ result.forget(aJSMs);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::GetComponentESModules(
+ nsIUTF8StringEnumerator** aESModules) {
+ nsCOMPtr<nsIUTF8StringEnumerator> result =
+ StaticComponents::GetComponentESModules();
+ result.forget(aESModules);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsComponentManagerImpl::GetManifestLocations(nsIArray** aLocations) {
+ NS_ENSURE_ARG_POINTER(aLocations);
+ *aLocations = nullptr;
+
+ if (!sModuleLocations) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ nsCOMPtr<nsIMutableArray> locations = nsArray::Create();
+ nsresult rv;
+ for (uint32_t i = 0; i < sModuleLocations->Length(); ++i) {
+ ComponentLocation& l = sModuleLocations->ElementAt(i);
+ FileLocation loc = l.location;
+ nsCString uriString;
+ loc.GetURIString(uriString);
+ nsCOMPtr<nsIURI> uri;
+ rv = NS_NewURI(getter_AddRefs(uri), uriString);
+ if (NS_SUCCEEDED(rv)) {
+ locations->AppendElement(uri);
+ }
+ }
+
+ locations.forget(aLocations);
+ return NS_OK;
+}
+
+EXPORT_XPCOM_API(nsresult)
+XRE_AddManifestLocation(NSLocationType aType, nsIFile* aLocation) {
+ nsComponentManagerImpl::InitializeModuleLocations();
+ nsComponentManagerImpl::ComponentLocation* c =
+ nsComponentManagerImpl::sModuleLocations->AppendElement();
+ c->type = aType;
+ c->location.Init(aLocation);
+
+ if (nsComponentManagerImpl::gComponentManager &&
+ nsComponentManagerImpl::NORMAL ==
+ nsComponentManagerImpl::gComponentManager->mStatus) {
+ nsComponentManagerImpl::gComponentManager->RegisterManifest(
+ aType, c->location, false);
+ }
+
+ return NS_OK;
+}
+
+EXPORT_XPCOM_API(nsresult)
+XRE_AddJarManifestLocation(NSLocationType aType, nsIFile* aLocation) {
+ nsComponentManagerImpl::InitializeModuleLocations();
+ nsComponentManagerImpl::ComponentLocation* c =
+ nsComponentManagerImpl::sModuleLocations->AppendElement();
+
+ c->type = aType;
+ c->location.Init(aLocation, "chrome.manifest");
+
+ if (nsComponentManagerImpl::gComponentManager &&
+ nsComponentManagerImpl::NORMAL ==
+ nsComponentManagerImpl::gComponentManager->mStatus) {
+ nsComponentManagerImpl::gComponentManager->RegisterManifest(
+ aType, c->location, false);
+ }
+
+ return NS_OK;
+}
+
+// Expose some important global interfaces to rust for the rust xpcom API. These
+// methods return a non-owning reference to the component manager, which should
+// live for the lifetime of XPCOM.
+extern "C" {
+
+const nsIComponentManager* Gecko_GetComponentManager() {
+ return nsComponentManagerImpl::gComponentManager;
+}
+
+const nsIServiceManager* Gecko_GetServiceManager() {
+ return nsComponentManagerImpl::gComponentManager;
+}
+
+const nsIComponentRegistrar* Gecko_GetComponentRegistrar() {
+ return nsComponentManagerImpl::gComponentManager;
+}
+
+// FFI-compatible version of `GetServiceHelper::operator()`.
+nsresult Gecko_GetServiceByModuleID(ModuleID aId, const nsIID* aIID,
+ void** aResult) {
+ return nsComponentManagerImpl::gComponentManager->GetService(aId, *aIID,
+ aResult);
+}
+
+// FFI-compatible version of `CreateInstanceHelper::operator()`.
+nsresult Gecko_CreateInstanceByModuleID(ModuleID aId, const nsIID* aIID,
+ void** aResult) {
+ const auto& entry = gStaticModules[size_t(aId)];
+ if (!entry.Active()) {
+ return NS_ERROR_FACTORY_NOT_REGISTERED;
+ }
+
+ nsresult rv = entry.CreateInstance(*aIID, aResult);
+ return rv;
+}
+}
diff --git a/xpcom/components/nsComponentManager.h b/xpcom/components/nsComponentManager.h
new file mode 100644
index 0000000000..5ad555b53b
--- /dev/null
+++ b/xpcom/components/nsComponentManager.h
@@ -0,0 +1,218 @@
+/* -*- 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 "nsIComponentManager.h"
+#include "nsIComponentRegistrar.h"
+#include "nsIMemoryReporter.h"
+#include "nsIServiceManager.h"
+#include "nsIFile.h"
+#include "mozilla/ArenaAllocator.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Module.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/UniquePtr.h"
+#include "nsXULAppAPI.h"
+#include "nsIFactory.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "PLDHashTable.h"
+#include "prtime.h"
+#include "nsCOMPtr.h"
+#include "nsWeakReference.h"
+#include "nsCOMArray.h"
+#include "nsTHashMap.h"
+#include "nsInterfaceHashtable.h"
+#include "nsClassHashtable.h"
+#include "nsTArray.h"
+
+#include "mozilla/Components.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Omnijar.h"
+#include "mozilla/Attributes.h"
+
+struct nsFactoryEntry;
+struct PRThread;
+
+#define NS_COMPONENTMANAGER_CID \
+ { /* 91775d60-d5dc-11d2-92fb-00e09805570f */ \
+ 0x91775d60, 0xd5dc, 0x11d2, { \
+ 0x92, 0xfb, 0x00, 0xe0, 0x98, 0x05, 0x57, 0x0f \
+ } \
+ }
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace {
+class EntryWrapper;
+} // namespace
+
+namespace mozilla {
+namespace xpcom {
+
+bool ProcessSelectorMatches(Module::ProcessSelector aSelector);
+bool FastProcessSelectorMatches(Module::ProcessSelector aSelector);
+
+} // namespace xpcom
+} // namespace mozilla
+
+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(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<nsIFactory> FindFactory(const nsCID& aClass);
+ already_AddRefed<nsIFactory> FindFactory(const char* aContractID,
+ uint32_t aContractIDLen);
+
+ already_AddRefed<nsIFactory> LoadFactory(nsFactoryEntry* aEntry);
+
+ nsTHashMap<nsIDPointerHashKey, nsFactoryEntry*> mFactories;
+ nsTHashMap<nsCStringHashKey, nsFactoryEntry*> mContractIDs;
+
+ mozilla::Monitor mLock MOZ_UNANNOTATED;
+
+ mozilla::Maybe<EntryWrapper> LookupByCID(const nsID& aCID);
+ mozilla::Maybe<EntryWrapper> LookupByCID(const mozilla::MonitorAutoLock&,
+ const nsID& aCID);
+
+ mozilla::Maybe<EntryWrapper> LookupByContractID(
+ const nsACString& aContractID);
+ mozilla::Maybe<EntryWrapper> LookupByContractID(
+ const mozilla::MonitorAutoLock&, const nsACString& aContractID);
+
+ nsresult GetService(mozilla::xpcom::ModuleID, const nsIID& aIID,
+ void** aResult);
+
+ static bool JSLoaderReady() { return gComponentManager->mJSLoaderReady; }
+
+ 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<ComponentLocation>* sModuleLocations;
+
+ // 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() = default;
+
+ NSLocationType mType;
+ mozilla::FileLocation mFile;
+ bool mChromeOnly;
+ };
+
+ void ManifestManifest(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;
+
+ struct PendingServiceInfo {
+ const nsCID* cid;
+ PRThread* thread;
+ };
+
+ inline PendingServiceInfo* AddPendingService(const nsCID& aServiceCID,
+ PRThread* aThread);
+ inline void RemovePendingService(mozilla::MonitorAutoLock& aLock,
+ const nsCID& aServiceCID);
+ inline PRThread* GetPendingServiceThread(const nsCID& aServiceCID) const;
+
+ nsTArray<PendingServiceInfo> mPendingServices;
+
+ bool mJSLoaderReady = false;
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+ private:
+ ~nsComponentManagerImpl();
+
+ nsresult GetServiceLocked(mozilla::Maybe<mozilla::MonitorAutoLock>& aLock,
+ EntryWrapper& aEntry, const nsIID& aIID,
+ void** aResult);
+};
+
+#define NS_MAX_FILENAME_LEN 1024
+
+#define NS_ERROR_IS_DIR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_XPCOM, 24)
+
+struct nsFactoryEntry {
+ // nsIComponentRegistrar.registerFactory support
+ nsFactoryEntry(const nsCID& aClass, nsIFactory* aFactory);
+
+ ~nsFactoryEntry() = default;
+
+ already_AddRefed<nsIFactory> GetFactory();
+
+ nsresult CreateInstance(const nsIID& aIID, void** aResult);
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
+
+ const nsCID mCID;
+
+ nsCOMPtr<nsIFactory> mFactory;
+ nsCOMPtr<nsISupports> mServiceObject;
+};
+
+#endif // nsComponentManager_h__
diff --git a/xpcom/components/nsComponentManagerUtils.cpp b/xpcom/components/nsComponentManagerUtils.cpp
new file mode 100644
index 0000000000..569c97060c
--- /dev/null
+++ b/xpcom/components/nsComponentManagerUtils.cpp
@@ -0,0 +1,259 @@
+/* -*- 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 nsXPCOM_h__
+# include "nsXPCOM.h"
+#endif
+
+#ifndef nsCOMPtr_h__
+# include "nsCOMPtr.h"
+#endif
+
+#ifdef MOZILLA_INTERNAL_API
+# include "nsComponentManager.h"
+#endif
+
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+
+#include "nsIComponentManager.h"
+
+#ifndef MOZILLA_INTERNAL_API
+
+nsresult CallGetService(const nsCID& aCID, const nsIID& aIID, void** aResult) {
+ nsCOMPtr<nsIServiceManager> servMgr;
+ nsresult status = NS_GetServiceManager(getter_AddRefs(servMgr));
+ if (servMgr) {
+ status = servMgr->GetService(aCID, aIID, aResult);
+ }
+ return status;
+}
+
+nsresult CallGetService(const char* aContractID, const nsIID& aIID,
+ void** aResult) {
+ nsCOMPtr<nsIServiceManager> servMgr;
+ nsresult status = NS_GetServiceManager(getter_AddRefs(servMgr));
+ if (servMgr) {
+ status = servMgr->GetServiceByContractID(aContractID, aIID, aResult);
+ }
+ return status;
+}
+
+#else
+
+nsresult CallGetService(const nsCID& aCID, const nsIID& aIID, void** aResult) {
+ nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
+ if (!compMgr) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return compMgr->nsComponentManagerImpl::GetService(aCID, aIID, aResult);
+}
+
+nsresult CallGetService(const char* aContractID, const nsIID& aIID,
+ void** aResult) {
+ nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
+ if (!compMgr) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return compMgr->nsComponentManagerImpl::GetServiceByContractID(aContractID,
+ aIID, aResult);
+}
+
+#endif
+
+#ifndef MOZILLA_INTERNAL_API
+
+nsresult CallCreateInstance(const nsCID& aCID, const nsIID& aIID,
+ void** aResult) {
+ nsCOMPtr<nsIComponentManager> compMgr;
+ nsresult status = NS_GetComponentManager(getter_AddRefs(compMgr));
+ if (compMgr) {
+ status = compMgr->CreateInstance(aCID, aIID, aResult);
+ }
+ return status;
+}
+
+nsresult CallCreateInstance(const char* aContractID, const nsIID& aIID,
+ void** aResult) {
+ nsCOMPtr<nsIComponentManager> compMgr;
+ nsresult status = NS_GetComponentManager(getter_AddRefs(compMgr));
+ if (compMgr)
+ status = compMgr->CreateInstanceByContractID(aContractID, aIID, aResult);
+ return status;
+}
+
+nsresult CallGetClassObject(const nsCID& aCID, const nsIID& aIID,
+ void** aResult) {
+ nsCOMPtr<nsIComponentManager> compMgr;
+ nsresult status = NS_GetComponentManager(getter_AddRefs(compMgr));
+ if (compMgr) {
+ status = compMgr->GetClassObject(aCID, aIID, aResult);
+ }
+ return status;
+}
+
+nsresult CallGetClassObject(const char* aContractID, const nsIID& aIID,
+ void** aResult) {
+ nsCOMPtr<nsIComponentManager> compMgr;
+ nsresult status = NS_GetComponentManager(getter_AddRefs(compMgr));
+ if (compMgr)
+ status = compMgr->GetClassObjectByContractID(aContractID, aIID, aResult);
+ return status;
+}
+
+#else
+
+nsresult CallCreateInstance(const nsCID& aCID, const nsIID& aIID,
+ void** aResult) {
+ nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
+ if (NS_WARN_IF(!compMgr)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return compMgr->nsComponentManagerImpl::CreateInstance(aCID, aIID, aResult);
+}
+
+nsresult CallCreateInstance(const char* aContractID, const nsIID& aIID,
+ void** aResult) {
+ nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
+ if (NS_WARN_IF(!compMgr)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return compMgr->nsComponentManagerImpl::CreateInstanceByContractID(
+ aContractID, aIID, aResult);
+}
+
+nsresult CallGetClassObject(const nsCID& aCID, const nsIID& aIID,
+ void** aResult) {
+ nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
+ if (NS_WARN_IF(!compMgr)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return compMgr->nsComponentManagerImpl::GetClassObject(aCID, aIID, aResult);
+}
+
+nsresult CallGetClassObject(const char* aContractID, const nsIID& aIID,
+ void** aResult) {
+ nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager;
+ if (NS_WARN_IF(!compMgr)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ return compMgr->nsComponentManagerImpl::GetClassObjectByContractID(
+ aContractID, aIID, aResult);
+}
+
+#endif
+
+nsresult nsCreateInstanceByCID::operator()(const nsIID& aIID,
+ void** aInstancePtr) const {
+ nsresult status = CallCreateInstance(mCID, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+nsresult nsCreateInstanceByContractID::operator()(const nsIID& aIID,
+ void** aInstancePtr) const {
+ nsresult status = CallCreateInstance(mContractID, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+nsresult nsCreateInstanceFromFactory::operator()(const nsIID& aIID,
+ void** aInstancePtr) const {
+ nsresult status = mFactory->CreateInstance(aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+nsresult nsGetClassObjectByCID::operator()(const nsIID& aIID,
+ void** aInstancePtr) const {
+ nsresult status = CallGetClassObject(mCID, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+nsresult nsGetClassObjectByContractID::operator()(const nsIID& aIID,
+ void** aInstancePtr) const {
+ nsresult status = CallGetClassObject(mContractID, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+nsresult nsGetServiceByCID::operator()(const nsIID& aIID,
+ void** aInstancePtr) const {
+ nsresult status = CallGetService(mCID, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+
+ return status;
+}
+
+nsresult nsGetServiceByCIDWithError::operator()(const nsIID& aIID,
+ void** aInstancePtr) const {
+ nsresult status = CallGetService(mCID, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
+
+nsresult nsGetServiceByContractID::operator()(const nsIID& aIID,
+ void** aInstancePtr) const {
+ nsresult status = CallGetService(mContractID, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+
+ return status;
+}
+
+nsresult nsGetServiceByContractIDWithError::operator()(
+ const nsIID& aIID, void** aInstancePtr) const {
+ nsresult status = CallGetService(mContractID, aIID, aInstancePtr);
+ if (NS_FAILED(status)) {
+ *aInstancePtr = 0;
+ }
+
+ if (mErrorPtr) {
+ *mErrorPtr = status;
+ }
+ return status;
+}
diff --git a/xpcom/components/nsComponentManagerUtils.h b/xpcom/components/nsComponentManagerUtils.h
new file mode 100644
index 0000000000..b6f15aabaa
--- /dev/null
+++ b/xpcom/components/nsComponentManagerUtils.h
@@ -0,0 +1,170 @@
+/* -*- 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 nsComponentManagerUtils_h__
+#define nsComponentManagerUtils_h__
+
+#include "nscore.h"
+#include "nsCOMPtr.h"
+
+#include "nsIFactory.h"
+
+nsresult CallCreateInstance(const nsCID& aCID, const nsIID& aIID,
+ void** aResult);
+
+nsresult CallCreateInstance(const char* aContractID, const nsIID& aIID,
+ void** aResult);
+
+nsresult CallGetClassObject(const nsCID& aCID, const nsIID& aIID,
+ void** aResult);
+
+nsresult CallGetClassObject(const char* aContractID, const nsIID& aIID,
+ void** aResult);
+
+class MOZ_STACK_CLASS nsCreateInstanceByCID final : public nsCOMPtr_helper {
+ public:
+ nsCreateInstanceByCID(const nsCID& aCID, nsresult* aErrorPtr)
+ : mCID(aCID), mErrorPtr(aErrorPtr) {}
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const override;
+
+ private:
+ const nsCID& mCID;
+ nsresult* mErrorPtr;
+};
+
+class MOZ_STACK_CLASS nsCreateInstanceByContractID final
+ : public nsCOMPtr_helper {
+ public:
+ nsCreateInstanceByContractID(const char* aContractID, nsresult* aErrorPtr)
+ : mContractID(aContractID), mErrorPtr(aErrorPtr) {}
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const override;
+
+ private:
+ const char* mContractID;
+ nsresult* mErrorPtr;
+};
+
+class MOZ_STACK_CLASS nsCreateInstanceFromFactory final
+ : public nsCOMPtr_helper {
+ public:
+ nsCreateInstanceFromFactory(nsIFactory* aFactory, nsresult* aErrorPtr)
+ : mFactory(aFactory), mErrorPtr(aErrorPtr) {}
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const override;
+
+ private:
+ nsIFactory* MOZ_NON_OWNING_REF mFactory;
+ nsresult* mErrorPtr;
+};
+
+inline const nsCreateInstanceByCID do_CreateInstance(const nsCID& aCID,
+ nsresult* aError = 0) {
+ return nsCreateInstanceByCID(aCID, aError);
+}
+
+inline const nsCreateInstanceByContractID do_CreateInstance(
+ const char* aContractID, nsresult* aError = 0) {
+ return nsCreateInstanceByContractID(aContractID, aError);
+}
+
+inline const nsCreateInstanceFromFactory do_CreateInstance(
+ nsIFactory* aFactory, nsresult* aError = 0) {
+ return nsCreateInstanceFromFactory(aFactory, aError);
+}
+
+class MOZ_STACK_CLASS nsGetClassObjectByCID final : public nsCOMPtr_helper {
+ public:
+ nsGetClassObjectByCID(const nsCID& aCID, nsresult* aErrorPtr)
+ : mCID(aCID), mErrorPtr(aErrorPtr) {}
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const override;
+
+ private:
+ const nsCID& mCID;
+ nsresult* mErrorPtr;
+};
+
+class MOZ_STACK_CLASS nsGetClassObjectByContractID final
+ : public nsCOMPtr_helper {
+ public:
+ nsGetClassObjectByContractID(const char* aContractID, nsresult* aErrorPtr)
+ : mContractID(aContractID), mErrorPtr(aErrorPtr) {}
+
+ virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const override;
+
+ private:
+ const char* mContractID;
+ nsresult* mErrorPtr;
+};
+
+/**
+ * do_GetClassObject can be used to improve performance of callers
+ * that call |CreateInstance| many times. They can cache the factory
+ * and call do_CreateInstance or CallCreateInstance with the cached
+ * factory rather than having the component manager retrieve it every
+ * time.
+ */
+inline const nsGetClassObjectByCID do_GetClassObject(const nsCID& aCID,
+ nsresult* aError = 0) {
+ return nsGetClassObjectByCID(aCID, aError);
+}
+
+inline const nsGetClassObjectByContractID do_GetClassObject(
+ const char* aContractID, nsresult* aError = 0) {
+ return nsGetClassObjectByContractID(aContractID, aError);
+}
+
+// type-safe shortcuts for calling |CreateInstance|
+template <class DestinationType>
+inline nsresult CallCreateInstance(const nsCID& aClass,
+ DestinationType** aDestination) {
+ MOZ_ASSERT(aDestination, "null parameter");
+
+ return CallCreateInstance(aClass, NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template <class DestinationType>
+inline nsresult CallCreateInstance(const char* aContractID,
+ DestinationType** aDestination) {
+ MOZ_ASSERT(aContractID, "null parameter");
+ MOZ_ASSERT(aDestination, "null parameter");
+
+ return CallCreateInstance(aContractID, NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template <class DestinationType>
+inline nsresult CallCreateInstance(nsIFactory* aFactory,
+ DestinationType** aDestination) {
+ MOZ_ASSERT(aFactory, "null parameter");
+ MOZ_ASSERT(aDestination, "null parameter");
+
+ return aFactory->CreateInstance(nullptr, NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template <class DestinationType>
+inline nsresult CallGetClassObject(const nsCID& aClass,
+ DestinationType** aDestination) {
+ MOZ_ASSERT(aDestination, "null parameter");
+
+ return CallGetClassObject(aClass, NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template <class DestinationType>
+inline nsresult CallGetClassObject(const char* aContractID,
+ DestinationType** aDestination) {
+ MOZ_ASSERT(aDestination, "null parameter");
+
+ return CallGetClassObject(aContractID, NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+#endif /* nsComponentManagerUtils_h__ */
diff --git a/xpcom/components/nsICategoryManager.idl b/xpcom/components/nsICategoryManager.idl
new file mode 100644
index 0000000000..12c1a9e4a9
--- /dev/null
+++ b/xpcom/components/nsICategoryManager.idl
@@ -0,0 +1,137 @@
+/* -*- 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"
+#include "nsISupportsPrimitives.idl"
+
+interface nsISimpleEnumerator;
+
+%{C++
+#include "nsString.h"
+%}
+
+/*
+ * nsICategoryManager
+ */
+
+[scriptable, builtinclass, uuid(de021d54-57a3-4025-ae63-4c8eedbe74c0)]
+interface nsICategoryEntry : nsISupportsCString
+{
+ readonly attribute ACString entry;
+
+ readonly attribute ACString value;
+};
+
+[builtinclass, 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.
+ */
+ ACString getCategoryEntry(in ACString aCategory, in ACString 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
+ */
+ ACString addCategoryEntry(in ACString aCategory, in ACString aEntry,
+ in ACString 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 ACString aCategory, in ACString aEntry,
+ in boolean aPersist);
+
+ /**
+ * Delete a category and all entries.
+ * @param aCategory The category to be deleted.
+ */
+ void deleteCategory(in ACString aCategory);
+
+ /**
+ * Enumerate the entries in a category.
+ * @param aCategory The category to be enumerated.
+ * @return a simple enumerator, each result QIs to
+ * nsICategoryEntry.
+ */
+ nsISimpleEnumerator enumerateCategory(in ACString aCategory);
+
+
+ /**
+ * Enumerate all existing categories
+ * @param aCategory The category to be enumerated.
+ * @return a simple enumerator, each result QIs to
+ * nsISupportsCString.
+ */
+ nsISimpleEnumerator enumerateCategories();
+
+ %{C++
+ template<size_t N>
+ nsresult
+ GetCategoryEntry(const char (&aCategory)[N], const nsACString& aEntry,
+ nsACString& aResult)
+ {
+ return GetCategoryEntry(nsLiteralCString(aCategory),
+ aEntry, aResult);
+ }
+
+ template<size_t N, size_t M>
+ nsresult
+ GetCategoryEntry(const char (&aCategory)[N], const char (&aEntry)[M],
+ nsACString& aResult)
+ {
+ return GetCategoryEntry(nsLiteralCString(aCategory),
+ nsLiteralCString(aEntry),
+ aResult);
+ }
+
+ nsresult
+ AddCategoryEntry(const nsACString& aCategory, const nsACString& aEntry,
+ const nsACString& aValue, bool aPersist, bool aReplace)
+ {
+ nsCString oldValue;
+ return AddCategoryEntry(aCategory, aEntry, aValue, aPersist, aReplace,
+ oldValue);
+ }
+
+ template<size_t N>
+ nsresult
+ AddCategoryEntry(const char (&aCategory)[N], const nsACString& aEntry,
+ const nsACString& aValue, bool aPersist, bool aReplace)
+ {
+ nsCString oldValue;
+ return AddCategoryEntry(nsLiteralCString(aCategory), aEntry, aValue,
+ aPersist, aReplace, oldValue);
+ }
+
+ template<size_t N>
+ nsresult
+ DeleteCategoryEntry(const char (&aCategory)[N], const nsACString& aEntry, bool aPersist)
+ {
+ return DeleteCategoryEntry(nsLiteralCString(aCategory), aEntry, aPersist);
+ }
+
+
+ template<size_t N>
+ nsresult
+ EnumerateCategory(const char (&aCategory)[N], nsISimpleEnumerator** aResult)
+ {
+ return EnumerateCategory(nsLiteralCString(aCategory), aResult);
+ }
+ %}
+};
diff --git a/xpcom/components/nsIClassInfo.idl b/xpcom/components/nsIClassInfo.idl
new file mode 100644
index 0000000000..1ebef34ecf
--- /dev/null
+++ b/xpcom/components/nsIClassInfo.idl
@@ -0,0 +1,74 @@
+/* -*- 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
+{
+ /**
+ * Returns a list of the interfaces which instances of this class promise
+ * to implement. Note that nsISupports is an implicit member of any such
+ * list, and need not be included.
+ */
+ readonly attribute Array<nsIIDRef> interfaces;
+
+ /**
+ * 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/void.
+ */
+ readonly attribute AUTF8String contractID;
+
+ /**
+ * A human readable string naming the class, or null/void.
+ */
+ readonly attribute AUTF8String 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 SINGLETON_CLASSINFO = 1 << 5;
+
+ // 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 0000000000..9abde9960c
--- /dev/null
+++ b/xpcom/components/nsIComponentManager.idl
@@ -0,0 +1,117 @@
+/* -*- 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;
+interface nsIUTF8StringEnumerator;
+
+[scriptable, builtinclass, 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 aIID : IID of interface requested
+ */
+ [noscript]
+ void createInstance(in nsCIDRef aClass,
+ 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 aIID : IID of interface requested
+ */
+ [noscript]
+ void createInstanceByContractID(in string aContractID,
+ 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();
+
+ /**
+ * Returns a list of JSM URLs which are used to create components. This
+ * should only be used in automation.
+ */
+ nsIUTF8StringEnumerator getComponentJSMs();
+
+ /**
+ * Returns a list of ESM URLs which are used to create components. This
+ * should only be used in automation.
+ */
+ nsIUTF8StringEnumerator getComponentESModules();
+};
+
+
+%{ 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 0000000000..6b85caffab
--- /dev/null
+++ b/xpcom/components/nsIComponentRegistrar.idl
@@ -0,0 +1,100 @@
+/* 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;
+
+[builtinclass, 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);
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * getContractIDs
+ *
+ * Return the list of all registered ContractIDs.
+ *
+ * @return : Array of ContractIDs. Elements of the array are the string
+ * encoding of Contract IDs.
+ */
+ Array<ACString> getContractIDs();
+
+ /**
+ * 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 0000000000..e0b3e6e756
--- /dev/null
+++ b/xpcom/components/nsIFactory.idl
@@ -0,0 +1,27 @@
+/* -*- 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(1bb40a56-9223-41e6-97d4-da97bdeb6a4d)]
+interface nsIFactory : nsISupports {
+ /**
+ * Creates an instance of a component.
+ *
+ * @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.
+ * NS_ERROR* - Method failure.
+ */
+ void createInstance(in nsIIDRef iid,
+ [retval, iid_is(iid)] out nsQIResult result);
+
+};
diff --git a/xpcom/components/nsIServiceManager.idl b/xpcom/components/nsIServiceManager.idl
new file mode 100644
index 0000000000..dd613070e9
--- /dev/null
+++ b/xpcom/components/nsIServiceManager.idl
@@ -0,0 +1,70 @@
+/* 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.
+ */
+
+[builtinclass, 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 false otherwise. Throws if the service does not
+ * implement the given IID.
+ *
+ * @param aClass or aContractID : aClass or aContractID of object
+ * instance requested
+ * @param aIID : IID of interface requested
+ * @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/nsServiceManagerUtils.h b/xpcom/components/nsServiceManagerUtils.h
new file mode 100644
index 0000000000..4bfbc804cc
--- /dev/null
+++ b/xpcom/components/nsServiceManagerUtils.h
@@ -0,0 +1,56 @@
+/* -*- 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 nsServiceManagerUtils_h__
+#define nsServiceManagerUtils_h__
+
+#include "nsCOMPtr.h"
+#include "nsString.h"
+
+inline nsGetServiceByCID do_GetService(const nsCID& aCID) {
+ return nsGetServiceByCID(aCID);
+}
+
+inline nsGetServiceByCIDWithError do_GetService(const nsCID& aCID,
+ nsresult* aError) {
+ return nsGetServiceByCIDWithError(aCID, aError);
+}
+
+inline nsGetServiceByContractID do_GetService(const char* aContractID) {
+ return nsGetServiceByContractID(aContractID);
+}
+
+inline nsGetServiceByContractIDWithError do_GetService(const char* aContractID,
+ nsresult* aError) {
+ return nsGetServiceByContractIDWithError(aContractID, aError);
+}
+
+nsresult CallGetService(const nsCID& aClass, const nsIID& aIID, void** aResult);
+
+nsresult CallGetService(const char* aContractID, const nsIID& aIID,
+ void** aResult);
+
+// type-safe shortcuts for calling |GetService|
+template <class DestinationType>
+inline nsresult CallGetService(const nsCID& aClass,
+ DestinationType** aDestination) {
+ MOZ_ASSERT(aDestination, "null parameter");
+
+ return CallGetService(aClass, NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+template <class DestinationType>
+inline nsresult CallGetService(const char* aContractID,
+ DestinationType** aDestination) {
+ MOZ_ASSERT(aContractID, "null parameter");
+ MOZ_ASSERT(aDestination, "null parameter");
+
+ return CallGetService(aContractID, NS_GET_TEMPLATE_IID(DestinationType),
+ reinterpret_cast<void**>(aDestination));
+}
+
+#endif
diff --git a/xpcom/components/test/python.toml b/xpcom/components/test/python.toml
new file mode 100644
index 0000000000..6ce1319672
--- /dev/null
+++ b/xpcom/components/test/python.toml
@@ -0,0 +1,4 @@
+[DEFAULT]
+subsuite = "xpcom"
+
+["test_gen_static_components.py"]
diff --git a/xpcom/components/test/test_gen_static_components.py b/xpcom/components/test/test_gen_static_components.py
new file mode 100644
index 0000000000..85f731c082
--- /dev/null
+++ b/xpcom/components/test/test_gen_static_components.py
@@ -0,0 +1,150 @@
+# 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/.
+
+import os
+import sys
+import unittest
+
+import mozunit
+
+sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
+import gen_static_components
+from gen_static_components import BackgroundTasksSelector
+
+
+class TestGenStaticComponents(unittest.TestCase):
+ def test_string(self):
+ # A string: we default to NO_TASKS.
+ clas = {
+ "cid": "{a8566880-0bc7-4822-adb9-748c9af5cce7}",
+ "contract_ids": ["@mozilla.org/dummy-class;1"],
+ "jsm": "resource:///modules/DummyClass.jsm",
+ "js_name": "dummyClass",
+ "constructor": "DummyClassImpl",
+ "categories": {
+ "dummy1": ["m-dummy1", "m-dummy2"],
+ },
+ "protocol_config": {
+ "scheme": "dummy",
+ "flags": [],
+ },
+ }
+
+ substs = gen_static_components.gen_substs([{"Classes": [clas]}])
+
+ self.assertEqual(substs["category_count"], 1)
+ self.assertEqual(
+ [s.strip() for s in substs["categories"].splitlines()],
+ [
+ '{ { 0x0 } /* "dummy1" */,',
+ "0, 2 },",
+ ],
+ )
+ self.assertEqual(
+ [s.strip() for s in substs["category_entries"].splitlines()],
+ [
+ '/* "dummy1" */',
+ '{ { 0x7 } /* "m-dummy1" */,',
+ '{ 0x10 } /* "@mozilla.org/dummy-class;1" */,',
+ "Module::BackgroundTasksSelector::NO_TASKS,",
+ "Module::ProcessSelector::ANY_PROCESS },",
+ '{ { 0x2b } /* "m-dummy2" */,',
+ '{ 0x10 } /* "@mozilla.org/dummy-class;1" */,',
+ "Module::BackgroundTasksSelector::NO_TASKS,",
+ "Module::ProcessSelector::ANY_PROCESS },",
+ ],
+ )
+
+ def test_dict(self):
+ # A dict, but no backgroundtasks selector: we default to NO_TASKS.
+ clas = {
+ "cid": "{a8566880-0bc7-4822-adb9-748c9af5cce7}",
+ "contract_ids": ["@mozilla.org/dummy-class;1"],
+ "jsm": "resource:///modules/DummyClass.jsm",
+ "js_name": "dummyClass",
+ "constructor": "DummyClassImpl",
+ "categories": {
+ "dummy1": {
+ "name": ["m-dummy1", "m-dummy2"],
+ },
+ },
+ "protocol_config": {
+ "scheme": "dummy",
+ "flags": [],
+ },
+ }
+
+ substs = gen_static_components.gen_substs([{"Classes": [clas]}])
+
+ self.assertEqual(substs["category_count"], 1)
+ self.assertEqual(
+ [s.strip() for s in substs["categories"].splitlines()],
+ [
+ '{ { 0x0 } /* "dummy1" */,',
+ "0, 2 },",
+ ],
+ )
+ self.assertEqual(
+ [s.strip() for s in substs["category_entries"].splitlines()],
+ [
+ '/* "dummy1" */',
+ '{ { 0x7 } /* "m-dummy1" */,',
+ '{ 0x10 } /* "@mozilla.org/dummy-class;1" */,',
+ "Module::BackgroundTasksSelector::NO_TASKS,",
+ "Module::ProcessSelector::ANY_PROCESS },",
+ '{ { 0x2b } /* "m-dummy2" */,',
+ '{ 0x10 } /* "@mozilla.org/dummy-class;1" */,',
+ "Module::BackgroundTasksSelector::NO_TASKS,",
+ "Module::ProcessSelector::ANY_PROCESS },",
+ ],
+ )
+
+ def test_dict_with_selector(self):
+ # A dict with a selector.
+ clas = {
+ "cid": "{a8566880-0bc7-4822-adb9-748c9af5cce7}",
+ "contract_ids": ["@mozilla.org/dummy-class;1"],
+ "jsm": "resource:///modules/DummyClass.jsm",
+ "js_name": "dummyClass",
+ "constructor": "DummyClassImpl",
+ "categories": {
+ "dummy1": {
+ "name": ["m-dummy1", "m-dummy2"],
+ "backgroundtasks": BackgroundTasksSelector.ALL_TASKS,
+ },
+ },
+ "protocol_config": {
+ "scheme": "dummy",
+ "flags": [],
+ },
+ }
+
+ substs = gen_static_components.gen_substs([{"Classes": [clas]}])
+
+ self.assertEqual(substs["category_count"], 1)
+ self.assertEqual(
+ [s.strip() for s in substs["categories"].splitlines()],
+ [
+ '{ { 0x0 } /* "dummy1" */,',
+ "0, 2 },",
+ ],
+ )
+ self.assertEqual(
+ [s.strip() for s in substs["category_entries"].splitlines()],
+ [
+ '/* "dummy1" */',
+ '{ { 0x7 } /* "m-dummy1" */,',
+ '{ 0x10 } /* "@mozilla.org/dummy-class;1" */,',
+ "Module::BackgroundTasksSelector::ALL_TASKS,",
+ "Module::ProcessSelector::ANY_PROCESS },",
+ '{ { 0x2b } /* "m-dummy2" */,',
+ '{ 0x10 } /* "@mozilla.org/dummy-class;1" */,',
+ "Module::BackgroundTasksSelector::ALL_TASKS,",
+ "Module::ProcessSelector::ANY_PROCESS },",
+ ],
+ )
+
+
+if __name__ == "__main__":
+ mozunit.main()