diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /xpcom/components | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | uxp-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz |
Add m-esr52 at 52.6.0
Diffstat (limited to 'xpcom/components')
-rw-r--r-- | xpcom/components/ManifestParser.cpp | 792 | ||||
-rw-r--r-- | xpcom/components/ManifestParser.h | 22 | ||||
-rw-r--r-- | xpcom/components/Module.h | 157 | ||||
-rw-r--r-- | xpcom/components/ModuleLoader.h | 44 | ||||
-rw-r--r-- | xpcom/components/ModuleUtils.h | 98 | ||||
-rw-r--r-- | xpcom/components/moz.build | 55 | ||||
-rw-r--r-- | xpcom/components/nsCategoryManager.cpp | 832 | ||||
-rw-r--r-- | xpcom/components/nsCategoryManager.h | 146 | ||||
-rw-r--r-- | xpcom/components/nsCategoryManagerUtils.h | 18 | ||||
-rw-r--r-- | xpcom/components/nsComponentManager.cpp | 2083 | ||||
-rw-r--r-- | xpcom/components/nsComponentManager.h | 363 | ||||
-rw-r--r-- | xpcom/components/nsICategoryManager.idl | 69 | ||||
-rw-r--r-- | xpcom/components/nsIClassInfo.idl | 87 | ||||
-rw-r--r-- | xpcom/components/nsIComponentManager.idl | 106 | ||||
-rw-r--r-- | xpcom/components/nsIComponentRegistrar.idl | 163 | ||||
-rw-r--r-- | xpcom/components/nsIFactory.idl | 42 | ||||
-rw-r--r-- | xpcom/components/nsIModule.idl | 82 | ||||
-rw-r--r-- | xpcom/components/nsIServiceManager.idl | 72 | ||||
-rw-r--r-- | xpcom/components/nsNativeModuleLoader.cpp | 220 | ||||
-rw-r--r-- | xpcom/components/nsNativeModuleLoader.h | 39 |
20 files changed, 5490 insertions, 0 deletions
diff --git a/xpcom/components/ManifestParser.cpp b/xpcom/components/ManifestParser.cpp new file mode 100644 index 0000000000..4dcfc8402c --- /dev/null +++ b/xpcom/components/ManifestParser.cpp @@ -0,0 +1,792 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" + +#include "ManifestParser.h" + +#include <string.h> + +#include "prio.h" +#include "prprf.h" +#if defined(XP_WIN) +#include <windows.h> +#elif defined(MOZ_WIDGET_COCOA) +#include <CoreServices/CoreServices.h> +#include "nsCocoaFeatures.h" +#elif defined(MOZ_WIDGET_GTK) +#include <gtk/gtk.h> +#endif + +#ifdef MOZ_WIDGET_ANDROID +#include "AndroidBridge.h" +#endif + +#include "mozilla/Services.h" + +#include "nsCRT.h" +#include "nsConsoleMessage.h" +#include "nsTextFormatter.h" +#include "nsVersionComparator.h" +#include "nsXPCOMCIDInternal.h" + +#include "nsIConsoleService.h" +#include "nsIScriptError.h" +#include "nsIXULAppInfo.h" +#include "nsIXULRuntime.h" + +using namespace mozilla; + +struct ManifestDirective +{ + const char* directive; + int argc; + + // Binary components are only allowed for APP locations. + bool apponly; + + // Some directives should only be delivered for APP or EXTENSION locations. + bool componentonly; + + bool ischrome; + + bool allowbootstrap; + + // The platform/contentaccessible flags only apply to content directives. + bool contentflags; + + // Function to handle this directive. This isn't a union because C++ still + // hasn't learned how to initialize unions in a sane way. + void (nsComponentManagerImpl::*mgrfunc)( + nsComponentManagerImpl::ManifestProcessingContext& aCx, + int aLineNo, char* const* aArgv); + void (nsChromeRegistry::*regfunc)( + nsChromeRegistry::ManifestProcessingContext& aCx, + int aLineNo, char* const* aArgv, int aFlags); + void* xptonlyfunc; + + bool isContract; +}; +static const ManifestDirective kParsingTable[] = { + { + "manifest", 1, false, false, true, true, false, + &nsComponentManagerImpl::ManifestManifest, nullptr, nullptr + }, + { + "binary-component", 1, true, true, false, false, false, + &nsComponentManagerImpl::ManifestBinaryComponent, nullptr, nullptr + }, + { + "interfaces", 1, false, true, false, false, false, + &nsComponentManagerImpl::ManifestXPT, nullptr, nullptr + }, + { + "component", 2, false, true, false, false, false, + &nsComponentManagerImpl::ManifestComponent, nullptr, nullptr + }, + { + "contract", 2, false, true, false, false, false, + &nsComponentManagerImpl::ManifestContract, nullptr, nullptr, true + }, + { + "category", 3, false, true, false, false, false, + &nsComponentManagerImpl::ManifestCategory, nullptr, nullptr + }, + { + "content", 2, false, true, true, true, true, + nullptr, &nsChromeRegistry::ManifestContent, nullptr + }, + { + "locale", 3, false, true, true, true, false, + nullptr, &nsChromeRegistry::ManifestLocale, nullptr + }, + { + "skin", 3, false, false, true, true, false, + nullptr, &nsChromeRegistry::ManifestSkin, nullptr + }, + { + "overlay", 2, false, true, true, false, false, + nullptr, &nsChromeRegistry::ManifestOverlay, nullptr + }, + { + "style", 2, false, false, true, false, false, + nullptr, &nsChromeRegistry::ManifestStyle, nullptr + }, + { + // NB: note that while skin manifests can use this, they are only allowed + // to use it for chrome://../skin/ URLs + "override", 2, false, false, true, true, false, + nullptr, &nsChromeRegistry::ManifestOverride, nullptr + }, + { + "resource", 2, false, true, true, true, false, + nullptr, &nsChromeRegistry::ManifestResource, nullptr + } +}; + +static const char kWhitespace[] = "\t "; + +static bool +IsNewline(char aChar) +{ + return aChar == '\n' || aChar == '\r'; +} + +namespace { +struct AutoPR_smprintf_free +{ + explicit AutoPR_smprintf_free(char* aBuf) : mBuf(aBuf) {} + + ~AutoPR_smprintf_free() + { + if (mBuf) { + PR_smprintf_free(mBuf); + } + } + + operator char*() const { return mBuf; } + + char* mBuf; +}; + +} // namespace + +/** + * If we are pre-loading XPTs, this method may do nothing because the + * console service is not initialized. + */ +void +LogMessage(const char* aMsg, ...) +{ + if (!nsComponentManagerImpl::gComponentManager) { + return; + } + + nsCOMPtr<nsIConsoleService> console = + do_GetService(NS_CONSOLESERVICE_CONTRACTID); + if (!console) { + return; + } + + va_list args; + va_start(args, aMsg); + AutoPR_smprintf_free formatted(PR_vsmprintf(aMsg, args)); + va_end(args); + + nsCOMPtr<nsIConsoleMessage> error = + new nsConsoleMessage(NS_ConvertUTF8toUTF16(formatted).get()); + console->LogMessage(error); +} + +/** + * If we are pre-loading XPTs, this method may do nothing because the + * console service is not initialized. + */ +void +LogMessageWithContext(FileLocation& aFile, + uint32_t aLineNumber, const char* aMsg, ...) +{ + va_list args; + va_start(args, aMsg); + AutoPR_smprintf_free formatted(PR_vsmprintf(aMsg, args)); + va_end(args); + if (!formatted) { + return; + } + + if (!nsComponentManagerImpl::gComponentManager) { + return; + } + + nsCString file; + aFile.GetURIString(file); + + nsCOMPtr<nsIScriptError> error = + do_CreateInstance(NS_SCRIPTERROR_CONTRACTID); + if (!error) { + // This can happen early in component registration. Fall back to a + // generic console message. + LogMessage("Warning: in '%s', line %i: %s", file.get(), + aLineNumber, (char*)formatted); + return; + } + + nsCOMPtr<nsIConsoleService> console = + do_GetService(NS_CONSOLESERVICE_CONTRACTID); + if (!console) { + return; + } + + nsresult rv = error->Init(NS_ConvertUTF8toUTF16(formatted), + NS_ConvertUTF8toUTF16(file), EmptyString(), + aLineNumber, 0, nsIScriptError::warningFlag, + "chrome registration"); + if (NS_FAILED(rv)) { + return; + } + + console->LogMessage(error); +} + +/** + * Check for a modifier flag of the following forms: + * "flag" (same as "true") + * "flag=yes|true|1" + * "flag="no|false|0" + * @param aFlag The flag to compare. + * @param aData The tokenized data to check; this is lowercased + * before being passed in. + * @param aResult If the flag is found, the value is assigned here. + * @return Whether the flag was handled. + */ +static bool +CheckFlag(const nsSubstring& aFlag, const nsSubstring& aData, bool& aResult) +{ + if (!StringBeginsWith(aData, aFlag)) { + return false; + } + + if (aFlag.Length() == aData.Length()) { + // the data is simply "flag", which is the same as "flag=yes" + aResult = true; + return true; + } + + if (aData.CharAt(aFlag.Length()) != '=') { + // the data is "flag2=", which is not anything we care about + return false; + } + + if (aData.Length() == aFlag.Length() + 1) { + aResult = false; + return true; + } + + switch (aData.CharAt(aFlag.Length() + 1)) { + case '1': + case 't': //true + case 'y': //yes + aResult = true; + return true; + + case '0': + case 'f': //false + case 'n': //no + aResult = false; + return true; + } + + return false; +} + +enum TriState +{ + eUnspecified, + eBad, + eOK +}; + +/** + * Check for a modifier flag of the following form: + * "flag=string" + * "flag!=string" + * @param aFlag The flag to compare. + * @param aData The tokenized data to check; this is lowercased + * before being passed in. + * @param aValue The value that is expected. + * @param aResult If this is "ok" when passed in, this is left alone. + * Otherwise if the flag is found it is set to eBad or eOK. + * @return Whether the flag was handled. + */ +static bool +CheckStringFlag(const nsSubstring& aFlag, const nsSubstring& aData, + const nsSubstring& aValue, TriState& aResult) +{ + if (aData.Length() < aFlag.Length() + 1) { + return false; + } + + if (!StringBeginsWith(aData, aFlag)) { + return false; + } + + bool comparison = true; + if (aData[aFlag.Length()] != '=') { + if (aData[aFlag.Length()] == '!' && + aData.Length() >= aFlag.Length() + 2 && + aData[aFlag.Length() + 1] == '=') { + comparison = false; + } else { + return false; + } + } + + if (aResult != eOK) { + nsDependentSubstring testdata = + Substring(aData, aFlag.Length() + (comparison ? 1 : 2)); + if (testdata.Equals(aValue)) { + aResult = comparison ? eOK : eBad; + } else { + aResult = comparison ? eBad : eOK; + } + } + + return true; +} + +/** + * Check for a modifier flag of the following form: + * "flag=version" + * "flag<=version" + * "flag<version" + * "flag>=version" + * "flag>version" + * @param aFlag The flag to compare. + * @param aData The tokenized data to check; this is lowercased + * before being passed in. + * @param aValue The value that is expected. If this is empty then no + * comparison will match. + * @param aResult If this is eOK when passed in, this is left alone. + * Otherwise if the flag is found it is set to eBad or eOK. + * @return Whether the flag was handled. + */ + +#define COMPARE_EQ 1 << 0 +#define COMPARE_LT 1 << 1 +#define COMPARE_GT 1 << 2 + +static bool +CheckVersionFlag(const nsString& aFlag, const nsString& aData, + const nsString& aValue, TriState& aResult) +{ + if (aData.Length() < aFlag.Length() + 2) { + return false; + } + + if (!StringBeginsWith(aData, aFlag)) { + return false; + } + + if (aValue.Length() == 0) { + if (aResult != eOK) { + aResult = eBad; + } + return true; + } + + uint32_t comparison; + nsAutoString testdata; + + switch (aData[aFlag.Length()]) { + case '=': + comparison = COMPARE_EQ; + testdata = Substring(aData, aFlag.Length() + 1); + break; + + case '<': + if (aData[aFlag.Length() + 1] == '=') { + comparison = COMPARE_EQ | COMPARE_LT; + testdata = Substring(aData, aFlag.Length() + 2); + } else { + comparison = COMPARE_LT; + testdata = Substring(aData, aFlag.Length() + 1); + } + break; + + case '>': + if (aData[aFlag.Length() + 1] == '=') { + comparison = COMPARE_EQ | COMPARE_GT; + testdata = Substring(aData, aFlag.Length() + 2); + } else { + comparison = COMPARE_GT; + testdata = Substring(aData, aFlag.Length() + 1); + } + break; + + default: + return false; + } + + if (testdata.Length() == 0) { + return false; + } + + if (aResult != eOK) { + int32_t c = mozilla::CompareVersions(NS_ConvertUTF16toUTF8(aValue).get(), + NS_ConvertUTF16toUTF8(testdata).get()); + if ((c == 0 && comparison & COMPARE_EQ) || + (c < 0 && comparison & COMPARE_LT) || + (c > 0 && comparison & COMPARE_GT)) { + aResult = eOK; + } else { + aResult = eBad; + } + } + + return true; +} + +// In-place conversion of ascii characters to lower case +static void +ToLowerCase(char* aToken) +{ + for (; *aToken; ++aToken) { + *aToken = NS_ToLower(*aToken); + } +} + +namespace { + +struct CachedDirective +{ + int lineno; + char* argv[4]; +}; + +} // namespace + + +/** + * For XPT-Only mode, the parser handles only directives of "manifest" + * and "interfaces", and always call the function given by |xptonlyfunc| + * variable of struct |ManifestDirective|. + * + * This function is safe to be called before the component manager is + * ready if aXPTOnly is true for it don't invoke any component during + * parsing. + */ +void +ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf, + bool aChromeOnly, bool aXPTOnly) +{ + nsComponentManagerImpl::ManifestProcessingContext mgrcx(aType, aFile, + aChromeOnly); + nsChromeRegistry::ManifestProcessingContext chromecx(aType, aFile); + nsresult rv; + + NS_NAMED_LITERAL_STRING(kPlatform, "platform"); + NS_NAMED_LITERAL_STRING(kContentAccessible, "contentaccessible"); + NS_NAMED_LITERAL_STRING(kRemoteEnabled, "remoteenabled"); + NS_NAMED_LITERAL_STRING(kRemoteRequired, "remoterequired"); + NS_NAMED_LITERAL_STRING(kApplication, "application"); + NS_NAMED_LITERAL_STRING(kAppVersion, "appversion"); + NS_NAMED_LITERAL_STRING(kGeckoVersion, "platformversion"); + NS_NAMED_LITERAL_STRING(kOs, "os"); + NS_NAMED_LITERAL_STRING(kOsVersion, "osversion"); + NS_NAMED_LITERAL_STRING(kABI, "abi"); + NS_NAMED_LITERAL_STRING(kProcess, "process"); +#if defined(MOZ_WIDGET_ANDROID) + NS_NAMED_LITERAL_STRING(kTablet, "tablet"); +#endif + + NS_NAMED_LITERAL_STRING(kMain, "main"); + NS_NAMED_LITERAL_STRING(kContent, "content"); + + // Obsolete + NS_NAMED_LITERAL_STRING(kXPCNativeWrappers, "xpcnativewrappers"); + + nsAutoString appID; + nsAutoString appVersion; + nsAutoString geckoVersion; + nsAutoString osTarget; + nsAutoString abi; + nsAutoString process; + + nsCOMPtr<nsIXULAppInfo> xapp; + if (!aXPTOnly) { + // Avoid to create any component for XPT only mode. + // No xapp means no ID, version, ..., modifiers checking. + xapp = do_GetService(XULAPPINFO_SERVICE_CONTRACTID); + } + if (xapp) { + nsAutoCString s; + rv = xapp->GetID(s); + if (NS_SUCCEEDED(rv)) { + CopyUTF8toUTF16(s, appID); + } + + rv = xapp->GetVersion(s); + if (NS_SUCCEEDED(rv)) { + CopyUTF8toUTF16(s, appVersion); + } + + rv = xapp->GetPlatformVersion(s); + if (NS_SUCCEEDED(rv)) { + CopyUTF8toUTF16(s, geckoVersion); + } + + nsCOMPtr<nsIXULRuntime> xruntime(do_QueryInterface(xapp)); + if (xruntime) { + rv = xruntime->GetOS(s); + if (NS_SUCCEEDED(rv)) { + ToLowerCase(s); + CopyUTF8toUTF16(s, osTarget); + } + + rv = xruntime->GetXPCOMABI(s); + if (NS_SUCCEEDED(rv) && osTarget.Length()) { + ToLowerCase(s); + CopyUTF8toUTF16(s, abi); + abi.Insert(char16_t('_'), 0); + abi.Insert(osTarget, 0); + } + } + } + + nsAutoString osVersion; +#if defined(XP_WIN) +#pragma warning(push) +#pragma warning(disable:4996) // VC12+ deprecates GetVersionEx + OSVERSIONINFO info = { sizeof(OSVERSIONINFO) }; + if (GetVersionEx(&info)) { + nsTextFormatter::ssprintf(osVersion, u"%ld.%ld", + info.dwMajorVersion, + info.dwMinorVersion); + } +#pragma warning(pop) +#elif defined(MOZ_WIDGET_COCOA) + SInt32 majorVersion = nsCocoaFeatures::OSXVersionMajor(); + SInt32 minorVersion = nsCocoaFeatures::OSXVersionMinor(); + nsTextFormatter::ssprintf(osVersion, u"%ld.%ld", + majorVersion, + minorVersion); +#elif defined(MOZ_WIDGET_GTK) + nsTextFormatter::ssprintf(osVersion, u"%ld.%ld", + gtk_major_version, + gtk_minor_version); +#elif defined(MOZ_WIDGET_ANDROID) + bool isTablet = false; + if (mozilla::AndroidBridge::Bridge()) { + mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build$VERSION", + "RELEASE", + osVersion); + isTablet = java::GeckoAppShell::IsTablet(); + } +#endif + + if (XRE_IsContentProcess()) { + process = kContent; + } else { + process = kMain; + } + + // Because contracts must be registered after CIDs, we save and process them + // at the end. + nsTArray<CachedDirective> contracts; + + char* token; + char* newline = aBuf; + uint32_t line = 0; + + // outer loop tokenizes by newline + while (*newline) { + while (*newline && IsNewline(*newline)) { + ++newline; + ++line; + } + if (!*newline) { + break; + } + + token = newline; + while (*newline && !IsNewline(*newline)) { + ++newline; + } + + if (*newline) { + *newline = '\0'; + ++newline; + } + ++line; + + if (*token == '#') { // ignore lines that begin with # as comments + continue; + } + + char* whitespace = token; + token = nsCRT::strtok(whitespace, kWhitespace, &whitespace); + if (!token) { + continue; + } + + const ManifestDirective* directive = nullptr; + for (const ManifestDirective* d = kParsingTable; + d < ArrayEnd(kParsingTable); + ++d) { + if (!strcmp(d->directive, token) && + (!aXPTOnly || d->xptonlyfunc)) { + directive = d; + break; + } + } + + if (!directive) { + LogMessageWithContext(aFile, line, + "Ignoring unrecognized chrome manifest directive '%s'.", + token); + continue; + } + + if (!directive->allowbootstrap && NS_BOOTSTRAPPED_LOCATION == aType) { + LogMessageWithContext(aFile, line, + "Bootstrapped manifest not allowed to use '%s' directive.", + token); + continue; + } + +#ifndef MOZ_BINARY_EXTENSIONS + if (directive->apponly && NS_APP_LOCATION != aType) { + LogMessageWithContext(aFile, line, + "Only application manifests may use the '%s' directive.", token); + continue; + } +#endif + + if (directive->componentonly && NS_SKIN_LOCATION == aType) { + LogMessageWithContext(aFile, line, + "Skin manifest not allowed to use '%s' directive.", + token); + continue; + } + + NS_ASSERTION(directive->argc < 4, "Need to reset argv array length"); + char* argv[4]; + for (int i = 0; i < directive->argc; ++i) { + argv[i] = nsCRT::strtok(whitespace, kWhitespace, &whitespace); + } + + if (!argv[directive->argc - 1]) { + LogMessageWithContext(aFile, line, + "Not enough arguments for chrome manifest directive '%s', expected %i.", + token, directive->argc); + continue; + } + + bool ok = true; + TriState stAppVersion = eUnspecified; + TriState stGeckoVersion = eUnspecified; + TriState stApp = eUnspecified; + TriState stOsVersion = eUnspecified; + TriState stOs = eUnspecified; + TriState stABI = eUnspecified; + TriState stProcess = eUnspecified; +#if defined(MOZ_WIDGET_ANDROID) + TriState stTablet = eUnspecified; +#endif + int flags = 0; + + while ((token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) && + ok) { + ToLowerCase(token); + NS_ConvertASCIItoUTF16 wtoken(token); + + if (CheckStringFlag(kApplication, wtoken, appID, stApp) || + CheckStringFlag(kOs, wtoken, osTarget, stOs) || + CheckStringFlag(kABI, wtoken, abi, stABI) || + CheckStringFlag(kProcess, wtoken, process, stProcess) || + CheckVersionFlag(kOsVersion, wtoken, osVersion, stOsVersion) || + CheckVersionFlag(kAppVersion, wtoken, appVersion, stAppVersion) || + CheckVersionFlag(kGeckoVersion, wtoken, geckoVersion, stGeckoVersion)) { + continue; + } + +#if defined(MOZ_WIDGET_ANDROID) + bool tablet = false; + if (CheckFlag(kTablet, wtoken, tablet)) { + stTablet = (tablet == isTablet) ? eOK : eBad; + continue; + } +#endif + + if (directive->contentflags) { + bool flag; + if (CheckFlag(kPlatform, wtoken, flag)) { + if (flag) + flags |= nsChromeRegistry::PLATFORM_PACKAGE; + continue; + } + if (CheckFlag(kContentAccessible, wtoken, flag)) { + if (flag) + flags |= nsChromeRegistry::CONTENT_ACCESSIBLE; + continue; + } + if (CheckFlag(kRemoteEnabled, wtoken, flag)) { + if (flag) + flags |= nsChromeRegistry::REMOTE_ALLOWED; + continue; + } + if (CheckFlag(kRemoteRequired, wtoken, flag)) { + if (flag) + flags |= nsChromeRegistry::REMOTE_REQUIRED; + continue; + } + } + + bool xpcNativeWrappers = true; // Dummy for CheckFlag. + if (CheckFlag(kXPCNativeWrappers, wtoken, xpcNativeWrappers)) { + LogMessageWithContext(aFile, line, + "Ignoring obsolete chrome registration modifier '%s'.", + token); + continue; + } + + LogMessageWithContext(aFile, line, + "Unrecognized chrome manifest modifier '%s'.", + token); + ok = false; + } + + if (!ok || + stApp == eBad || + stAppVersion == eBad || + stGeckoVersion == eBad || + stOs == eBad || + stOsVersion == eBad || +#ifdef MOZ_WIDGET_ANDROID + stTablet == eBad || +#endif + stABI == eBad || + stProcess == eBad) { + continue; + } + + if (directive->regfunc) { + if (GeckoProcessType_Default != XRE_GetProcessType()) { + continue; + } + + if (!nsChromeRegistry::gChromeRegistry) { + nsCOMPtr<nsIChromeRegistry> cr = + mozilla::services::GetChromeRegistryService(); + if (!nsChromeRegistry::gChromeRegistry) { + LogMessageWithContext(aFile, line, + "Chrome registry isn't available yet."); + continue; + } + } + + (nsChromeRegistry::gChromeRegistry->*(directive->regfunc))( + chromecx, line, argv, flags); + } else if (directive->ischrome || !aChromeOnly) { + if (directive->isContract) { + CachedDirective* cd = contracts.AppendElement(); + cd->lineno = line; + cd->argv[0] = argv[0]; + cd->argv[1] = argv[1]; + } else { + (nsComponentManagerImpl::gComponentManager->*(directive->mgrfunc))( + mgrcx, line, argv); + } + } + } + + for (uint32_t i = 0; i < contracts.Length(); ++i) { + CachedDirective& d = contracts[i]; + nsComponentManagerImpl::gComponentManager->ManifestContract(mgrcx, + d.lineno, + d.argv); + } +} diff --git a/xpcom/components/ManifestParser.h b/xpcom/components/ManifestParser.h new file mode 100644 index 0000000000..019ec326c0 --- /dev/null +++ b/xpcom/components/ManifestParser.h @@ -0,0 +1,22 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef ManifestParser_h +#define ManifestParser_h + +#include "nsComponentManager.h" +#include "nsChromeRegistry.h" +#include "mozilla/FileLocation.h" + +void ParseManifest(NSLocationType aType, mozilla::FileLocation& aFile, + char* aBuf, bool aChromeOnly, bool aXPTOnly = false); + +void LogMessage(const char* aMsg, ...); + +void LogMessageWithContext(mozilla::FileLocation& aFile, + uint32_t aLineNumber, const char* aMsg, ...); + +#endif // ManifestParser_h diff --git a/xpcom/components/Module.h b/xpcom/components/Module.h new file mode 100644 index 0000000000..21d07e82ee --- /dev/null +++ b/xpcom/components/Module.h @@ -0,0 +1,157 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_Module_h +#define mozilla_Module_h + +#include "nscore.h" +#include "nsID.h" +#include "nsIFactory.h" +#include "nsCOMPtr.h" // for already_AddRefed + +namespace mozilla { + +/** + * A module implements one or more XPCOM components. This structure is used + * for both binary and script modules, but the registration members + * (cids/contractids/categoryentries) are unused for modules which are loaded + * via a module loader. + */ +struct Module +{ + static const unsigned int kVersion = 52; + + struct CIDEntry; + + typedef already_AddRefed<nsIFactory> (*GetFactoryProcPtr)( + const Module& module, const CIDEntry& entry); + + typedef nsresult (*ConstructorProcPtr)(nsISupports* aOuter, + const nsIID& aIID, + void** aResult); + + typedef nsresult (*LoadFuncPtr)(); + typedef void (*UnloadFuncPtr)(); + + /** + * This selector allows CIDEntrys to be marked so that they're only loaded + * into certain kinds of processes. Selectors can be combined. + */ + enum ProcessSelector + { + ANY_PROCESS = 0x0, + MAIN_PROCESS_ONLY = 0x1, + CONTENT_PROCESS_ONLY = 0x2, + + /** + * By default, modules are not loaded in the GPU process, even if + * ANY_PROCESS is specified. This flag enables a module in the + * GPU process. + */ + ALLOW_IN_GPU_PROCESS = 0x4 + }; + + /** + * The constructor callback is an implementation detail of the default binary + * loader and may be null. + */ + struct CIDEntry + { + const nsCID* cid; + bool service; + GetFactoryProcPtr getFactoryProc; + ConstructorProcPtr constructorProc; + ProcessSelector processSelector; + }; + + struct ContractIDEntry + { + const char* contractid; + nsID const* cid; + ProcessSelector processSelector; + }; + + struct CategoryEntry + { + const char* category; + const char* entry; + const char* value; + }; + + /** + * Binary compatibility check, should be kModuleVersion. + */ + unsigned int mVersion; + + /** + * An array of CIDs (class IDs) implemented by this module. The final entry + * should be { nullptr }. + */ + const CIDEntry* mCIDs; + + /** + * An array of mappings from contractid to CID. The final entry should + * be { nullptr }. + */ + const ContractIDEntry* mContractIDs; + + /** + * An array of category manager entries. The final entry should be + * { nullptr }. + */ + const CategoryEntry* mCategoryEntries; + + /** + * When the component manager tries to get the factory for a CID, it first + * checks for this module-level getfactory callback. If this function is + * not implemented, it checks the CIDEntry getfactory callback. If that is + * also nullptr, a generic factory is generated using the CIDEntry + * constructor callback which must be non-nullptr. + */ + GetFactoryProcPtr getFactoryProc; + + /** + * Optional Function which are called when this module is loaded and + * at shutdown. These are not C++ constructor/destructors to avoid + * calling them too early in startup or too late in shutdown. + */ + LoadFuncPtr loadProc; + UnloadFuncPtr unloadProc; + + /** + * Optional flags which control whether the module loads on a process-type + * basis. + */ + ProcessSelector selector; +}; + +} // namespace mozilla + +#if defined(MOZILLA_INTERNAL_API) +# define NSMODULE_NAME(_name) _name##_NSModule +# if defined(_MSC_VER) +# pragma section(".kPStaticModules$M", read) +# pragma comment(linker, "/merge:.kPStaticModules=.rdata") +# define NSMODULE_SECTION __declspec(allocate(".kPStaticModules$M"), dllexport) +# elif defined(__GNUC__) +# if defined(__ELF__) +# define NSMODULE_SECTION __attribute__((section(".kPStaticModules"), visibility("default"))) +# elif defined(__MACH__) +# define NSMODULE_SECTION __attribute__((section("__DATA, .kPStaticModules"), visibility("default"))) +# elif defined (_WIN32) +# define NSMODULE_SECTION __attribute__((section(".kPStaticModules"), dllexport)) +# endif +# endif +# if !defined(NSMODULE_SECTION) +# error Do not know how to define sections. +# endif +# define NSMODULE_DEFN(_name) extern NSMODULE_SECTION mozilla::Module const *const NSMODULE_NAME(_name) +#else +# define NSMODULE_NAME(_name) NSModule +# define NSMODULE_DEFN(_name) extern "C" NS_EXPORT mozilla::Module const *const NSModule +#endif + +#endif // mozilla_Module_h diff --git a/xpcom/components/ModuleLoader.h b/xpcom/components/ModuleLoader.h new file mode 100644 index 0000000000..c2c920d4da --- /dev/null +++ b/xpcom/components/ModuleLoader.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_ModuleLoader_h +#define mozilla_ModuleLoader_h + +#include "nsISupports.h" +#include "mozilla/Module.h" +#include "mozilla/FileLocation.h" + +#define MOZILLA_MODULELOADER_PSEUDO_IID \ +{ 0xD951A8CE, 0x6E9F, 0x464F, \ + { 0x8A, 0xC8, 0x14, 0x61, 0xC0, 0xD3, 0x63, 0xC8 } } + +namespace mozilla { + +/** + * Module loaders are responsible for loading a component file. The static + * component loader is special and does not use this abstract interface. + * + * @note Implementations of this interface should be threadsafe, + * methods may be called from any thread. + */ +class ModuleLoader : public nsISupports +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_MODULELOADER_PSEUDO_IID) + + /** + * Return the module for a specified file. The caller should cache + * the module: the implementer should not expect for the same file + * to be loaded multiple times. The Module object should either be + * statically or permanently allocated; it will not be freed. + */ + virtual const Module* LoadModule(mozilla::FileLocation& aFile) = 0; +}; +NS_DEFINE_STATIC_IID_ACCESSOR(ModuleLoader, MOZILLA_MODULELOADER_PSEUDO_IID) + +} // namespace mozilla + +#endif // mozilla_ModuleLoader_h diff --git a/xpcom/components/ModuleUtils.h b/xpcom/components/ModuleUtils.h new file mode 100644 index 0000000000..9c31ed76bd --- /dev/null +++ b/xpcom/components/ModuleUtils.h @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_GenericModule_h +#define mozilla_GenericModule_h + +#include "mozilla/Attributes.h" +#include "mozilla/Module.h" + +#define NS_GENERIC_FACTORY_CONSTRUCTOR(_InstanceClass) \ +static nsresult \ +_InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \ + void **aResult) \ +{ \ + RefPtr<_InstanceClass> inst; \ + \ + *aResult = nullptr; \ + if (nullptr != aOuter) { \ + return NS_ERROR_NO_AGGREGATION; \ + } \ + \ + inst = new _InstanceClass(); \ + return inst->QueryInterface(aIID, aResult); \ +} + +#define NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(_InstanceClass, _InitMethod) \ +static nsresult \ +_InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \ + void **aResult) \ +{ \ + nsresult rv; \ + \ + RefPtr<_InstanceClass> inst; \ + \ + *aResult = nullptr; \ + if (nullptr != aOuter) { \ + return NS_ERROR_NO_AGGREGATION; \ + } \ + \ + inst = new _InstanceClass(); \ + rv = inst->_InitMethod(); \ + if (NS_SUCCEEDED(rv)) { \ + rv = inst->QueryInterface(aIID, aResult); \ + } \ + \ + return rv; \ +} + +// 'Constructor' that uses an existing getter function that gets a singleton. +// NOTE: assumes that getter does an AddRef - so additional AddRef is not done. +#define NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(_InstanceClass, _GetterProc) \ +static nsresult \ +_InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \ + void **aResult) \ +{ \ + RefPtr<_InstanceClass> inst; \ + \ + *aResult = nullptr; \ + if (nullptr != aOuter) { \ + return NS_ERROR_NO_AGGREGATION; \ + } \ + \ + inst = already_AddRefed<_InstanceClass>(_GetterProc()); \ + if (nullptr == inst) { \ + return NS_ERROR_OUT_OF_MEMORY; \ + } \ + return inst->QueryInterface(aIID, aResult); \ +} + +#ifndef MOZILLA_INTERNAL_API + +#include "nsIModule.h" +#include "nsISupportsUtils.h" + +namespace mozilla { + +class GenericModule final : public nsIModule +{ + ~GenericModule() {} + +public: + explicit GenericModule(const mozilla::Module* aData) : mData(aData) {} + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIMODULE + +private: + const mozilla::Module* mData; +}; + +} // namespace mozilla + +#endif + +#endif // mozilla_GenericModule_h diff --git a/xpcom/components/moz.build b/xpcom/components/moz.build new file mode 100644 index 0000000000..73a85cb3b1 --- /dev/null +++ b/xpcom/components/moz.build @@ -0,0 +1,55 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +XPIDL_SOURCES += [ + 'nsICategoryManager.idl', + 'nsIClassInfo.idl', + 'nsIComponentManager.idl', + 'nsIComponentRegistrar.idl', + 'nsIFactory.idl', + 'nsIModule.idl', + 'nsIServiceManager.idl', +] + +XPIDL_MODULE = 'xpcom_components' + +EXPORTS += [ + 'nsCategoryManagerUtils.h', +] + +EXPORTS.mozilla += [ + 'Module.h', + 'ModuleLoader.h', + 'ModuleUtils.h', +] + +# nsCategoryManager.cpp and nsComponentManager.cpp cannot be built in +# unified mode because they use thea PL_ARENA_CONST_ALIGN_MASK macro +# with plarena.h. +SOURCES += [ + 'nsCategoryManager.cpp', + 'nsComponentManager.cpp', +] + +UNIFIED_SOURCES += [ + 'ManifestParser.cpp', + 'nsNativeModuleLoader.cpp', +] + +FINAL_LIBRARY = 'xul' + +LOCAL_INCLUDES += [ + '!..', + '../base', + '../build', + '../ds', + '../reflect/xptinfo', + '/chrome', + '/modules/libjar', +] + +if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']: + CXXFLAGS += CONFIG['TK_CFLAGS'] diff --git a/xpcom/components/nsCategoryManager.cpp b/xpcom/components/nsCategoryManager.cpp new file mode 100644 index 0000000000..527c787190 --- /dev/null +++ b/xpcom/components/nsCategoryManager.cpp @@ -0,0 +1,832 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define PL_ARENA_CONST_ALIGN_MASK 7 + +#include "nsICategoryManager.h" +#include "nsCategoryManager.h" + +#include "plarena.h" +#include "prio.h" +#include "prprf.h" +#include "prlock.h" +#include "nsCOMPtr.h" +#include "nsTHashtable.h" +#include "nsClassHashtable.h" +#include "nsIFactory.h" +#include "nsIStringEnumerator.h" +#include "nsSupportsPrimitives.h" +#include "nsComponentManagerUtils.h" +#include "nsServiceManagerUtils.h" +#include "nsIObserver.h" +#include "nsIObserverService.h" +#include "nsReadableUtils.h" +#include "nsCRT.h" +#include "nsQuickSort.h" +#include "nsEnumeratorUtils.h" +#include "nsThreadUtils.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/Services.h" + +#include "ManifestParser.h" +#include "nsISimpleEnumerator.h" + +using namespace mozilla; +class nsIComponentLoaderManager; + +/* + CategoryDatabase + contains 0 or more 1-1 mappings of string to Category + each Category contains 0 or more 1-1 mappings of string keys to string values + + In other words, the CategoryDatabase is a tree, whose root is a hashtable. + Internal nodes (or Categories) are hashtables. Leaf nodes are strings. + + The leaf strings are allocated in an arena, because we assume they're not + going to change much ;) +*/ + +#define NS_CATEGORYMANAGER_ARENA_SIZE (1024 * 8) + +// pulled in from nsComponentManager.cpp +char* ArenaStrdup(const char* aStr, PLArenaPool* aArena); + +// +// BaseStringEnumerator is subclassed by EntryEnumerator and +// CategoryEnumerator +// +class BaseStringEnumerator + : public nsISimpleEnumerator + , private nsIUTF8StringEnumerator +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSISIMPLEENUMERATOR + NS_DECL_NSIUTF8STRINGENUMERATOR + +protected: + // Callback function for NS_QuickSort to sort mArray + static int SortCallback(const void*, const void*, void*); + + BaseStringEnumerator() + : mArray(nullptr) + , mCount(0) + , mSimpleCurItem(0) + , mStringCurItem(0) + { + } + + // A virtual destructor is needed here because subclasses of + // BaseStringEnumerator do not implement their own Release() method. + + virtual ~BaseStringEnumerator() + { + delete [] mArray; + } + + void Sort(); + + const char** mArray; + uint32_t mCount; + uint32_t mSimpleCurItem; + uint32_t mStringCurItem; +}; + +NS_IMPL_ISUPPORTS(BaseStringEnumerator, nsISimpleEnumerator, + nsIUTF8StringEnumerator) + +NS_IMETHODIMP +BaseStringEnumerator::HasMoreElements(bool* aResult) +{ + *aResult = (mSimpleCurItem < mCount); + + return NS_OK; +} + +NS_IMETHODIMP +BaseStringEnumerator::GetNext(nsISupports** aResult) +{ + if (mSimpleCurItem >= mCount) { + return NS_ERROR_FAILURE; + } + + nsSupportsDependentCString* str = + new nsSupportsDependentCString(mArray[mSimpleCurItem++]); + if (!str) { + return NS_ERROR_OUT_OF_MEMORY; + } + + *aResult = str; + NS_ADDREF(*aResult); + return NS_OK; +} + +NS_IMETHODIMP +BaseStringEnumerator::HasMore(bool* aResult) +{ + *aResult = (mStringCurItem < mCount); + + return NS_OK; +} + +NS_IMETHODIMP +BaseStringEnumerator::GetNext(nsACString& aResult) +{ + if (mStringCurItem >= mCount) { + return NS_ERROR_FAILURE; + } + + aResult = nsDependentCString(mArray[mStringCurItem++]); + return NS_OK; +} + +int +BaseStringEnumerator::SortCallback(const void* aE1, const void* aE2, + void* /*unused*/) +{ + char const* const* s1 = reinterpret_cast<char const* const*>(aE1); + char const* const* s2 = reinterpret_cast<char const* const*>(aE2); + + return strcmp(*s1, *s2); +} + +void +BaseStringEnumerator::Sort() +{ + NS_QuickSort(mArray, mCount, sizeof(mArray[0]), SortCallback, nullptr); +} + +// +// EntryEnumerator is the wrapper that allows nsICategoryManager::EnumerateCategory +// +class EntryEnumerator + : public BaseStringEnumerator +{ +public: + static EntryEnumerator* Create(nsTHashtable<CategoryLeaf>& aTable); +}; + + +EntryEnumerator* +EntryEnumerator::Create(nsTHashtable<CategoryLeaf>& aTable) +{ + EntryEnumerator* enumObj = new EntryEnumerator(); + if (!enumObj) { + return nullptr; + } + + enumObj->mArray = new char const* [aTable.Count()]; + if (!enumObj->mArray) { + delete enumObj; + return nullptr; + } + + for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) { + CategoryLeaf* leaf = iter.Get(); + if (leaf->value) { + enumObj->mArray[enumObj->mCount++] = leaf->GetKey(); + } + } + + enumObj->Sort(); + + return enumObj; +} + + +// +// CategoryNode implementations +// + +CategoryNode* +CategoryNode::Create(PLArenaPool* aArena) +{ + return new (aArena) CategoryNode(); +} + +CategoryNode::~CategoryNode() +{ +} + +void* +CategoryNode::operator new(size_t aSize, PLArenaPool* aArena) +{ + void* p; + PL_ARENA_ALLOCATE(p, aArena, aSize); + return p; +} + +nsresult +CategoryNode::GetLeaf(const char* aEntryName, + char** aResult) +{ + MutexAutoLock lock(mLock); + nsresult rv = NS_ERROR_NOT_AVAILABLE; + CategoryLeaf* ent = mTable.GetEntry(aEntryName); + + if (ent && ent->value) { + *aResult = NS_strdup(ent->value); + if (*aResult) { + rv = NS_OK; + } + } + + return rv; +} + +nsresult +CategoryNode::AddLeaf(const char* aEntryName, + const char* aValue, + bool aReplace, + char** aResult, + PLArenaPool* aArena) +{ + if (aResult) { + *aResult = nullptr; + } + + MutexAutoLock lock(mLock); + CategoryLeaf* leaf = mTable.GetEntry(aEntryName); + + if (!leaf) { + const char* arenaEntryName = ArenaStrdup(aEntryName, aArena); + if (!arenaEntryName) { + return NS_ERROR_OUT_OF_MEMORY; + } + + leaf = mTable.PutEntry(arenaEntryName); + if (!leaf) { + return NS_ERROR_OUT_OF_MEMORY; + } + } + + if (leaf->value && !aReplace) { + return NS_ERROR_INVALID_ARG; + } + + const char* arenaValue = ArenaStrdup(aValue, aArena); + if (!arenaValue) { + return NS_ERROR_OUT_OF_MEMORY; + } + + if (aResult && leaf->value) { + *aResult = ToNewCString(nsDependentCString(leaf->value)); + if (!*aResult) { + return NS_ERROR_OUT_OF_MEMORY; + } + } + + leaf->value = arenaValue; + return NS_OK; +} + +void +CategoryNode::DeleteLeaf(const char* aEntryName) +{ + // we don't throw any errors, because it normally doesn't matter + // and it makes JS a lot cleaner + MutexAutoLock lock(mLock); + + // we can just remove the entire hash entry without introspection + mTable.RemoveEntry(aEntryName); +} + +nsresult +CategoryNode::Enumerate(nsISimpleEnumerator** aResult) +{ + if (NS_WARN_IF(!aResult)) { + return NS_ERROR_INVALID_ARG; + } + + MutexAutoLock lock(mLock); + EntryEnumerator* enumObj = EntryEnumerator::Create(mTable); + + if (!enumObj) { + return NS_ERROR_OUT_OF_MEMORY; + } + + *aResult = enumObj; + NS_ADDREF(*aResult); + return NS_OK; +} + +size_t +CategoryNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) +{ + // We don't measure the strings pointed to by the entries because the + // pointers are non-owning. + return mTable.ShallowSizeOfExcludingThis(aMallocSizeOf); +} + +// +// CategoryEnumerator class +// + +class CategoryEnumerator + : public BaseStringEnumerator +{ +public: + static CategoryEnumerator* Create(nsClassHashtable<nsDepCharHashKey, + CategoryNode>& aTable); +}; + +CategoryEnumerator* +CategoryEnumerator::Create(nsClassHashtable<nsDepCharHashKey, CategoryNode>& + aTable) +{ + CategoryEnumerator* enumObj = new CategoryEnumerator(); + if (!enumObj) { + return nullptr; + } + + enumObj->mArray = new const char* [aTable.Count()]; + if (!enumObj->mArray) { + delete enumObj; + return nullptr; + } + + for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) { + // if a category has no entries, we pretend it doesn't exist + CategoryNode* aNode = iter.UserData(); + if (aNode->Count()) { + const char* str = iter.Key(); + enumObj->mArray[enumObj->mCount++] = str; + } + } + + return enumObj; +} + + +// +// nsCategoryManager implementations +// + +NS_IMPL_QUERY_INTERFACE(nsCategoryManager, nsICategoryManager, nsIMemoryReporter) + +NS_IMETHODIMP_(MozExternalRefCountType) +nsCategoryManager::AddRef() +{ + return 2; +} + +NS_IMETHODIMP_(MozExternalRefCountType) +nsCategoryManager::Release() +{ + return 1; +} + +nsCategoryManager* nsCategoryManager::gCategoryManager; + +/* static */ nsCategoryManager* +nsCategoryManager::GetSingleton() +{ + if (!gCategoryManager) { + gCategoryManager = new nsCategoryManager(); + } + return gCategoryManager; +} + +/* static */ void +nsCategoryManager::Destroy() +{ + // The nsMemoryReporterManager gets destroyed before the nsCategoryManager, + // so we don't need to unregister the nsCategoryManager as a memory reporter. + // In debug builds we assert that unregistering fails, as a way (imperfect + // but better than nothing) of testing the "destroyed before" part. + MOZ_ASSERT(NS_FAILED(UnregisterWeakMemoryReporter(gCategoryManager))); + + delete gCategoryManager; + gCategoryManager = nullptr; +} + +nsresult +nsCategoryManager::Create(nsISupports* aOuter, REFNSIID aIID, void** aResult) +{ + if (aOuter) { + return NS_ERROR_NO_AGGREGATION; + } + + return GetSingleton()->QueryInterface(aIID, aResult); +} + +nsCategoryManager::nsCategoryManager() + : mLock("nsCategoryManager") + , mSuppressNotifications(false) +{ + PL_INIT_ARENA_POOL(&mArena, "CategoryManagerArena", + NS_CATEGORYMANAGER_ARENA_SIZE); +} + +void +nsCategoryManager::InitMemoryReporter() +{ + RegisterWeakMemoryReporter(this); +} + +nsCategoryManager::~nsCategoryManager() +{ + // the hashtable contains entries that must be deleted before the arena is + // destroyed, or else you will have PRLocks undestroyed and other Really + // Bad Stuff (TM) + mTable.Clear(); + + PL_FinishArenaPool(&mArena); +} + +inline CategoryNode* +nsCategoryManager::get_category(const char* aName) +{ + CategoryNode* node; + if (!mTable.Get(aName, &node)) { + return nullptr; + } + return node; +} + +MOZ_DEFINE_MALLOC_SIZE_OF(CategoryManagerMallocSizeOf) + +NS_IMETHODIMP +nsCategoryManager::CollectReports(nsIHandleReportCallback* aHandleReport, + nsISupports* aData, bool aAnonymize) +{ + MOZ_COLLECT_REPORT( + "explicit/xpcom/category-manager", KIND_HEAP, UNITS_BYTES, + SizeOfIncludingThis(CategoryManagerMallocSizeOf), + "Memory used for the XPCOM category manager."); + + return NS_OK; +} + +size_t +nsCategoryManager::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) +{ + size_t n = aMallocSizeOf(this); + + n += PL_SizeOfArenaPoolExcludingPool(&mArena, aMallocSizeOf); + + n += mTable.ShallowSizeOfExcludingThis(aMallocSizeOf); + for (auto iter = mTable.ConstIter(); !iter.Done(); iter.Next()) { + // We don't measure the key string because it's a non-owning pointer. + n += iter.Data()->SizeOfExcludingThis(aMallocSizeOf); + } + + return n; +} + +namespace { + +class CategoryNotificationRunnable : public Runnable +{ +public: + CategoryNotificationRunnable(nsISupports* aSubject, + const char* aTopic, + const char* aData) + : mSubject(aSubject) + , mTopic(aTopic) + , mData(aData) + { + } + + NS_DECL_NSIRUNNABLE + +private: + nsCOMPtr<nsISupports> mSubject; + const char* mTopic; + NS_ConvertUTF8toUTF16 mData; +}; + +NS_IMETHODIMP +CategoryNotificationRunnable::Run() +{ + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + if (observerService) { + observerService->NotifyObservers(mSubject, mTopic, mData.get()); + } + + return NS_OK; +} + +} // namespace + + +void +nsCategoryManager::NotifyObservers(const char* aTopic, + const char* aCategoryName, + const char* aEntryName) +{ + if (mSuppressNotifications) { + return; + } + + RefPtr<CategoryNotificationRunnable> r; + + if (aEntryName) { + nsCOMPtr<nsISupportsCString> entry = + do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID); + if (!entry) { + return; + } + + nsresult rv = entry->SetData(nsDependentCString(aEntryName)); + if (NS_FAILED(rv)) { + return; + } + + r = new CategoryNotificationRunnable(entry, aTopic, aCategoryName); + } else { + r = new CategoryNotificationRunnable(NS_ISUPPORTS_CAST(nsICategoryManager*, + this), + aTopic, aCategoryName); + } + + NS_DispatchToMainThread(r); +} + +NS_IMETHODIMP +nsCategoryManager::GetCategoryEntry(const char* aCategoryName, + const char* aEntryName, + char** aResult) +{ + if (NS_WARN_IF(!aCategoryName) || + NS_WARN_IF(!aEntryName) || + NS_WARN_IF(!aResult)) { + return NS_ERROR_INVALID_ARG; + } + + nsresult status = NS_ERROR_NOT_AVAILABLE; + + CategoryNode* category; + { + MutexAutoLock lock(mLock); + category = get_category(aCategoryName); + } + + if (category) { + status = category->GetLeaf(aEntryName, aResult); + } + + return status; +} + +NS_IMETHODIMP +nsCategoryManager::AddCategoryEntry(const char* aCategoryName, + const char* aEntryName, + const char* aValue, + bool aPersist, + bool aReplace, + char** aResult) +{ + if (aPersist) { + NS_ERROR("Category manager doesn't support persistence."); + return NS_ERROR_INVALID_ARG; + } + + AddCategoryEntry(aCategoryName, aEntryName, aValue, aReplace, aResult); + return NS_OK; +} + +void +nsCategoryManager::AddCategoryEntry(const char* aCategoryName, + const char* aEntryName, + const char* aValue, + bool aReplace, + char** aOldValue) +{ + if (aOldValue) { + *aOldValue = nullptr; + } + + // Before we can insert a new entry, we'll need to + // find the |CategoryNode| to put it in... + CategoryNode* category; + { + MutexAutoLock lock(mLock); + category = get_category(aCategoryName); + + if (!category) { + // That category doesn't exist yet; let's make it. + category = CategoryNode::Create(&mArena); + + char* categoryName = ArenaStrdup(aCategoryName, &mArena); + mTable.Put(categoryName, category); + } + } + + if (!category) { + return; + } + + // We will need the return value of AddLeaf even if the called doesn't want it + char* oldEntry = nullptr; + + nsresult rv = category->AddLeaf(aEntryName, + aValue, + aReplace, + &oldEntry, + &mArena); + + if (NS_SUCCEEDED(rv)) { + if (oldEntry) { + NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID, + aCategoryName, aEntryName); + } + NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID, + aCategoryName, aEntryName); + + if (aOldValue) { + *aOldValue = oldEntry; + } else { + free(oldEntry); + } + } +} + +NS_IMETHODIMP +nsCategoryManager::DeleteCategoryEntry(const char* aCategoryName, + const char* aEntryName, + bool aDontPersist) +{ + if (NS_WARN_IF(!aCategoryName) || + NS_WARN_IF(!aEntryName)) { + return NS_ERROR_INVALID_ARG; + } + + /* + Note: no errors are reported since failure to delete + probably won't hurt you, and returning errors seriously + inconveniences JS clients + */ + + CategoryNode* category; + { + MutexAutoLock lock(mLock); + category = get_category(aCategoryName); + } + + if (category) { + category->DeleteLeaf(aEntryName); + + NotifyObservers(NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID, + aCategoryName, aEntryName); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsCategoryManager::DeleteCategory(const char* aCategoryName) +{ + if (NS_WARN_IF(!aCategoryName)) { + return NS_ERROR_INVALID_ARG; + } + + // the categories are arena-allocated, so we don't + // actually delete them. We just remove all of the + // leaf nodes. + + CategoryNode* category; + { + MutexAutoLock lock(mLock); + category = get_category(aCategoryName); + } + + if (category) { + category->Clear(); + NotifyObservers(NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID, + aCategoryName, nullptr); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsCategoryManager::EnumerateCategory(const char* aCategoryName, + nsISimpleEnumerator** aResult) +{ + if (NS_WARN_IF(!aCategoryName) || + NS_WARN_IF(!aResult)) { + return NS_ERROR_INVALID_ARG; + } + + CategoryNode* category; + { + MutexAutoLock lock(mLock); + category = get_category(aCategoryName); + } + + if (!category) { + return NS_NewEmptyEnumerator(aResult); + } + + return category->Enumerate(aResult); +} + +NS_IMETHODIMP +nsCategoryManager::EnumerateCategories(nsISimpleEnumerator** aResult) +{ + if (NS_WARN_IF(!aResult)) { + return NS_ERROR_INVALID_ARG; + } + + MutexAutoLock lock(mLock); + CategoryEnumerator* enumObj = CategoryEnumerator::Create(mTable); + + if (!enumObj) { + return NS_ERROR_OUT_OF_MEMORY; + } + + *aResult = enumObj; + NS_ADDREF(*aResult); + return NS_OK; +} + +struct writecat_struct +{ + PRFileDesc* fd; + bool success; +}; + +nsresult +nsCategoryManager::SuppressNotifications(bool aSuppress) +{ + mSuppressNotifications = aSuppress; + return NS_OK; +} + +/* + * CreateServicesFromCategory() + * + * Given a category, this convenience functions enumerates the category and + * creates a service of every CID or ContractID registered under the category. + * If observerTopic is non null and the service implements nsIObserver, + * this will attempt to notify the observer with the origin, observerTopic string + * as parameter. + */ +void +NS_CreateServicesFromCategory(const char* aCategory, + nsISupports* aOrigin, + const char* aObserverTopic, + const char16_t* aObserverData) +{ + nsresult rv; + + nsCOMPtr<nsICategoryManager> categoryManager = + do_GetService("@mozilla.org/categorymanager;1"); + if (!categoryManager) { + return; + } + + nsCOMPtr<nsISimpleEnumerator> enumerator; + rv = categoryManager->EnumerateCategory(aCategory, + getter_AddRefs(enumerator)); + if (NS_FAILED(rv)) { + return; + } + + nsCOMPtr<nsIUTF8StringEnumerator> senumerator = + do_QueryInterface(enumerator); + if (!senumerator) { + NS_WARNING("Category enumerator doesn't support nsIUTF8StringEnumerator."); + return; + } + + bool hasMore; + while (NS_SUCCEEDED(senumerator->HasMore(&hasMore)) && hasMore) { + // From here on just skip any error we get. + nsAutoCString entryString; + if (NS_FAILED(senumerator->GetNext(entryString))) { + continue; + } + + nsXPIDLCString contractID; + rv = categoryManager->GetCategoryEntry(aCategory, entryString.get(), + getter_Copies(contractID)); + if (NS_FAILED(rv)) { + continue; + } + + nsCOMPtr<nsISupports> instance = do_GetService(contractID); + if (!instance) { + LogMessage("While creating services from category '%s', could not create service for entry '%s', contract ID '%s'", + aCategory, entryString.get(), contractID.get()); + continue; + } + + if (aObserverTopic) { + // try an observer, if it implements it. + nsCOMPtr<nsIObserver> observer = do_QueryInterface(instance); + if (observer) { + observer->Observe(aOrigin, aObserverTopic, + aObserverData ? aObserverData : u""); + } else { + LogMessage("While creating services from category '%s', service for entry '%s', contract ID '%s' does not implement nsIObserver.", + aCategory, entryString.get(), contractID.get()); + } + } + } +} diff --git a/xpcom/components/nsCategoryManager.h b/xpcom/components/nsCategoryManager.h new file mode 100644 index 0000000000..3a4faed721 --- /dev/null +++ b/xpcom/components/nsCategoryManager.h @@ -0,0 +1,146 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +#ifndef NSCATEGORYMANAGER_H +#define NSCATEGORYMANAGER_H + +#include "prio.h" +#include "plarena.h" +#include "nsClassHashtable.h" +#include "nsICategoryManager.h" +#include "nsIMemoryReporter.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/Mutex.h" +#include "mozilla/Attributes.h" + +class nsIMemoryReporter; + +/* 16d222a6-1dd2-11b2-b693-f38b02c021b2 */ +#define NS_CATEGORYMANAGER_CID \ +{ 0x16d222a6, 0x1dd2, 0x11b2, \ + {0xb6, 0x93, 0xf3, 0x8b, 0x02, 0xc0, 0x21, 0xb2} } + +/** + * a "leaf-node", managed by the nsCategoryNode hashtable. + * + * we need to keep a "persistent value" (which will be written to the registry) + * and a non-persistent value (for the current runtime): these are usually + * the same, except when aPersist==false. The strings are permanently arena- + * allocated, and will never go away. + */ +class CategoryLeaf : public nsDepCharHashKey +{ +public: + explicit CategoryLeaf(const char* aKey) : nsDepCharHashKey(aKey), value(nullptr) {} + const char* value; +}; + + +/** + * CategoryNode keeps a hashtable of its entries. + * the CategoryNode itself is permanently allocated in + * the arena. + */ +class CategoryNode +{ +public: + nsresult GetLeaf(const char* aEntryName, + char** aResult); + + nsresult AddLeaf(const char* aEntryName, + const char* aValue, + bool aReplace, + char** aResult, + PLArenaPool* aArena); + + void DeleteLeaf(const char* aEntryName); + + void Clear() + { + mozilla::MutexAutoLock lock(mLock); + mTable.Clear(); + } + + uint32_t Count() + { + mozilla::MutexAutoLock lock(mLock); + uint32_t tCount = mTable.Count(); + return tCount; + } + + nsresult Enumerate(nsISimpleEnumerator** aResult); + + // CategoryNode is arena-allocated, with the strings + static CategoryNode* Create(PLArenaPool* aArena); + ~CategoryNode(); + void operator delete(void*) {} + + size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf); + +private: + CategoryNode() : mLock("CategoryLeaf") {} + + void* operator new(size_t aSize, PLArenaPool* aArena); + + nsTHashtable<CategoryLeaf> mTable; + mozilla::Mutex mLock; +}; + + +/** + * The main implementation of nsICategoryManager. + * + * This implementation is thread-safe. + */ +class nsCategoryManager final + : public nsICategoryManager + , public nsIMemoryReporter +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSICATEGORYMANAGER + NS_DECL_NSIMEMORYREPORTER + + /** + * Suppress or unsuppress notifications of category changes to the + * observer service. This is to be used by nsComponentManagerImpl + * on startup while reading the stored category list. + */ + nsresult SuppressNotifications(bool aSuppress); + + void AddCategoryEntry(const char* aCategory, + const char* aKey, + const char* aValue, + bool aReplace = true, + char** aOldValue = nullptr); + + static nsresult Create(nsISupports* aOuter, REFNSIID aIID, void** aResult); + void InitMemoryReporter(); + + static nsCategoryManager* GetSingleton(); + static void Destroy(); + +private: + static nsCategoryManager* gCategoryManager; + + nsCategoryManager(); + ~nsCategoryManager(); + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf); + + CategoryNode* get_category(const char* aName); + void NotifyObservers(const char* aTopic, + const char* aCategoryName, // must be a static string + const char* aEntryName); + + PLArenaPool mArena; + nsClassHashtable<nsDepCharHashKey, CategoryNode> mTable; + mozilla::Mutex mLock; + bool mSuppressNotifications; +}; + +#endif diff --git a/xpcom/components/nsCategoryManagerUtils.h b/xpcom/components/nsCategoryManagerUtils.h new file mode 100644 index 0000000000..a83d097c27 --- /dev/null +++ b/xpcom/components/nsCategoryManagerUtils.h @@ -0,0 +1,18 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsCategoryManagerUtils_h__ +#define nsCategoryManagerUtils_h__ + +#include "nsICategoryManager.h" + +void +NS_CreateServicesFromCategory(const char* aCategory, + nsISupports* aOrigin, + const char* aObserverTopic, + const char16_t* aObserverData = nullptr); + +#endif diff --git a/xpcom/components/nsComponentManager.cpp b/xpcom/components/nsComponentManager.cpp new file mode 100644 index 0000000000..b9eb8e275f --- /dev/null +++ b/xpcom/components/nsComponentManager.cpp @@ -0,0 +1,2083 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This Original Code has been modified by IBM Corporation. + * Modifications made by IBM described herein are + * Copyright (c) International Business Machines + * Corporation, 2000 + * + * Modifications to Mozilla code or documentation + * identified per MPL Section 3.3 + * + * Date Modified by Description of modification + * 04/20/2000 IBM Corp. Added PR_CALLBACK for Optlink use in OS2 + */ + +#include <stdlib.h> +#include "nscore.h" +#include "nsISupports.h" +#include "nspr.h" +#include "nsCRT.h" // for atoll + +// Arena used by component manager for storing contractid string, dll +// location strings and small objects +// CAUTION: Arena align mask needs to be defined before including plarena.h +// currently from nsComponentManager.h +#define PL_ARENA_CONST_ALIGN_MASK 7 +#define NS_CM_BLOCK_SIZE (1024 * 8) + +#include "nsCategoryManager.h" +#include "nsCOMPtr.h" +#include "nsComponentManager.h" +#include "nsDirectoryService.h" +#include "nsDirectoryServiceDefs.h" +#include "nsCategoryManager.h" +#include "nsCategoryManagerUtils.h" +#include "xptiprivate.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/XPTInterfaceInfoManager.h" +#include "nsIConsoleService.h" +#include "nsIObserverService.h" +#include "nsISimpleEnumerator.h" +#include "nsIStringEnumerator.h" +#include "nsXPCOM.h" +#include "nsXPCOMPrivate.h" +#include "nsISupportsPrimitives.h" +#include "nsIClassInfo.h" +#include "nsLocalFile.h" +#include "nsReadableUtils.h" +#include "nsString.h" +#include "nsXPIDLString.h" +#include "prcmon.h" +#include "xptinfo.h" // this after nsISupports, to pick up IID so that xpt stuff doesn't try to define it itself... +#include "nsThreadUtils.h" +#include "prthread.h" +#include "private/pprthred.h" +#include "nsTArray.h" +#include "prio.h" +#include "ManifestParser.h" +#include "nsNetUtil.h" +#include "mozilla/Services.h" + +#include "mozilla/GenericFactory.h" +#include "nsSupportsPrimitives.h" +#include "nsArray.h" +#include "nsIMutableArray.h" +#include "nsArrayEnumerator.h" +#include "nsStringEnumerator.h" +#include "mozilla/FileUtils.h" +#include "mozilla/UniquePtr.h" +#include "nsDataHashtable.h" + +#include <new> // for placement new + +#include "mozilla/Omnijar.h" + +#include "mozilla/Logging.h" +#include "LogModulePrefWatcher.h" + +using namespace mozilla; + +static LazyLogModule nsComponentManagerLog("nsComponentManager"); + +#if 0 || defined (DEBUG_timeless) + #define SHOW_DENIED_ON_SHUTDOWN + #define SHOW_CI_ON_EXISTING_SERVICE +#endif + +// Bloated registry buffer size to improve startup performance -- needs to +// be big enough to fit the entire file into memory or it'll thrash. +// 512K is big enough to allow for some future growth in the registry. +#define BIG_REGISTRY_BUFLEN (512*1024) + +// Common Key Names +const char xpcomComponentsKeyName[] = "software/mozilla/XPCOM/components"; +const char xpcomKeyName[] = "software/mozilla/XPCOM"; + +// Common Value Names +const char fileSizeValueName[] = "FileSize"; +const char lastModValueName[] = "LastModTimeStamp"; +const char nativeComponentType[] = "application/x-mozilla-native"; +const char staticComponentType[] = "application/x-mozilla-static"; + +NS_DEFINE_CID(kCategoryManagerCID, NS_CATEGORYMANAGER_CID); + +#define UID_STRING_LENGTH 39 + +nsresult +nsGetServiceFromCategory::operator()(const nsIID& aIID, + void** aInstancePtr) const +{ + nsresult rv; + nsXPIDLCString value; + nsCOMPtr<nsICategoryManager> catman; + nsComponentManagerImpl* compMgr = nsComponentManagerImpl::gComponentManager; + if (!compMgr) { + rv = NS_ERROR_NOT_INITIALIZED; + goto error; + } + + if (!mCategory || !mEntry) { + // when categories have defaults, use that for null mEntry + rv = NS_ERROR_NULL_POINTER; + goto error; + } + + rv = compMgr->nsComponentManagerImpl::GetService(kCategoryManagerCID, + NS_GET_IID(nsICategoryManager), + getter_AddRefs(catman)); + if (NS_FAILED(rv)) { + goto error; + } + + /* find the contractID for category.entry */ + rv = catman->GetCategoryEntry(mCategory, mEntry, + getter_Copies(value)); + if (NS_FAILED(rv)) { + goto error; + } + if (!value) { + rv = NS_ERROR_SERVICE_NOT_AVAILABLE; + goto error; + } + + rv = compMgr->nsComponentManagerImpl::GetServiceByContractID(value, + aIID, + aInstancePtr); + if (NS_FAILED(rv)) { +error: + *aInstancePtr = 0; + } + if (mErrorPtr) { + *mErrorPtr = rv; + } + return rv; +} + +//////////////////////////////////////////////////////////////////////////////// +// Arena helper functions +//////////////////////////////////////////////////////////////////////////////// +char* +ArenaStrndup(const char* aStr, uint32_t aLen, PLArenaPool* aArena) +{ + void* mem; + // Include trailing null in the aLen + PL_ARENA_ALLOCATE(mem, aArena, aLen + 1); + if (mem) { + memcpy(mem, aStr, aLen + 1); + } + return static_cast<char*>(mem); +} + +char* +ArenaStrdup(const char* aStr, PLArenaPool* aArena) +{ + return ArenaStrndup(aStr, strlen(aStr), aArena); +} + +// GetService and a few other functions need to exit their mutex mid-function +// without reentering it later in the block. This class supports that +// style of early-exit that MutexAutoUnlock doesn't. + +namespace { + +class MOZ_STACK_CLASS MutexLock +{ +public: + explicit MutexLock(SafeMutex& aMutex) + : mMutex(aMutex) + , mLocked(false) + { + Lock(); + } + + ~MutexLock() + { + if (mLocked) { + Unlock(); + } + } + + void Lock() + { + NS_ASSERTION(!mLocked, "Re-entering a mutex"); + mMutex.Lock(); + mLocked = true; + } + + void Unlock() + { + NS_ASSERTION(mLocked, "Exiting a mutex that isn't held!"); + mMutex.Unlock(); + mLocked = false; + } + +private: + SafeMutex& mMutex; + bool mLocked; +}; + +} // namespace + +// this is safe to call during InitXPCOM +static already_AddRefed<nsIFile> +GetLocationFromDirectoryService(const char* aProp) +{ + nsCOMPtr<nsIProperties> directoryService; + nsDirectoryService::Create(nullptr, + NS_GET_IID(nsIProperties), + getter_AddRefs(directoryService)); + + if (!directoryService) { + return nullptr; + } + + nsCOMPtr<nsIFile> file; + nsresult rv = directoryService->Get(aProp, + NS_GET_IID(nsIFile), + getter_AddRefs(file)); + if (NS_FAILED(rv)) { + return nullptr; + } + + return file.forget(); +} + +static already_AddRefed<nsIFile> +CloneAndAppend(nsIFile* aBase, const nsACString& aAppend) +{ + nsCOMPtr<nsIFile> f; + aBase->Clone(getter_AddRefs(f)); + if (!f) { + return nullptr; + } + + f->AppendNative(aAppend); + return f.forget(); +} + +//////////////////////////////////////////////////////////////////////////////// +// nsComponentManagerImpl +//////////////////////////////////////////////////////////////////////////////// + +nsresult +nsComponentManagerImpl::Create(nsISupports* aOuter, REFNSIID aIID, + void** aResult) +{ + if (aOuter) { + return NS_ERROR_NO_AGGREGATION; + } + + if (!gComponentManager) { + return NS_ERROR_FAILURE; + } + + return gComponentManager->QueryInterface(aIID, aResult); +} + +static const int CONTRACTID_HASHTABLE_INITIAL_LENGTH = 1024; + +nsComponentManagerImpl::nsComponentManagerImpl() + : mFactories(CONTRACTID_HASHTABLE_INITIAL_LENGTH) + , mContractIDs(CONTRACTID_HASHTABLE_INITIAL_LENGTH) + , mLock("nsComponentManagerImpl.mLock") + , mStatus(NOT_INITIALIZED) +{ +} + +nsTArray<const mozilla::Module*>* nsComponentManagerImpl::sStaticModules; + +NSMODULE_DEFN(start_kPStaticModules); +NSMODULE_DEFN(end_kPStaticModules); + +/* The content between start_kPStaticModules and end_kPStaticModules is gathered + * by the linker from various objects containing symbols in a specific section. + * ASAN considers (rightfully) the use of this content as a global buffer + * overflow. But this is a deliberate and well-considered choice, with no proper + * way to make ASAN happy. */ +MOZ_ASAN_BLACKLIST +/* static */ void +nsComponentManagerImpl::InitializeStaticModules() +{ + if (sStaticModules) { + return; + } + + sStaticModules = new nsTArray<const mozilla::Module*>; + for (const mozilla::Module * const* staticModules = + &NSMODULE_NAME(start_kPStaticModules) + 1; + staticModules < &NSMODULE_NAME(end_kPStaticModules); ++staticModules) + if (*staticModules) { // ASAN adds padding + sStaticModules->AppendElement(*staticModules); + } +} + +nsTArray<nsComponentManagerImpl::ComponentLocation>* +nsComponentManagerImpl::sModuleLocations; + +/* static */ void +nsComponentManagerImpl::InitializeModuleLocations() +{ + if (sModuleLocations) { + return; + } + + sModuleLocations = new nsTArray<ComponentLocation>; +} + +nsresult +nsComponentManagerImpl::Init() +{ + MOZ_ASSERT(NOT_INITIALIZED == mStatus); + + // Initialize our arena + PL_INIT_ARENA_POOL(&mArena, "ComponentManagerArena", NS_CM_BLOCK_SIZE); + + nsCOMPtr<nsIFile> greDir = + GetLocationFromDirectoryService(NS_GRE_DIR); + nsCOMPtr<nsIFile> appDir = + GetLocationFromDirectoryService(NS_XPCOM_CURRENT_PROCESS_DIR); + + InitializeStaticModules(); + + nsresult rv = mNativeModuleLoader.Init(); + if (NS_FAILED(rv)) { + return rv; + } + + nsCategoryManager::GetSingleton()->SuppressNotifications(true); + + RegisterModule(&kXPCOMModule, nullptr); + + for (uint32_t i = 0; i < sStaticModules->Length(); ++i) { + RegisterModule((*sStaticModules)[i], nullptr); + } + + bool loadChromeManifests = (XRE_GetProcessType() != GeckoProcessType_GPU); + if (loadChromeManifests) { + // The overall order in which chrome.manifests are expected to be treated + // is the following: + // - greDir + // - greDir's omni.ja + // - appDir + // - appDir's omni.ja + + InitializeModuleLocations(); + ComponentLocation* cl = sModuleLocations->AppendElement(); + nsCOMPtr<nsIFile> lf = CloneAndAppend(greDir, + NS_LITERAL_CSTRING("chrome.manifest")); + cl->type = NS_APP_LOCATION; + cl->location.Init(lf); + + RefPtr<nsZipArchive> greOmnijar = + mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE); + if (greOmnijar) { + cl = sModuleLocations->AppendElement(); + cl->type = NS_APP_LOCATION; + cl->location.Init(greOmnijar, "chrome.manifest"); + } + + bool equals = false; + appDir->Equals(greDir, &equals); + if (!equals) { + cl = sModuleLocations->AppendElement(); + cl->type = NS_APP_LOCATION; + lf = CloneAndAppend(appDir, NS_LITERAL_CSTRING("chrome.manifest")); + cl->location.Init(lf); + } + + RefPtr<nsZipArchive> appOmnijar = + mozilla::Omnijar::GetReader(mozilla::Omnijar::APP); + if (appOmnijar) { + cl = sModuleLocations->AppendElement(); + cl->type = NS_APP_LOCATION; + cl->location.Init(appOmnijar, "chrome.manifest"); + } + + RereadChromeManifests(false); + } + + nsCategoryManager::GetSingleton()->SuppressNotifications(false); + + RegisterWeakMemoryReporter(this); + + // NB: The logging preference watcher needs to be registered late enough in + // startup that it's okay to use the preference system, but also as soon as + // possible so that log modules enabled via preferences are turned on as + // early as possible. + // + // We can't initialize the preference watcher when the log module manager is + // initialized, as a number of things attempt to start logging before the + // preference system is initialized. + // + // The preference system is registered as a component so at this point during + // component manager initialization we know it is setup and we can register + // for notifications. + LogModulePrefWatcher::RegisterPrefWatcher(); + + // Unfortunately, we can't register the nsCategoryManager memory reporter + // in its constructor (which is triggered by the GetSingleton() call + // above) because the memory reporter manager isn't initialized at that + // point. So we wait until now. + nsCategoryManager::GetSingleton()->InitMemoryReporter(); + + MOZ_LOG(nsComponentManagerLog, LogLevel::Debug, + ("nsComponentManager: Initialized.")); + + mStatus = NORMAL; + + return NS_OK; +} + +static bool +ProcessSelectorMatches(Module::ProcessSelector aSelector) +{ + GeckoProcessType type = XRE_GetProcessType(); + if (type == GeckoProcessType_GPU) { + return !!(aSelector & Module::ALLOW_IN_GPU_PROCESS); + } + + if (aSelector & Module::MAIN_PROCESS_ONLY) { + return type == GeckoProcessType_Default; + } + if (aSelector & Module::CONTENT_PROCESS_ONLY) { + return type == GeckoProcessType_Content; + } + return true; +} + +static const int kModuleVersionWithSelector = 51; + +void +nsComponentManagerImpl::RegisterModule(const mozilla::Module* aModule, + FileLocation* aFile) +{ + mLock.AssertNotCurrentThreadOwns(); + + if (aModule->mVersion >= kModuleVersionWithSelector && + !ProcessSelectorMatches(aModule->selector)) + { + return; + } + + { + // Scope the monitor so that we don't hold it while calling into the + // category manager. + MutexLock lock(mLock); + + KnownModule* m; + if (aFile) { + nsCString uri; + aFile->GetURIString(uri); + NS_ASSERTION(!mKnownModules.Get(uri), + "Must not register a binary module twice."); + + m = new KnownModule(aModule, *aFile); + mKnownModules.Put(uri, m); + } else { + m = new KnownModule(aModule); + mKnownStaticModules.AppendElement(m); + } + + if (aModule->mCIDs) { + const mozilla::Module::CIDEntry* entry; + for (entry = aModule->mCIDs; entry->cid; ++entry) { + RegisterCIDEntryLocked(entry, m); + } + } + + if (aModule->mContractIDs) { + const mozilla::Module::ContractIDEntry* entry; + for (entry = aModule->mContractIDs; entry->contractid; ++entry) { + RegisterContractIDLocked(entry); + } + MOZ_ASSERT(!entry->cid, "Incorrectly terminated contract list"); + } + } + + if (aModule->mCategoryEntries) { + const mozilla::Module::CategoryEntry* entry; + for (entry = aModule->mCategoryEntries; entry->category; ++entry) + nsCategoryManager::GetSingleton()->AddCategoryEntry(entry->category, + entry->entry, + entry->value); + } +} + +void +nsComponentManagerImpl::RegisterCIDEntryLocked( + const mozilla::Module::CIDEntry* aEntry, + KnownModule* aModule) +{ + mLock.AssertCurrentThreadOwns(); + + if (!ProcessSelectorMatches(aEntry->processSelector)) { + return; + } + + nsFactoryEntry* f = mFactories.Get(*aEntry->cid); + if (f) { + NS_WARNING("Re-registering a CID?"); + + char idstr[NSID_LENGTH]; + aEntry->cid->ToProvidedString(idstr); + + nsCString existing; + if (f->mModule) { + existing = f->mModule->Description(); + } else { + existing = "<unknown module>"; + } + SafeMutexAutoUnlock unlock(mLock); + LogMessage("While registering XPCOM module %s, trying to re-register CID '%s' already registered by %s.", + aModule->Description().get(), + idstr, + existing.get()); + return; + } + + f = new nsFactoryEntry(aEntry, aModule); + mFactories.Put(*aEntry->cid, f); +} + +void +nsComponentManagerImpl::RegisterContractIDLocked( + const mozilla::Module::ContractIDEntry* aEntry) +{ + mLock.AssertCurrentThreadOwns(); + + if (!ProcessSelectorMatches(aEntry->processSelector)) { + return; + } + + nsFactoryEntry* f = mFactories.Get(*aEntry->cid); + if (!f) { + NS_WARNING("No CID found when attempting to map contract ID"); + + char idstr[NSID_LENGTH]; + aEntry->cid->ToProvidedString(idstr); + + SafeMutexAutoUnlock unlock(mLock); + LogMessage("Could not map contract ID '%s' to CID %s because no implementation of the CID is registered.", + aEntry->contractid, + idstr); + + return; + } + + mContractIDs.Put(nsDependentCString(aEntry->contractid), f); +} + +static void +CutExtension(nsCString& aPath) +{ + int32_t dotPos = aPath.RFindChar('.'); + if (kNotFound == dotPos) { + aPath.Truncate(); + } else { + aPath.Cut(0, dotPos + 1); + } +} + +static void +DoRegisterManifest(NSLocationType aType, + FileLocation& aFile, + bool aChromeOnly, + bool aXPTOnly) +{ + MOZ_ASSERT(!aXPTOnly || !nsComponentManagerImpl::gComponentManager); + uint32_t len; + FileLocation::Data data; + UniquePtr<char[]> buf; + nsresult rv = aFile.GetData(data); + if (NS_SUCCEEDED(rv)) { + rv = data.GetSize(&len); + } + if (NS_SUCCEEDED(rv)) { + buf = MakeUnique<char[]>(len + 1); + rv = data.Copy(buf.get(), len); + } + if (NS_SUCCEEDED(rv)) { + buf[len] = '\0'; + ParseManifest(aType, aFile, buf.get(), aChromeOnly, aXPTOnly); + } else if (NS_BOOTSTRAPPED_LOCATION != aType) { + nsCString uri; + aFile.GetURIString(uri); + LogMessage("Could not read chrome manifest '%s'.", uri.get()); + } +} + +void +nsComponentManagerImpl::RegisterManifest(NSLocationType aType, + FileLocation& aFile, + bool aChromeOnly) +{ + DoRegisterManifest(aType, aFile, aChromeOnly, false); +} + +void +nsComponentManagerImpl::ManifestManifest(ManifestProcessingContext& aCx, + int aLineNo, char* const* aArgv) +{ + char* file = aArgv[0]; + FileLocation f(aCx.mFile, file); + RegisterManifest(aCx.mType, f, aCx.mChromeOnly); +} + +void +nsComponentManagerImpl::ManifestBinaryComponent(ManifestProcessingContext& aCx, + int aLineNo, + char* const* aArgv) +{ + if (aCx.mFile.IsZip()) { + NS_WARNING("Cannot load binary components from a jar."); + LogMessageWithContext(aCx.mFile, aLineNo, + "Cannot load binary components from a jar."); + return; + } + + FileLocation f(aCx.mFile, aArgv[0]); + nsCString uri; + f.GetURIString(uri); + + if (mKnownModules.Get(uri)) { + NS_WARNING("Attempting to register a binary component twice."); + LogMessageWithContext(aCx.mFile, aLineNo, + "Attempting to register a binary component twice."); + return; + } + + const mozilla::Module* m = mNativeModuleLoader.LoadModule(f); + // The native module loader should report an error here, we don't have to + if (!m) { + return; + } + + RegisterModule(m, &f); +} + +static void +DoRegisterXPT(FileLocation& aFile) +{ + uint32_t len; + FileLocation::Data data; + UniquePtr<char[]> buf; + nsresult rv = aFile.GetData(data); + if (NS_SUCCEEDED(rv)) { + rv = data.GetSize(&len); + } + if (NS_SUCCEEDED(rv)) { + buf = MakeUnique<char[]>(len); + rv = data.Copy(buf.get(), len); + } + if (NS_SUCCEEDED(rv)) { + XPTInterfaceInfoManager::GetSingleton()->RegisterBuffer(buf.get(), len); + } else { + nsCString uri; + aFile.GetURIString(uri); + LogMessage("Could not read '%s'.", uri.get()); + } +} + +void +nsComponentManagerImpl::ManifestXPT(ManifestProcessingContext& aCx, + int aLineNo, char* const* aArgv) +{ + FileLocation f(aCx.mFile, aArgv[0]); + DoRegisterXPT(f); +} + +void +nsComponentManagerImpl::ManifestComponent(ManifestProcessingContext& aCx, + int aLineNo, char* const* aArgv) +{ + mLock.AssertNotCurrentThreadOwns(); + + char* id = aArgv[0]; + char* file = aArgv[1]; + + nsID cid; + if (!cid.Parse(id)) { + LogMessageWithContext(aCx.mFile, aLineNo, + "Malformed CID: '%s'.", id); + return; + } + + // Precompute the hash/file data outside of the lock + FileLocation fl(aCx.mFile, file); + nsCString hash; + fl.GetURIString(hash); + + MutexLock lock(mLock); + nsFactoryEntry* f = mFactories.Get(cid); + if (f) { + char idstr[NSID_LENGTH]; + cid.ToProvidedString(idstr); + + nsCString existing; + if (f->mModule) { + existing = f->mModule->Description(); + } else { + existing = "<unknown module>"; + } + + lock.Unlock(); + + LogMessageWithContext(aCx.mFile, aLineNo, + "Trying to re-register CID '%s' already registered by %s.", + idstr, + existing.get()); + return; + } + + KnownModule* km; + + km = mKnownModules.Get(hash); + if (!km) { + km = new KnownModule(fl); + mKnownModules.Put(hash, km); + } + + void* place; + + PL_ARENA_ALLOCATE(place, &mArena, sizeof(nsCID)); + nsID* permanentCID = static_cast<nsID*>(place); + *permanentCID = cid; + + PL_ARENA_ALLOCATE(place, &mArena, sizeof(mozilla::Module::CIDEntry)); + mozilla::Module::CIDEntry* e = new (place) mozilla::Module::CIDEntry(); + e->cid = permanentCID; + + f = new nsFactoryEntry(e, km); + mFactories.Put(cid, f); +} + +void +nsComponentManagerImpl::ManifestContract(ManifestProcessingContext& aCx, + int aLineNo, char* const* aArgv) +{ + mLock.AssertNotCurrentThreadOwns(); + + char* contract = aArgv[0]; + char* id = aArgv[1]; + + nsID cid; + if (!cid.Parse(id)) { + LogMessageWithContext(aCx.mFile, aLineNo, + "Malformed CID: '%s'.", id); + return; + } + + MutexLock lock(mLock); + nsFactoryEntry* f = mFactories.Get(cid); + if (!f) { + lock.Unlock(); + LogMessageWithContext(aCx.mFile, aLineNo, + "Could not map contract ID '%s' to CID %s because no implementation of the CID is registered.", + contract, id); + return; + } + + mContractIDs.Put(nsDependentCString(contract), f); +} + +void +nsComponentManagerImpl::ManifestCategory(ManifestProcessingContext& aCx, + int aLineNo, char* const* aArgv) +{ + char* category = aArgv[0]; + char* key = aArgv[1]; + char* value = aArgv[2]; + + nsCategoryManager::GetSingleton()-> + AddCategoryEntry(category, key, value); +} + +void +nsComponentManagerImpl::RereadChromeManifests(bool aChromeOnly) +{ + for (uint32_t i = 0; i < sModuleLocations->Length(); ++i) { + ComponentLocation& l = sModuleLocations->ElementAt(i); + RegisterManifest(l.type, l.location, aChromeOnly); + } +} + +bool +nsComponentManagerImpl::KnownModule::EnsureLoader() +{ + if (!mLoader) { + nsCString extension; + mFile.GetURIString(extension); + CutExtension(extension); + mLoader = + nsComponentManagerImpl::gComponentManager->LoaderForExtension(extension); + } + return !!mLoader; +} + +bool +nsComponentManagerImpl::KnownModule::Load() +{ + if (mFailed) { + return false; + } + if (!mModule) { + if (!EnsureLoader()) { + return false; + } + + mModule = mLoader->LoadModule(mFile); + + if (!mModule) { + mFailed = true; + return false; + } + } + if (!mLoaded) { + if (mModule->loadProc) { + nsresult rv = mModule->loadProc(); + if (NS_FAILED(rv)) { + mFailed = true; + return false; + } + } + mLoaded = true; + } + return true; +} + +nsCString +nsComponentManagerImpl::KnownModule::Description() const +{ + nsCString s; + if (mFile) { + mFile.GetURIString(s); + } else { + s = "<static module>"; + } + return s; +} + +nsresult nsComponentManagerImpl::Shutdown(void) +{ + MOZ_ASSERT(NORMAL == mStatus); + + mStatus = SHUTDOWN_IN_PROGRESS; + + // Shutdown the component manager + MOZ_LOG(nsComponentManagerLog, LogLevel::Debug, + ("nsComponentManager: Beginning Shutdown.")); + + UnregisterWeakMemoryReporter(this); + + // Release all cached factories + mContractIDs.Clear(); + mFactories.Clear(); // XXX release the objects, don't just clear + mLoaderMap.Clear(); + mKnownModules.Clear(); + mKnownStaticModules.Clear(); + + delete sStaticModules; + delete sModuleLocations; + + // Unload libraries + mNativeModuleLoader.UnloadLibraries(); + + // delete arena for strings and small objects + PL_FinishArenaPool(&mArena); + + mStatus = SHUTDOWN_COMPLETE; + + MOZ_LOG(nsComponentManagerLog, LogLevel::Debug, + ("nsComponentManager: Shutdown complete.")); + + return NS_OK; +} + +nsComponentManagerImpl::~nsComponentManagerImpl() +{ + MOZ_LOG(nsComponentManagerLog, LogLevel::Debug, + ("nsComponentManager: Beginning destruction.")); + + if (SHUTDOWN_COMPLETE != mStatus) { + Shutdown(); + } + + MOZ_LOG(nsComponentManagerLog, LogLevel::Debug, + ("nsComponentManager: Destroyed.")); +} + +NS_IMPL_ISUPPORTS(nsComponentManagerImpl, + nsIComponentManager, + nsIServiceManager, + nsIComponentRegistrar, + nsISupportsWeakReference, + nsIInterfaceRequestor, + nsIMemoryReporter) + +nsresult +nsComponentManagerImpl::GetInterface(const nsIID& aUuid, void** aResult) +{ + NS_WARNING("This isn't supported"); + // fall through to QI as anything QIable is a superset of what can be + // got via the GetInterface() + return QueryInterface(aUuid, aResult); +} + +nsFactoryEntry* +nsComponentManagerImpl::GetFactoryEntry(const char* aContractID, + uint32_t aContractIDLen) +{ + SafeMutexAutoLock lock(mLock); + return mContractIDs.Get(nsDependentCString(aContractID, aContractIDLen)); +} + + +nsFactoryEntry* +nsComponentManagerImpl::GetFactoryEntry(const nsCID& aClass) +{ + SafeMutexAutoLock lock(mLock); + return mFactories.Get(aClass); +} + +already_AddRefed<nsIFactory> +nsComponentManagerImpl::FindFactory(const nsCID& aClass) +{ + nsFactoryEntry* e = GetFactoryEntry(aClass); + if (!e) { + return nullptr; + } + + return e->GetFactory(); +} + +already_AddRefed<nsIFactory> +nsComponentManagerImpl::FindFactory(const char* aContractID, + uint32_t aContractIDLen) +{ + nsFactoryEntry* entry = GetFactoryEntry(aContractID, aContractIDLen); + if (!entry) { + return nullptr; + } + + return entry->GetFactory(); +} + +/** + * GetClassObject() + * + * Given a classID, this finds the singleton ClassObject that implements the CID. + * Returns an interface of type aIID off the singleton classobject. + */ +NS_IMETHODIMP +nsComponentManagerImpl::GetClassObject(const nsCID& aClass, const nsIID& aIID, + void** aResult) +{ + nsresult rv; + + if (MOZ_LOG_TEST(nsComponentManagerLog, LogLevel::Debug)) { + char* buf = aClass.ToString(); + PR_LogPrint("nsComponentManager: GetClassObject(%s)", buf); + if (buf) { + free(buf); + } + } + + MOZ_ASSERT(aResult != nullptr); + + nsCOMPtr<nsIFactory> factory = FindFactory(aClass); + if (!factory) { + return NS_ERROR_FACTORY_NOT_REGISTERED; + } + + rv = factory->QueryInterface(aIID, aResult); + + MOZ_LOG(nsComponentManagerLog, LogLevel::Warning, + ("\t\tGetClassObject() %s", NS_SUCCEEDED(rv) ? "succeeded" : "FAILED")); + + return rv; +} + + +NS_IMETHODIMP +nsComponentManagerImpl::GetClassObjectByContractID(const char* aContractID, + const nsIID& aIID, + void** aResult) +{ + if (NS_WARN_IF(!aResult) || + NS_WARN_IF(!aContractID)) { + return NS_ERROR_INVALID_ARG; + } + + nsresult rv; + + MOZ_LOG(nsComponentManagerLog, LogLevel::Debug, + ("nsComponentManager: GetClassObject(%s)", aContractID)); + + nsCOMPtr<nsIFactory> factory = FindFactory(aContractID, strlen(aContractID)); + if (!factory) { + return NS_ERROR_FACTORY_NOT_REGISTERED; + } + + rv = factory->QueryInterface(aIID, aResult); + + MOZ_LOG(nsComponentManagerLog, LogLevel::Warning, + ("\t\tGetClassObject() %s", NS_SUCCEEDED(rv) ? "succeeded" : "FAILED")); + + return rv; +} + +/** + * CreateInstance() + * + * Create an instance of an object that implements an interface and belongs + * to the implementation aClass using the factory. The factory is immediately + * released and not held onto for any longer. + */ +NS_IMETHODIMP +nsComponentManagerImpl::CreateInstance(const nsCID& aClass, + nsISupports* aDelegate, + const nsIID& aIID, + void** aResult) +{ + // test this first, since there's no point in creating a component during + // shutdown -- whether it's available or not would depend on the order it + // occurs in the list + if (gXPCOMShuttingDown) { + // When processing shutdown, don't process new GetService() requests +#ifdef SHOW_DENIED_ON_SHUTDOWN + nsXPIDLCString cid, iid; + cid.Adopt(aClass.ToString()); + iid.Adopt(aIID.ToString()); + fprintf(stderr, "Creating new instance on shutdown. Denied.\n" + " CID: %s\n IID: %s\n", cid.get(), iid.get()); +#endif /* SHOW_DENIED_ON_SHUTDOWN */ + return NS_ERROR_UNEXPECTED; + } + + if (!aResult) { + return NS_ERROR_NULL_POINTER; + } + *aResult = nullptr; + + nsFactoryEntry* entry = GetFactoryEntry(aClass); + + if (!entry) { + return NS_ERROR_FACTORY_NOT_REGISTERED; + } + +#ifdef SHOW_CI_ON_EXISTING_SERVICE + if (entry->mServiceObject) { + nsXPIDLCString cid; + cid.Adopt(aClass.ToString()); + nsAutoCString message; + message = NS_LITERAL_CSTRING("You are calling CreateInstance \"") + + cid + + NS_LITERAL_CSTRING("\" when a service for this CID already exists!"); + NS_ERROR(message.get()); + } +#endif + + nsresult rv; + nsCOMPtr<nsIFactory> factory = entry->GetFactory(); + if (factory) { + rv = factory->CreateInstance(aDelegate, aIID, aResult); + if (NS_SUCCEEDED(rv) && !*aResult) { + NS_ERROR("Factory did not return an object but returned success!"); + rv = NS_ERROR_SERVICE_NOT_FOUND; + } + } else { + // Translate error values + rv = NS_ERROR_FACTORY_NOT_REGISTERED; + } + + if (MOZ_LOG_TEST(nsComponentManagerLog, LogLevel::Warning)) { + char* buf = aClass.ToString(); + MOZ_LOG(nsComponentManagerLog, LogLevel::Warning, + ("nsComponentManager: CreateInstance(%s) %s", buf, + NS_SUCCEEDED(rv) ? "succeeded" : "FAILED")); + if (buf) { + free(buf); + } + } + + return rv; +} + +/** + * CreateInstanceByContractID() + * + * A variant of CreateInstance() that creates an instance of the object that + * implements the interface aIID and whose implementation has a contractID aContractID. + * + * This is only a convenience routine that turns around can calls the + * CreateInstance() with classid and iid. + */ +NS_IMETHODIMP +nsComponentManagerImpl::CreateInstanceByContractID(const char* aContractID, + nsISupports* aDelegate, + const nsIID& aIID, + void** aResult) +{ + if (NS_WARN_IF(!aContractID)) { + return NS_ERROR_INVALID_ARG; + } + + // test this first, since there's no point in creating a component during + // shutdown -- whether it's available or not would depend on the order it + // occurs in the list + if (gXPCOMShuttingDown) { + // When processing shutdown, don't process new GetService() requests +#ifdef SHOW_DENIED_ON_SHUTDOWN + nsXPIDLCString iid; + iid.Adopt(aIID.ToString()); + fprintf(stderr, "Creating new instance on shutdown. Denied.\n" + " ContractID: %s\n IID: %s\n", aContractID, iid.get()); +#endif /* SHOW_DENIED_ON_SHUTDOWN */ + return NS_ERROR_UNEXPECTED; + } + + if (!aResult) { + return NS_ERROR_NULL_POINTER; + } + *aResult = nullptr; + + nsFactoryEntry* entry = GetFactoryEntry(aContractID, strlen(aContractID)); + + if (!entry) { + return NS_ERROR_FACTORY_NOT_REGISTERED; + } + +#ifdef SHOW_CI_ON_EXISTING_SERVICE + if (entry->mServiceObject) { + nsAutoCString message; + message = + NS_LITERAL_CSTRING("You are calling CreateInstance \"") + + nsDependentCString(aContractID) + + NS_LITERAL_CSTRING("\" when a service for this CID already exists! " + "Add it to abusedContracts to track down the service consumer."); + NS_ERROR(message.get()); + } +#endif + + nsresult rv; + nsCOMPtr<nsIFactory> factory = entry->GetFactory(); + if (factory) { + + rv = factory->CreateInstance(aDelegate, aIID, aResult); + if (NS_SUCCEEDED(rv) && !*aResult) { + NS_ERROR("Factory did not return an object but returned success!"); + rv = NS_ERROR_SERVICE_NOT_FOUND; + } + } else { + // Translate error values + rv = NS_ERROR_FACTORY_NOT_REGISTERED; + } + + MOZ_LOG(nsComponentManagerLog, LogLevel::Warning, + ("nsComponentManager: CreateInstanceByContractID(%s) %s", aContractID, + NS_SUCCEEDED(rv) ? "succeeded" : "FAILED")); + + return rv; +} + +nsresult +nsComponentManagerImpl::FreeServices() +{ + NS_ASSERTION(gXPCOMShuttingDown, + "Must be shutting down in order to free all services"); + + if (!gXPCOMShuttingDown) { + return NS_ERROR_FAILURE; + } + + for (auto iter = mFactories.Iter(); !iter.Done(); iter.Next()) { + nsFactoryEntry* entry = iter.UserData(); + entry->mFactory = nullptr; + entry->mServiceObject = nullptr; + } + + return NS_OK; +} + +// This should only ever be called within the monitor! +nsComponentManagerImpl::PendingServiceInfo* +nsComponentManagerImpl::AddPendingService(const nsCID& aServiceCID, + PRThread* aThread) +{ + PendingServiceInfo* newInfo = mPendingServices.AppendElement(); + if (newInfo) { + newInfo->cid = &aServiceCID; + newInfo->thread = aThread; + } + return newInfo; +} + +// This should only ever be called within the monitor! +void +nsComponentManagerImpl::RemovePendingService(const nsCID& aServiceCID) +{ + uint32_t pendingCount = mPendingServices.Length(); + for (uint32_t index = 0; index < pendingCount; ++index) { + const PendingServiceInfo& info = mPendingServices.ElementAt(index); + if (info.cid->Equals(aServiceCID)) { + mPendingServices.RemoveElementAt(index); + return; + } + } +} + +// This should only ever be called within the monitor! +PRThread* +nsComponentManagerImpl::GetPendingServiceThread(const nsCID& aServiceCID) const +{ + uint32_t pendingCount = mPendingServices.Length(); + for (uint32_t index = 0; index < pendingCount; ++index) { + const PendingServiceInfo& info = mPendingServices.ElementAt(index); + if (info.cid->Equals(aServiceCID)) { + return info.thread; + } + } + return nullptr; +} + +NS_IMETHODIMP +nsComponentManagerImpl::GetService(const nsCID& aClass, + const nsIID& aIID, + void** aResult) +{ + // test this first, since there's no point in returning a service during + // shutdown -- whether it's available or not would depend on the order it + // occurs in the list + if (gXPCOMShuttingDown) { + // When processing shutdown, don't process new GetService() requests +#ifdef SHOW_DENIED_ON_SHUTDOWN + nsXPIDLCString cid, iid; + cid.Adopt(aClass.ToString()); + iid.Adopt(aIID.ToString()); + fprintf(stderr, "Getting service on shutdown. Denied.\n" + " CID: %s\n IID: %s\n", cid.get(), iid.get()); +#endif /* SHOW_DENIED_ON_SHUTDOWN */ + return NS_ERROR_UNEXPECTED; + } + + // `service` must be released after the lock is released, so it must be + // declared before the lock in this C++ block. + nsCOMPtr<nsISupports> service; + MutexLock lock(mLock); + + nsFactoryEntry* entry = mFactories.Get(aClass); + if (!entry) { + return NS_ERROR_FACTORY_NOT_REGISTERED; + } + + if (entry->mServiceObject) { + lock.Unlock(); + return entry->mServiceObject->QueryInterface(aIID, aResult); + } + + PRThread* currentPRThread = PR_GetCurrentThread(); + MOZ_ASSERT(currentPRThread, "This should never be null!"); + + // Needed to optimize the event loop below. + nsIThread* currentThread = nullptr; + + PRThread* pendingPRThread; + while ((pendingPRThread = GetPendingServiceThread(aClass))) { + if (pendingPRThread == currentPRThread) { + NS_ERROR("Recursive GetService!"); + return NS_ERROR_NOT_AVAILABLE; + } + + + SafeMutexAutoUnlock unlockPending(mLock); + + if (!currentThread) { + currentThread = NS_GetCurrentThread(); + MOZ_ASSERT(currentThread, "This should never be null!"); + } + + // This will process a single event or yield the thread if no event is + // pending. + if (!NS_ProcessNextEvent(currentThread, false)) { + PR_Sleep(PR_INTERVAL_NO_WAIT); + } + } + + // It's still possible that the other thread failed to create the + // service so we're not guaranteed to have an entry or service yet. + if (entry->mServiceObject) { + lock.Unlock(); + return entry->mServiceObject->QueryInterface(aIID, aResult); + } + +#ifdef DEBUG + PendingServiceInfo* newInfo = +#endif + AddPendingService(aClass, currentPRThread); + NS_ASSERTION(newInfo, "Failed to add info to the array!"); + + // We need to not be holding the service manager's lock while calling + // CreateInstance, because it invokes user code which could try to re-enter + // the service manager: + + nsresult rv; + { + SafeMutexAutoUnlock unlock(mLock); + rv = CreateInstance(aClass, nullptr, aIID, getter_AddRefs(service)); + } + if (NS_SUCCEEDED(rv) && !service) { + NS_ERROR("Factory did not return an object but returned success"); + return NS_ERROR_SERVICE_NOT_FOUND; + } + +#ifdef DEBUG + pendingPRThread = GetPendingServiceThread(aClass); + MOZ_ASSERT(pendingPRThread == currentPRThread, + "Pending service array has been changed!"); +#endif + RemovePendingService(aClass); + + if (NS_FAILED(rv)) { + return rv; + } + + NS_ASSERTION(!entry->mServiceObject, "Created two instances of a service!"); + + entry->mServiceObject = service.forget(); + + lock.Unlock(); + nsISupports** sresult = reinterpret_cast<nsISupports**>(aResult); + *sresult = entry->mServiceObject; + (*sresult)->AddRef(); + + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::IsServiceInstantiated(const nsCID& aClass, + const nsIID& aIID, + bool* aResult) +{ + // Now we want to get the service if we already got it. If not, we don't want + // to create an instance of it. mmh! + + // test this first, since there's no point in returning a service during + // shutdown -- whether it's available or not would depend on the order it + // occurs in the list + if (gXPCOMShuttingDown) { + // When processing shutdown, don't process new GetService() requests +#ifdef SHOW_DENIED_ON_SHUTDOWN + nsXPIDLCString cid, iid; + cid.Adopt(aClass.ToString()); + iid.Adopt(aIID.ToString()); + fprintf(stderr, "Checking for service on shutdown. Denied.\n" + " CID: %s\n IID: %s\n", cid.get(), iid.get()); +#endif /* SHOW_DENIED_ON_SHUTDOWN */ + return NS_ERROR_UNEXPECTED; + } + + nsresult rv = NS_ERROR_SERVICE_NOT_AVAILABLE; + nsFactoryEntry* entry; + + { + SafeMutexAutoLock lock(mLock); + entry = mFactories.Get(aClass); + } + + if (entry && entry->mServiceObject) { + nsCOMPtr<nsISupports> service; + rv = entry->mServiceObject->QueryInterface(aIID, getter_AddRefs(service)); + *aResult = (service != nullptr); + } + + return rv; +} + +NS_IMETHODIMP +nsComponentManagerImpl::IsServiceInstantiatedByContractID( + const char* aContractID, + const nsIID& aIID, + bool* aResult) +{ + // Now we want to get the service if we already got it. If not, we don't want + // to create an instance of it. mmh! + + // test this first, since there's no point in returning a service during + // shutdown -- whether it's available or not would depend on the order it + // occurs in the list + if (gXPCOMShuttingDown) { + // When processing shutdown, don't process new GetService() requests +#ifdef SHOW_DENIED_ON_SHUTDOWN + nsXPIDLCString iid; + iid.Adopt(aIID.ToString()); + fprintf(stderr, "Checking for service on shutdown. Denied.\n" + " ContractID: %s\n IID: %s\n", aContractID, iid.get()); +#endif /* SHOW_DENIED_ON_SHUTDOWN */ + return NS_ERROR_UNEXPECTED; + } + + nsresult rv = NS_ERROR_SERVICE_NOT_AVAILABLE; + nsFactoryEntry* entry; + { + SafeMutexAutoLock lock(mLock); + entry = mContractIDs.Get(nsDependentCString(aContractID)); + } + + if (entry && entry->mServiceObject) { + nsCOMPtr<nsISupports> service; + rv = entry->mServiceObject->QueryInterface(aIID, getter_AddRefs(service)); + *aResult = (service != nullptr); + } + return rv; +} + + +NS_IMETHODIMP +nsComponentManagerImpl::GetServiceByContractID(const char* aContractID, + const nsIID& aIID, + void** aResult) +{ + // test this first, since there's no point in returning a service during + // shutdown -- whether it's available or not would depend on the order it + // occurs in the list + if (gXPCOMShuttingDown) { + // When processing shutdown, don't process new GetService() requests +#ifdef SHOW_DENIED_ON_SHUTDOWN + nsXPIDLCString iid; + iid.Adopt(aIID.ToString()); + fprintf(stderr, "Getting service on shutdown. Denied.\n" + " ContractID: %s\n IID: %s\n", aContractID, iid.get()); +#endif /* SHOW_DENIED_ON_SHUTDOWN */ + return NS_ERROR_UNEXPECTED; + } + + // `service` must be released after the lock is released, so it must be + // declared before the lock in this C++ block. + nsCOMPtr<nsISupports> service; + MutexLock lock(mLock); + + nsFactoryEntry* entry = mContractIDs.Get(nsDependentCString(aContractID)); + if (!entry) { + return NS_ERROR_FACTORY_NOT_REGISTERED; + } + + if (entry->mServiceObject) { + // We need to not be holding the service manager's monitor while calling + // QueryInterface, because it invokes user code which could try to re-enter + // the service manager, or try to grab some other lock/monitor/condvar + // and deadlock, e.g. bug 282743. + // `entry` is valid until XPCOM shutdown, so we can safely use it after + // exiting the lock. + lock.Unlock(); + return entry->mServiceObject->QueryInterface(aIID, aResult); + } + + PRThread* currentPRThread = PR_GetCurrentThread(); + MOZ_ASSERT(currentPRThread, "This should never be null!"); + + // Needed to optimize the event loop below. + nsIThread* currentThread = nullptr; + + PRThread* pendingPRThread; + while ((pendingPRThread = GetPendingServiceThread(*entry->mCIDEntry->cid))) { + if (pendingPRThread == currentPRThread) { + NS_ERROR("Recursive GetService!"); + return NS_ERROR_NOT_AVAILABLE; + } + + SafeMutexAutoUnlock unlockPending(mLock); + + if (!currentThread) { + currentThread = NS_GetCurrentThread(); + MOZ_ASSERT(currentThread, "This should never be null!"); + } + + // This will process a single event or yield the thread if no event is + // pending. + if (!NS_ProcessNextEvent(currentThread, false)) { + PR_Sleep(PR_INTERVAL_NO_WAIT); + } + } + + if (currentThread && entry->mServiceObject) { + // If we have a currentThread then we must have waited on another thread + // to create the service. Grab it now if that succeeded. + lock.Unlock(); + return entry->mServiceObject->QueryInterface(aIID, aResult); + } + +#ifdef DEBUG + PendingServiceInfo* newInfo = +#endif + AddPendingService(*entry->mCIDEntry->cid, currentPRThread); + NS_ASSERTION(newInfo, "Failed to add info to the array!"); + + // We need to not be holding the service manager's lock while calling + // CreateInstance, because it invokes user code which could try to re-enter + // the service manager: + + nsresult rv; + { + SafeMutexAutoUnlock unlock(mLock); + rv = CreateInstanceByContractID(aContractID, nullptr, aIID, + getter_AddRefs(service)); + } + if (NS_SUCCEEDED(rv) && !service) { + NS_ERROR("Factory did not return an object but returned success"); + return NS_ERROR_SERVICE_NOT_FOUND; + } + +#ifdef DEBUG + pendingPRThread = GetPendingServiceThread(*entry->mCIDEntry->cid); + MOZ_ASSERT(pendingPRThread == currentPRThread, + "Pending service array has been changed!"); +#endif + RemovePendingService(*entry->mCIDEntry->cid); + + if (NS_FAILED(rv)) { + return rv; + } + + NS_ASSERTION(!entry->mServiceObject, "Created two instances of a service!"); + + entry->mServiceObject = service.forget(); + + lock.Unlock(); + + nsISupports** sresult = reinterpret_cast<nsISupports**>(aResult); + *sresult = entry->mServiceObject; + (*sresult)->AddRef(); + + return NS_OK; +} + +already_AddRefed<mozilla::ModuleLoader> +nsComponentManagerImpl::LoaderForExtension(const nsACString& aExt) +{ + nsCOMPtr<mozilla::ModuleLoader> loader = mLoaderMap.Get(aExt); + if (!loader) { + loader = do_GetServiceFromCategory("module-loader", + PromiseFlatCString(aExt).get()); + if (!loader) { + return nullptr; + } + + mLoaderMap.Put(aExt, loader); + } + + return loader.forget(); +} + +NS_IMETHODIMP +nsComponentManagerImpl::RegisterFactory(const nsCID& aClass, + const char* aName, + const char* aContractID, + nsIFactory* aFactory) +{ + if (!aFactory) { + // If a null factory is passed in, this call just wants to reset + // the contract ID to point to an existing CID entry. + if (!aContractID) { + return NS_ERROR_INVALID_ARG; + } + + SafeMutexAutoLock lock(mLock); + nsFactoryEntry* oldf = mFactories.Get(aClass); + if (!oldf) { + return NS_ERROR_FACTORY_NOT_REGISTERED; + } + + mContractIDs.Put(nsDependentCString(aContractID), oldf); + return NS_OK; + } + + nsAutoPtr<nsFactoryEntry> f(new nsFactoryEntry(aClass, aFactory)); + + SafeMutexAutoLock lock(mLock); + nsFactoryEntry* oldf = mFactories.Get(aClass); + if (oldf) { + return NS_ERROR_FACTORY_EXISTS; + } + + if (aContractID) { + mContractIDs.Put(nsDependentCString(aContractID), f); + } + + mFactories.Put(aClass, f.forget()); + + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::UnregisterFactory(const nsCID& aClass, + nsIFactory* aFactory) +{ + // Don't release the dying factory or service object until releasing + // the component manager monitor. + nsCOMPtr<nsIFactory> dyingFactory; + nsCOMPtr<nsISupports> dyingServiceObject; + + { + SafeMutexAutoLock lock(mLock); + nsFactoryEntry* f = mFactories.Get(aClass); + if (!f || f->mFactory != aFactory) { + return NS_ERROR_FACTORY_NOT_REGISTERED; + } + + mFactories.Remove(aClass); + + // This might leave a stale contractid -> factory mapping in + // place, so null out the factory entry (see + // nsFactoryEntry::GetFactory) + f->mFactory.swap(dyingFactory); + f->mServiceObject.swap(dyingServiceObject); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::AutoRegister(nsIFile* aLocation) +{ + XRE_AddManifestLocation(NS_EXTENSION_LOCATION, aLocation); + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::AutoUnregister(nsIFile* aLocation) +{ + NS_ERROR("AutoUnregister not implemented."); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsComponentManagerImpl::RegisterFactoryLocation(const nsCID& aCID, + const char* aClassName, + const char* aContractID, + nsIFile* aFile, + const char* aLoaderStr, + const char* aType) +{ + NS_ERROR("RegisterFactoryLocation not implemented."); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsComponentManagerImpl::UnregisterFactoryLocation(const nsCID& aCID, + nsIFile* aFile) +{ + NS_ERROR("UnregisterFactoryLocation not implemented."); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsComponentManagerImpl::IsCIDRegistered(const nsCID& aClass, + bool* aResult) +{ + *aResult = (nullptr != GetFactoryEntry(aClass)); + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::IsContractIDRegistered(const char* aClass, + bool* aResult) +{ + if (NS_WARN_IF(!aClass)) { + return NS_ERROR_INVALID_ARG; + } + + nsFactoryEntry* entry = GetFactoryEntry(aClass, strlen(aClass)); + + if (entry) { + // UnregisterFactory might have left a stale nsFactoryEntry in + // mContractIDs, so we should check to see whether this entry has + // anything useful. + *aResult = (bool(entry->mModule) || + bool(entry->mFactory) || + bool(entry->mServiceObject)); + } else { + *aResult = false; + } + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::EnumerateCIDs(nsISimpleEnumerator** aEnumerator) +{ + nsCOMArray<nsISupports> array; + for (auto iter = mFactories.Iter(); !iter.Done(); iter.Next()) { + const nsID& id = iter.Key(); + nsCOMPtr<nsISupportsID> wrapper = new nsSupportsID(); + wrapper->SetData(&id); + array.AppendObject(wrapper); + } + return NS_NewArrayEnumerator(aEnumerator, array); +} + +NS_IMETHODIMP +nsComponentManagerImpl::EnumerateContractIDs(nsISimpleEnumerator** aEnumerator) +{ + nsTArray<nsCString>* array = new nsTArray<nsCString>; + for (auto iter = mContractIDs.Iter(); !iter.Done(); iter.Next()) { + const nsACString& contract = iter.Key(); + array->AppendElement(contract); + } + + nsCOMPtr<nsIUTF8StringEnumerator> e; + nsresult rv = NS_NewAdoptingUTF8StringEnumerator(getter_AddRefs(e), array); + if (NS_FAILED(rv)) { + return rv; + } + + return CallQueryInterface(e, aEnumerator); +} + +NS_IMETHODIMP +nsComponentManagerImpl::CIDToContractID(const nsCID& aClass, + char** aResult) +{ + NS_ERROR("CIDTOContractID not implemented"); + return NS_ERROR_FACTORY_NOT_REGISTERED; +} + +NS_IMETHODIMP +nsComponentManagerImpl::ContractIDToCID(const char* aContractID, + nsCID** aResult) +{ + { + SafeMutexAutoLock lock(mLock); + nsFactoryEntry* entry = mContractIDs.Get(nsDependentCString(aContractID)); + if (entry) { + *aResult = (nsCID*)moz_xmalloc(sizeof(nsCID)); + **aResult = *entry->mCIDEntry->cid; + return NS_OK; + } + } + *aResult = nullptr; + return NS_ERROR_FACTORY_NOT_REGISTERED; +} + +MOZ_DEFINE_MALLOC_SIZE_OF(ComponentManagerMallocSizeOf) + +NS_IMETHODIMP +nsComponentManagerImpl::CollectReports(nsIHandleReportCallback* aHandleReport, + nsISupports* aData, bool aAnonymize) +{ + MOZ_COLLECT_REPORT( + "explicit/xpcom/component-manager", KIND_HEAP, UNITS_BYTES, + SizeOfIncludingThis(ComponentManagerMallocSizeOf), + "Memory used for the XPCOM component manager."); + + return NS_OK; +} + +size_t +nsComponentManagerImpl::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) + const +{ + size_t n = aMallocSizeOf(this); + + n += mLoaderMap.ShallowSizeOfExcludingThis(aMallocSizeOf); + + n += mFactories.ShallowSizeOfExcludingThis(aMallocSizeOf); + for (auto iter = mFactories.ConstIter(); !iter.Done(); iter.Next()) { + n += iter.Data()->SizeOfIncludingThis(aMallocSizeOf); + } + + n += mContractIDs.ShallowSizeOfExcludingThis(aMallocSizeOf); + for (auto iter = mContractIDs.ConstIter(); !iter.Done(); iter.Next()) { + // We don't measure the nsFactoryEntry data because it's owned by + // mFactories (which is measured above). + n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf); + } + + n += sStaticModules->ShallowSizeOfIncludingThis(aMallocSizeOf); + n += sModuleLocations->ShallowSizeOfIncludingThis(aMallocSizeOf); + + n += mKnownStaticModules.ShallowSizeOfExcludingThis(aMallocSizeOf); + n += mKnownModules.ShallowSizeOfExcludingThis(aMallocSizeOf); + + n += PL_SizeOfArenaPoolExcludingPool(&mArena, aMallocSizeOf); + + n += mPendingServices.ShallowSizeOfExcludingThis(aMallocSizeOf); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mLoaderMap's keys and values + // - mMon + // - sStaticModules' entries + // - sModuleLocations' entries + // - mNativeModuleLoader + // - mKnownStaticModules' entries? + // - mKnownModules' keys and values? + + return n; +} + +//////////////////////////////////////////////////////////////////////////////// +// nsFactoryEntry +//////////////////////////////////////////////////////////////////////////////// + +nsFactoryEntry::nsFactoryEntry(const mozilla::Module::CIDEntry* aEntry, + nsComponentManagerImpl::KnownModule* aModule) + : mCIDEntry(aEntry) + , mModule(aModule) +{ +} + +nsFactoryEntry::nsFactoryEntry(const nsCID& aCID, nsIFactory* aFactory) + : mCIDEntry(nullptr) + , mModule(nullptr) + , mFactory(aFactory) +{ + mozilla::Module::CIDEntry* e = new mozilla::Module::CIDEntry(); + nsCID* cid = new nsCID; + *cid = aCID; + e->cid = cid; + mCIDEntry = e; +} + +nsFactoryEntry::~nsFactoryEntry() +{ + // If this was a RegisterFactory entry, we own the CIDEntry/CID + if (!mModule) { + delete mCIDEntry->cid; + delete mCIDEntry; + } +} + +already_AddRefed<nsIFactory> +nsFactoryEntry::GetFactory() +{ + nsComponentManagerImpl::gComponentManager->mLock.AssertNotCurrentThreadOwns(); + + if (!mFactory) { + // RegisterFactory then UnregisterFactory can leave an entry in mContractIDs + // pointing to an unusable nsFactoryEntry. + if (!mModule) { + return nullptr; + } + + if (!mModule->Load()) { + return nullptr; + } + + // Don't set mFactory directly, it needs to be locked + nsCOMPtr<nsIFactory> factory; + + if (mModule->Module()->getFactoryProc) { + factory = mModule->Module()->getFactoryProc(*mModule->Module(), + *mCIDEntry); + } else if (mCIDEntry->getFactoryProc) { + factory = mCIDEntry->getFactoryProc(*mModule->Module(), *mCIDEntry); + } else { + NS_ASSERTION(mCIDEntry->constructorProc, "no getfactory or constructor"); + factory = new mozilla::GenericFactory(mCIDEntry->constructorProc); + } + if (!factory) { + return nullptr; + } + + SafeMutexAutoLock lock(nsComponentManagerImpl::gComponentManager->mLock); + // Threads can race to set mFactory + if (!mFactory) { + factory.swap(mFactory); + } + } + nsCOMPtr<nsIFactory> factory = mFactory; + return factory.forget(); +} + +size_t +nsFactoryEntry::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) +{ + size_t n = aMallocSizeOf(this); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mCIDEntry; + // - mModule; + // - mFactory; + // - mServiceObject; + + return n; +} + +//////////////////////////////////////////////////////////////////////////////// +// Static Access Functions +//////////////////////////////////////////////////////////////////////////////// + +nsresult +NS_GetComponentManager(nsIComponentManager** aResult) +{ + if (!nsComponentManagerImpl::gComponentManager) { + return NS_ERROR_NOT_INITIALIZED; + } + + NS_ADDREF(*aResult = nsComponentManagerImpl::gComponentManager); + return NS_OK; +} + +nsresult +NS_GetServiceManager(nsIServiceManager** aResult) +{ + if (!nsComponentManagerImpl::gComponentManager) { + return NS_ERROR_NOT_INITIALIZED; + } + + NS_ADDREF(*aResult = nsComponentManagerImpl::gComponentManager); + return NS_OK; +} + + +nsresult +NS_GetComponentRegistrar(nsIComponentRegistrar** aResult) +{ + if (!nsComponentManagerImpl::gComponentManager) { + return NS_ERROR_NOT_INITIALIZED; + } + + NS_ADDREF(*aResult = nsComponentManagerImpl::gComponentManager); + return NS_OK; +} + +EXPORT_XPCOM_API(nsresult) +XRE_AddStaticComponent(const mozilla::Module* aComponent) +{ + nsComponentManagerImpl::InitializeStaticModules(); + nsComponentManagerImpl::sStaticModules->AppendElement(aComponent); + + if (nsComponentManagerImpl::gComponentManager && + nsComponentManagerImpl::NORMAL == + nsComponentManagerImpl::gComponentManager->mStatus) { + nsComponentManagerImpl::gComponentManager->RegisterModule(aComponent, + nullptr); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsComponentManagerImpl::AddBootstrappedManifestLocation(nsIFile* aLocation) +{ + nsString path; + nsresult rv = aLocation->GetPath(path); + if (NS_FAILED(rv)) { + return rv; + } + + if (Substring(path, path.Length() - 4).EqualsLiteral(".xpi")) { + return XRE_AddJarManifestLocation(NS_BOOTSTRAPPED_LOCATION, aLocation); + } + + nsCOMPtr<nsIFile> manifest = + CloneAndAppend(aLocation, NS_LITERAL_CSTRING("chrome.manifest")); + return XRE_AddManifestLocation(NS_BOOTSTRAPPED_LOCATION, manifest); +} + +NS_IMETHODIMP +nsComponentManagerImpl::RemoveBootstrappedManifestLocation(nsIFile* aLocation) +{ + nsCOMPtr<nsIChromeRegistry> cr = mozilla::services::GetChromeRegistryService(); + if (!cr) { + return NS_ERROR_FAILURE; + } + + nsString path; + nsresult rv = aLocation->GetPath(path); + if (NS_FAILED(rv)) { + return rv; + } + + nsComponentManagerImpl::ComponentLocation elem; + elem.type = NS_BOOTSTRAPPED_LOCATION; + + if (Substring(path, path.Length() - 4).EqualsLiteral(".xpi")) { + elem.location.Init(aLocation, "chrome.manifest"); + } else { + nsCOMPtr<nsIFile> lf = + CloneAndAppend(aLocation, NS_LITERAL_CSTRING("chrome.manifest")); + elem.location.Init(lf); + } + + // Remove reference. + nsComponentManagerImpl::sModuleLocations->RemoveElement(elem, + ComponentLocationComparator()); + + rv = cr->CheckForNewChrome(); + return rv; +} + +NS_IMETHODIMP +nsComponentManagerImpl::GetManifestLocations(nsIArray** aLocations) +{ + NS_ENSURE_ARG_POINTER(aLocations); + *aLocations = nullptr; + + if (!sModuleLocations) { + return NS_ERROR_NOT_INITIALIZED; + } + + nsCOMPtr<nsIMutableArray> locations = nsArray::Create(); + nsresult rv; + for (uint32_t i = 0; i < sModuleLocations->Length(); ++i) { + ComponentLocation& l = sModuleLocations->ElementAt(i); + FileLocation loc = l.location; + nsCString uriString; + loc.GetURIString(uriString); + nsCOMPtr<nsIURI> uri; + rv = NS_NewURI(getter_AddRefs(uri), uriString); + if (NS_SUCCEEDED(rv)) { + locations->AppendElement(uri, false); + } + } + + locations.forget(aLocations); + return NS_OK; +} + +EXPORT_XPCOM_API(nsresult) +XRE_AddManifestLocation(NSLocationType aType, nsIFile* aLocation) +{ + nsComponentManagerImpl::InitializeModuleLocations(); + nsComponentManagerImpl::ComponentLocation* c = + nsComponentManagerImpl::sModuleLocations->AppendElement(); + c->type = aType; + c->location.Init(aLocation); + + if (nsComponentManagerImpl::gComponentManager && + nsComponentManagerImpl::NORMAL == + nsComponentManagerImpl::gComponentManager->mStatus) { + nsComponentManagerImpl::gComponentManager->RegisterManifest(aType, + c->location, + false); + } + + return NS_OK; +} + +EXPORT_XPCOM_API(nsresult) +XRE_AddJarManifestLocation(NSLocationType aType, nsIFile* aLocation) +{ + nsComponentManagerImpl::InitializeModuleLocations(); + nsComponentManagerImpl::ComponentLocation* c = + nsComponentManagerImpl::sModuleLocations->AppendElement(); + + c->type = aType; + c->location.Init(aLocation, "chrome.manifest"); + + if (nsComponentManagerImpl::gComponentManager && + nsComponentManagerImpl::NORMAL == + nsComponentManagerImpl::gComponentManager->mStatus) { + nsComponentManagerImpl::gComponentManager->RegisterManifest(aType, + c->location, + false); + } + + return NS_OK; +} + diff --git a/xpcom/components/nsComponentManager.h b/xpcom/components/nsComponentManager.h new file mode 100644 index 0000000000..f0e0f684a1 --- /dev/null +++ b/xpcom/components/nsComponentManager.h @@ -0,0 +1,363 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsComponentManager_h__ +#define nsComponentManager_h__ + +#include "nsXPCOM.h" + +#include "xpcom-private.h" +#include "nsIComponentManager.h" +#include "nsIComponentRegistrar.h" +#include "nsIMemoryReporter.h" +#include "nsIServiceManager.h" +#include "nsIFile.h" +#include "mozilla/Atomics.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/Module.h" +#include "mozilla/ModuleLoader.h" +#include "mozilla/Mutex.h" +#include "nsXULAppAPI.h" +#include "nsNativeModuleLoader.h" +#include "nsIFactory.h" +#include "nsIInterfaceRequestor.h" +#include "nsIInterfaceRequestorUtils.h" +#include "PLDHashTable.h" +#include "prtime.h" +#include "nsCOMPtr.h" +#include "nsAutoPtr.h" +#include "nsWeakReference.h" +#include "plarena.h" +#include "nsCOMArray.h" +#include "nsDataHashtable.h" +#include "nsInterfaceHashtable.h" +#include "nsClassHashtable.h" +#include "nsTArray.h" + +#include "mozilla/Omnijar.h" +#include "mozilla/Attributes.h" + +struct nsFactoryEntry; +class nsIServiceManager; +struct PRThread; + +#define NS_COMPONENTMANAGER_CID \ +{ /* 91775d60-d5dc-11d2-92fb-00e09805570f */ \ + 0x91775d60, \ + 0xd5dc, \ + 0x11d2, \ + {0x92, 0xfb, 0x00, 0xe0, 0x98, 0x05, 0x57, 0x0f} \ +} + +/* keys for registry use */ +extern const char xpcomKeyName[]; +extern const char xpcomComponentsKeyName[]; +extern const char lastModValueName[]; +extern const char fileSizeValueName[]; +extern const char nativeComponentType[]; +extern const char staticComponentType[]; + +#ifdef DEBUG +#define XPCOM_CHECK_PENDING_CIDS +#endif +//////////////////////////////////////////////////////////////////////////////// + +extern const mozilla::Module kXPCOMModule; + +/** + * This is a wrapper around mozilla::Mutex which provides runtime + * checking for a deadlock where the same thread tries to lock a mutex while + * it is already locked. This checking is present in both debug and release + * builds. + */ +class SafeMutex +{ +public: + explicit SafeMutex(const char* aName) + : mMutex(aName) + , mOwnerThread(nullptr) + { + } + + ~SafeMutex() {} + + void Lock() + { + AssertNotCurrentThreadOwns(); + mMutex.Lock(); + MOZ_ASSERT(mOwnerThread == nullptr); + mOwnerThread = PR_GetCurrentThread(); + } + + void Unlock() + { + MOZ_ASSERT(mOwnerThread == PR_GetCurrentThread()); + mOwnerThread = nullptr; + mMutex.Unlock(); + } + + void AssertCurrentThreadOwns() const + { + // This method is a debug-only check + MOZ_ASSERT(mOwnerThread == PR_GetCurrentThread()); + } + + MOZ_NEVER_INLINE void AssertNotCurrentThreadOwns() const + { + // This method is a release-mode check + if (PR_GetCurrentThread() == mOwnerThread) { + MOZ_CRASH(); + } + } + +private: + mozilla::Mutex mMutex; + mozilla::Atomic<PRThread*, mozilla::Relaxed> mOwnerThread; +}; + +typedef mozilla::BaseAutoLock<SafeMutex> SafeMutexAutoLock; +typedef mozilla::BaseAutoUnlock<SafeMutex> SafeMutexAutoUnlock; + +class nsComponentManagerImpl final + : public nsIComponentManager + , public nsIServiceManager + , public nsSupportsWeakReference + , public nsIComponentRegistrar + , public nsIInterfaceRequestor + , public nsIMemoryReporter +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSICOMPONENTMANAGER + NS_DECL_NSICOMPONENTREGISTRAR + NS_DECL_NSIMEMORYREPORTER + + static nsresult Create(nsISupports* aOuter, REFNSIID aIID, void** aResult); + + nsresult RegistryLocationForFile(nsIFile* aFile, + nsCString& aResult); + nsresult FileForRegistryLocation(const nsCString& aLocation, + nsIFile** aSpec); + + NS_DECL_NSISERVICEMANAGER + + // nsComponentManagerImpl methods: + nsComponentManagerImpl(); + + static nsComponentManagerImpl* gComponentManager; + nsresult Init(); + + nsresult Shutdown(void); + + nsresult FreeServices(); + + already_AddRefed<mozilla::ModuleLoader> LoaderForExtension(const nsACString& aExt); + nsInterfaceHashtable<nsCStringHashKey, mozilla::ModuleLoader> mLoaderMap; + + already_AddRefed<nsIFactory> FindFactory(const nsCID& aClass); + already_AddRefed<nsIFactory> FindFactory(const char* aContractID, + uint32_t aContractIDLen); + + already_AddRefed<nsIFactory> LoadFactory(nsFactoryEntry* aEntry); + + nsFactoryEntry* GetFactoryEntry(const char* aContractID, + uint32_t aContractIDLen); + nsFactoryEntry* GetFactoryEntry(const nsCID& aClass); + + nsDataHashtable<nsIDHashKey, nsFactoryEntry*> mFactories; + nsDataHashtable<nsCStringHashKey, nsFactoryEntry*> mContractIDs; + + SafeMutex mLock; + + static void InitializeStaticModules(); + static void InitializeModuleLocations(); + + struct ComponentLocation + { + NSLocationType type; + mozilla::FileLocation location; + }; + + class ComponentLocationComparator + { + public: + bool Equals(const ComponentLocation& aA, const ComponentLocation& aB) const + { + return (aA.type == aB.type && aA.location.Equals(aB.location)); + } + }; + + static nsTArray<const mozilla::Module*>* sStaticModules; + static nsTArray<ComponentLocation>* sModuleLocations; + + nsNativeModuleLoader mNativeModuleLoader; + + class KnownModule + { + public: + /** + * Static or binary module. + */ + KnownModule(const mozilla::Module* aModule, mozilla::FileLocation& aFile) + : mModule(aModule) + , mFile(aFile) + , mLoaded(false) + , mFailed(false) + { + } + + explicit KnownModule(const mozilla::Module* aModule) + : mModule(aModule) + , mLoaded(false) + , mFailed(false) + { + } + + explicit KnownModule(mozilla::FileLocation& aFile) + : mModule(nullptr) + , mFile(aFile) + , mLoader(nullptr) + , mLoaded(false) + , mFailed(false) + { + } + + ~KnownModule() + { + if (mLoaded && mModule->unloadProc) { + mModule->unloadProc(); + } + } + + bool EnsureLoader(); + bool Load(); + + const mozilla::Module* Module() const { return mModule; } + + /** + * For error logging, get a description of this module, either the + * file path, or <static module>. + */ + nsCString Description() const; + + private: + const mozilla::Module* mModule; + mozilla::FileLocation mFile; + nsCOMPtr<mozilla::ModuleLoader> mLoader; + bool mLoaded; + bool mFailed; + }; + + // The KnownModule is kept alive by these members, it is + // referenced by pointer from the factory entries. + nsTArray<nsAutoPtr<KnownModule>> mKnownStaticModules; + // The key is the URI string of the module + nsClassHashtable<nsCStringHashKey, KnownModule> mKnownModules; + + // Mutex not held + void RegisterModule(const mozilla::Module* aModule, + mozilla::FileLocation* aFile); + + + // Mutex held + void RegisterCIDEntryLocked(const mozilla::Module::CIDEntry* aEntry, + KnownModule* aModule); + void RegisterContractIDLocked(const mozilla::Module::ContractIDEntry* aEntry); + + // Mutex not held + void RegisterManifest(NSLocationType aType, mozilla::FileLocation& aFile, + bool aChromeOnly); + + struct ManifestProcessingContext + { + ManifestProcessingContext(NSLocationType aType, + mozilla::FileLocation& aFile, bool aChromeOnly) + : mType(aType) + , mFile(aFile) + , mChromeOnly(aChromeOnly) + { + } + + ~ManifestProcessingContext() {} + + NSLocationType mType; + mozilla::FileLocation mFile; + bool mChromeOnly; + }; + + void ManifestManifest(ManifestProcessingContext& aCx, int aLineNo, + char* const* aArgv); + void ManifestBinaryComponent(ManifestProcessingContext& aCx, int aLineNo, + char* const* aArgv); + void ManifestXPT(ManifestProcessingContext& aCx, int aLineNo, + char* const* aArgv); + void ManifestComponent(ManifestProcessingContext& aCx, int aLineNo, + char* const* aArgv); + void ManifestContract(ManifestProcessingContext& aCx, int aLineNo, + char* const* aArgv); + void ManifestCategory(ManifestProcessingContext& aCx, int aLineNo, + char* const* aArgv); + + void RereadChromeManifests(bool aChromeOnly = true); + + // Shutdown + enum + { + NOT_INITIALIZED, + NORMAL, + SHUTDOWN_IN_PROGRESS, + SHUTDOWN_COMPLETE + } mStatus; + + PLArenaPool mArena; + + struct PendingServiceInfo + { + const nsCID* cid; + PRThread* thread; + }; + + inline PendingServiceInfo* AddPendingService(const nsCID& aServiceCID, + PRThread* aThread); + inline void RemovePendingService(const nsCID& aServiceCID); + inline PRThread* GetPendingServiceThread(const nsCID& aServiceCID) const; + + nsTArray<PendingServiceInfo> mPendingServices; + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; + +private: + ~nsComponentManagerImpl(); +}; + + +#define NS_MAX_FILENAME_LEN 1024 + +#define NS_ERROR_IS_DIR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_XPCOM, 24) + +struct nsFactoryEntry +{ + nsFactoryEntry(const mozilla::Module::CIDEntry* aEntry, + nsComponentManagerImpl::KnownModule* aModule); + + // nsIComponentRegistrar.registerFactory support + nsFactoryEntry(const nsCID& aClass, nsIFactory* aFactory); + + ~nsFactoryEntry(); + + already_AddRefed<nsIFactory> GetFactory(); + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf); + + const mozilla::Module::CIDEntry* mCIDEntry; + nsComponentManagerImpl::KnownModule* mModule; + + nsCOMPtr<nsIFactory> mFactory; + nsCOMPtr<nsISupports> mServiceObject; +}; + +#endif // nsComponentManager_h__ diff --git a/xpcom/components/nsICategoryManager.idl b/xpcom/components/nsICategoryManager.idl new file mode 100644 index 0000000000..e33d3ed8bf --- /dev/null +++ b/xpcom/components/nsICategoryManager.idl @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsISimpleEnumerator; + +/* + * nsICategoryManager + */ + +[scriptable, uuid(3275b2cd-af6d-429a-80d7-f0c5120342ac)] +interface nsICategoryManager : nsISupports +{ + /** + * Get the value for the given category's entry. + * @param aCategory The name of the category ("protocol") + * @param aEntry The entry you're looking for ("http") + * @return The value. + */ + string getCategoryEntry(in string aCategory, in string aEntry); + + /** + * Add an entry to a category. + * @param aCategory The name of the category ("protocol") + * @param aEntry The entry to be added ("http") + * @param aValue The value for the entry ("moz.httprulez.1") + * @param aPersist Should this data persist between invocations? + * @param aReplace Should we replace an existing entry? + * @return Previous entry, if any + */ + string addCategoryEntry(in string aCategory, in string aEntry, + in string aValue, in boolean aPersist, + in boolean aReplace); + + /** + * Delete an entry from the category. + * @param aCategory The name of the category ("protocol") + * @param aEntry The entry to be added ("http") + * @param aPersist Delete persistent data from registry, if present? + */ + void deleteCategoryEntry(in string aCategory, in string aEntry, + in boolean aPersist); + + /** + * Delete a category and all entries. + * @param aCategory The category to be deleted. + */ + void deleteCategory(in string aCategory); + + /** + * Enumerate the entries in a category. + * @param aCategory The category to be enumerated. + * @return a simple enumerator, each result QIs to + * nsISupportsCString. + */ + nsISimpleEnumerator enumerateCategory(in string aCategory); + + /** + * Enumerate all existing categories + * @param aCategory The category to be enumerated. + * @return a simple enumerator, each result QIs to + * nsISupportsCString. + */ + nsISimpleEnumerator enumerateCategories(); +}; + diff --git a/xpcom/components/nsIClassInfo.idl b/xpcom/components/nsIClassInfo.idl new file mode 100644 index 0000000000..639d12aa87 --- /dev/null +++ b/xpcom/components/nsIClassInfo.idl @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsIXPCScriptable; + +/** + * Provides information about a specific implementation class. If you want + * your class to implement nsIClassInfo, see nsIClassInfoImpl.h for + * instructions--you most likely do not want to inherit from nsIClassInfo. + */ + +[scriptable, uuid(a60569d7-d401-4677-ba63-2aa5971af25d)] +interface nsIClassInfo : nsISupports +{ + /** + * Get an ordered list of the interface ids that instances of the class + * promise to implement. Note that nsISupports is an implicit member + * of any such list and need not be included. + * + * Should set *count = 0 and *array = null and return NS_OK if getting the + * list is not supported. + */ + void getInterfaces(out uint32_t count, + [array, size_is(count), retval] out nsIIDPtr array); + + /** + * Return an object to assist XPConnect in supplying JavaScript-specific + * behavior to callers of the instance object, or null if not needed. + */ + nsIXPCScriptable getScriptableHelper(); + + /** + * A contract ID through which an instance of this class can be created + * (or accessed as a service, if |flags & SINGLETON|), or null. + */ + readonly attribute string contractID; + + /** + * A human readable string naming the class, or null. + */ + readonly attribute string classDescription; + + /** + * A class ID through which an instance of this class can be created + * (or accessed as a service, if |flags & SINGLETON|), or null. + */ + readonly attribute nsCIDPtr classID; + + /** + * Bitflags for 'flags' attribute. + */ + const uint32_t SINGLETON = 1 << 0; + const uint32_t THREADSAFE = 1 << 1; + const uint32_t MAIN_THREAD_ONLY = 1 << 2; + const uint32_t DOM_OBJECT = 1 << 3; + const uint32_t PLUGIN_OBJECT = 1 << 4; + const uint32_t SINGLETON_CLASSINFO = 1 << 5; + + /** + * 'flags' attribute bitflag: whether objects of this type implement + * nsIContent. + */ + const uint32_t CONTENT_NODE = 1 << 6; + + // The high order bit is RESERVED for consumers of these flags. + // No implementor of this interface should ever return flags + // with this bit set. + const uint32_t RESERVED = 1 << 31; + + + readonly attribute uint32_t flags; + + /** + * Also a class ID through which an instance of this class can be created + * (or accessed as a service, if |flags & SINGLETON|). If the class does + * not have a CID, it should return NS_ERROR_NOT_AVAILABLE. This attribute + * exists so C++ callers can avoid allocating and freeing a CID, as would + * happen if they used classID. + */ + [noscript] readonly attribute nsCID classIDNoAlloc; + +}; diff --git a/xpcom/components/nsIComponentManager.idl b/xpcom/components/nsIComponentManager.idl new file mode 100644 index 0000000000..1e5693a285 --- /dev/null +++ b/xpcom/components/nsIComponentManager.idl @@ -0,0 +1,106 @@ +/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * The nsIComponentManager interface. + */ + +#include "nsISupports.idl" + +interface nsIFile; +interface nsIFactory; +interface nsIArray; + +[scriptable, uuid(d604ffc3-1ba3-4f6c-b65f-1ed4199364c3)] +interface nsIComponentManager : nsISupports +{ + /** + * getClassObject + * + * Returns the factory object that can be used to create instances of + * CID aClass + * + * @param aClass The classid of the factory that is being requested + */ + void getClassObject(in nsCIDRef aClass, + in nsIIDRef aIID, + [iid_is(aIID),retval] out nsQIResult result); + + /** + * getClassObjectByContractID + * + * Returns the factory object that can be used to create instances of + * CID aClass + * + * @param aClass The classid of the factory that is being requested + */ + void getClassObjectByContractID(in string aContractID, + in nsIIDRef aIID, + [iid_is(aIID),retval] out nsQIResult result); + + + /** + * createInstance + * + * Create an instance of the CID aClass and return the interface aIID. + * + * @param aClass : ClassID of object instance requested + * @param aDelegate : Used for aggregation + * @param aIID : IID of interface requested + */ + void createInstance(in nsCIDRef aClass, + in nsISupports aDelegate, + in nsIIDRef aIID, + [iid_is(aIID),retval] out nsQIResult result); + + /** + * createInstanceByContractID + * + * Create an instance of the CID that implements aContractID and return the + * interface aIID. + * + * @param aContractID : aContractID of object instance requested + * @param aDelegate : Used for aggregation + * @param aIID : IID of interface requested + */ + void createInstanceByContractID(in string aContractID, + in nsISupports aDelegate, + in nsIIDRef aIID, + [iid_is(aIID),retval] out nsQIResult result); + + /** + * addBootstrappedManifestLocation + * + * Adds a bootstrapped manifest location on runtime. + * + * @param aLocation : A directory where chrome.manifest resides, + * or an XPI with it on the root. + */ + void addBootstrappedManifestLocation(in nsIFile aLocation); + + /** + * removeBootstrappedManifestLocation + * + * Removes a bootstrapped manifest location on runtime. + * + * @param aLocation : A directory where chrome.manifest resides, + * or an XPI with it on the root. + */ + void removeBootstrappedManifestLocation(in nsIFile aLocation); + + /** + * getManifestLocations + * + * Get an array of nsIURIs of all registered and builtin manifest locations. + */ + nsIArray getManifestLocations(); +}; + + +%{ C++ +#ifdef MOZILLA_INTERNAL_API +#include "nsComponentManagerUtils.h" +#endif +%} C++ diff --git a/xpcom/components/nsIComponentRegistrar.idl b/xpcom/components/nsIComponentRegistrar.idl new file mode 100644 index 0000000000..e12acdf25b --- /dev/null +++ b/xpcom/components/nsIComponentRegistrar.idl @@ -0,0 +1,163 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * The nsIComponentRegistrar interface. + */ + +#include "nsISupports.idl" + +interface nsIFile; +interface nsIFactory; +interface nsISimpleEnumerator; + +[scriptable, uuid(2417cbfe-65ad-48a6-b4b6-eb84db174392)] +interface nsIComponentRegistrar : nsISupports +{ + /** + * autoRegister + * + * Register a .manifest file, or an entire directory containing + * these files. Registration lasts for this run only, and is not cached. + * + * @note Formerly this method would register component files directly. This + * is no longer supported. + */ + void autoRegister(in nsIFile aSpec); + + /** + * autoUnregister + * @status OBSOLETE: This method is no longer implemented, but preserved + * in this interface for binary compatibility with + * Mozilla 1.9.2. + */ + void autoUnregister(in nsIFile aSpec); + + + /** + * registerFactory + * + * Register a factory with a given ContractID, CID and Class Name. + * + * @param aClass : CID of object + * @param aClassName : Class Name of CID (unused) + * @param aContractID : ContractID associated with CID aClass. May be null + * if no contract ID is needed. + * @param aFactory : Factory that will be registered for CID aClass. + * If aFactory is null, the contract will be associated + * with a previously registered CID. + */ + void registerFactory(in nsCIDRef aClass, + in string aClassName, + in string aContractID, + in nsIFactory aFactory); + + /** + * unregisterFactory + * + * Unregister a factory associated with CID aClass. + * + * @param aClass : CID being unregistered + * @param aFactory : Factory previously registered to create instances of + * CID aClass. + * + * @throws NS_ERROR* Method failure. + */ + void unregisterFactory(in nsCIDRef aClass, + in nsIFactory aFactory); + + /** + * registerFactoryLocation + * @status OBSOLETE: This method is no longer implemented, but preserved + * in this interface for binary compatibility with + * Mozilla 1.9.2. + */ + void registerFactoryLocation(in nsCIDRef aClass, + in string aClassName, + in string aContractID, + in nsIFile aFile, + in string aLoaderStr, + in string aType); + + /** + * unregisterFactoryLocation + * @status OBSOLETE: This method is no longer implemented, but preserved + * in this interface for binary compatibility with + * Mozilla 1.9.2. + */ + void unregisterFactoryLocation(in nsCIDRef aClass, + in nsIFile aFile); + + /** + * isCIDRegistered + * + * Returns true if a factory is registered for the CID. + * + * @param aClass : CID queried for registeration + * @return : true if a factory is registered for CID + * false otherwise. + */ + boolean isCIDRegistered(in nsCIDRef aClass); + + /** + * isContractIDRegistered + * + * Returns true if a factory is registered for the contract id. + * + * @param aClass : contract id queried for registeration + * @return : true if a factory is registered for contract id + * false otherwise. + */ + boolean isContractIDRegistered(in string aContractID); + + /** + * enumerateCIDs + * + * Enumerate the list of all registered CIDs. + * + * @return : enumerator for CIDs. Elements of the enumeration can be QI'ed + * for the nsISupportsID interface. From the nsISupportsID, you + * can obtain the actual CID. + */ + nsISimpleEnumerator enumerateCIDs(); + + /** + * enumerateContractIDs + * + * Enumerate the list of all registered ContractIDs. + * + * @return : enumerator for ContractIDs. Elements of the enumeration can be + * QI'ed for the nsISupportsCString interface. From the + * nsISupportsCString interface, you can obtain the actual + * Contract ID string. + */ + nsISimpleEnumerator enumerateContractIDs(); + + /** + * CIDToContractID + * @status OBSOLETE: This method is no longer implemented, but preserved + * in this interface for binary compatibility with + * Mozilla 1.9.2. + */ + string CIDToContractID(in nsCIDRef aClass); + + /** + * contractIDToCID + * + * Returns the CID for a given Contract ID, if one exists and is registered. + * + * @return : Contract ID. + */ + nsCIDPtr contractIDToCID(in string aContractID); +}; + + + + + + + + + + diff --git a/xpcom/components/nsIFactory.idl b/xpcom/components/nsIFactory.idl new file mode 100644 index 0000000000..54152976f9 --- /dev/null +++ b/xpcom/components/nsIFactory.idl @@ -0,0 +1,42 @@ +/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +/** + * A class factory allows the creation of nsISupports derived + * components without specifying a concrete base class. + */ + +[scriptable, object, uuid(00000001-0000-0000-c000-000000000046)] +interface nsIFactory : nsISupports { + /** + * Creates an instance of a component. + * + * @param aOuter Pointer to a component that wishes to be aggregated + * in the resulting instance. This will be nullptr if no + * aggregation is requested. + * @param iid The IID of the interface being requested in + * the component which is being currently created. + * @param result [out] Pointer to the newly created instance, if successful. + * @throws NS_NOINTERFACE - Interface not accessible. + * @throws NS_ERROR_NO_AGGREGATION - if an 'outer' object is supplied, but the + * component is not aggregatable. + * NS_ERROR* - Method failure. + */ + void createInstance(in nsISupports aOuter, in nsIIDRef iid, + [retval, iid_is(iid)] out nsQIResult result); + + /** + * LockFactory provides the client a way to keep the component + * in memory until it is finished with it. The client can call + * LockFactory(PR_TRUE) to lock the factory and LockFactory(PR_FALSE) + * to release the factory. + * + * @param lock - Must be PR_TRUE or PR_FALSE + * @throws NS_ERROR* - Method failure. + */ + void lockFactory(in boolean lock); +}; diff --git a/xpcom/components/nsIModule.idl b/xpcom/components/nsIModule.idl new file mode 100644 index 0000000000..4570a01991 --- /dev/null +++ b/xpcom/components/nsIModule.idl @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsIFile; +interface nsIComponentManager; + +/** + * The nsIModule interface. + */ + +[scriptable, uuid(7392D032-5371-11d3-994E-00805FD26FEE)] +interface nsIModule : nsISupports +{ + /** + * Object Instance Creation + * + * Obtains a Class Object from a nsIModule for a given CID and IID pair. + * This class object can either be query to a nsIFactory or a may be + * query to a nsIClassInfo. + * + * @param aCompMgr : The global component manager + * @param aClass : ClassID of object instance requested + * @param aIID : IID of interface requested + * + */ + void getClassObject(in nsIComponentManager aCompMgr, + in nsCIDRef aClass, + in nsIIDRef aIID, + [retval, iid_is(aIID)] out nsQIResult aResult); + + + /** + * One time registration callback + * + * When the nsIModule is discovered, this method will be + * called so that any setup registration can be preformed. + * + * @param aCompMgr : The global component manager + * @param aLocation : The location of the nsIModule on disk + * @param aLoaderStr: Opaque loader specific string + * @param aType : Loader Type being used to load this module + */ + void registerSelf(in nsIComponentManager aCompMgr, + in nsIFile aLocation, + in string aLoaderStr, + in string aType); + /** + * One time unregistration callback + * + * When the nsIModule is being unregistered, this method will be + * called so that any unregistration can be preformed + * + * @param aCompMgr : The global component manager + * @param aLocation : The location of the nsIModule on disk + * @param aLoaderStr : Opaque loader specific string + * + */ + void unregisterSelf(in nsIComponentManager aCompMgr, + in nsIFile aLocation, + in string aLoaderStr); + + /** + * Module load management + * + * @param aCompMgr : The global component manager + * + * @return indicates to the caller if the module can be unloaded. + * Returning PR_TRUE isn't a guarantee that the module will be + * unloaded. It constitues only willingness of the module to be + * unloaded. It is very important to ensure that no outstanding + * references to the module's code/data exist before returning + * PR_TRUE. + * Returning PR_FALSE guaratees that the module won't be unloaded. + */ + boolean canUnload(in nsIComponentManager aCompMgr); +}; + + diff --git a/xpcom/components/nsIServiceManager.idl b/xpcom/components/nsIServiceManager.idl new file mode 100644 index 0000000000..45c1c3cc2c --- /dev/null +++ b/xpcom/components/nsIServiceManager.idl @@ -0,0 +1,72 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +#include "nsISupports.idl" + +/** + * The nsIServiceManager manager interface provides a means to obtain + * global services in an application. The service manager depends on the + * repository to find and instantiate factories to obtain services. + * + * Users of the service manager must first obtain a pointer to the global + * service manager by calling NS_GetServiceManager. After that, + * they can request specific services by calling GetService. When they are + * finished they can NS_RELEASE() the service as usual. + * + * A user of a service may keep references to particular services indefinitely + * and only must call Release when it shuts down. + */ + +[scriptable, uuid(8bb35ed9-e332-462d-9155-4a002ab5c958)] +interface nsIServiceManager : nsISupports +{ + /** + * getServiceByContractID + * + * Returns the instance that implements aClass or aContractID and the + * interface aIID. This may result in the instance being created. + * + * @param aClass or aContractID : aClass or aContractID of object + * instance requested + * @param aIID : IID of interface requested + * @param result : resulting service + */ + void getService(in nsCIDRef aClass, + in nsIIDRef aIID, + [iid_is(aIID),retval] out nsQIResult result); + + void getServiceByContractID(in string aContractID, + in nsIIDRef aIID, + [iid_is(aIID),retval] out nsQIResult result); + + /** + * isServiceInstantiated + * + * isServiceInstantiated will return a true if the service has already + * been created, or throw otherwise + * + * @param aClass or aContractID : aClass or aContractID of object + * instance requested + * @param aIID : IID of interface requested + * @throws NS_ERROR_SERVICE_NOT_AVAILABLE if the service hasn't been + * instantiated + * @throws NS_NOINTERFACE if the IID given isn't supported by the object + */ + boolean isServiceInstantiated(in nsCIDRef aClass, in nsIIDRef aIID); + boolean isServiceInstantiatedByContractID(in string aContractID, in nsIIDRef aIID); +}; + + +%{C++ +// Observing xpcom autoregistration. Topics will be 'start' and 'stop'. +#define NS_XPCOM_AUTOREGISTRATION_OBSERVER_ID "xpcom-autoregistration" + +#ifdef MOZILLA_INTERNAL_API +#include "nsXPCOM.h" +#include "nsComponentManagerUtils.h" +#include "nsServiceManagerUtils.h" +#endif +%} + diff --git a/xpcom/components/nsNativeModuleLoader.cpp b/xpcom/components/nsNativeModuleLoader.cpp new file mode 100644 index 0000000000..6452f5d1bd --- /dev/null +++ b/xpcom/components/nsNativeModuleLoader.cpp @@ -0,0 +1,220 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * This Original Code has been modified by IBM Corporation. + * Modifications made by IBM described herein are + * Copyright (c) International Business Machines + * Corporation, 2000 + * + * Modifications to Mozilla code or documentation + * identified per MPL Section 3.3 + * + * Date Modified by Description of modification + * 04/20/2000 IBM Corp. Added PR_CALLBACK for Optlink use in OS2 + */ + +#include "nsNativeModuleLoader.h" + +#include "mozilla/Logging.h" +#include "prinit.h" +#include "prerror.h" + +#include "nsComponentManager.h" +#include "ManifestParser.h" // for LogMessage +#include "nsCRTGlue.h" +#include "nsThreadUtils.h" +#include "nsTraceRefcnt.h" + +#include "nsIFile.h" +#include "mozilla/WindowsDllBlocklist.h" + +#ifdef XP_WIN +#include <windows.h> +#endif + +#ifdef XP_MACOSX +#include <signal.h> +#endif + +#ifdef DEBUG +#define IMPLEMENT_BREAK_AFTER_LOAD +#endif + +using namespace mozilla; + +static LazyLogModule sNativeModuleLoaderLog("nsNativeModuleLoader"); +#define LOG(level, args) MOZ_LOG(sNativeModuleLoaderLog, level, args) + +nsresult +nsNativeModuleLoader::Init() +{ + MOZ_ASSERT(NS_IsMainThread(), "Startup not on main thread?"); + LOG(LogLevel::Debug, ("nsNativeModuleLoader::Init()")); + return NS_OK; +} + +class LoadModuleMainThreadRunnable : public Runnable +{ +public: + LoadModuleMainThreadRunnable(nsNativeModuleLoader* aLoader, + FileLocation& aFile) + : mManager(nsComponentManagerImpl::gComponentManager) + , mLoader(aLoader) + , mFile(aFile) + , mResult(nullptr) + { + } + + NS_IMETHOD Run() override + { + mResult = mLoader->LoadModule(mFile); + return NS_OK; + } + + RefPtr<nsComponentManagerImpl> mManager; + nsNativeModuleLoader* mLoader; + FileLocation mFile; + const mozilla::Module* mResult; +}; + +const mozilla::Module* +nsNativeModuleLoader::LoadModule(FileLocation& aFile) +{ + if (aFile.IsZip()) { + NS_ERROR("Binary components cannot be loaded from JARs"); + return nullptr; + } + nsCOMPtr<nsIFile> file = aFile.GetBaseFile(); + nsresult rv; + + if (!NS_IsMainThread()) { + // If this call is off the main thread, synchronously proxy it + // to the main thread. + RefPtr<LoadModuleMainThreadRunnable> r = + new LoadModuleMainThreadRunnable(this, aFile); + NS_DispatchToMainThread(r, NS_DISPATCH_SYNC); + return r->mResult; + } + + nsCOMPtr<nsIHashable> hashedFile(do_QueryInterface(file)); + if (!hashedFile) { + NS_ERROR("nsIFile is not nsIHashable"); + return nullptr; + } + + nsAutoCString filePath; + file->GetNativePath(filePath); + + NativeLoadData data; + + if (mLibraries.Get(hashedFile, &data)) { + NS_ASSERTION(data.mModule, "Corrupt mLibraries hash"); + LOG(LogLevel::Debug, + ("nsNativeModuleLoader::LoadModule(\"%s\") - found in cache", + filePath.get())); + return data.mModule; + } + + // We haven't loaded this module before + { +#ifdef HAS_DLL_BLOCKLIST + AutoSetXPCOMLoadOnMainThread guard; +#endif + rv = file->Load(&data.mLibrary); + } + + if (NS_FAILED(rv)) { + char errorMsg[1024] = "<unknown; can't get error from NSPR>"; + + if (PR_GetErrorTextLength() < (int)sizeof(errorMsg)) { + PR_GetErrorText(errorMsg); + } + + LogMessage("Failed to load native module at path '%s': (%lx) %s", + filePath.get(), rv, errorMsg); + + return nullptr; + } + +#ifdef IMPLEMENT_BREAK_AFTER_LOAD + nsAutoCString leafName; + file->GetNativeLeafName(leafName); + + char* env = getenv("XPCOM_BREAK_ON_LOAD"); + char* blist; + if (env && *env && (blist = strdup(env))) { + char* nextTok = blist; + while (char* token = NS_strtok(":", &nextTok)) { + if (leafName.Find(token, true) != kNotFound) { + NS_BREAK(); + } + } + + free(blist); + } +#endif + + void* module = PR_FindSymbol(data.mLibrary, "NSModule"); + if (!module) { + LogMessage("Native module at path '%s' doesn't export symbol `NSModule`.", + filePath.get()); + PR_UnloadLibrary(data.mLibrary); + return nullptr; + } + + data.mModule = *(mozilla::Module const* const*)module; + if (mozilla::Module::kVersion != data.mModule->mVersion) { + LogMessage("Native module at path '%s' is incompatible with this version of Firefox, has version %i, expected %i.", + filePath.get(), data.mModule->mVersion, + mozilla::Module::kVersion); + PR_UnloadLibrary(data.mLibrary); + return nullptr; + } + + mLibraries.Put(hashedFile, data); // infallible + return data.mModule; +} + +void +nsNativeModuleLoader::UnloadLibraries() +{ + MOZ_ASSERT(NS_IsMainThread(), "Shutdown not on main thread?"); + + for (auto iter = mLibraries.Iter(); !iter.Done(); iter.Next()) { + NativeLoadData& loadData = iter.Data(); + loadData.mModule = nullptr; + } + + for (auto iter = mLibraries.Iter(); !iter.Done(); iter.Next()) { + if (MOZ_LOG_TEST(sNativeModuleLoaderLog, LogLevel::Debug)) { + nsIHashable* hashedFile = iter.Key(); + nsCOMPtr<nsIFile> file(do_QueryInterface(hashedFile)); + + nsAutoCString filePath; + file->GetNativePath(filePath); + + LOG(LogLevel::Debug, + ("nsNativeModuleLoader::UnloaderFunc(\"%s\")", filePath.get())); + } + +#ifdef NS_BUILD_REFCNT_LOGGING + nsTraceRefcnt::SetActivityIsLegal(false); +#endif + +#if 0 + // XXXbsmedberg: do this as soon as the static-destructor crash(es) + // are fixed + NativeLoadData& loadData = iter.Data(); + PRStatus ret = PR_UnloadLibrary(loadData.mLibrary); + NS_ASSERTION(ret == PR_SUCCESS, "Failed to unload library"); +#endif + +#ifdef NS_BUILD_REFCNT_LOGGING + nsTraceRefcnt::SetActivityIsLegal(true); +#endif + + iter.Remove(); + } +} diff --git a/xpcom/components/nsNativeModuleLoader.h b/xpcom/components/nsNativeModuleLoader.h new file mode 100644 index 0000000000..b05a4a7a23 --- /dev/null +++ b/xpcom/components/nsNativeModuleLoader.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsNativeModuleLoader_h__ +#define nsNativeModuleLoader_h__ + +#include "nsDataHashtable.h" +#include "nsHashKeys.h" +#include "prlink.h" + +namespace mozilla { +class FileLocation; +} // namespace mozilla + +class nsNativeModuleLoader final +{ +public: + const mozilla::Module* LoadModule(mozilla::FileLocation& aFile); + + nsresult Init(); + + void UnloadLibraries(); + +private: + struct NativeLoadData + { + NativeLoadData() : mModule(nullptr), mLibrary(nullptr) {} + + const mozilla::Module* mModule; + PRLibrary* mLibrary; + }; + + nsDataHashtable<nsHashableHashKey, NativeLoadData> mLibraries; +}; + +#endif /* nsNativeModuleLoader_h__ */ |