summaryrefslogtreecommitdiffstats
path: root/xpcom/build
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /xpcom/build
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'xpcom/build')
-rw-r--r--xpcom/build/BinaryPath.h188
-rw-r--r--xpcom/build/FileLocation.cpp224
-rw-r--r--xpcom/build/FileLocation.h134
-rw-r--r--xpcom/build/FrozenFunctions.cpp138
-rw-r--r--xpcom/build/IOInterposer.cpp582
-rw-r--r--xpcom/build/IOInterposer.h295
-rw-r--r--xpcom/build/IOInterposerPrivate.h167
-rw-r--r--xpcom/build/LateWriteChecks.cpp258
-rw-r--r--xpcom/build/LateWriteChecks.h60
-rw-r--r--xpcom/build/MainThreadIOLogger.cpp225
-rw-r--r--xpcom/build/MainThreadIOLogger.h19
-rw-r--r--xpcom/build/NSPRInterposer.cpp185
-rw-r--r--xpcom/build/NSPRInterposer.h28
-rw-r--r--xpcom/build/Omnijar.cpp188
-rw-r--r--xpcom/build/Omnijar.h160
-rw-r--r--xpcom/build/PoisonIOInterposer.h91
-rw-r--r--xpcom/build/PoisonIOInterposerBase.cpp291
-rw-r--r--xpcom/build/PoisonIOInterposerMac.cpp386
-rw-r--r--xpcom/build/PoisonIOInterposerStub.cpp16
-rw-r--r--xpcom/build/PoisonIOInterposerWin.cpp501
-rw-r--r--xpcom/build/ServiceList.h46
-rw-r--r--xpcom/build/Services.cpp71
-rw-r--r--xpcom/build/Services.h45
-rw-r--r--xpcom/build/XPCOM.h178
-rw-r--r--xpcom/build/XPCOMInit.cpp1126
-rw-r--r--xpcom/build/XPCOMModule.inc81
-rw-r--r--xpcom/build/XREChildData.h51
-rw-r--r--xpcom/build/XREShellData.h29
-rw-r--r--xpcom/build/mach_override.c789
-rw-r--r--xpcom/build/mach_override.h121
-rw-r--r--xpcom/build/moz.build102
-rw-r--r--xpcom/build/nsWindowsDllInterceptor.h1127
-rw-r--r--xpcom/build/nsXPCOM.h433
-rw-r--r--xpcom/build/nsXPCOMCID.h185
-rw-r--r--xpcom/build/nsXPCOMCIDInternal.h54
-rw-r--r--xpcom/build/nsXPCOMPrivate.h317
-rw-r--r--xpcom/build/nsXPCOMStrings.cpp366
-rw-r--r--xpcom/build/nsXREAppData.h164
-rw-r--r--xpcom/build/nsXULAppAPI.h538
-rw-r--r--xpcom/build/perfprobe.cpp242
-rw-r--r--xpcom/build/perfprobe.h204
-rw-r--r--xpcom/build/xpcom_alpha.def256
-rw-r--r--xpcom/build/xrecore.h25
43 files changed, 10686 insertions, 0 deletions
diff --git a/xpcom/build/BinaryPath.h b/xpcom/build/BinaryPath.h
new file mode 100644
index 000000000..374763c79
--- /dev/null
+++ b/xpcom/build/BinaryPath.h
@@ -0,0 +1,188 @@
+/* -*- 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_BinaryPath_h
+#define mozilla_BinaryPath_h
+
+#include "nsXPCOMPrivate.h" // for MAXPATHLEN
+#ifdef XP_WIN
+#include <windows.h>
+#elif defined(XP_MACOSX)
+#include <CoreFoundation/CoreFoundation.h>
+#elif defined(XP_UNIX)
+#include <sys/stat.h>
+#include <string.h>
+#endif
+
+namespace mozilla {
+
+class BinaryPath
+{
+public:
+#ifdef XP_WIN
+ static nsresult Get(const char* argv0, char aResult[MAXPATHLEN])
+ {
+ wchar_t wide_path[MAXPATHLEN];
+ nsresult rv = GetW(argv0, wide_path);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ WideCharToMultiByte(CP_UTF8, 0, wide_path, -1,
+ aResult, MAXPATHLEN, nullptr, nullptr);
+ return NS_OK;
+ }
+
+private:
+ static nsresult GetW(const char* argv0, wchar_t aResult[MAXPATHLEN])
+ {
+ if (::GetModuleFileNameW(0, aResult, MAXPATHLEN)) {
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+ }
+
+#elif defined(XP_MACOSX)
+ static nsresult Get(const char* argv0, char aResult[MAXPATHLEN])
+ {
+ // Works even if we're not bundled.
+ CFBundleRef appBundle = CFBundleGetMainBundle();
+ if (!appBundle) {
+ return NS_ERROR_FAILURE;
+ }
+
+ CFURLRef executableURL = CFBundleCopyExecutableURL(appBundle);
+ if (!executableURL) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+ if (CFURLGetFileSystemRepresentation(executableURL, false, (UInt8*)aResult,
+ MAXPATHLEN)) {
+ // Sanitize path in case the app was launched from Terminal via
+ // './firefox' for example.
+ size_t readPos = 0;
+ size_t writePos = 0;
+ while (aResult[readPos] != '\0') {
+ if (aResult[readPos] == '.' && aResult[readPos + 1] == '/') {
+ readPos += 2;
+ } else {
+ aResult[writePos] = aResult[readPos];
+ readPos++;
+ writePos++;
+ }
+ }
+ aResult[writePos] = '\0';
+ rv = NS_OK;
+ } else {
+ rv = NS_ERROR_FAILURE;
+ }
+
+ CFRelease(executableURL);
+ return rv;
+ }
+
+#elif defined(ANDROID)
+ static nsresult Get(const char* argv0, char aResult[MAXPATHLEN])
+ {
+ // On Android, we use the GRE_HOME variable that is set by the Java
+ // bootstrap code.
+ const char* greHome = getenv("GRE_HOME");
+#if defined(MOZ_WIDGET_GONK)
+ if (!greHome) {
+ greHome = "/system/b2g";
+ }
+#endif
+
+ if (!greHome) {
+ return NS_ERROR_FAILURE;
+ }
+
+ snprintf(aResult, MAXPATHLEN, "%s/%s", greHome, "dummy");
+ aResult[MAXPATHLEN - 1] = '\0';
+ return NS_OK;
+ }
+
+#elif defined(XP_UNIX)
+ static nsresult Get(const char* aArgv0, char aResult[MAXPATHLEN])
+ {
+ struct stat fileStat;
+ // on unix, there is no official way to get the path of the current binary.
+ // instead of using the MOZILLA_FIVE_HOME hack, which doesn't scale to
+ // multiple applications, we will try a series of techniques:
+ //
+ // 1) use realpath() on argv[0], which works unless we're loaded from the
+ // PATH. Only do so if argv[0] looks like a path (contains a /).
+ // 2) manually walk through the PATH and look for ourself
+ // 3) give up
+ if (strchr(aArgv0, '/') && realpath(aArgv0, aResult) &&
+ stat(aResult, &fileStat) == 0) {
+ return NS_OK;
+ }
+
+ const char* path = getenv("PATH");
+ if (!path) {
+ return NS_ERROR_FAILURE;
+ }
+
+ char* pathdup = strdup(path);
+ if (!pathdup) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ bool found = false;
+ char* token = strtok(pathdup, ":");
+ while (token) {
+ char tmpPath[MAXPATHLEN];
+ sprintf(tmpPath, "%s/%s", token, aArgv0);
+ if (realpath(tmpPath, aResult) && stat(aResult, &fileStat) == 0) {
+ found = true;
+ break;
+ }
+ token = strtok(nullptr, ":");
+ }
+ free(pathdup);
+ if (found) {
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+ }
+
+#else
+#error Oops, you need platform-specific code here
+#endif
+
+public:
+ static nsresult GetFile(const char* aArgv0, nsIFile** aResult)
+ {
+ nsCOMPtr<nsIFile> lf;
+#ifdef XP_WIN
+ wchar_t exePath[MAXPATHLEN];
+ nsresult rv = GetW(aArgv0, exePath);
+#else
+ char exePath[MAXPATHLEN];
+ nsresult rv = Get(aArgv0, exePath);
+#endif
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+#ifdef XP_WIN
+ rv = NS_NewLocalFile(nsDependentString(exePath), true,
+ getter_AddRefs(lf));
+#else
+ rv = NS_NewNativeLocalFile(nsDependentCString(exePath), true,
+ getter_AddRefs(lf));
+#endif
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ NS_ADDREF(*aResult = lf);
+ return NS_OK;
+ }
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_BinaryPath_h */
diff --git a/xpcom/build/FileLocation.cpp b/xpcom/build/FileLocation.cpp
new file mode 100644
index 000000000..03c1dc027
--- /dev/null
+++ b/xpcom/build/FileLocation.cpp
@@ -0,0 +1,224 @@
+/* -*- 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 "FileLocation.h"
+#include "nsZipArchive.h"
+#include "nsURLHelper.h"
+
+namespace mozilla {
+
+FileLocation::FileLocation()
+{
+}
+
+FileLocation::~FileLocation()
+{
+}
+
+FileLocation::FileLocation(nsIFile* aFile)
+{
+ Init(aFile);
+}
+
+FileLocation::FileLocation(nsIFile* aFile, const char* aPath)
+{
+ Init(aFile, aPath);
+}
+
+FileLocation::FileLocation(const FileLocation& aFile, const char* aPath)
+{
+ if (aFile.IsZip()) {
+ if (aFile.mBaseFile) {
+ Init(aFile.mBaseFile, aFile.mPath.get());
+ }
+ else {
+ Init(aFile.mBaseZip, aFile.mPath.get());
+ }
+ if (aPath) {
+ int32_t i = mPath.RFindChar('/');
+ if (kNotFound == i) {
+ mPath.Truncate(0);
+ } else {
+ mPath.Truncate(i + 1);
+ }
+ mPath += aPath;
+ }
+ } else {
+ if (aPath) {
+ nsCOMPtr<nsIFile> cfile;
+ aFile.mBaseFile->GetParent(getter_AddRefs(cfile));
+
+#if defined(XP_WIN)
+ nsAutoCString pathStr(aPath);
+ char* p;
+ uint32_t len = pathStr.GetMutableData(&p);
+ for (; len; ++p, --len) {
+ if ('/' == *p) {
+ *p = '\\';
+ }
+ }
+ cfile->AppendRelativeNativePath(pathStr);
+#else
+ cfile->AppendRelativeNativePath(nsDependentCString(aPath));
+#endif
+ Init(cfile);
+ } else {
+ Init(aFile.mBaseFile);
+ }
+ }
+}
+
+void
+FileLocation::Init(nsIFile* aFile)
+{
+ mBaseZip = nullptr;
+ mBaseFile = aFile;
+ mPath.Truncate();
+}
+
+void
+FileLocation::Init(nsIFile* aFile, const char* aPath)
+{
+ mBaseZip = nullptr;
+ mBaseFile = aFile;
+ mPath = aPath;
+}
+
+void
+FileLocation::Init(nsZipArchive* aZip, const char* aPath)
+{
+ mBaseZip = aZip;
+ mBaseFile = nullptr;
+ mPath = aPath;
+}
+
+void
+FileLocation::GetURIString(nsACString& aResult) const
+{
+ if (mBaseFile) {
+ net_GetURLSpecFromActualFile(mBaseFile, aResult);
+ } else if (mBaseZip) {
+ RefPtr<nsZipHandle> handler = mBaseZip->GetFD();
+ handler->mFile.GetURIString(aResult);
+ }
+ if (IsZip()) {
+ aResult.Insert("jar:", 0);
+ aResult += "!/";
+ aResult += mPath;
+ }
+}
+
+already_AddRefed<nsIFile>
+FileLocation::GetBaseFile()
+{
+ if (IsZip() && mBaseZip) {
+ RefPtr<nsZipHandle> handler = mBaseZip->GetFD();
+ if (handler) {
+ return handler->mFile.GetBaseFile();
+ }
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIFile> file = mBaseFile;
+ return file.forget();
+}
+
+bool
+FileLocation::Equals(const FileLocation& aFile) const
+{
+ if (mPath != aFile.mPath) {
+ return false;
+ }
+
+ if (mBaseFile && aFile.mBaseFile) {
+ bool eq;
+ return NS_SUCCEEDED(mBaseFile->Equals(aFile.mBaseFile, &eq)) && eq;
+ }
+
+ const FileLocation* a = this;
+ const FileLocation* b = &aFile;
+ if (a->mBaseZip) {
+ RefPtr<nsZipHandle> handler = a->mBaseZip->GetFD();
+ a = &handler->mFile;
+ }
+ if (b->mBaseZip) {
+ RefPtr<nsZipHandle> handler = b->mBaseZip->GetFD();
+ b = &handler->mFile;
+ }
+
+ return a->Equals(*b);
+}
+
+nsresult
+FileLocation::GetData(Data& aData)
+{
+ if (!IsZip()) {
+ return mBaseFile->OpenNSPRFileDesc(PR_RDONLY, 0444, &aData.mFd.rwget());
+ }
+ aData.mZip = mBaseZip;
+ if (!aData.mZip) {
+ aData.mZip = new nsZipArchive();
+ aData.mZip->OpenArchive(mBaseFile);
+ }
+ aData.mItem = aData.mZip->GetItem(mPath.get());
+ if (aData.mItem) {
+ return NS_OK;
+ }
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+}
+
+nsresult
+FileLocation::Data::GetSize(uint32_t* aResult)
+{
+ if (mFd) {
+ PRFileInfo64 fileInfo;
+ if (PR_SUCCESS != PR_GetOpenFileInfo64(mFd, &fileInfo)) {
+ return NS_ErrorAccordingToNSPR();
+ }
+
+ if (fileInfo.size > int64_t(UINT32_MAX)) {
+ return NS_ERROR_FILE_TOO_BIG;
+ }
+
+ *aResult = fileInfo.size;
+ return NS_OK;
+ }
+ else if (mItem) {
+ *aResult = mItem->RealSize();
+ return NS_OK;
+ }
+ return NS_ERROR_NOT_INITIALIZED;
+}
+
+nsresult
+FileLocation::Data::Copy(char* aBuf, uint32_t aLen)
+{
+ if (mFd) {
+ for (uint32_t totalRead = 0; totalRead < aLen;) {
+ int32_t read = PR_Read(mFd, aBuf + totalRead,
+ XPCOM_MIN(aLen - totalRead, uint32_t(INT32_MAX)));
+ if (read < 0) {
+ return NS_ErrorAccordingToNSPR();
+ }
+ totalRead += read;
+ }
+ return NS_OK;
+ }
+ else if (mItem) {
+ nsZipCursor cursor(mItem, mZip, reinterpret_cast<uint8_t*>(aBuf),
+ aLen, true);
+ uint32_t readLen;
+ cursor.Copy(&readLen);
+ if (readLen != aLen) {
+ nsZipArchive::sFileCorruptedReason = "FileLocation::Data: insufficient data";
+ return NS_ERROR_FILE_CORRUPTED;
+ }
+ return NS_OK;
+ }
+ return NS_ERROR_NOT_INITIALIZED;
+}
+
+} /* namespace mozilla */
diff --git a/xpcom/build/FileLocation.h b/xpcom/build/FileLocation.h
new file mode 100644
index 000000000..e9a3fc5d0
--- /dev/null
+++ b/xpcom/build/FileLocation.h
@@ -0,0 +1,134 @@
+/* -*- 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_FileLocation_h
+#define mozilla_FileLocation_h
+
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsIFile.h"
+#include "FileUtils.h"
+
+class nsZipArchive;
+class nsZipItem;
+
+namespace mozilla {
+
+class FileLocation
+{
+public:
+ /**
+ * FileLocation is an helper to handle different kind of file locations
+ * within Gecko:
+ * - on filesystems
+ * - in archives
+ * - in archives within archives
+ * As such, it stores a path within an archive, as well as the archive
+ * path itself, or the complete file path alone when on a filesystem.
+ * When the archive is in an archive, an nsZipArchive is stored instead
+ * of a file path.
+ */
+ FileLocation();
+ ~FileLocation();
+
+ /**
+ * Constructor for plain files
+ */
+ explicit FileLocation(nsIFile* aFile);
+
+ /**
+ * Constructors for path within an archive. The archive can be given either
+ * as nsIFile or nsZipArchive.
+ */
+ FileLocation(nsIFile* aZip, const char* aPath);
+
+ FileLocation(nsZipArchive* aZip, const char* aPath);
+
+ /**
+ * Creates a new file location relative to another one.
+ */
+ FileLocation(const FileLocation& aFile, const char* aPath = nullptr);
+
+ /**
+ * Initialization functions corresponding to constructors
+ */
+ void Init(nsIFile* aFile);
+
+ void Init(nsIFile* aZip, const char* aPath);
+
+ void Init(nsZipArchive* aZip, const char* aPath);
+
+ /**
+ * Returns an URI string corresponding to the file location
+ */
+ void GetURIString(nsACString& aResult) const;
+
+ /**
+ * Returns the base file of the location, where base file is defined as:
+ * - The file itself when the location is on a filesystem
+ * - The archive file when the location is in an archive
+ * - The outer archive file when the location is in an archive in an archive
+ */
+ already_AddRefed<nsIFile> GetBaseFile();
+
+ /**
+ * Returns whether the "base file" (see GetBaseFile) is an archive
+ */
+ bool IsZip() const { return !mPath.IsEmpty(); }
+
+ /**
+ * Returns the path within the archive, when within an archive
+ */
+ void GetPath(nsACString& aResult) const { aResult = mPath; }
+
+ /**
+ * Boolean value corresponding to whether the file location is initialized
+ * or not.
+ */
+ explicit operator bool() const { return mBaseFile || mBaseZip; }
+
+ /**
+ * Returns whether another FileLocation points to the same resource
+ */
+ bool Equals(const FileLocation& aFile) const;
+
+ /**
+ * Data associated with a FileLocation.
+ */
+ class Data
+ {
+ public:
+ /**
+ * Returns the data size
+ */
+ nsresult GetSize(uint32_t* aResult);
+
+ /**
+ * Copies the data in the given buffer
+ */
+ nsresult Copy(char* aBuf, uint32_t aLen);
+ protected:
+ friend class FileLocation;
+ nsZipItem* mItem;
+ RefPtr<nsZipArchive> mZip;
+ mozilla::AutoFDClose mFd;
+ };
+
+ /**
+ * Returns the data associated with the resource pointed at by the file
+ * location.
+ */
+ nsresult GetData(Data& aData);
+private:
+ nsCOMPtr<nsIFile> mBaseFile;
+ RefPtr<nsZipArchive> mBaseZip;
+ nsCString mPath;
+}; /* class FileLocation */
+
+} /* namespace mozilla */
+
+#endif /* mozilla_FileLocation_h */
diff --git a/xpcom/build/FrozenFunctions.cpp b/xpcom/build/FrozenFunctions.cpp
new file mode 100644
index 000000000..8a957ca45
--- /dev/null
+++ b/xpcom/build/FrozenFunctions.cpp
@@ -0,0 +1,138 @@
+/* -*- 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 "nsXPCOM.h"
+#include "nsXPCOMPrivate.h"
+#include "nsXPCOMStrings.h"
+#include "xptcall.h"
+
+#include <string.h>
+
+/**
+ * Private Method to register an exit routine. This method
+ * used to allow you to setup a callback that will be called from
+ * the NS_ShutdownXPCOM function after all services and
+ * components have gone away. It was fatally flawed in that the component
+ * DLL could be released before the exit function was called; it is now a
+ * stub implementation that does nothing.
+ */
+XPCOM_API(nsresult)
+NS_RegisterXPCOMExitRoutine(XPCOMExitRoutine aExitRoutine, uint32_t aPriority);
+
+XPCOM_API(nsresult)
+NS_UnregisterXPCOMExitRoutine(XPCOMExitRoutine aExitRoutine);
+
+static const XPCOMFunctions kFrozenFunctions = {
+ XPCOM_GLUE_VERSION,
+ sizeof(XPCOMFunctions),
+ &NS_InitXPCOM2,
+ &NS_ShutdownXPCOM,
+ &NS_GetServiceManager,
+ &NS_GetComponentManager,
+ &NS_GetComponentRegistrar,
+ &NS_GetMemoryManager,
+ &NS_NewLocalFile,
+ &NS_NewNativeLocalFile,
+ &NS_RegisterXPCOMExitRoutine,
+ &NS_UnregisterXPCOMExitRoutine,
+
+ // these functions were added post 1.4
+ &NS_GetDebug,
+ nullptr,
+
+ // these functions were added post 1.6
+ &NS_StringContainerInit,
+ &NS_StringContainerFinish,
+ &NS_StringGetData,
+ &NS_StringSetData,
+ &NS_StringSetDataRange,
+ &NS_StringCopy,
+ &NS_CStringContainerInit,
+ &NS_CStringContainerFinish,
+ &NS_CStringGetData,
+ &NS_CStringSetData,
+ &NS_CStringSetDataRange,
+ &NS_CStringCopy,
+ &NS_CStringToUTF16,
+ &NS_UTF16ToCString,
+ &NS_StringCloneData,
+ &NS_CStringCloneData,
+
+ // these functions were added post 1.7 (post Firefox 1.0)
+ &moz_xmalloc,
+ &moz_xrealloc,
+ &free,
+ &NS_StringContainerInit2,
+ &NS_CStringContainerInit2,
+ &NS_StringGetMutableData,
+ &NS_CStringGetMutableData,
+ nullptr,
+
+ // these functions were added post 1.8
+ &NS_DebugBreak,
+ &NS_LogInit,
+ &NS_LogTerm,
+ &NS_LogAddRef,
+ &NS_LogRelease,
+ &NS_LogCtor,
+ &NS_LogDtor,
+ &NS_LogCOMPtrAddRef,
+ &NS_LogCOMPtrRelease,
+ &NS_GetXPTCallStub,
+ &NS_DestroyXPTCallStub,
+ &NS_InvokeByIndex,
+ nullptr,
+ nullptr,
+ &NS_StringSetIsVoid,
+ &NS_StringGetIsVoid,
+ &NS_CStringSetIsVoid,
+ &NS_CStringGetIsVoid,
+
+ // these functions were added post 1.9, but then made obsolete
+ nullptr,
+ nullptr,
+
+ &NS_CycleCollectorSuspect3,
+};
+
+EXPORT_XPCOM_API(nsresult)
+NS_GetFrozenFunctions(XPCOMFunctions* aFunctions, const char* /* aLibraryPath */)
+{
+ if (!aFunctions) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (aFunctions->version != XPCOM_GLUE_VERSION) {
+ return NS_ERROR_FAILURE;
+ }
+
+ uint32_t size = aFunctions->size;
+ if (size > sizeof(XPCOMFunctions)) {
+ size = sizeof(XPCOMFunctions);
+ }
+
+ size -= offsetof(XPCOMFunctions, init);
+
+ memcpy(&aFunctions->init, &kFrozenFunctions.init, size);
+
+ return NS_OK;
+}
+
+/*
+ * Stubs for nsXPCOMPrivate.h
+ */
+
+EXPORT_XPCOM_API(nsresult)
+NS_RegisterXPCOMExitRoutine(XPCOMExitRoutine aExitRoutine, uint32_t aPriority)
+{
+ return NS_OK;
+}
+
+EXPORT_XPCOM_API(nsresult)
+NS_UnregisterXPCOMExitRoutine(XPCOMExitRoutine aExitRoutine)
+{
+ return NS_OK;
+}
diff --git a/xpcom/build/IOInterposer.cpp b/xpcom/build/IOInterposer.cpp
new file mode 100644
index 000000000..1c3ff54d5
--- /dev/null
+++ b/xpcom/build/IOInterposer.cpp
@@ -0,0 +1,582 @@
+/* -*- 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 <algorithm>
+#include <vector>
+
+#include "IOInterposer.h"
+
+#include "IOInterposerPrivate.h"
+#include "MainThreadIOLogger.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/ThreadLocal.h"
+#include "nscore.h" // for NS_FREE_PERMANENT_DATA
+#if !defined(XP_WIN)
+#include "NSPRInterposer.h"
+#endif // !defined(XP_WIN)
+#include "nsXULAppAPI.h"
+#include "PoisonIOInterposer.h"
+
+using namespace mozilla;
+
+namespace {
+
+/** Find if a vector contains a specific element */
+template<class T>
+bool
+VectorContains(const std::vector<T>& aVector, const T& aElement)
+{
+ return std::find(aVector.begin(), aVector.end(), aElement) != aVector.end();
+}
+
+/** Remove element from a vector */
+template<class T>
+void
+VectorRemove(std::vector<T>& aVector, const T& aElement)
+{
+ typename std::vector<T>::iterator newEnd =
+ std::remove(aVector.begin(), aVector.end(), aElement);
+ aVector.erase(newEnd, aVector.end());
+}
+
+/** Lists of Observers */
+struct ObserverLists
+{
+private:
+ ~ObserverLists() {}
+
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ObserverLists)
+
+ ObserverLists() {}
+
+ ObserverLists(ObserverLists const& aOther)
+ : mCreateObservers(aOther.mCreateObservers)
+ , mReadObservers(aOther.mReadObservers)
+ , mWriteObservers(aOther.mWriteObservers)
+ , mFSyncObservers(aOther.mFSyncObservers)
+ , mStatObservers(aOther.mStatObservers)
+ , mCloseObservers(aOther.mCloseObservers)
+ , mStageObservers(aOther.mStageObservers)
+ {
+ }
+ // Lists of observers for I/O events.
+ // These are implemented as vectors since they are allowed to survive gecko,
+ // without reporting leaks. This is necessary for the IOInterposer to be used
+ // for late-write checks.
+ std::vector<IOInterposeObserver*> mCreateObservers;
+ std::vector<IOInterposeObserver*> mReadObservers;
+ std::vector<IOInterposeObserver*> mWriteObservers;
+ std::vector<IOInterposeObserver*> mFSyncObservers;
+ std::vector<IOInterposeObserver*> mStatObservers;
+ std::vector<IOInterposeObserver*> mCloseObservers;
+ std::vector<IOInterposeObserver*> mStageObservers;
+};
+
+class PerThreadData
+{
+public:
+ explicit PerThreadData(bool aIsMainThread = false)
+ : mIsMainThread(aIsMainThread)
+ , mIsHandlingObservation(false)
+ , mCurrentGeneration(0)
+ {
+ MOZ_COUNT_CTOR(PerThreadData);
+ }
+
+ ~PerThreadData()
+ {
+ MOZ_COUNT_DTOR(PerThreadData);
+ }
+
+ void CallObservers(IOInterposeObserver::Observation& aObservation)
+ {
+ // Prevent recursive reporting.
+ if (mIsHandlingObservation) {
+ return;
+ }
+
+ mIsHandlingObservation = true;
+ // Decide which list of observers to inform
+ std::vector<IOInterposeObserver*>* observers = nullptr;
+ switch (aObservation.ObservedOperation()) {
+ case IOInterposeObserver::OpCreateOrOpen:
+ observers = &mObserverLists->mCreateObservers;
+ break;
+ case IOInterposeObserver::OpRead:
+ observers = &mObserverLists->mReadObservers;
+ break;
+ case IOInterposeObserver::OpWrite:
+ observers = &mObserverLists->mWriteObservers;
+ break;
+ case IOInterposeObserver::OpFSync:
+ observers = &mObserverLists->mFSyncObservers;
+ break;
+ case IOInterposeObserver::OpStat:
+ observers = &mObserverLists->mStatObservers;
+ break;
+ case IOInterposeObserver::OpClose:
+ observers = &mObserverLists->mCloseObservers;
+ break;
+ case IOInterposeObserver::OpNextStage:
+ observers = &mObserverLists->mStageObservers;
+ break;
+ default: {
+ // Invalid IO operation, see documentation comment for
+ // IOInterposer::Report()
+ MOZ_ASSERT(false);
+ // Just ignore it in non-debug builds.
+ return;
+ }
+ }
+ MOZ_ASSERT(observers);
+
+ // Inform observers
+ for (auto i = observers->begin(), e = observers->end(); i != e; ++i) {
+ (*i)->Observe(aObservation);
+ }
+ mIsHandlingObservation = false;
+ }
+
+ inline uint32_t GetCurrentGeneration() const { return mCurrentGeneration; }
+
+ inline bool IsMainThread() const { return mIsMainThread; }
+
+ inline void SetObserverLists(uint32_t aNewGeneration,
+ RefPtr<ObserverLists>& aNewLists)
+ {
+ mCurrentGeneration = aNewGeneration;
+ mObserverLists = aNewLists;
+ }
+
+ inline void ClearObserverLists()
+ {
+ if (mObserverLists) {
+ mCurrentGeneration = 0;
+ mObserverLists = nullptr;
+ }
+ }
+
+private:
+ bool mIsMainThread;
+ bool mIsHandlingObservation;
+ uint32_t mCurrentGeneration;
+ RefPtr<ObserverLists> mObserverLists;
+};
+
+class MasterList
+{
+public:
+ MasterList()
+ : mObservedOperations(IOInterposeObserver::OpNone)
+ , mIsEnabled(true)
+ {
+ MOZ_COUNT_CTOR(MasterList);
+ }
+
+ ~MasterList()
+ {
+ MOZ_COUNT_DTOR(MasterList);
+ }
+
+ inline void Disable() { mIsEnabled = false; }
+ inline void Enable() { mIsEnabled = true; }
+
+ void Register(IOInterposeObserver::Operation aOp,
+ IOInterposeObserver* aObserver)
+ {
+ IOInterposer::AutoLock lock(mLock);
+
+ ObserverLists* newLists = nullptr;
+ if (mObserverLists) {
+ newLists = new ObserverLists(*mObserverLists);
+ } else {
+ newLists = new ObserverLists();
+ }
+ // You can register to observe multiple types of observations
+ // but you'll never be registered twice for the same observations.
+ if (aOp & IOInterposeObserver::OpCreateOrOpen &&
+ !VectorContains(newLists->mCreateObservers, aObserver)) {
+ newLists->mCreateObservers.push_back(aObserver);
+ }
+ if (aOp & IOInterposeObserver::OpRead &&
+ !VectorContains(newLists->mReadObservers, aObserver)) {
+ newLists->mReadObservers.push_back(aObserver);
+ }
+ if (aOp & IOInterposeObserver::OpWrite &&
+ !VectorContains(newLists->mWriteObservers, aObserver)) {
+ newLists->mWriteObservers.push_back(aObserver);
+ }
+ if (aOp & IOInterposeObserver::OpFSync &&
+ !VectorContains(newLists->mFSyncObservers, aObserver)) {
+ newLists->mFSyncObservers.push_back(aObserver);
+ }
+ if (aOp & IOInterposeObserver::OpStat &&
+ !VectorContains(newLists->mStatObservers, aObserver)) {
+ newLists->mStatObservers.push_back(aObserver);
+ }
+ if (aOp & IOInterposeObserver::OpClose &&
+ !VectorContains(newLists->mCloseObservers, aObserver)) {
+ newLists->mCloseObservers.push_back(aObserver);
+ }
+ if (aOp & IOInterposeObserver::OpNextStage &&
+ !VectorContains(newLists->mStageObservers, aObserver)) {
+ newLists->mStageObservers.push_back(aObserver);
+ }
+ mObserverLists = newLists;
+ mObservedOperations =
+ (IOInterposeObserver::Operation)(mObservedOperations | aOp);
+
+ mCurrentGeneration++;
+ }
+
+ void Unregister(IOInterposeObserver::Operation aOp,
+ IOInterposeObserver* aObserver)
+ {
+ IOInterposer::AutoLock lock(mLock);
+
+ ObserverLists* newLists = nullptr;
+ if (mObserverLists) {
+ newLists = new ObserverLists(*mObserverLists);
+ } else {
+ newLists = new ObserverLists();
+ }
+
+ if (aOp & IOInterposeObserver::OpCreateOrOpen) {
+ VectorRemove(newLists->mCreateObservers, aObserver);
+ if (newLists->mCreateObservers.empty()) {
+ mObservedOperations =
+ (IOInterposeObserver::Operation)(mObservedOperations &
+ ~IOInterposeObserver::OpCreateOrOpen);
+ }
+ }
+ if (aOp & IOInterposeObserver::OpRead) {
+ VectorRemove(newLists->mReadObservers, aObserver);
+ if (newLists->mReadObservers.empty()) {
+ mObservedOperations =
+ (IOInterposeObserver::Operation)(mObservedOperations &
+ ~IOInterposeObserver::OpRead);
+ }
+ }
+ if (aOp & IOInterposeObserver::OpWrite) {
+ VectorRemove(newLists->mWriteObservers, aObserver);
+ if (newLists->mWriteObservers.empty()) {
+ mObservedOperations =
+ (IOInterposeObserver::Operation)(mObservedOperations &
+ ~IOInterposeObserver::OpWrite);
+ }
+ }
+ if (aOp & IOInterposeObserver::OpFSync) {
+ VectorRemove(newLists->mFSyncObservers, aObserver);
+ if (newLists->mFSyncObservers.empty()) {
+ mObservedOperations =
+ (IOInterposeObserver::Operation)(mObservedOperations &
+ ~IOInterposeObserver::OpFSync);
+ }
+ }
+ if (aOp & IOInterposeObserver::OpStat) {
+ VectorRemove(newLists->mStatObservers, aObserver);
+ if (newLists->mStatObservers.empty()) {
+ mObservedOperations =
+ (IOInterposeObserver::Operation)(mObservedOperations &
+ ~IOInterposeObserver::OpStat);
+ }
+ }
+ if (aOp & IOInterposeObserver::OpClose) {
+ VectorRemove(newLists->mCloseObservers, aObserver);
+ if (newLists->mCloseObservers.empty()) {
+ mObservedOperations =
+ (IOInterposeObserver::Operation)(mObservedOperations &
+ ~IOInterposeObserver::OpClose);
+ }
+ }
+ if (aOp & IOInterposeObserver::OpNextStage) {
+ VectorRemove(newLists->mStageObservers, aObserver);
+ if (newLists->mStageObservers.empty()) {
+ mObservedOperations =
+ (IOInterposeObserver::Operation)(mObservedOperations &
+ ~IOInterposeObserver::OpNextStage);
+ }
+ }
+ mObserverLists = newLists;
+ mCurrentGeneration++;
+ }
+
+ void Update(PerThreadData& aPtd)
+ {
+ if (mCurrentGeneration == aPtd.GetCurrentGeneration()) {
+ return;
+ }
+ // If the generation counts don't match then we need to update the current
+ // thread's observer list with the new master list.
+ IOInterposer::AutoLock lock(mLock);
+ aPtd.SetObserverLists(mCurrentGeneration, mObserverLists);
+ }
+
+ inline bool IsObservedOperation(IOInterposeObserver::Operation aOp)
+ {
+ // The quick reader may observe that no locks are being employed here,
+ // hence the result of the operations is truly undefined. However, most
+ // computers will usually return either true or false, which is good enough.
+ // It is not a problem if we occasionally report more or less IO than is
+ // actually occurring.
+ return mIsEnabled && !!(mObservedOperations & aOp);
+ }
+
+private:
+ RefPtr<ObserverLists> mObserverLists;
+ // Note, we cannot use mozilla::Mutex here as the ObserverLists may be leaked
+ // (We want to monitor IO during shutdown). Furthermore, as we may have to
+ // unregister observers during shutdown an OffTheBooksMutex is not an option
+ // either, as its base calls into sDeadlockDetector which may be nullptr
+ // during shutdown.
+ IOInterposer::Mutex mLock;
+ // Flags tracking which operations are being observed
+ IOInterposeObserver::Operation mObservedOperations;
+ // Used for quickly disabling everything by IOInterposer::Disable()
+ Atomic<bool> mIsEnabled;
+ // Used to inform threads that the master observer list has changed
+ Atomic<uint32_t> mCurrentGeneration;
+};
+
+// Special observation used by IOInterposer::EnteringNextStage()
+class NextStageObservation : public IOInterposeObserver::Observation
+{
+public:
+ NextStageObservation()
+ : IOInterposeObserver::Observation(IOInterposeObserver::OpNextStage,
+ "IOInterposer", false)
+ {
+ mStart = TimeStamp::Now();
+ mEnd = mStart;
+ }
+};
+
+// List of observers registered
+static StaticAutoPtr<MasterList> sMasterList;
+static MOZ_THREAD_LOCAL(PerThreadData*) sThreadLocalData;
+static bool sThreadLocalDataInitialized;
+} // namespace
+
+IOInterposeObserver::Observation::Observation(Operation aOperation,
+ const char* aReference,
+ bool aShouldReport)
+ : mOperation(aOperation)
+ , mReference(aReference)
+ , mShouldReport(IOInterposer::IsObservedOperation(aOperation) &&
+ aShouldReport)
+{
+ if (mShouldReport) {
+ mStart = TimeStamp::Now();
+ }
+}
+
+IOInterposeObserver::Observation::Observation(Operation aOperation,
+ const TimeStamp& aStart,
+ const TimeStamp& aEnd,
+ const char* aReference)
+ : mOperation(aOperation)
+ , mStart(aStart)
+ , mEnd(aEnd)
+ , mReference(aReference)
+ , mShouldReport(false)
+{
+}
+
+const char*
+IOInterposeObserver::Observation::ObservedOperationString() const
+{
+ switch (mOperation) {
+ case OpCreateOrOpen:
+ return "create/open";
+ case OpRead:
+ return "read";
+ case OpWrite:
+ return "write";
+ case OpFSync:
+ return "fsync";
+ case OpStat:
+ return "stat";
+ case OpClose:
+ return "close";
+ case OpNextStage:
+ return "NextStage";
+ default:
+ return "unknown";
+ }
+}
+
+void
+IOInterposeObserver::Observation::Report()
+{
+ if (mShouldReport) {
+ mEnd = TimeStamp::Now();
+ IOInterposer::Report(*this);
+ }
+}
+
+bool
+IOInterposer::Init()
+{
+ // Don't initialize twice...
+ if (sMasterList) {
+ return true;
+ }
+ if (!sThreadLocalData.init()) {
+ return false;
+ }
+ sThreadLocalDataInitialized = true;
+ bool isMainThread = true;
+ RegisterCurrentThread(isMainThread);
+ sMasterList = new MasterList();
+
+ MainThreadIOLogger::Init();
+
+ // Now we initialize the various interposers depending on platform
+ InitPoisonIOInterposer();
+ // We don't hook NSPR on Windows because PoisonIOInterposer captures a
+ // superset of the former's events.
+#if !defined(XP_WIN)
+ InitNSPRIOInterposing();
+#endif
+ return true;
+}
+
+bool
+IOInterposeObserver::IsMainThread()
+{
+ if (!sThreadLocalDataInitialized) {
+ return false;
+ }
+ PerThreadData* ptd = sThreadLocalData.get();
+ if (!ptd) {
+ return false;
+ }
+ return ptd->IsMainThread();
+}
+
+void
+IOInterposer::Clear()
+{
+ /* Clear() is a no-op on release builds so that we may continue to trap I/O
+ until process termination. In leak-checking builds, we need to shut down
+ IOInterposer so that all references are properly released. */
+#ifdef NS_FREE_PERMANENT_DATA
+ UnregisterCurrentThread();
+ sMasterList = nullptr;
+#endif
+}
+
+void
+IOInterposer::Disable()
+{
+ if (!sMasterList) {
+ return;
+ }
+ sMasterList->Disable();
+}
+
+void
+IOInterposer::Enable()
+{
+ if (!sMasterList) {
+ return;
+ }
+ sMasterList->Enable();
+}
+
+void
+IOInterposer::Report(IOInterposeObserver::Observation& aObservation)
+{
+ PerThreadData* ptd = sThreadLocalData.get();
+ if (!ptd) {
+ // In this case the current thread is not registered with IOInterposer.
+ // Alternatively we could take the slow path and just lock everything if
+ // we're not registered. That could potentially perform poorly, though.
+ return;
+ }
+
+ if (!sMasterList) {
+ // If there is no longer a master list then we should clear the local one.
+ ptd->ClearObserverLists();
+ return;
+ }
+
+ sMasterList->Update(*ptd);
+
+ // Don't try to report if there's nobody listening.
+ if (!IOInterposer::IsObservedOperation(aObservation.ObservedOperation())) {
+ return;
+ }
+
+ ptd->CallObservers(aObservation);
+}
+
+bool
+IOInterposer::IsObservedOperation(IOInterposeObserver::Operation aOp)
+{
+ return sMasterList && sMasterList->IsObservedOperation(aOp);
+}
+
+void
+IOInterposer::Register(IOInterposeObserver::Operation aOp,
+ IOInterposeObserver* aObserver)
+{
+ MOZ_ASSERT(aObserver);
+ if (!sMasterList || !aObserver) {
+ return;
+ }
+
+ sMasterList->Register(aOp, aObserver);
+}
+
+void
+IOInterposer::Unregister(IOInterposeObserver::Operation aOp,
+ IOInterposeObserver* aObserver)
+{
+ if (!sMasterList) {
+ return;
+ }
+
+ sMasterList->Unregister(aOp, aObserver);
+}
+
+void
+IOInterposer::RegisterCurrentThread(bool aIsMainThread)
+{
+ if (!sThreadLocalDataInitialized) {
+ return;
+ }
+ MOZ_ASSERT(!sThreadLocalData.get());
+ PerThreadData* curThreadData = new PerThreadData(aIsMainThread);
+ sThreadLocalData.set(curThreadData);
+}
+
+void
+IOInterposer::UnregisterCurrentThread()
+{
+ if (!sThreadLocalDataInitialized) {
+ return;
+ }
+ PerThreadData* curThreadData = sThreadLocalData.get();
+ MOZ_ASSERT(curThreadData);
+ sThreadLocalData.set(nullptr);
+ delete curThreadData;
+}
+
+void
+IOInterposer::EnteringNextStage()
+{
+ if (!sMasterList) {
+ return;
+ }
+ NextStageObservation observation;
+ Report(observation);
+}
+
diff --git a/xpcom/build/IOInterposer.h b/xpcom/build/IOInterposer.h
new file mode 100644
index 000000000..3634572a5
--- /dev/null
+++ b/xpcom/build/IOInterposer.h
@@ -0,0 +1,295 @@
+/* -*- 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_IOInterposer_h
+#define mozilla_IOInterposer_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/GuardObjects.h"
+#include "mozilla/TimeStamp.h"
+
+namespace mozilla {
+
+/**
+ * Interface for I/O interposer observers. This is separate from the
+ * IOInterposer because we have multiple uses for these observations.
+ */
+class IOInterposeObserver
+{
+public:
+ enum Operation
+ {
+ OpNone = 0,
+ OpCreateOrOpen = (1 << 0),
+ OpRead = (1 << 1),
+ OpWrite = (1 << 2),
+ OpFSync = (1 << 3),
+ OpStat = (1 << 4),
+ OpClose = (1 << 5),
+ OpNextStage = (1 << 6), // Meta - used when leaving startup, entering shutdown
+ OpWriteFSync = (OpWrite | OpFSync),
+ OpAll = (OpCreateOrOpen | OpRead | OpWrite | OpFSync | OpStat | OpClose),
+ OpAllWithStaging = (OpAll | OpNextStage)
+ };
+
+ /** A representation of an I/O observation */
+ class Observation
+ {
+ protected:
+ /**
+ * This constructor is for use by subclasses that are intended to take
+ * timing measurements via RAII. The |aShouldReport| parameter may be
+ * used to make the measurement and reporting conditional on the
+ * satisfaction of an arbitrary predicate that was evaluated
+ * in the subclass. Note that IOInterposer::IsObservedOperation() is
+ * always ANDed with aShouldReport, so the subclass does not need to
+ * include a call to that function explicitly.
+ */
+ Observation(Operation aOperation, const char* aReference,
+ bool aShouldReport = true);
+
+ public:
+ /**
+ * Since this constructor accepts start and end times, it does *not* take
+ * its own timings, nor does it report itself.
+ */
+ Observation(Operation aOperation, const TimeStamp& aStart,
+ const TimeStamp& aEnd, const char* aReference);
+
+ /**
+ * Operation observed, this is one of the individual Operation values.
+ * Combinations of these flags are only used when registering observers.
+ */
+ Operation ObservedOperation() const { return mOperation; }
+
+ /**
+ * Return the observed operation as a human-readable string.
+ */
+ const char* ObservedOperationString() const;
+
+ /** Time at which the I/O operation was started */
+ TimeStamp Start() const { return mStart; }
+
+ /**
+ * Time at which the I/O operation ended, for asynchronous methods this is
+ * the time at which the call initiating the asynchronous request returned.
+ */
+ TimeStamp End() const { return mEnd; }
+
+ /**
+ * Duration of the operation, for asynchronous I/O methods this is the
+ * duration of the call initiating the asynchronous request.
+ */
+ TimeDuration Duration() const { return mEnd - mStart; }
+
+ /**
+ * IO reference, function name or name of component (sqlite) that did IO
+ * this is in addition the generic operation. This attribute may be platform
+ * specific, but should only take a finite number of distinct values.
+ * E.g. sqlite-commit, CreateFile, NtReadFile, fread, fsync, mmap, etc.
+ * I.e. typically the platform specific function that did the IO.
+ */
+ const char* Reference() const { return mReference; }
+
+ /** Request filename associated with the I/O operation, null if unknown */
+ virtual const char16_t* Filename() { return nullptr; }
+
+ virtual ~Observation() {}
+
+ protected:
+ void
+ Report();
+
+ Operation mOperation;
+ TimeStamp mStart;
+ TimeStamp mEnd;
+ const char* mReference; // Identifies the source of the Observation
+ bool mShouldReport; // Measure and report if true
+ };
+
+ /**
+ * Invoked whenever an implementation of the IOInterposeObserver should
+ * observe aObservation. Implement this and do your thing...
+ * But do consider if it is wise to use IO functions in this method, they are
+ * likely to cause recursion :)
+ * At least, see PoisonIOInterposer.h and register your handle as a debug file
+ * even, if you don't initialize the poison IO interposer, someone else might.
+ *
+ * Remark: Observations may occur on any thread.
+ */
+ virtual void Observe(Observation& aObservation) = 0;
+
+ virtual ~IOInterposeObserver() {}
+
+protected:
+ /**
+ * We don't use NS_IsMainThread() because we need to be able to determine the
+ * main thread outside of XPCOM Initialization. IOInterposer observers should
+ * call this function instead.
+ */
+ static bool IsMainThread();
+};
+
+/**
+ * These functions are responsible for ensuring that events are routed to the
+ * appropriate observers.
+ */
+namespace IOInterposer {
+
+/**
+ * This function must be called from the main-thread when no other threads are
+ * running before any of the other methods on this class may be used.
+ *
+ * IO reports can however, safely assume that IsObservedOperation() will
+ * return false until the IOInterposer is initialized.
+ *
+ * Remark, it's safe to call this method multiple times, so just call it when
+ * you to utilize IO interposing.
+ *
+ * Using the IOInterposerInit class is preferred to calling this directly.
+ */
+bool Init();
+
+/**
+ * This function must be called from the main thread, and furthermore
+ * it must be called when no other threads are executing. Effectively
+ * restricting us to calling it only during shutdown.
+ *
+ * Callers should take care that no other consumers are subscribed to events,
+ * as these events will stop when this function is called.
+ *
+ * In practice, we don't use this method as the IOInterposer is used for
+ * late-write checks.
+ */
+void Clear();
+
+/**
+ * This function immediately disables IOInterposer functionality in a fast,
+ * thread-safe manner. Primarily for use by the crash reporter.
+ */
+void Disable();
+
+/**
+ * This function re-enables IOInterposer functionality in a fast, thread-safe
+ * manner. Primarily for use by the crash reporter.
+ */
+void Enable();
+
+/**
+ * Report IO to registered observers.
+ * Notice that the reported operation must be either OpRead, OpWrite or
+ * OpFSync. You are not allowed to report an observation with OpWriteFSync or
+ * OpAll, these are just auxiliary values for use with Register().
+ *
+ * If the IO call you're reporting does multiple things, write and fsync, you
+ * can choose to call Report() twice once with write and once with FSync. You
+ * may not call Report() with OpWriteFSync! The Observation::mOperation
+ * attribute is meant to be generic, not perfect.
+ *
+ * Notice that there is no reason to report an observation with an operation
+ * which is not being observed. Use IsObservedOperation() to check if the
+ * operation you are about to report is being observed. This is especially
+ * important if you are constructing expensive observations containing
+ * filename and full-path.
+ *
+ * Remark: Init() must be called before any IO is reported. But
+ * IsObservedOperation() will return false until Init() is called.
+ */
+void Report(IOInterposeObserver::Observation& aObservation);
+
+/**
+ * Return whether or not an operation is observed. Reporters should not
+ * report operations that are not being observed by anybody. This mechanism
+ * allows us to avoid reporting I/O when no observers are registered.
+ */
+bool IsObservedOperation(IOInterposeObserver::Operation aOp);
+
+/**
+ * Register IOInterposeObserver, the observer object will receive all
+ * observations for the given operation aOp.
+ *
+ * Remark: Init() must be called before observers are registered.
+ */
+void Register(IOInterposeObserver::Operation aOp,
+ IOInterposeObserver* aObserver);
+
+/**
+ * Unregister an IOInterposeObserver for a given operation
+ * Remark: It is always safe to unregister for all operations, even if yoú
+ * didn't register for them all.
+ * I.e. IOInterposer::Unregister(IOInterposeObserver::OpAll, aObserver)
+ *
+ * Remark: Init() must be called before observers are unregistered.
+ */
+void Unregister(IOInterposeObserver::Operation aOp,
+ IOInterposeObserver* aObserver);
+
+/**
+ * Registers the current thread with the IOInterposer. This must be done to
+ * ensure that per-thread data is created in an orderly fashion.
+ * We could have written this to initialize that data lazily, however this
+ * could have unintended consequences if a thread that is not aware of
+ * IOInterposer was implicitly registered: its per-thread data would never
+ * be deleted because it would not know to unregister itself.
+ *
+ * @param aIsMainThread true if IOInterposer should treat the current thread
+ * as the main thread.
+ */
+void RegisterCurrentThread(bool aIsMainThread = false);
+
+/**
+ * Unregisters the current thread with the IOInterposer. This is important
+ * to call when a thread is shutting down because it cleans up data that
+ * is stored in a TLS slot.
+ */
+void UnregisterCurrentThread();
+
+/**
+ * Called to inform observers that the process has transitioned out of the
+ * startup stage or into the shutdown stage. Main thread only.
+ */
+void EnteringNextStage();
+
+} // namespace IOInterposer
+
+class IOInterposerInit
+{
+public:
+ IOInterposerInit()
+ {
+#if !defined(RELEASE_OR_BETA)
+ IOInterposer::Init();
+#endif
+ }
+
+ ~IOInterposerInit()
+ {
+#if !defined(RELEASE_OR_BETA)
+ IOInterposer::Clear();
+#endif
+ }
+};
+
+class MOZ_RAII AutoIOInterposerDisable final
+{
+public:
+ explicit AutoIOInterposerDisable(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ IOInterposer::Disable();
+ }
+ ~AutoIOInterposerDisable()
+ {
+ IOInterposer::Enable();
+ }
+
+private:
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+} // namespace mozilla
+
+#endif // mozilla_IOInterposer_h
diff --git a/xpcom/build/IOInterposerPrivate.h b/xpcom/build/IOInterposerPrivate.h
new file mode 100644
index 000000000..549321062
--- /dev/null
+++ b/xpcom/build/IOInterposerPrivate.h
@@ -0,0 +1,167 @@
+/* -*- 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 xpcom_build_IOInterposerPrivate_h
+#define xpcom_build_IOInterposerPrivate_h
+
+/* This header file contains declarations for helper classes that are
+ to be used exclusively by IOInterposer and its observers. This header
+ file is not to be used by anything else and MUST NOT be exported! */
+
+#include <prcvar.h>
+#include <prlock.h>
+
+namespace mozilla {
+namespace IOInterposer {
+
+/**
+ * The following classes are simple wrappers for PRLock and PRCondVar.
+ * IOInterposer and friends use these instead of Mozilla::Mutex et al because
+ * of the fact that IOInterposer is permitted to run until the process
+ * terminates; we can't use anything that plugs into leak checkers or deadlock
+ * detectors because IOInterposer will outlive those and generate false
+ * positives.
+ */
+
+class Monitor
+{
+public:
+ Monitor()
+ : mLock(PR_NewLock())
+ , mCondVar(PR_NewCondVar(mLock))
+ {
+ }
+
+ ~Monitor()
+ {
+ PR_DestroyCondVar(mCondVar);
+ mCondVar = nullptr;
+ PR_DestroyLock(mLock);
+ mLock = nullptr;
+ }
+
+ void Lock()
+ {
+ PR_Lock(mLock);
+ }
+
+ void Unlock()
+ {
+ PR_Unlock(mLock);
+ }
+
+ bool Wait(PRIntervalTime aTimeout = PR_INTERVAL_NO_TIMEOUT)
+ {
+ return PR_WaitCondVar(mCondVar, aTimeout) == PR_SUCCESS;
+ }
+
+ bool Notify()
+ {
+ return PR_NotifyCondVar(mCondVar) == PR_SUCCESS;
+ }
+
+private:
+ PRLock* mLock;
+ PRCondVar* mCondVar;
+};
+
+class MonitorAutoLock
+{
+public:
+ explicit MonitorAutoLock(Monitor& aMonitor)
+ : mMonitor(aMonitor)
+ {
+ mMonitor.Lock();
+ }
+
+ ~MonitorAutoLock()
+ {
+ mMonitor.Unlock();
+ }
+
+ bool Wait(PRIntervalTime aTimeout = PR_INTERVAL_NO_TIMEOUT)
+ {
+ return mMonitor.Wait(aTimeout);
+ }
+
+ bool Notify()
+ {
+ return mMonitor.Notify();
+ }
+
+private:
+ Monitor& mMonitor;
+};
+
+class MonitorAutoUnlock
+{
+public:
+ explicit MonitorAutoUnlock(Monitor& aMonitor)
+ : mMonitor(aMonitor)
+ {
+ mMonitor.Unlock();
+ }
+
+ ~MonitorAutoUnlock()
+ {
+ mMonitor.Lock();
+ }
+
+private:
+ Monitor& mMonitor;
+};
+
+class Mutex
+{
+public:
+ Mutex()
+ : mPRLock(PR_NewLock())
+ {
+ }
+
+ ~Mutex()
+ {
+ PR_DestroyLock(mPRLock);
+ mPRLock = nullptr;
+ }
+
+ void Lock()
+ {
+ PR_Lock(mPRLock);
+ }
+
+ void Unlock()
+ {
+ PR_Unlock(mPRLock);
+ }
+
+private:
+ PRLock* mPRLock;
+};
+
+class AutoLock
+{
+public:
+ explicit AutoLock(Mutex& aLock)
+ : mLock(aLock)
+ {
+ mLock.Lock();
+ }
+
+ ~AutoLock()
+ {
+ mLock.Unlock();
+ }
+
+private:
+ Mutex& mLock;
+};
+
+} // namespace IOInterposer
+} // namespace mozilla
+
+#endif // xpcom_build_IOInterposerPrivate_h
+
diff --git a/xpcom/build/LateWriteChecks.cpp b/xpcom/build/LateWriteChecks.cpp
new file mode 100644
index 000000000..69dbedc0f
--- /dev/null
+++ b/xpcom/build/LateWriteChecks.cpp
@@ -0,0 +1,258 @@
+/* -*- 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 <algorithm>
+
+#include "mozilla/IOInterposer.h"
+#include "mozilla/PoisonIOInterposer.h"
+#include "mozilla/ProcessedStack.h"
+#include "mozilla/SHA1.h"
+#include "mozilla/Scoped.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Telemetry.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsPrintfCString.h"
+#include "mozilla/StackWalk.h"
+#include "plstr.h"
+#include "prio.h"
+
+#ifdef XP_WIN
+#define NS_T(str) L ## str
+#define NS_SLASH "\\"
+#include <fcntl.h>
+#include <io.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <windows.h>
+#else
+#define NS_SLASH "/"
+#endif
+
+#include "LateWriteChecks.h"
+
+#define OBSERVE_LATE_WRITES
+
+using namespace mozilla;
+
+/*************************** Auxiliary Declarations ***************************/
+
+// This a wrapper over a file descriptor that provides a Printf method and
+// computes the sha1 of the data that passes through it.
+class SHA1Stream
+{
+public:
+ explicit SHA1Stream(FILE* aStream)
+ : mFile(aStream)
+ {
+ MozillaRegisterDebugFILE(mFile);
+ }
+
+ void Printf(const char* aFormat, ...)
+ {
+ MOZ_ASSERT(mFile);
+ va_list list;
+ va_start(list, aFormat);
+ nsAutoCString str;
+ str.AppendPrintf(aFormat, list);
+ va_end(list);
+ mSHA1.update(str.get(), str.Length());
+ fwrite(str.get(), 1, str.Length(), mFile);
+ }
+ void Finish(SHA1Sum::Hash& aHash)
+ {
+ int fd = fileno(mFile);
+ fflush(mFile);
+ MozillaUnRegisterDebugFD(fd);
+ fclose(mFile);
+ mSHA1.finish(aHash);
+ mFile = nullptr;
+ }
+private:
+ FILE* mFile;
+ SHA1Sum mSHA1;
+};
+
+static void
+RecordStackWalker(uint32_t aFrameNumber, void* aPC, void* aSP, void* aClosure)
+{
+ std::vector<uintptr_t>* stack =
+ static_cast<std::vector<uintptr_t>*>(aClosure);
+ stack->push_back(reinterpret_cast<uintptr_t>(aPC));
+}
+
+/**************************** Late-Write Observer ****************************/
+
+/**
+ * An implementation of IOInterposeObserver to be registered with IOInterposer.
+ * This observer logs all writes as late writes.
+ */
+class LateWriteObserver final : public IOInterposeObserver
+{
+public:
+ explicit LateWriteObserver(const char* aProfileDirectory)
+ : mProfileDirectory(PL_strdup(aProfileDirectory))
+ {
+ }
+ ~LateWriteObserver()
+ {
+ PL_strfree(mProfileDirectory);
+ mProfileDirectory = nullptr;
+ }
+
+ void Observe(IOInterposeObserver::Observation& aObservation);
+private:
+ char* mProfileDirectory;
+};
+
+void
+LateWriteObserver::Observe(IOInterposeObserver::Observation& aOb)
+{
+#ifdef OBSERVE_LATE_WRITES
+ // Crash if that is the shutdown check mode
+ if (gShutdownChecks == SCM_CRASH) {
+ MOZ_CRASH();
+ }
+
+ // If we have shutdown mode SCM_NOTHING or we can't record then abort
+ if (gShutdownChecks == SCM_NOTHING || !Telemetry::CanRecordExtended()) {
+ return;
+ }
+
+ // Write the stack and loaded libraries to a file. We can get here
+ // concurrently from many writes, so we use multiple temporary files.
+ std::vector<uintptr_t> rawStack;
+
+ MozStackWalk(RecordStackWalker, /* skipFrames */ 0, /* maxFrames */ 0,
+ reinterpret_cast<void*>(&rawStack), 0, nullptr);
+ Telemetry::ProcessedStack stack = Telemetry::GetStackAndModules(rawStack);
+
+ nsPrintfCString nameAux("%s%s%s", mProfileDirectory,
+ NS_SLASH, "Telemetry.LateWriteTmpXXXXXX");
+ char* name;
+ nameAux.GetMutableData(&name);
+
+ // We want the sha1 of the entire file, so please don't write to fd
+ // directly; use sha1Stream.
+ FILE* stream;
+#ifdef XP_WIN
+ HANDLE hFile;
+ do {
+ // mkstemp isn't supported so keep trying until we get a file
+ int result = _mktemp_s(name, strlen(name) + 1);
+ hFile = CreateFileA(name, GENERIC_WRITE, 0, nullptr, CREATE_NEW,
+ FILE_ATTRIBUTE_NORMAL, nullptr);
+ } while (GetLastError() == ERROR_FILE_EXISTS);
+
+ if (hFile == INVALID_HANDLE_VALUE) {
+ NS_RUNTIMEABORT("Um, how did we get here?");
+ }
+
+ // http://support.microsoft.com/kb/139640
+ int fd = _open_osfhandle((intptr_t)hFile, _O_APPEND);
+ if (fd == -1) {
+ NS_RUNTIMEABORT("Um, how did we get here?");
+ }
+
+ stream = _fdopen(fd, "w");
+#else
+ int fd = mkstemp(name);
+ stream = fdopen(fd, "w");
+#endif
+
+ SHA1Stream sha1Stream(stream);
+
+ size_t numModules = stack.GetNumModules();
+ sha1Stream.Printf("%u\n", (unsigned)numModules);
+ for (size_t i = 0; i < numModules; ++i) {
+ Telemetry::ProcessedStack::Module module = stack.GetModule(i);
+ sha1Stream.Printf("%s %s\n", module.mBreakpadId.c_str(),
+ module.mName.c_str());
+ }
+
+ size_t numFrames = stack.GetStackSize();
+ sha1Stream.Printf("%u\n", (unsigned)numFrames);
+ for (size_t i = 0; i < numFrames; ++i) {
+ const Telemetry::ProcessedStack::Frame& frame = stack.GetFrame(i);
+ // NOTE: We write the offsets, while the atos tool expects a value with
+ // the virtual address added. For example, running otool -l on the the firefox
+ // binary shows
+ // cmd LC_SEGMENT_64
+ // cmdsize 632
+ // segname __TEXT
+ // vmaddr 0x0000000100000000
+ // so to print the line matching the offset 123 one has to run
+ // atos -o firefox 0x100000123.
+ sha1Stream.Printf("%d %x\n", frame.mModIndex, (unsigned)frame.mOffset);
+ }
+
+ SHA1Sum::Hash sha1;
+ sha1Stream.Finish(sha1);
+
+ // Note: These files should be deleted by telemetry once it reads them. If
+ // there were no telemetry runs by the time we shut down, we just add files
+ // to the existing ones instead of replacing them. Given that each of these
+ // files is a bug to be fixed, that is probably the right thing to do.
+
+ // We append the sha1 of the contents to the file name. This provides a simple
+ // client side deduplication.
+ nsPrintfCString finalName("%s%s", mProfileDirectory,
+ "/Telemetry.LateWriteFinal-");
+ for (int i = 0; i < 20; ++i) {
+ finalName.AppendPrintf("%02x", sha1[i]);
+ }
+ PR_Delete(finalName.get());
+ PR_Rename(name, finalName.get());
+#endif
+}
+
+/******************************* Setup/Teardown *******************************/
+
+static StaticAutoPtr<LateWriteObserver> sLateWriteObserver;
+
+namespace mozilla {
+
+void
+InitLateWriteChecks()
+{
+ nsCOMPtr<nsIFile> mozFile;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mozFile));
+ if (mozFile) {
+ nsAutoCString nativePath;
+ nsresult rv = mozFile->GetNativePath(nativePath);
+ if (NS_SUCCEEDED(rv) && nativePath.get()) {
+ sLateWriteObserver = new LateWriteObserver(nativePath.get());
+ }
+ }
+}
+
+void
+BeginLateWriteChecks()
+{
+ if (sLateWriteObserver) {
+ IOInterposer::Register(
+ IOInterposeObserver::OpWriteFSync,
+ sLateWriteObserver
+ );
+ }
+}
+
+void
+StopLateWriteChecks()
+{
+ if (sLateWriteObserver) {
+ IOInterposer::Unregister(
+ IOInterposeObserver::OpAll,
+ sLateWriteObserver
+ );
+ // Deallocation would not be thread-safe, and StopLateWriteChecks() is
+ // called at shutdown and only in special cases.
+ // sLateWriteObserver = nullptr;
+ }
+}
+
+} // namespace mozilla
diff --git a/xpcom/build/LateWriteChecks.h b/xpcom/build/LateWriteChecks.h
new file mode 100644
index 000000000..96d981cc1
--- /dev/null
+++ b/xpcom/build/LateWriteChecks.h
@@ -0,0 +1,60 @@
+/* -*- 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_LateWriteChecks_h
+#define mozilla_LateWriteChecks_h
+
+// This file, along with LateWriteChecks.cpp, serves to check for and report
+// late writes. The idea is discover writes to the file system that happens
+// during shutdown such that these maybe be moved forward and the process may be
+// killed without waiting for static destructors.
+
+namespace mozilla {
+
+/** Different shutdown check modes */
+enum ShutdownChecksMode
+{
+ SCM_CRASH, /** Crash on shutdown check failure */
+ SCM_RECORD, /** Record shutdown check violations */
+ SCM_NOTHING /** Don't attempt any shutdown checks */
+};
+
+/**
+ * Current shutdown check mode.
+ * This variable is defined and initialized in nsAppRunner.cpp
+ */
+extern ShutdownChecksMode gShutdownChecks;
+
+/**
+ * Allocate structures and acquire information from XPCOM necessary to do late
+ * write checks. This function must be invoked before BeginLateWriteChecks()
+ * and before XPCOM has stopped working.
+ */
+void InitLateWriteChecks();
+
+/**
+ * Begin recording all writes as late-writes. This function should be called
+ * when all legitimate writes have occurred. This function does not rely on
+ * XPCOM as it is designed to be invoked during XPCOM shutdown.
+ *
+ * For late-write checks to work you must initialize one or more backends that
+ * reports IO through the IOInterposer API. PoisonIOInterposer would probably
+ * be the backend of choice in this case.
+ *
+ * Note: BeginLateWriteChecks() must have been invoked before this function.
+ */
+void BeginLateWriteChecks();
+
+/**
+ * Stop recording all writes as late-writes, call this function when you want
+ * late-write checks to stop. I.e. exception handling, or the special case on
+ * Mac described in bug 826029.
+ */
+void StopLateWriteChecks();
+
+} // namespace mozilla
+
+#endif // mozilla_LateWriteChecks_h
diff --git a/xpcom/build/MainThreadIOLogger.cpp b/xpcom/build/MainThreadIOLogger.cpp
new file mode 100644
index 000000000..0ca942cb9
--- /dev/null
+++ b/xpcom/build/MainThreadIOLogger.cpp
@@ -0,0 +1,225 @@
+/* -*- 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 "MainThreadIOLogger.h"
+
+#include "GeckoProfiler.h"
+#include "IOInterposerPrivate.h"
+#include "mozilla/IOInterposer.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/TimeStamp.h"
+#include "nsAutoPtr.h"
+#include "nsNativeCharsetUtils.h"
+
+/**
+ * This code uses NSPR stuff and STL containers because it must be detached
+ * from leak checking code; this observer runs until the process terminates.
+ */
+
+#include <prenv.h>
+#include <prprf.h>
+#include <prthread.h>
+#include <vector>
+
+namespace {
+
+struct ObservationWithStack
+{
+ ObservationWithStack(mozilla::IOInterposeObserver::Observation& aObs,
+ ProfilerBacktrace* aStack)
+ : mObservation(aObs)
+ , mStack(aStack)
+ {
+ const char16_t* filename = aObs.Filename();
+ if (filename) {
+ mFilename = filename;
+ }
+ }
+
+ mozilla::IOInterposeObserver::Observation mObservation;
+ ProfilerBacktrace* mStack;
+ nsString mFilename;
+};
+
+class MainThreadIOLoggerImpl final : public mozilla::IOInterposeObserver
+{
+public:
+ MainThreadIOLoggerImpl();
+ ~MainThreadIOLoggerImpl();
+
+ bool Init();
+
+ void Observe(Observation& aObservation);
+
+private:
+ static void sIOThreadFunc(void* aArg);
+ void IOThreadFunc();
+
+ TimeStamp mLogStartTime;
+ const char* mFileName;
+ PRThread* mIOThread;
+ IOInterposer::Monitor mMonitor;
+ bool mShutdownRequired;
+ std::vector<ObservationWithStack> mObservations;
+};
+
+static StaticAutoPtr<MainThreadIOLoggerImpl> sImpl;
+
+MainThreadIOLoggerImpl::MainThreadIOLoggerImpl()
+ : mFileName(nullptr)
+ , mIOThread(nullptr)
+ , mShutdownRequired(false)
+{
+}
+
+MainThreadIOLoggerImpl::~MainThreadIOLoggerImpl()
+{
+ if (!mIOThread) {
+ return;
+ }
+ {
+ // Scope for lock
+ IOInterposer::MonitorAutoLock lock(mMonitor);
+ mShutdownRequired = true;
+ lock.Notify();
+ }
+ PR_JoinThread(mIOThread);
+ mIOThread = nullptr;
+}
+
+bool
+MainThreadIOLoggerImpl::Init()
+{
+ if (mFileName) {
+ // Already initialized
+ return true;
+ }
+ mFileName = PR_GetEnv("MOZ_MAIN_THREAD_IO_LOG");
+ if (!mFileName) {
+ // Can't start
+ return false;
+ }
+ mIOThread = PR_CreateThread(PR_USER_THREAD, &sIOThreadFunc, this,
+ PR_PRIORITY_LOW, PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD, 0);
+ if (!mIOThread) {
+ return false;
+ }
+ return true;
+}
+
+/* static */ void
+MainThreadIOLoggerImpl::sIOThreadFunc(void* aArg)
+{
+ PR_SetCurrentThreadName("MainThreadIOLogger");
+ MainThreadIOLoggerImpl* obj = static_cast<MainThreadIOLoggerImpl*>(aArg);
+ obj->IOThreadFunc();
+}
+
+void
+MainThreadIOLoggerImpl::IOThreadFunc()
+{
+ PRFileDesc* fd = PR_Open(mFileName, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
+ PR_IRUSR | PR_IWUSR | PR_IRGRP);
+ if (!fd) {
+ IOInterposer::MonitorAutoLock lock(mMonitor);
+ mShutdownRequired = true;
+ std::vector<ObservationWithStack>().swap(mObservations);
+ return;
+ }
+ mLogStartTime = TimeStamp::Now();
+ {
+ // Scope for lock
+ IOInterposer::MonitorAutoLock lock(mMonitor);
+ while (true) {
+ while (!mShutdownRequired && mObservations.empty()) {
+ lock.Wait();
+ }
+ if (mShutdownRequired) {
+ break;
+ }
+ // Pull events off the shared array onto a local one
+ std::vector<ObservationWithStack> observationsToWrite;
+ observationsToWrite.swap(mObservations);
+
+ // Release the lock so that we're not holding anybody up during I/O
+ IOInterposer::MonitorAutoUnlock unlock(mMonitor);
+
+ // Now write the events.
+ for (auto i = observationsToWrite.begin(), e = observationsToWrite.end();
+ i != e; ++i) {
+ if (i->mObservation.ObservedOperation() == OpNextStage) {
+ PR_fprintf(fd, "%f,NEXT-STAGE\n",
+ (TimeStamp::Now() - mLogStartTime).ToMilliseconds());
+ continue;
+ }
+ double durationMs = i->mObservation.Duration().ToMilliseconds();
+ nsAutoCString nativeFilename;
+ nativeFilename.AssignLiteral("(not available)");
+ if (!i->mFilename.IsEmpty()) {
+ if (NS_FAILED(NS_CopyUnicodeToNative(i->mFilename, nativeFilename))) {
+ nativeFilename.AssignLiteral("(conversion failed)");
+ }
+ }
+ /**
+ * Format:
+ * Start Timestamp (Milliseconds), Operation, Duration (Milliseconds), Event Source, Filename
+ */
+ if (PR_fprintf(fd, "%f,%s,%f,%s,%s\n",
+ (i->mObservation.Start() - mLogStartTime).ToMilliseconds(),
+ i->mObservation.ObservedOperationString(), durationMs,
+ i->mObservation.Reference(), nativeFilename.get()) > 0) {
+ ProfilerBacktrace* stack = i->mStack;
+ if (stack) {
+ // TODO: Write out the callstack
+ // (This will be added in a later bug)
+ profiler_free_backtrace(stack);
+ }
+ }
+ }
+ }
+ }
+ PR_Close(fd);
+}
+
+void
+MainThreadIOLoggerImpl::Observe(Observation& aObservation)
+{
+ if (!mFileName || !IsMainThread()) {
+ return;
+ }
+ IOInterposer::MonitorAutoLock lock(mMonitor);
+ if (mShutdownRequired) {
+ // The writer thread isn't running. Don't enqueue any more data.
+ return;
+ }
+ // Passing nullptr as aStack parameter for now
+ mObservations.push_back(ObservationWithStack(aObservation, nullptr));
+ lock.Notify();
+}
+
+} // namespace
+
+namespace mozilla {
+
+namespace MainThreadIOLogger {
+
+bool
+Init()
+{
+ nsAutoPtr<MainThreadIOLoggerImpl> impl(new MainThreadIOLoggerImpl());
+ if (!impl->Init()) {
+ return false;
+ }
+ sImpl = impl.forget();
+ IOInterposer::Register(IOInterposeObserver::OpAllWithStaging, sImpl);
+ return true;
+}
+
+} // namespace MainThreadIOLogger
+
+} // namespace mozilla
+
diff --git a/xpcom/build/MainThreadIOLogger.h b/xpcom/build/MainThreadIOLogger.h
new file mode 100644
index 000000000..df9e30b53
--- /dev/null
+++ b/xpcom/build/MainThreadIOLogger.h
@@ -0,0 +1,19 @@
+/* -*- 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_MainThreadIOLogger_h
+#define mozilla_MainThreadIOLogger_h
+
+namespace mozilla {
+namespace MainThreadIOLogger {
+
+bool Init();
+
+} // namespace MainThreadIOLogger
+} // namespace mozilla
+
+#endif // mozilla_MainThreadIOLogger_h
+
diff --git a/xpcom/build/NSPRInterposer.cpp b/xpcom/build/NSPRInterposer.cpp
new file mode 100644
index 000000000..a7c53a97a
--- /dev/null
+++ b/xpcom/build/NSPRInterposer.cpp
@@ -0,0 +1,185 @@
+/* -*- 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 "IOInterposer.h"
+#include "NSPRInterposer.h"
+
+#include "prio.h"
+#include "private/pprio.h"
+#include "nsDebug.h"
+#include "nscore.h"
+
+namespace {
+
+using namespace mozilla;
+
+/* Original IO methods */
+PRCloseFN sCloseFn = nullptr;
+PRReadFN sReadFn = nullptr;
+PRWriteFN sWriteFn = nullptr;
+PRFsyncFN sFSyncFn = nullptr;
+PRFileInfoFN sFileInfoFn = nullptr;
+PRFileInfo64FN sFileInfo64Fn = nullptr;
+
+/**
+ * RAII class for timing the duration of an NSPR I/O call and reporting the
+ * result to the IOInterposeObserver API.
+ */
+class NSPRIOAutoObservation : public IOInterposeObserver::Observation
+{
+public:
+ explicit NSPRIOAutoObservation(IOInterposeObserver::Operation aOp)
+ : IOInterposeObserver::Observation(aOp, "NSPRIOInterposer")
+ {
+ }
+
+ ~NSPRIOAutoObservation()
+ {
+ Report();
+ }
+};
+
+PRStatus PR_CALLBACK
+interposedClose(PRFileDesc* aFd)
+{
+ // If we don't have a valid original function pointer something is very wrong.
+ NS_ASSERTION(sCloseFn, "NSPR IO Interposing: sCloseFn is NULL");
+
+ NSPRIOAutoObservation timer(IOInterposeObserver::OpClose);
+ return sCloseFn(aFd);
+}
+
+int32_t PR_CALLBACK
+interposedRead(PRFileDesc* aFd, void* aBuf, int32_t aAmt)
+{
+ // If we don't have a valid original function pointer something is very wrong.
+ NS_ASSERTION(sReadFn, "NSPR IO Interposing: sReadFn is NULL");
+
+ NSPRIOAutoObservation timer(IOInterposeObserver::OpRead);
+ return sReadFn(aFd, aBuf, aAmt);
+}
+
+int32_t PR_CALLBACK
+interposedWrite(PRFileDesc* aFd, const void* aBuf, int32_t aAmt)
+{
+ // If we don't have a valid original function pointer something is very wrong.
+ NS_ASSERTION(sWriteFn, "NSPR IO Interposing: sWriteFn is NULL");
+
+ NSPRIOAutoObservation timer(IOInterposeObserver::OpWrite);
+ return sWriteFn(aFd, aBuf, aAmt);
+}
+
+PRStatus PR_CALLBACK
+interposedFSync(PRFileDesc* aFd)
+{
+ // If we don't have a valid original function pointer something is very wrong.
+ NS_ASSERTION(sFSyncFn, "NSPR IO Interposing: sFSyncFn is NULL");
+
+ NSPRIOAutoObservation timer(IOInterposeObserver::OpFSync);
+ return sFSyncFn(aFd);
+}
+
+PRStatus PR_CALLBACK
+interposedFileInfo(PRFileDesc* aFd, PRFileInfo* aInfo)
+{
+ // If we don't have a valid original function pointer something is very wrong.
+ NS_ASSERTION(sFileInfoFn, "NSPR IO Interposing: sFileInfoFn is NULL");
+
+ NSPRIOAutoObservation timer(IOInterposeObserver::OpStat);
+ return sFileInfoFn(aFd, aInfo);
+}
+
+PRStatus PR_CALLBACK
+interposedFileInfo64(PRFileDesc* aFd, PRFileInfo64* aInfo)
+{
+ // If we don't have a valid original function pointer something is very wrong.
+ NS_ASSERTION(sFileInfo64Fn, "NSPR IO Interposing: sFileInfo64Fn is NULL");
+
+ NSPRIOAutoObservation timer(IOInterposeObserver::OpStat);
+ return sFileInfo64Fn(aFd, aInfo);
+}
+
+} // namespace
+
+namespace mozilla {
+
+void
+InitNSPRIOInterposing()
+{
+ // Check that we have not interposed any of the IO methods before
+ MOZ_ASSERT(!sCloseFn && !sReadFn && !sWriteFn && !sFSyncFn && !sFileInfoFn &&
+ !sFileInfo64Fn);
+
+ // We can't actually use this assertion because we initialize this code
+ // before XPCOM is initialized, so NS_IsMainThread() always returns false.
+ // MOZ_ASSERT(NS_IsMainThread());
+
+ // Get IO methods from NSPR and const cast the structure so we can modify it.
+ PRIOMethods* methods = const_cast<PRIOMethods*>(PR_GetFileMethods());
+
+ // Something is badly wrong if we don't get IO methods... However, we don't
+ // want to crash over that in non-debug builds. This is unlikely to happen
+ // so an assert is enough, no need to report it to the caller.
+ MOZ_ASSERT(methods);
+ if (!methods) {
+ return;
+ }
+
+ // Store original functions
+ sCloseFn = methods->close;
+ sReadFn = methods->read;
+ sWriteFn = methods->write;
+ sFSyncFn = methods->fsync;
+ sFileInfoFn = methods->fileInfo;
+ sFileInfo64Fn = methods->fileInfo64;
+
+ // Overwrite with our interposed functions
+ methods->close = &interposedClose;
+ methods->read = &interposedRead;
+ methods->write = &interposedWrite;
+ methods->fsync = &interposedFSync;
+ methods->fileInfo = &interposedFileInfo;
+ methods->fileInfo64 = &interposedFileInfo64;
+}
+
+void
+ClearNSPRIOInterposing()
+{
+ // If we have already cleared IO interposing, or not initialized it this is
+ // actually bad.
+ MOZ_ASSERT(sCloseFn && sReadFn && sWriteFn && sFSyncFn && sFileInfoFn &&
+ sFileInfo64Fn);
+
+ // Get IO methods from NSPR and const cast the structure so we can modify it.
+ PRIOMethods* methods = const_cast<PRIOMethods*>(PR_GetFileMethods());
+
+ // Something is badly wrong if we don't get IO methods... However, we don't
+ // want to crash over that in non-debug builds. This is unlikely to happen
+ // so an assert is enough, no need to report it to the caller.
+ MOZ_ASSERT(methods);
+ if (!methods) {
+ return;
+ }
+
+ // Restore original functions
+ methods->close = sCloseFn;
+ methods->read = sReadFn;
+ methods->write = sWriteFn;
+ methods->fsync = sFSyncFn;
+ methods->fileInfo = sFileInfoFn;
+ methods->fileInfo64 = sFileInfo64Fn;
+
+ // Forget about original functions
+ sCloseFn = nullptr;
+ sReadFn = nullptr;
+ sWriteFn = nullptr;
+ sFSyncFn = nullptr;
+ sFileInfoFn = nullptr;
+ sFileInfo64Fn = nullptr;
+}
+
+} // namespace mozilla
+
diff --git a/xpcom/build/NSPRInterposer.h b/xpcom/build/NSPRInterposer.h
new file mode 100644
index 000000000..2b8715077
--- /dev/null
+++ b/xpcom/build/NSPRInterposer.h
@@ -0,0 +1,28 @@
+/* -*- 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 NSPRINTERPOSER_H_
+#define NSPRINTERPOSER_H_
+
+namespace mozilla {
+
+/**
+ * Initialize IO interposing for NSPR. This will report NSPR read, writes and
+ * fsyncs to the IOInterposerObserver. It is only safe to call this from the
+ * main-thread when no other threads are running.
+ */
+void InitNSPRIOInterposing();
+
+/**
+ * Removes interception of NSPR IO methods as setup by InitNSPRIOInterposing.
+ * Note, that it is only safe to call this on the main-thread when all other
+ * threads have stopped. Which is typically the case at shutdown.
+ */
+void ClearNSPRIOInterposing();
+
+} // namespace mozilla
+
+#endif // NSPRINTERPOSER_H_
diff --git a/xpcom/build/Omnijar.cpp b/xpcom/build/Omnijar.cpp
new file mode 100644
index 000000000..e1a8a1e5b
--- /dev/null
+++ b/xpcom/build/Omnijar.cpp
@@ -0,0 +1,188 @@
+/* -*- 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 "Omnijar.h"
+
+#include "nsDirectoryService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIFile.h"
+#include "nsZipArchive.h"
+#include "nsNetUtil.h"
+
+namespace mozilla {
+
+StaticRefPtr<nsIFile> Omnijar::sPath[2];
+StaticRefPtr<nsZipArchive> Omnijar::sReader[2];
+StaticRefPtr<nsZipArchive> Omnijar::sOuterReader[2];
+bool Omnijar::sInitialized = false;
+bool Omnijar::sIsUnified = false;
+
+static const char* sProp[2] = {
+ NS_GRE_DIR, NS_XPCOM_CURRENT_PROCESS_DIR
+};
+
+#define SPROP(Type) ((Type == mozilla::Omnijar::GRE) ? sProp[GRE] : sProp[APP])
+
+void
+Omnijar::CleanUpOne(Type aType)
+{
+ if (sReader[aType]) {
+ sReader[aType]->CloseArchive();
+ sReader[aType] = nullptr;
+ }
+ if (sOuterReader[aType]) {
+ sOuterReader[aType]->CloseArchive();
+ sOuterReader[aType] = nullptr;
+ }
+ sPath[aType] = nullptr;
+}
+
+void
+Omnijar::InitOne(nsIFile* aPath, Type aType)
+{
+ nsCOMPtr<nsIFile> file;
+ if (aPath) {
+ file = aPath;
+ } else {
+ nsCOMPtr<nsIFile> dir;
+ nsDirectoryService::gService->Get(SPROP(aType), NS_GET_IID(nsIFile),
+ getter_AddRefs(dir));
+ NS_NAMED_LITERAL_CSTRING(kOmnijarName, NS_STRINGIFY(OMNIJAR_NAME));
+ if (NS_FAILED(dir->Clone(getter_AddRefs(file))) ||
+ NS_FAILED(file->AppendNative(kOmnijarName))) {
+ return;
+ }
+ }
+ bool isFile;
+ if (NS_FAILED(file->IsFile(&isFile)) || !isFile) {
+ // If we're not using an omni.jar for GRE, and we don't have an
+ // omni.jar for APP, check if both directories are the same.
+ if ((aType == APP) && (!sPath[GRE])) {
+ nsCOMPtr<nsIFile> greDir, appDir;
+ bool equals;
+ nsDirectoryService::gService->Get(sProp[GRE], NS_GET_IID(nsIFile),
+ getter_AddRefs(greDir));
+ nsDirectoryService::gService->Get(sProp[APP], NS_GET_IID(nsIFile),
+ getter_AddRefs(appDir));
+ if (NS_SUCCEEDED(greDir->Equals(appDir, &equals)) && equals) {
+ sIsUnified = true;
+ }
+ }
+ return;
+ }
+
+ bool equals;
+ if ((aType == APP) && (sPath[GRE]) &&
+ NS_SUCCEEDED(sPath[GRE]->Equals(file, &equals)) && equals) {
+ // If we're using omni.jar on both GRE and APP and their path
+ // is the same, we're in the unified case.
+ sIsUnified = true;
+ return;
+ }
+
+ RefPtr<nsZipArchive> zipReader = new nsZipArchive();
+ if (NS_FAILED(zipReader->OpenArchive(file))) {
+ return;
+ }
+
+ RefPtr<nsZipArchive> outerReader;
+ RefPtr<nsZipHandle> handle;
+ if (NS_SUCCEEDED(nsZipHandle::Init(zipReader, NS_STRINGIFY(OMNIJAR_NAME),
+ getter_AddRefs(handle)))) {
+ outerReader = zipReader;
+ zipReader = new nsZipArchive();
+ if (NS_FAILED(zipReader->OpenArchive(handle))) {
+ return;
+ }
+ }
+
+ CleanUpOne(aType);
+ sReader[aType] = zipReader;
+ sOuterReader[aType] = outerReader;
+ sPath[aType] = file;
+}
+
+void
+Omnijar::Init(nsIFile* aGrePath, nsIFile* aAppPath)
+{
+ InitOne(aGrePath, GRE);
+ InitOne(aAppPath, APP);
+ sInitialized = true;
+}
+
+void
+Omnijar::CleanUp()
+{
+ CleanUpOne(GRE);
+ CleanUpOne(APP);
+ sInitialized = false;
+}
+
+already_AddRefed<nsZipArchive>
+Omnijar::GetReader(nsIFile* aPath)
+{
+ MOZ_ASSERT(IsInitialized(), "Omnijar not initialized");
+
+ bool equals;
+ nsresult rv;
+
+ if (sPath[GRE]) {
+ rv = sPath[GRE]->Equals(aPath, &equals);
+ if (NS_SUCCEEDED(rv) && equals) {
+ return IsNested(GRE) ? GetOuterReader(GRE) : GetReader(GRE);
+ }
+ }
+ if (sPath[APP]) {
+ rv = sPath[APP]->Equals(aPath, &equals);
+ if (NS_SUCCEEDED(rv) && equals) {
+ return IsNested(APP) ? GetOuterReader(APP) : GetReader(APP);
+ }
+ }
+ return nullptr;
+}
+
+nsresult
+Omnijar::GetURIString(Type aType, nsACString& aResult)
+{
+ MOZ_ASSERT(IsInitialized(), "Omnijar not initialized");
+
+ aResult.Truncate();
+
+ // Return an empty string for APP in the unified case.
+ if ((aType == APP) && sIsUnified) {
+ return NS_OK;
+ }
+
+ nsAutoCString omniJarSpec;
+ if (sPath[aType]) {
+ nsresult rv = NS_GetURLSpecFromActualFile(sPath[aType], omniJarSpec);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ aResult = "jar:";
+ if (IsNested(aType)) {
+ aResult += "jar:";
+ }
+ aResult += omniJarSpec;
+ aResult += "!";
+ if (IsNested(aType)) {
+ aResult += "/" NS_STRINGIFY(OMNIJAR_NAME) "!";
+ }
+ } else {
+ nsCOMPtr<nsIFile> dir;
+ nsDirectoryService::gService->Get(SPROP(aType), NS_GET_IID(nsIFile),
+ getter_AddRefs(dir));
+ nsresult rv = NS_GetURLSpecFromActualFile(dir, aResult);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+ aResult += "/";
+ return NS_OK;
+}
+
+} /* namespace mozilla */
diff --git a/xpcom/build/Omnijar.h b/xpcom/build/Omnijar.h
new file mode 100644
index 000000000..35a208bae
--- /dev/null
+++ b/xpcom/build/Omnijar.h
@@ -0,0 +1,160 @@
+/* -*- 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_Omnijar_h
+#define mozilla_Omnijar_h
+
+#include "nscore.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsIFile.h"
+#include "nsZipArchive.h"
+
+#include "mozilla/StaticPtr.h"
+
+namespace mozilla {
+
+class Omnijar
+{
+private:
+ /**
+ * Store an nsIFile for an omni.jar. We can store two paths here, one
+ * for GRE (corresponding to resource://gre/) and one for APP
+ * (corresponding to resource:/// and resource://app/), but only
+ * store one when both point to the same location (unified).
+ */
+ static StaticRefPtr<nsIFile> sPath[2];
+
+ /**
+ * Cached nsZipArchives for the corresponding sPath
+ */
+ static StaticRefPtr<nsZipArchive> sReader[2];
+
+ /**
+ * Cached nsZipArchives for the outer jar, when using nested jars.
+ * Otherwise nullptr.
+ */
+ static StaticRefPtr<nsZipArchive> sOuterReader[2];
+
+ /**
+ * Has Omnijar::Init() been called?
+ */
+ static bool sInitialized;
+
+ /**
+ * Is using unified GRE/APP jar?
+ */
+ static bool sIsUnified;
+
+public:
+ enum Type
+ {
+ GRE = 0,
+ APP = 1
+ };
+
+private:
+ /**
+ * Returns whether we are using nested jars.
+ */
+ static inline bool IsNested(Type aType)
+ {
+ MOZ_ASSERT(IsInitialized(), "Omnijar not initialized");
+ return !!sOuterReader[aType];
+ }
+
+ /**
+ * Returns a nsZipArchive pointer for the outer jar file when using nested
+ * jars. Returns nullptr in the same cases GetPath() would, or if not using
+ * nested jars.
+ */
+ static inline already_AddRefed<nsZipArchive> GetOuterReader(Type aType)
+ {
+ MOZ_ASSERT(IsInitialized(), "Omnijar not initialized");
+ RefPtr<nsZipArchive> reader = sOuterReader[aType].get();
+ return reader.forget();
+ }
+
+public:
+ /**
+ * Returns whether SetBase has been called at least once with
+ * a valid nsIFile
+ */
+ static inline bool IsInitialized() { return sInitialized; }
+
+ /**
+ * Initializes the Omnijar API with the given directory or file for GRE and
+ * APP. Each of the paths given can be:
+ * - a file path, pointing to the omnijar file,
+ * - a directory path, pointing to a directory containing an "omni.jar" file,
+ * - nullptr for autodetection of an "omni.jar" file.
+ */
+ static void Init(nsIFile* aGrePath = nullptr, nsIFile* aAppPath = nullptr);
+
+ /**
+ * Cleans up the Omnijar API
+ */
+ static void CleanUp();
+
+ /**
+ * Returns an nsIFile pointing to the omni.jar file for GRE or APP.
+ * Returns nullptr when there is no corresponding omni.jar.
+ * Also returns nullptr for APP in the unified case.
+ */
+ static inline already_AddRefed<nsIFile> GetPath(Type aType)
+ {
+ MOZ_ASSERT(IsInitialized(), "Omnijar not initialized");
+ nsCOMPtr<nsIFile> path = sPath[aType].get();
+ return path.forget();
+ }
+
+ /**
+ * Returns whether GRE or APP use an omni.jar. Returns PR_False for
+ * APP when using an omni.jar in the unified case.
+ */
+ static inline bool HasOmnijar(Type aType)
+ {
+ MOZ_ASSERT(IsInitialized(), "Omnijar not initialized");
+ return !!sPath[aType];
+ }
+
+ /**
+ * Returns a nsZipArchive pointer for the omni.jar file for GRE or
+ * APP. Returns nullptr in the same cases GetPath() would.
+ */
+ static inline already_AddRefed<nsZipArchive> GetReader(Type aType)
+ {
+ MOZ_ASSERT(IsInitialized(), "Omnijar not initialized");
+ RefPtr<nsZipArchive> reader = sReader[aType].get();
+ return reader.forget();
+ }
+
+ /**
+ * Returns a nsZipArchive pointer for the given path IAOI the given
+ * path is the omni.jar for either GRE or APP.
+ */
+ static already_AddRefed<nsZipArchive> GetReader(nsIFile* aPath);
+
+ /**
+ * Returns the URI string corresponding to the omni.jar or directory
+ * for GRE or APP. i.e. jar:/path/to/omni.jar!/ for omni.jar and
+ * /path/to/base/dir/ otherwise. Returns an empty string for APP in
+ * the unified case.
+ * The returned URI is guaranteed to end with a slash.
+ */
+ static nsresult GetURIString(Type aType, nsACString& aResult);
+
+private:
+ /**
+ * Used internally, respectively by Init() and CleanUp()
+ */
+ static void InitOne(nsIFile* aPath, Type aType);
+ static void CleanUpOne(Type aType);
+}; /* class Omnijar */
+
+} /* namespace mozilla */
+
+#endif /* mozilla_Omnijar_h */
diff --git a/xpcom/build/PoisonIOInterposer.h b/xpcom/build/PoisonIOInterposer.h
new file mode 100644
index 000000000..4a4d1426d
--- /dev/null
+++ b/xpcom/build/PoisonIOInterposer.h
@@ -0,0 +1,91 @@
+/* -*- 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_PoisonIOInterposer_h
+#define mozilla_PoisonIOInterposer_h
+
+#include "mozilla/Types.h"
+#include <stdio.h>
+
+MOZ_BEGIN_EXTERN_C
+
+/** Register file handle to be ignored by poisoning IO interposer. This function
+ * and the corresponding UnRegister function are necessary for exchange of handles
+ * between binaries not using the same CRT on Windows (which happens when one of
+ * them links the static CRT). In such cases, giving file descriptors or FILEs
+ * doesn't work because _get_osfhandle fails with "invalid parameter". */
+void MozillaRegisterDebugHandle(intptr_t aHandle);
+
+/** Register file descriptor to be ignored by poisoning IO interposer */
+void MozillaRegisterDebugFD(int aFd);
+
+/** Register file to be ignored by poisoning IO interposer */
+void MozillaRegisterDebugFILE(FILE* aFile);
+
+/** Unregister file handle from being ignored by poisoning IO interposer */
+void MozillaUnRegisterDebugHandle(intptr_t aHandle);
+
+/** Unregister file descriptor from being ignored by poisoning IO interposer */
+void MozillaUnRegisterDebugFD(int aFd);
+
+/** Unregister file from being ignored by poisoning IO interposer */
+void MozillaUnRegisterDebugFILE(FILE* aFile);
+
+MOZ_END_EXTERN_C
+
+#if defined(XP_WIN) || defined(XP_MACOSX)
+
+#ifdef __cplusplus
+namespace mozilla {
+
+/**
+ * Check if a file is registered as a debug file.
+ */
+bool IsDebugFile(intptr_t aFileID);
+
+/**
+ * Initialize IO poisoning, this is only safe to do on the main-thread when no
+ * other threads are running.
+ *
+ * Please, note that this probably has performance implications as all
+ */
+void InitPoisonIOInterposer();
+
+#ifdef XP_MACOSX
+/**
+ * Check that writes are dirty before reporting I/O (Mac OS X only)
+ * This is necessary for late-write checks on Mac OS X, but reading the buffer
+ * from file to see if we're writing dirty bits is expensive, so we don't want
+ * to do this for everything else that uses
+ */
+void OnlyReportDirtyWrites();
+#endif /* XP_MACOSX */
+
+/**
+ * Clear IO poisoning, this is only safe to do on the main-thread when no other
+ * threads are running.
+ */
+void ClearPoisonIOInterposer();
+
+} // namespace mozilla
+#endif /* __cplusplus */
+
+#else /* XP_WIN || XP_MACOSX */
+
+#ifdef __cplusplus
+namespace mozilla {
+inline bool IsDebugFile(intptr_t aFileID) { return true; }
+inline void InitPoisonIOInterposer() {}
+inline void ClearPoisonIOInterposer() {}
+#ifdef XP_MACOSX
+inline void OnlyReportDirtyWrites() {}
+#endif /* XP_MACOSX */
+} // namespace mozilla
+#endif /* __cplusplus */
+
+#endif /* XP_WIN || XP_MACOSX */
+
+#endif // mozilla_PoisonIOInterposer_h
diff --git a/xpcom/build/PoisonIOInterposerBase.cpp b/xpcom/build/PoisonIOInterposerBase.cpp
new file mode 100644
index 000000000..0e5754392
--- /dev/null
+++ b/xpcom/build/PoisonIOInterposerBase.cpp
@@ -0,0 +1,291 @@
+/* -*- 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/Mutex.h"
+#include "mozilla/Scoped.h"
+#include "mozilla/UniquePtr.h"
+
+#include <algorithm>
+
+#include "PoisonIOInterposer.h"
+
+#ifdef MOZ_REPLACE_MALLOC
+#include "replace_malloc_bridge.h"
+#endif
+
+// Auxiliary method to convert file descriptors to ids
+#if defined(XP_WIN32)
+#include <io.h>
+inline intptr_t
+FileDescriptorToHandle(int aFd)
+{
+ return _get_osfhandle(aFd);
+}
+#else
+inline intptr_t
+FileDescriptorToHandle(int aFd)
+{
+ return aFd;
+}
+#endif /* if not XP_WIN32 */
+
+using namespace mozilla;
+
+namespace {
+struct DebugFilesAutoLockTraits
+{
+ typedef PRLock* type;
+ typedef const PRLock* const_type;
+ static const_type empty() { return nullptr; }
+ static void release(type aL) { PR_Unlock(aL); }
+};
+
+class DebugFilesAutoLock : public Scoped<DebugFilesAutoLockTraits>
+{
+ static PRLock* Lock;
+public:
+ static void Clear();
+ static PRLock* getDebugFileIDsLock()
+ {
+ // On windows this static is not thread safe, but we know that the first
+ // call is from
+ // * An early registration of a debug FD or
+ // * The call to InitWritePoisoning.
+ // Since the early debug FDs are logs created early in the main thread
+ // and no writes are trapped before InitWritePoisoning, we are safe.
+ if (!Lock) {
+ Lock = PR_NewLock();
+ }
+
+ // We have to use something lower level than a mutex. If we don't, we
+ // can get recursive in here when called from logging a call to free.
+ return Lock;
+ }
+
+ DebugFilesAutoLock()
+ : Scoped<DebugFilesAutoLockTraits>(getDebugFileIDsLock())
+ {
+ PR_Lock(get());
+ }
+};
+
+PRLock* DebugFilesAutoLock::Lock;
+void
+DebugFilesAutoLock::Clear()
+{
+ MOZ_ASSERT(Lock != nullptr);
+ Lock = nullptr;
+}
+
+// The ChunkedList<T> class implements, at the high level, a non-iterable
+// list of instances of T. Its goal is to be somehow minimalist for the
+// use case of storing the debug files handles here, with the property of
+// not requiring a lock to look up whether it contains a specific value.
+// It is also chunked in blocks of chunk_size bytes so that its
+// initialization doesn't require a memory allocation, while keeping the
+// possibility to increase its size as necessary. Note that chunks are
+// never deallocated (except in the destructor).
+// All operations are essentially O(N) but N is not expected to be large
+// enough to matter.
+template <typename T, size_t chunk_size=64>
+class ChunkedList {
+ struct ListChunk {
+ static const size_t kLength = \
+ (chunk_size - sizeof(ListChunk*)) / sizeof(mozilla::Atomic<T>);
+
+ mozilla::Atomic<T> mElements[kLength];
+ mozilla::UniquePtr<ListChunk> mNext;
+
+ ListChunk() : mNext(nullptr) {}
+ };
+
+ ListChunk mList;
+ mozilla::Atomic<size_t> mLength;
+
+public:
+ ChunkedList() : mLength(0) {}
+
+ ~ChunkedList() {
+ // There can be writes happening after this destructor runs, so keep
+ // the list contents and don't reset mLength. But if there are more
+ // elements left than the first chunk can hold, then all hell breaks
+ // loose for any write that would happen after that because any extra
+ // chunk would be deallocated, so just crash in that case.
+ MOZ_RELEASE_ASSERT(mLength <= ListChunk::kLength);
+ }
+
+ // Add an element at the end of the last chunk of the list. Create a new
+ // chunk if there is not enough room.
+ // This is not thread-safe with another thread calling Add or Remove.
+ void Add(T aValue)
+ {
+ ListChunk *list = &mList;
+ size_t position = mLength;
+ for (; position >= ListChunk::kLength; position -= ListChunk::kLength) {
+ if (!list->mNext) {
+ list->mNext.reset(new ListChunk());
+ }
+ list = list->mNext.get();
+ }
+ // Use an order of operations that ensures any racing Contains call
+ // can't be hurt.
+ list->mElements[position] = aValue;
+ mLength++;
+ }
+
+ // Remove an element from the list by replacing it with the last element
+ // of the list, and then shrinking the list.
+ // This is not thread-safe with another thread calling Add or Remove.
+ void Remove(T aValue)
+ {
+ if (!mLength) {
+ return;
+ }
+ ListChunk *list = &mList;
+ size_t last = mLength - 1;
+ do {
+ size_t position = 0;
+ // Look for an element matching the given value.
+ for (; position < ListChunk::kLength; position++) {
+ if (aValue == list->mElements[position]) {
+ ListChunk *last_list = list;
+ // Look for the last element in the list, starting from where we are
+ // instead of starting over.
+ for (; last >= ListChunk::kLength; last -= ListChunk::kLength) {
+ last_list = last_list->mNext.get();
+ }
+ // Use an order of operations that ensures any racing Contains call
+ // can't be hurt.
+ T value = last_list->mElements[last];
+ list->mElements[position] = value;
+ mLength--;
+ return;
+ }
+ }
+ last -= ListChunk::kLength;
+ list = list->mNext.get();
+ } while (list);
+ }
+
+ // Returns whether the list contains the given value. It is meant to be safe
+ // to use without locking, with the tradeoff of being not entirely accurate
+ // if another thread adds or removes an element while this function runs.
+ bool Contains(T aValue)
+ {
+ ListChunk *list = &mList;
+ // Fix the range of the lookup to whatever the list length is when the
+ // function is called.
+ size_t length = mLength;
+ do {
+ size_t list_length = ListChunk::kLength;
+ list_length = std::min(list_length, length);
+ for (size_t position = 0; position < list_length; position++) {
+ if (aValue == list->mElements[position]) {
+ return true;
+ }
+ }
+ length -= ListChunk::kLength;
+ list = list->mNext.get();
+ } while (list);
+
+ return false;
+ }
+};
+
+typedef ChunkedList<intptr_t> FdList;
+
+// Return a list used to hold the IDs of the current debug files. On unix
+// an ID is a file descriptor. On Windows it is a file HANDLE.
+FdList&
+getDebugFileIDs()
+{
+ static FdList DebugFileIDs;
+ return DebugFileIDs;
+}
+
+
+} // namespace
+
+namespace mozilla {
+
+// Auxiliary Method to test if a file descriptor is registered to be ignored
+// by the poisoning IO interposer
+bool
+IsDebugFile(intptr_t aFileID)
+{
+ return getDebugFileIDs().Contains(aFileID);
+}
+
+} // namespace mozilla
+
+extern "C" {
+
+void
+MozillaRegisterDebugHandle(intptr_t aHandle)
+{
+ DebugFilesAutoLock lockedScope;
+ FdList& DebugFileIDs = getDebugFileIDs();
+ MOZ_ASSERT(!DebugFileIDs.Contains(aHandle));
+ DebugFileIDs.Add(aHandle);
+}
+
+void
+MozillaRegisterDebugFD(int aFd)
+{
+ MozillaRegisterDebugHandle(FileDescriptorToHandle(aFd));
+}
+
+void
+MozillaRegisterDebugFILE(FILE* aFile)
+{
+ int fd = fileno(aFile);
+ if (fd == 1 || fd == 2) {
+ return;
+ }
+ MozillaRegisterDebugFD(fd);
+}
+
+void
+MozillaUnRegisterDebugHandle(intptr_t aHandle)
+{
+ DebugFilesAutoLock lockedScope;
+ FdList& DebugFileIDs = getDebugFileIDs();
+ MOZ_ASSERT(DebugFileIDs.Contains(aHandle));
+ DebugFileIDs.Remove(aHandle);
+}
+
+void
+MozillaUnRegisterDebugFD(int aFd)
+{
+ MozillaUnRegisterDebugHandle(FileDescriptorToHandle(aFd));
+}
+
+void
+MozillaUnRegisterDebugFILE(FILE* aFile)
+{
+ int fd = fileno(aFile);
+ if (fd == 1 || fd == 2) {
+ return;
+ }
+ fflush(aFile);
+ MozillaUnRegisterDebugFD(fd);
+}
+
+} // extern "C"
+
+#ifdef MOZ_REPLACE_MALLOC
+void
+DebugFdRegistry::RegisterHandle(intptr_t aHandle)
+{
+ MozillaRegisterDebugHandle(aHandle);
+}
+
+void
+DebugFdRegistry::UnRegisterHandle(intptr_t aHandle)
+{
+ MozillaUnRegisterDebugHandle(aHandle);
+}
+#endif
diff --git a/xpcom/build/PoisonIOInterposerMac.cpp b/xpcom/build/PoisonIOInterposerMac.cpp
new file mode 100644
index 000000000..966269495
--- /dev/null
+++ b/xpcom/build/PoisonIOInterposerMac.cpp
@@ -0,0 +1,386 @@
+/* -*- 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 "PoisonIOInterposer.h"
+#include "mach_override.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/IOInterposer.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/ProcessedStack.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "nsPrintfCString.h"
+#include "mozilla/StackWalk.h"
+#include "nsTraceRefcnt.h"
+#include "plstr.h"
+#include "prio.h"
+
+#include <algorithm>
+#include <vector>
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <aio.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#ifdef MOZ_REPLACE_MALLOC
+#include "replace_malloc_bridge.h"
+#endif
+
+namespace {
+
+using namespace mozilla;
+
+// Bit tracking if poisoned writes are enabled
+static bool sIsEnabled = false;
+
+// Check if writes are dirty before reporting IO
+static bool sOnlyReportDirtyWrites = false;
+
+// Routines for write validation
+bool IsValidWrite(int aFd, const void* aWbuf, size_t aCount);
+bool IsIPCWrite(int aFd, const struct stat& aBuf);
+
+/******************************** IO AutoTimer ********************************/
+
+/**
+ * RAII class for timing the duration of an I/O call and reporting the result
+ * to the IOInterposeObserver API.
+ */
+class MacIOAutoObservation : public IOInterposeObserver::Observation
+{
+public:
+ MacIOAutoObservation(IOInterposeObserver::Operation aOp, int aFd)
+ : IOInterposeObserver::Observation(aOp, sReference, sIsEnabled &&
+ !IsDebugFile(aFd))
+ , mFd(aFd)
+ , mHasQueriedFilename(false)
+ , mFilename(nullptr)
+ {
+ }
+
+ MacIOAutoObservation(IOInterposeObserver::Operation aOp, int aFd,
+ const void* aBuf, size_t aCount)
+ : IOInterposeObserver::Observation(aOp, sReference, sIsEnabled &&
+ !IsDebugFile(aFd) &&
+ IsValidWrite(aFd, aBuf, aCount))
+ , mFd(aFd)
+ , mHasQueriedFilename(false)
+ , mFilename(nullptr)
+ {
+ }
+
+ // Custom implementation of IOInterposeObserver::Observation::Filename
+ const char16_t* Filename() override;
+
+ ~MacIOAutoObservation()
+ {
+ Report();
+ if (mFilename) {
+ free(mFilename);
+ mFilename = nullptr;
+ }
+ }
+
+private:
+ int mFd;
+ bool mHasQueriedFilename;
+ char16_t* mFilename;
+ static const char* sReference;
+};
+
+const char* MacIOAutoObservation::sReference = "PoisonIOInterposer";
+
+// Get filename for this observation
+const char16_t*
+MacIOAutoObservation::Filename()
+{
+ // If mHasQueriedFilename is true, then we already have it
+ if (mHasQueriedFilename) {
+ return mFilename;
+ }
+ char filename[MAXPATHLEN];
+ if (fcntl(mFd, F_GETPATH, filename) != -1) {
+ mFilename = UTF8ToNewUnicode(nsDependentCString(filename));
+ } else {
+ mFilename = nullptr;
+ }
+ mHasQueriedFilename = true;
+
+ // Return filename
+ return mFilename;
+}
+
+/****************************** Write Validation ******************************/
+
+// We want to detect "actual" writes, not IPC. Some IPC mechanisms are
+// implemented with file descriptors, so filter them out.
+bool
+IsIPCWrite(int aFd, const struct stat& aBuf)
+{
+ if ((aBuf.st_mode & S_IFMT) == S_IFIFO) {
+ return true;
+ }
+
+ if ((aBuf.st_mode & S_IFMT) != S_IFSOCK) {
+ return false;
+ }
+
+ sockaddr_storage address;
+ socklen_t len = sizeof(address);
+ if (getsockname(aFd, (sockaddr*)&address, &len) != 0) {
+ return true; // Ignore the aFd if we can't find what it is.
+ }
+
+ return address.ss_family == AF_UNIX;
+}
+
+// We want to report actual disk IO not things that don't move bits on the disk
+bool
+IsValidWrite(int aFd, const void* aWbuf, size_t aCount)
+{
+ // Ignore writes of zero bytes, Firefox does some during shutdown.
+ if (aCount == 0) {
+ return false;
+ }
+
+ {
+ struct stat buf;
+ int rv = fstat(aFd, &buf);
+ if (rv != 0) {
+ return true;
+ }
+
+ if (IsIPCWrite(aFd, buf)) {
+ return false;
+ }
+ }
+
+ // For writev we pass a nullptr aWbuf. We should only get here from
+ // dbm, and it uses write, so assert that we have aWbuf.
+ if (!aWbuf) {
+ return true;
+ }
+
+ // Break, here if we're allowed to report non-dirty writes
+ if (!sOnlyReportDirtyWrites) {
+ return true;
+ }
+
+ // As a really bad hack, accept writes that don't change the on disk
+ // content. This is needed because dbm doesn't keep track of dirty bits
+ // and can end up writing the same data to disk twice. Once when the
+ // user (nss) asks it to sync and once when closing the database.
+ auto wbuf2 = MakeUniqueFallible<char[]>(aCount);
+ if (!wbuf2) {
+ return true;
+ }
+ off_t pos = lseek(aFd, 0, SEEK_CUR);
+ if (pos == -1) {
+ return true;
+ }
+ ssize_t r = read(aFd, wbuf2.get(), aCount);
+ if (r < 0 || (size_t)r != aCount) {
+ return true;
+ }
+ int cmp = memcmp(aWbuf, wbuf2.get(), aCount);
+ if (cmp != 0) {
+ return true;
+ }
+ off_t pos2 = lseek(aFd, pos, SEEK_SET);
+ if (pos2 != pos) {
+ return true;
+ }
+
+ // Otherwise this is not a valid write
+ return false;
+}
+
+/*************************** Function Interception ***************************/
+
+/** Structure for declaration of function override */
+struct FuncData
+{
+ const char* Name; // Name of the function for the ones we use dlsym
+ const void* Wrapper; // The function that we will replace 'Function' with
+ void* Function; // The function that will be replaced with 'Wrapper'
+ void* Buffer; // Will point to the jump buffer that lets us call
+ // 'Function' after it has been replaced.
+};
+
+// Wrap aio_write. We have not seen it before, so just assert/report it.
+typedef ssize_t (*aio_write_t)(struct aiocb* aAioCbp);
+ssize_t wrap_aio_write(struct aiocb* aAioCbp);
+FuncData aio_write_data = { 0, (void*)wrap_aio_write, (void*)aio_write };
+ssize_t
+wrap_aio_write(struct aiocb* aAioCbp)
+{
+ MacIOAutoObservation timer(IOInterposeObserver::OpWrite,
+ aAioCbp->aio_fildes);
+
+ aio_write_t old_write = (aio_write_t)aio_write_data.Buffer;
+ return old_write(aAioCbp);
+}
+
+// Wrap pwrite-like functions.
+// We have not seen them before, so just assert/report it.
+typedef ssize_t (*pwrite_t)(int aFd, const void* buf, size_t aNumBytes,
+ off_t aOffset);
+template<FuncData& foo>
+ssize_t
+wrap_pwrite_temp(int aFd, const void* aBuf, size_t aNumBytes, off_t aOffset)
+{
+ MacIOAutoObservation timer(IOInterposeObserver::OpWrite, aFd);
+ pwrite_t old_write = (pwrite_t)foo.Buffer;
+ return old_write(aFd, aBuf, aNumBytes, aOffset);
+}
+
+// Define a FuncData for a pwrite-like functions.
+#define DEFINE_PWRITE_DATA(X, NAME) \
+FuncData X ## _data = { NAME, (void*) wrap_pwrite_temp<X ## _data> }; \
+
+// This exists everywhere.
+DEFINE_PWRITE_DATA(pwrite, "pwrite")
+// These exist on 32 bit OS X
+DEFINE_PWRITE_DATA(pwrite_NOCANCEL_UNIX2003, "pwrite$NOCANCEL$UNIX2003");
+DEFINE_PWRITE_DATA(pwrite_UNIX2003, "pwrite$UNIX2003");
+// This exists on 64 bit OS X
+DEFINE_PWRITE_DATA(pwrite_NOCANCEL, "pwrite$NOCANCEL");
+
+
+typedef ssize_t (*writev_t)(int aFd, const struct iovec* aIov, int aIovCount);
+template<FuncData& foo>
+ssize_t
+wrap_writev_temp(int aFd, const struct iovec* aIov, int aIovCount)
+{
+ MacIOAutoObservation timer(IOInterposeObserver::OpWrite, aFd, nullptr,
+ aIovCount);
+ writev_t old_write = (writev_t)foo.Buffer;
+ return old_write(aFd, aIov, aIovCount);
+}
+
+// Define a FuncData for a writev-like functions.
+#define DEFINE_WRITEV_DATA(X, NAME) \
+FuncData X ## _data = { NAME, (void*) wrap_writev_temp<X ## _data> }; \
+
+// This exists everywhere.
+DEFINE_WRITEV_DATA(writev, "writev");
+// These exist on 32 bit OS X
+DEFINE_WRITEV_DATA(writev_NOCANCEL_UNIX2003, "writev$NOCANCEL$UNIX2003");
+DEFINE_WRITEV_DATA(writev_UNIX2003, "writev$UNIX2003");
+// This exists on 64 bit OS X
+DEFINE_WRITEV_DATA(writev_NOCANCEL, "writev$NOCANCEL");
+
+typedef ssize_t (*write_t)(int aFd, const void* aBuf, size_t aCount);
+template<FuncData& foo>
+ssize_t
+wrap_write_temp(int aFd, const void* aBuf, size_t aCount)
+{
+ MacIOAutoObservation timer(IOInterposeObserver::OpWrite, aFd, aBuf, aCount);
+ write_t old_write = (write_t)foo.Buffer;
+ return old_write(aFd, aBuf, aCount);
+}
+
+// Define a FuncData for a write-like functions.
+#define DEFINE_WRITE_DATA(X, NAME) \
+FuncData X ## _data = { NAME, (void*) wrap_write_temp<X ## _data> }; \
+
+// This exists everywhere.
+DEFINE_WRITE_DATA(write, "write");
+// These exist on 32 bit OS X
+DEFINE_WRITE_DATA(write_NOCANCEL_UNIX2003, "write$NOCANCEL$UNIX2003");
+DEFINE_WRITE_DATA(write_UNIX2003, "write$UNIX2003");
+// This exists on 64 bit OS X
+DEFINE_WRITE_DATA(write_NOCANCEL, "write$NOCANCEL");
+
+FuncData* Functions[] = {
+ &aio_write_data,
+
+ &pwrite_data,
+ &pwrite_NOCANCEL_UNIX2003_data,
+ &pwrite_UNIX2003_data,
+ &pwrite_NOCANCEL_data,
+
+ &write_data,
+ &write_NOCANCEL_UNIX2003_data,
+ &write_UNIX2003_data,
+ &write_NOCANCEL_data,
+
+ &writev_data,
+ &writev_NOCANCEL_UNIX2003_data,
+ &writev_UNIX2003_data,
+ &writev_NOCANCEL_data
+};
+
+const int NumFunctions = ArrayLength(Functions);
+
+} // namespace
+
+/******************************** IO Poisoning ********************************/
+
+namespace mozilla {
+
+void
+InitPoisonIOInterposer()
+{
+ // Enable reporting from poisoned write methods
+ sIsEnabled = true;
+
+ // Make sure we only poison writes once!
+ static bool WritesArePoisoned = false;
+ if (WritesArePoisoned) {
+ return;
+ }
+ WritesArePoisoned = true;
+
+ // stdout and stderr are OK.
+ MozillaRegisterDebugFD(1);
+ MozillaRegisterDebugFD(2);
+
+#ifdef MOZ_REPLACE_MALLOC
+ // The contract with InitDebugFd is that the given registry can be used
+ // at any moment, so the instance needs to persist longer than the scope
+ // of this functions.
+ static DebugFdRegistry registry;
+ ReplaceMalloc::InitDebugFd(registry);
+#endif
+
+ for (int i = 0; i < NumFunctions; ++i) {
+ FuncData* d = Functions[i];
+ if (!d->Function) {
+ d->Function = dlsym(RTLD_DEFAULT, d->Name);
+ }
+ if (!d->Function) {
+ continue;
+ }
+ DebugOnly<mach_error_t> t = mach_override_ptr(d->Function, d->Wrapper,
+ &d->Buffer);
+ MOZ_ASSERT(t == err_none);
+ }
+}
+
+void
+OnlyReportDirtyWrites()
+{
+ sOnlyReportDirtyWrites = true;
+}
+
+void
+ClearPoisonIOInterposer()
+{
+ // Not sure how or if we can unpoison the functions. Would be nice, but no
+ // worries we won't need to do this anyway.
+ sIsEnabled = false;
+}
+
+} // namespace mozilla
diff --git a/xpcom/build/PoisonIOInterposerStub.cpp b/xpcom/build/PoisonIOInterposerStub.cpp
new file mode 100644
index 000000000..1f3daa353
--- /dev/null
+++ b/xpcom/build/PoisonIOInterposerStub.cpp
@@ -0,0 +1,16 @@
+/* -*- 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 <stdio.h>
+
+extern "C" {
+
+void MozillaRegisterDebugFD(int aFd) {}
+void MozillaRegisterDebugFILE(FILE* aFile) {}
+void MozillaUnRegisterDebugFD(int aFd) {}
+void MozillaUnRegisterDebugFILE(FILE* aFile) {}
+
+} // extern "C"
diff --git a/xpcom/build/PoisonIOInterposerWin.cpp b/xpcom/build/PoisonIOInterposerWin.cpp
new file mode 100644
index 000000000..752743b34
--- /dev/null
+++ b/xpcom/build/PoisonIOInterposerWin.cpp
@@ -0,0 +1,501 @@
+/* -*- 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 "PoisonIOInterposer.h"
+
+#include <algorithm>
+#include <stdio.h>
+#include <vector>
+
+#include <io.h>
+#include <windows.h>
+#include <winternl.h>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/FileUtilsWin.h"
+#include "mozilla/IOInterposer.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/TimeStamp.h"
+#include "nsTArray.h"
+#include "nsWindowsDllInterceptor.h"
+#include "plstr.h"
+
+#ifdef MOZ_REPLACE_MALLOC
+#include "replace_malloc_bridge.h"
+#endif
+
+using namespace mozilla;
+
+namespace {
+
+// Keep track of poisoned state. Notice that there is no reason to lock access
+// to this variable as it's only changed in InitPoisonIOInterposer and
+// ClearPoisonIOInterposer which may only be called on the main-thread when no
+// other threads are running.
+static bool sIOPoisoned = false;
+
+/************************ Internal NT API Declarations ************************/
+
+/*
+ * Function pointer declaration for internal NT routine to create/open files.
+ * For documentation on the NtCreateFile routine, see MSDN.
+ */
+typedef NTSTATUS (NTAPI* NtCreateFileFn)(
+ PHANDLE aFileHandle,
+ ACCESS_MASK aDesiredAccess,
+ POBJECT_ATTRIBUTES aObjectAttributes,
+ PIO_STATUS_BLOCK aIoStatusBlock,
+ PLARGE_INTEGER aAllocationSize,
+ ULONG aFileAttributes,
+ ULONG aShareAccess,
+ ULONG aCreateDisposition,
+ ULONG aCreateOptions,
+ PVOID aEaBuffer,
+ ULONG aEaLength);
+
+/**
+ * Function pointer declaration for internal NT routine to read data from file.
+ * For documentation on the NtReadFile routine, see ZwReadFile on MSDN.
+ */
+typedef NTSTATUS (NTAPI* NtReadFileFn)(
+ HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ PVOID aBuffer,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey);
+
+/**
+ * Function pointer declaration for internal NT routine to read data from file.
+ * No documentation exists, see wine sources for details.
+ */
+typedef NTSTATUS (NTAPI* NtReadFileScatterFn)(
+ HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ FILE_SEGMENT_ELEMENT* aSegments,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey);
+
+/**
+ * Function pointer declaration for internal NT routine to write data to file.
+ * For documentation on the NtWriteFile routine, see ZwWriteFile on MSDN.
+ */
+typedef NTSTATUS (NTAPI* NtWriteFileFn)(
+ HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ PVOID aBuffer,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey);
+
+/**
+ * Function pointer declaration for internal NT routine to write data to file.
+ * No documentation exists, see wine sources for details.
+ */
+typedef NTSTATUS (NTAPI* NtWriteFileGatherFn)(
+ HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ FILE_SEGMENT_ELEMENT* aSegments,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey);
+
+/**
+ * Function pointer declaration for internal NT routine to flush to disk.
+ * For documentation on the NtFlushBuffersFile routine, see ZwFlushBuffersFile
+ * on MSDN.
+ */
+typedef NTSTATUS (NTAPI* NtFlushBuffersFileFn)(
+ HANDLE aFileHandle,
+ PIO_STATUS_BLOCK aIoStatusBlock);
+
+typedef struct _FILE_NETWORK_OPEN_INFORMATION* PFILE_NETWORK_OPEN_INFORMATION;
+/**
+ * Function pointer delaration for internal NT routine to query file attributes.
+ * (equivalent to stat)
+ */
+typedef NTSTATUS (NTAPI* NtQueryFullAttributesFileFn)(
+ POBJECT_ATTRIBUTES aObjectAttributes,
+ PFILE_NETWORK_OPEN_INFORMATION aFileInformation);
+
+/*************************** Auxiliary Declarations ***************************/
+
+/**
+ * RAII class for timing the duration of an I/O call and reporting the result
+ * to the IOInterposeObserver API.
+ */
+class WinIOAutoObservation : public IOInterposeObserver::Observation
+{
+public:
+ WinIOAutoObservation(IOInterposeObserver::Operation aOp,
+ HANDLE aFileHandle, const LARGE_INTEGER* aOffset)
+ : IOInterposeObserver::Observation(
+ aOp, sReference, !IsDebugFile(reinterpret_cast<intptr_t>(aFileHandle)))
+ , mFileHandle(aFileHandle)
+ , mHasQueriedFilename(false)
+ , mFilename(nullptr)
+ {
+ if (mShouldReport) {
+ mOffset.QuadPart = aOffset ? aOffset->QuadPart : 0;
+ }
+ }
+
+ WinIOAutoObservation(IOInterposeObserver::Operation aOp, nsAString& aFilename)
+ : IOInterposeObserver::Observation(aOp, sReference)
+ , mFileHandle(nullptr)
+ , mHasQueriedFilename(false)
+ , mFilename(nullptr)
+ {
+ if (mShouldReport) {
+ nsAutoString dosPath;
+ if (NtPathToDosPath(aFilename, dosPath)) {
+ mFilename = ToNewUnicode(dosPath);
+ mHasQueriedFilename = true;
+ }
+ mOffset.QuadPart = 0;
+ }
+ }
+
+ // Custom implementation of IOInterposeObserver::Observation::Filename
+ const char16_t* Filename() override;
+
+ ~WinIOAutoObservation()
+ {
+ Report();
+ if (mFilename) {
+ MOZ_ASSERT(mHasQueriedFilename);
+ free(mFilename);
+ mFilename = nullptr;
+ }
+ }
+
+private:
+ HANDLE mFileHandle;
+ LARGE_INTEGER mOffset;
+ bool mHasQueriedFilename;
+ char16_t* mFilename;
+ static const char* sReference;
+};
+
+const char* WinIOAutoObservation::sReference = "PoisonIOInterposer";
+
+// Get filename for this observation
+const char16_t*
+WinIOAutoObservation::Filename()
+{
+ // If mHasQueriedFilename is true, then filename is already stored in mFilename
+ if (mHasQueriedFilename) {
+ return mFilename;
+ }
+
+ nsAutoString utf16Filename;
+ if (HandleToFilename(mFileHandle, mOffset, utf16Filename)) {
+ // Heap allocate with leakable memory
+ mFilename = ToNewUnicode(utf16Filename);
+ }
+ mHasQueriedFilename = true;
+
+ // Return filename
+ return mFilename;
+}
+
+/*************************** IO Interposing Methods ***************************/
+
+// Function pointers to original functions
+static NtCreateFileFn gOriginalNtCreateFile;
+static NtReadFileFn gOriginalNtReadFile;
+static NtReadFileScatterFn gOriginalNtReadFileScatter;
+static NtWriteFileFn gOriginalNtWriteFile;
+static NtWriteFileGatherFn gOriginalNtWriteFileGather;
+static NtFlushBuffersFileFn gOriginalNtFlushBuffersFile;
+static NtQueryFullAttributesFileFn gOriginalNtQueryFullAttributesFile;
+
+static NTSTATUS NTAPI
+InterposedNtCreateFile(PHANDLE aFileHandle,
+ ACCESS_MASK aDesiredAccess,
+ POBJECT_ATTRIBUTES aObjectAttributes,
+ PIO_STATUS_BLOCK aIoStatusBlock,
+ PLARGE_INTEGER aAllocationSize,
+ ULONG aFileAttributes,
+ ULONG aShareAccess,
+ ULONG aCreateDisposition,
+ ULONG aCreateOptions,
+ PVOID aEaBuffer,
+ ULONG aEaLength)
+{
+ // Report IO
+ const wchar_t* buf =
+ aObjectAttributes ? aObjectAttributes->ObjectName->Buffer : L"";
+ uint32_t len =
+ aObjectAttributes ? aObjectAttributes->ObjectName->Length / sizeof(WCHAR) :
+ 0;
+ nsDependentSubstring filename(buf, len);
+ WinIOAutoObservation timer(IOInterposeObserver::OpCreateOrOpen, filename);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtCreateFile);
+
+ // Execute original function
+ return gOriginalNtCreateFile(aFileHandle,
+ aDesiredAccess,
+ aObjectAttributes,
+ aIoStatusBlock,
+ aAllocationSize,
+ aFileAttributes,
+ aShareAccess,
+ aCreateDisposition,
+ aCreateOptions,
+ aEaBuffer,
+ aEaLength);
+}
+
+static NTSTATUS NTAPI
+InterposedNtReadFile(HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ PVOID aBuffer,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey)
+{
+ // Report IO
+ WinIOAutoObservation timer(IOInterposeObserver::OpRead, aFileHandle, aOffset);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtReadFile);
+
+ // Execute original function
+ return gOriginalNtReadFile(aFileHandle,
+ aEvent,
+ aApc,
+ aApcCtx,
+ aIoStatus,
+ aBuffer,
+ aLength,
+ aOffset,
+ aKey);
+}
+
+static NTSTATUS NTAPI
+InterposedNtReadFileScatter(HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ FILE_SEGMENT_ELEMENT* aSegments,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey)
+{
+ // Report IO
+ WinIOAutoObservation timer(IOInterposeObserver::OpRead, aFileHandle, aOffset);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtReadFileScatter);
+
+ // Execute original function
+ return gOriginalNtReadFileScatter(aFileHandle,
+ aEvent,
+ aApc,
+ aApcCtx,
+ aIoStatus,
+ aSegments,
+ aLength,
+ aOffset,
+ aKey);
+}
+
+// Interposed NtWriteFile function
+static NTSTATUS NTAPI
+InterposedNtWriteFile(HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ PVOID aBuffer,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey)
+{
+ // Report IO
+ WinIOAutoObservation timer(IOInterposeObserver::OpWrite, aFileHandle,
+ aOffset);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtWriteFile);
+
+ // Execute original function
+ return gOriginalNtWriteFile(aFileHandle,
+ aEvent,
+ aApc,
+ aApcCtx,
+ aIoStatus,
+ aBuffer,
+ aLength,
+ aOffset,
+ aKey);
+}
+
+// Interposed NtWriteFileGather function
+static NTSTATUS NTAPI
+InterposedNtWriteFileGather(HANDLE aFileHandle,
+ HANDLE aEvent,
+ PIO_APC_ROUTINE aApc,
+ PVOID aApcCtx,
+ PIO_STATUS_BLOCK aIoStatus,
+ FILE_SEGMENT_ELEMENT* aSegments,
+ ULONG aLength,
+ PLARGE_INTEGER aOffset,
+ PULONG aKey)
+{
+ // Report IO
+ WinIOAutoObservation timer(IOInterposeObserver::OpWrite, aFileHandle,
+ aOffset);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtWriteFileGather);
+
+ // Execute original function
+ return gOriginalNtWriteFileGather(aFileHandle,
+ aEvent,
+ aApc,
+ aApcCtx,
+ aIoStatus,
+ aSegments,
+ aLength,
+ aOffset,
+ aKey);
+}
+
+static NTSTATUS NTAPI
+InterposedNtFlushBuffersFile(HANDLE aFileHandle,
+ PIO_STATUS_BLOCK aIoStatusBlock)
+{
+ // Report IO
+ WinIOAutoObservation timer(IOInterposeObserver::OpFSync, aFileHandle,
+ nullptr);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtFlushBuffersFile);
+
+ // Execute original function
+ return gOriginalNtFlushBuffersFile(aFileHandle,
+ aIoStatusBlock);
+}
+
+static NTSTATUS NTAPI
+InterposedNtQueryFullAttributesFile(
+ POBJECT_ATTRIBUTES aObjectAttributes,
+ PFILE_NETWORK_OPEN_INFORMATION aFileInformation)
+{
+ // Report IO
+ const wchar_t* buf =
+ aObjectAttributes ? aObjectAttributes->ObjectName->Buffer : L"";
+ uint32_t len =
+ aObjectAttributes ? aObjectAttributes->ObjectName->Length / sizeof(WCHAR) :
+ 0;
+ nsDependentSubstring filename(buf, len);
+ WinIOAutoObservation timer(IOInterposeObserver::OpStat, filename);
+
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtQueryFullAttributesFile);
+
+ // Execute original function
+ return gOriginalNtQueryFullAttributesFile(aObjectAttributes,
+ aFileInformation);
+}
+
+} // namespace
+
+/******************************** IO Poisoning ********************************/
+
+// Windows DLL interceptor
+static WindowsDllInterceptor sNtDllInterceptor;
+
+namespace mozilla {
+
+void
+InitPoisonIOInterposer()
+{
+ // Don't poison twice... as this function may only be invoked on the main
+ // thread when no other threads are running, it safe to allow multiple calls
+ // to InitPoisonIOInterposer() without complaining (ie. failing assertions).
+ if (sIOPoisoned) {
+ return;
+ }
+ sIOPoisoned = true;
+
+ // Stdout and Stderr are OK.
+ MozillaRegisterDebugFD(1);
+ MozillaRegisterDebugFD(2);
+
+#ifdef MOZ_REPLACE_MALLOC
+ // The contract with InitDebugFd is that the given registry can be used
+ // at any moment, so the instance needs to persist longer than the scope
+ // of this functions.
+ static DebugFdRegistry registry;
+ ReplaceMalloc::InitDebugFd(registry);
+#endif
+
+ // Initialize dll interceptor and add hooks
+ sNtDllInterceptor.Init("ntdll.dll");
+ sNtDllInterceptor.AddHook(
+ "NtCreateFile",
+ reinterpret_cast<intptr_t>(InterposedNtCreateFile),
+ reinterpret_cast<void**>(&gOriginalNtCreateFile));
+ sNtDllInterceptor.AddHook(
+ "NtReadFile",
+ reinterpret_cast<intptr_t>(InterposedNtReadFile),
+ reinterpret_cast<void**>(&gOriginalNtReadFile));
+ sNtDllInterceptor.AddHook(
+ "NtReadFileScatter",
+ reinterpret_cast<intptr_t>(InterposedNtReadFileScatter),
+ reinterpret_cast<void**>(&gOriginalNtReadFileScatter));
+ sNtDllInterceptor.AddHook(
+ "NtWriteFile",
+ reinterpret_cast<intptr_t>(InterposedNtWriteFile),
+ reinterpret_cast<void**>(&gOriginalNtWriteFile));
+ sNtDllInterceptor.AddHook(
+ "NtWriteFileGather",
+ reinterpret_cast<intptr_t>(InterposedNtWriteFileGather),
+ reinterpret_cast<void**>(&gOriginalNtWriteFileGather));
+ sNtDllInterceptor.AddHook(
+ "NtFlushBuffersFile",
+ reinterpret_cast<intptr_t>(InterposedNtFlushBuffersFile),
+ reinterpret_cast<void**>(&gOriginalNtFlushBuffersFile));
+ sNtDllInterceptor.AddHook(
+ "NtQueryFullAttributesFile",
+ reinterpret_cast<intptr_t>(InterposedNtQueryFullAttributesFile),
+ reinterpret_cast<void**>(&gOriginalNtQueryFullAttributesFile));
+}
+
+void
+ClearPoisonIOInterposer()
+{
+ MOZ_ASSERT(false);
+ if (sIOPoisoned) {
+ // Destroy the DLL interceptor
+ sIOPoisoned = false;
+ sNtDllInterceptor = WindowsDllInterceptor();
+ }
+}
+
+} // namespace mozilla
diff --git a/xpcom/build/ServiceList.h b/xpcom/build/ServiceList.h
new file mode 100644
index 000000000..3bb8308dd
--- /dev/null
+++ b/xpcom/build/ServiceList.h
@@ -0,0 +1,46 @@
+/* -*- 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/. */
+// IWYU pragma: private, include "mozilla/Services.h"
+
+MOZ_SERVICE(ChromeRegistryService, nsIChromeRegistry,
+ "@mozilla.org/chrome/chrome-registry;1")
+MOZ_SERVICE(ToolkitChromeRegistryService, nsIToolkitChromeRegistry,
+ "@mozilla.org/chrome/chrome-registry;1")
+MOZ_SERVICE(XULChromeRegistryService, nsIXULChromeRegistry,
+ "@mozilla.org/chrome/chrome-registry;1")
+MOZ_SERVICE(XULOverlayProviderService, nsIXULOverlayProvider,
+ "@mozilla.org/chrome/chrome-registry;1")
+MOZ_SERVICE(IOService, nsIIOService,
+ "@mozilla.org/network/io-service;1")
+MOZ_SERVICE(ObserverService, nsIObserverService,
+ "@mozilla.org/observer-service;1")
+MOZ_SERVICE(StringBundleService, nsIStringBundleService,
+ "@mozilla.org/intl/stringbundle;1")
+MOZ_SERVICE(XPConnect, nsIXPConnect,
+ "@mozilla.org/js/xpc/XPConnect;1")
+MOZ_SERVICE(InDOMUtils, inIDOMUtils,
+ "@mozilla.org/inspector/dom-utils;1")
+MOZ_SERVICE(PermissionManager, nsIPermissionManager,
+ "@mozilla.org/permissionmanager;1");
+MOZ_SERVICE(ServiceWorkerManager, nsIServiceWorkerManager,
+ "@mozilla.org/serviceworkers/manager;1");
+MOZ_SERVICE(AsyncShutdown, nsIAsyncShutdownService,
+ "@mozilla.org/async-shutdown-service;1")
+MOZ_SERVICE(UUIDGenerator, nsIUUIDGenerator,
+ "@mozilla.org/uuid-generator;1");
+MOZ_SERVICE(GfxInfo, nsIGfxInfo,
+ "@mozilla.org/gfx/info;1");
+
+#ifdef MOZ_USE_NAMESPACE
+namespace mozilla {
+#endif
+
+MOZ_SERVICE(HistoryService, IHistory,
+ "@mozilla.org/browser/history;1")
+
+#ifdef MOZ_USE_NAMESPACE
+} // namespace mozilla
+#endif
diff --git a/xpcom/build/Services.cpp b/xpcom/build/Services.cpp
new file mode 100644
index 000000000..6c521391e
--- /dev/null
+++ b/xpcom/build/Services.cpp
@@ -0,0 +1,71 @@
+/* -*- 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/Likely.h"
+#include "mozilla/Services.h"
+#include "nsComponentManager.h"
+#include "nsIObserverService.h"
+#include "nsNetCID.h"
+#include "nsObserverService.h"
+#include "nsXPCOMPrivate.h"
+#include "nsIIOService.h"
+#include "nsIDirectoryService.h"
+#include "nsIChromeRegistry.h"
+#include "nsIStringBundle.h"
+#include "nsIToolkitChromeRegistry.h"
+#include "nsIXULOverlayProvider.h"
+#include "IHistory.h"
+#include "nsIXPConnect.h"
+#include "inIDOMUtils.h"
+#include "nsIPermissionManager.h"
+#include "nsIServiceWorkerManager.h"
+#include "nsIAsyncShutdown.h"
+#include "nsIUUIDGenerator.h"
+#include "nsIGfxInfo.h"
+
+using namespace mozilla;
+using namespace mozilla::services;
+
+/*
+ * Define a global variable and a getter for every service in ServiceList.
+ * eg. gIOService and GetIOService()
+ */
+#define MOZ_SERVICE(NAME, TYPE, CONTRACT_ID) \
+ static TYPE* g##NAME = nullptr; \
+ \
+ already_AddRefed<TYPE> \
+ mozilla::services::Get##NAME() \
+ { \
+ if (MOZ_UNLIKELY(gXPCOMShuttingDown)) { \
+ return nullptr; \
+ } \
+ if (!g##NAME) { \
+ nsCOMPtr<TYPE> os = do_GetService(CONTRACT_ID); \
+ os.swap(g##NAME); \
+ } \
+ nsCOMPtr<TYPE> ret = g##NAME; \
+ return ret.forget(); \
+ } \
+ NS_EXPORT_(already_AddRefed<TYPE>) \
+ mozilla::services::_external_Get##NAME() \
+ { \
+ return Get##NAME(); \
+ }
+
+#include "ServiceList.h"
+#undef MOZ_SERVICE
+
+/**
+ * Clears service cache, sets gXPCOMShuttingDown
+ */
+void
+mozilla::services::Shutdown()
+{
+ gXPCOMShuttingDown = true;
+#define MOZ_SERVICE(NAME, TYPE, CONTRACT_ID) NS_IF_RELEASE(g##NAME);
+#include "ServiceList.h"
+#undef MOZ_SERVICE
+}
diff --git a/xpcom/build/Services.h b/xpcom/build/Services.h
new file mode 100644
index 000000000..84a355399
--- /dev/null
+++ b/xpcom/build/Services.h
@@ -0,0 +1,45 @@
+/* -*- 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_Services_h
+#define mozilla_Services_h
+
+#include "nscore.h"
+#include "nsCOMPtr.h"
+
+#define MOZ_USE_NAMESPACE
+#define MOZ_SERVICE(NAME, TYPE, SERVICE_CID) class TYPE;
+
+#include "ServiceList.h"
+#undef MOZ_SERVICE
+#undef MOZ_USE_NAMESPACE
+
+namespace mozilla {
+namespace services {
+
+#ifdef MOZILLA_INTERNAL_API
+#define MOZ_SERVICE(NAME, TYPE, SERVICE_CID) \
+ already_AddRefed<TYPE> Get##NAME(); \
+ NS_EXPORT_(already_AddRefed<TYPE>) _external_Get##NAME();
+
+#include "ServiceList.h"
+#undef MOZ_SERVICE
+#else
+#define MOZ_SERVICE(NAME, TYPE, SERVICE_CID) \
+ NS_IMPORT_(already_AddRefed<TYPE>) _external_Get##NAME(); \
+ inline already_AddRefed<TYPE> Get##NAME() \
+ { \
+ return _external_Get##NAME(); \
+ }
+
+#include "ServiceList.h"
+#undef MOZ_SERVICE
+#endif
+
+} // namespace services
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/build/XPCOM.h b/xpcom/build/XPCOM.h
new file mode 100644
index 000000000..f52a87c69
--- /dev/null
+++ b/xpcom/build/XPCOM.h
@@ -0,0 +1,178 @@
+/* -*- 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_XPCOM_h
+#define mozilla_XPCOM_h
+
+// NOTE: the following headers are sorted topologically, not alphabetically.
+// Do not reorder them without review from bsmedberg.
+
+// system headers required by XPCOM headers
+
+#include <string.h>
+
+// core headers required by pretty much everything else
+
+#include "nscore.h"
+
+#include "nsXPCOMCID.h"
+#include "nsXPCOM.h"
+
+#include "nsError.h"
+#include "nsDebug.h"
+#include "nsMemory.h"
+
+#include "nsID.h"
+
+#include "nsISupports.h"
+
+#include "nsTArray.h"
+#include "nsTWeakRef.h"
+
+#include "nsCOMPtr.h"
+#include "nsCOMArray.h"
+
+#ifndef MOZILLA_INTERNAL_API
+#include "nsStringAPI.h"
+#else
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsNativeCharsetUtils.h"
+#endif
+
+#include "nsISupportsUtils.h"
+#include "nsISupportsImpl.h"
+
+// core data structures
+
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
+#include "nsBaseHashtable.h"
+#include "nsDataHashtable.h"
+#include "nsInterfaceHashtable.h"
+#include "nsClassHashtable.h"
+#include "nsRefPtrHashtable.h"
+
+// interfaces that inherit directly from nsISupports
+
+#include "nsIArray.h"
+#include "nsIAtom.h"
+#include "nsIAtomService.h"
+#include "nsICategoryManager.h"
+#include "nsIClassInfo.h"
+#include "nsIComponentManager.h"
+#include "nsIConsoleListener.h"
+#include "nsIConsoleMessage.h"
+#include "nsIConsoleService.h"
+#include "nsIDebug2.h"
+#include "nsIDirectoryEnumerator.h"
+#include "nsIEnvironment.h"
+#include "nsIErrorService.h"
+#include "nsIEventTarget.h"
+#include "nsIException.h"
+#include "nsIFactory.h"
+#include "nsIFile.h"
+#include "nsIHashable.h"
+#include "nsIINIParser.h"
+#include "nsIInputStream.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsILineInputStream.h"
+#include "nsIMemory.h"
+#include "nsIMutable.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsIOutputStream.h"
+#include "nsIProcess.h"
+#include "nsIProperties.h"
+#include "nsIPropertyBag2.h"
+#include "nsIRunnable.h"
+#include "nsISeekableStream.h"
+#include "nsISerializable.h"
+#include "nsIServiceManager.h"
+#include "nsIScriptableInputStream.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIStreamBufferAccess.h"
+#include "nsIStringEnumerator.h"
+#include "nsIStorageStream.h"
+#include "nsISupportsIterators.h"
+#include "nsISupportsPrimitives.h"
+#include "nsISupportsPriority.h"
+#include "nsIThreadManager.h"
+#include "nsITimer.h"
+#include "nsIUUIDGenerator.h"
+#include "nsIUnicharInputStream.h"
+#include "nsIUnicharOutputStream.h"
+#include "nsIUnicharLineInputStream.h"
+#include "nsIVariant.h"
+#include "nsIVersionComparator.h"
+#include "nsIWritablePropertyBag2.h"
+
+// interfaces that include something above
+
+#include "nsIAsyncInputStream.h"
+#include "nsIAsyncOutputStream.h"
+#include "nsIBinaryInputStream.h"
+#include "nsIBinaryOutputStream.h"
+#include "nsIConverterInputStream.h"
+#include "nsIConverterOutputStream.h"
+#include "nsIInputStreamTee.h"
+#include "nsIMultiplexInputStream.h"
+#include "nsIMutableArray.h"
+#include "nsIPersistentProperties2.h"
+#include "nsIStringStream.h"
+#include "nsIThread.h"
+#include "nsIThreadPool.h"
+
+// interfaces that include something above
+
+#include "nsILocalFileWin.h"
+#include "nsIObjectInputStream.h"
+#include "nsIObjectOutputStream.h"
+#include "nsIPipe.h"
+
+#ifdef MOZ_WIDGET_COCOA
+#include "nsILocalFileMac.h"
+#include "nsIMacUtils.h"
+#endif
+
+// xpcom/glue utility headers
+
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+
+#include "nsIWeakReferenceUtils.h"
+#include "nsWeakReference.h"
+
+#include "nsArrayEnumerator.h"
+#include "nsArrayUtils.h"
+#include "nsCRTGlue.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsDeque.h"
+#include "nsEnumeratorUtils.h"
+#include "nsIClassInfoImpl.h"
+#include "mozilla/ModuleUtils.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsINIParser.h"
+#include "nsProxyRelease.h"
+#include "nsTObserverArray.h"
+#include "nsTextFormatter.h"
+#include "nsThreadUtils.h"
+#include "nsVersionComparator.h"
+#include "nsXPTCUtils.h"
+
+// xpcom/base utility headers
+
+#include "nsAgg.h"
+#include "nsAutoRef.h"
+#include "nsInterfaceRequestorAgg.h"
+
+// xpcom/io utility headers
+
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+
+#endif // mozilla_XPCOM_h
diff --git a/xpcom/build/XPCOMInit.cpp b/xpcom/build/XPCOMInit.cpp
new file mode 100644
index 000000000..c72ea48d7
--- /dev/null
+++ b/xpcom/build/XPCOMInit.cpp
@@ -0,0 +1,1126 @@
+/* -*- 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 "base/basictypes.h"
+
+#include "mozilla/AbstractThread.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Poison.h"
+#include "mozilla/SharedThreadPool.h"
+#include "mozilla/XPCOM.h"
+#include "nsXULAppAPI.h"
+
+#include "nsXPCOMPrivate.h"
+#include "nsXPCOMCIDInternal.h"
+
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/dom/VideoDecoderManagerChild.h"
+
+#include "prlink.h"
+
+#include "nsCycleCollector.h"
+#include "nsObserverList.h"
+#include "nsObserverService.h"
+#include "nsProperties.h"
+#include "nsPersistentProperties.h"
+#include "nsScriptableInputStream.h"
+#include "nsBinaryStream.h"
+#include "nsStorageStream.h"
+#include "nsPipe.h"
+#include "nsScriptableBase64Encoder.h"
+
+#include "nsMemoryImpl.h"
+#include "nsDebugImpl.h"
+#include "nsTraceRefcnt.h"
+#include "nsErrorService.h"
+
+// Disable deprecation warnings generated by nsISupportsArray and associated
+// classes.
+#if defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#elif defined(_MSC_VER)
+#pragma warning (push)
+#pragma warning (disable : 4996)
+#endif
+
+#include "nsSupportsArray.h"
+
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#elif defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+
+#include "nsArray.h"
+#include "nsINIParserImpl.h"
+#include "nsSupportsPrimitives.h"
+#include "nsConsoleService.h"
+
+#include "nsComponentManager.h"
+#include "nsCategoryManagerUtils.h"
+#include "nsIServiceManager.h"
+
+#include "nsThreadManager.h"
+#include "nsThreadPool.h"
+
+#include "xptinfo.h"
+#include "nsIInterfaceInfoManager.h"
+#include "xptiprivate.h"
+#include "mozilla/XPTInterfaceInfoManager.h"
+
+#include "nsTimerImpl.h"
+#include "TimerThread.h"
+
+#include "nsThread.h"
+#include "nsProcess.h"
+#include "nsEnvironment.h"
+#include "nsVersionComparatorImpl.h"
+
+#include "nsIFile.h"
+#include "nsLocalFile.h"
+#if defined(XP_UNIX)
+#include "nsNativeCharsetUtils.h"
+#endif
+#include "nsDirectoryService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsCategoryManager.h"
+#include "nsICategoryManager.h"
+#include "nsMultiplexInputStream.h"
+
+#include "nsStringStream.h"
+extern nsresult nsStringInputStreamConstructor(nsISupports*, REFNSIID, void**);
+
+#include "nsAtomService.h"
+#include "nsAtomTable.h"
+#include "nsISupportsImpl.h"
+
+#include "nsHashPropertyBag.h"
+
+#include "nsUnicharInputStream.h"
+#include "nsVariant.h"
+
+#include "nsUUIDGenerator.h"
+
+#include "nsIOUtil.h"
+
+#include "SpecialSystemDirectory.h"
+
+#if defined(XP_WIN)
+#include "mozilla/WindowsVersion.h"
+#include "nsWindowsRegKey.h"
+#endif
+
+#ifdef MOZ_WIDGET_COCOA
+#include "nsMacUtilsImpl.h"
+#endif
+
+#include "nsSystemInfo.h"
+#include "nsMemoryReporterManager.h"
+#include "nsMemoryInfoDumper.h"
+#include "nsSecurityConsoleMessage.h"
+#include "nsMessageLoop.h"
+
+#include "nsStatusReporterManager.h"
+
+#include <locale.h>
+#include "mozilla/Services.h"
+#include "mozilla/Omnijar.h"
+#include "mozilla/HangMonitor.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/BackgroundHangMonitor.h"
+
+#include "nsChromeRegistry.h"
+#include "nsChromeProtocolHandler.h"
+#include "mozilla/PoisonIOInterposer.h"
+#include "mozilla/LateWriteChecks.h"
+
+#include "mozilla/scache/StartupCache.h"
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/message_loop.h"
+
+#include "mozilla/ipc/BrowserProcessSubThread.h"
+#include "mozilla/AvailableMemoryTracker.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/CountingAllocatorBase.h"
+#include "mozilla/SystemMemoryReporter.h"
+#include "mozilla/UniquePtr.h"
+
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+
+#include "ogg/ogg.h"
+#if defined(MOZ_VPX) && !defined(MOZ_VPX_NO_MEM_REPORTING)
+#if defined(HAVE_STDINT_H)
+// mozilla-config.h defines HAVE_STDINT_H, and then it's defined *again* in
+// vpx_config.h (which we include via vpx_mem.h, below). This redefinition
+// triggers a build warning on MSVC, so we have to #undef it first.
+#undef HAVE_STDINT_H
+#endif
+#include "vpx_mem/vpx_mem.h"
+#endif
+
+#include "GeckoProfiler.h"
+
+#include "jsapi.h"
+#include "js/Initialization.h"
+
+#include "gfxPlatform.h"
+
+#if EXPOSE_INTL_API
+#include "unicode/putil.h"
+#endif
+
+using namespace mozilla;
+using base::AtExitManager;
+using mozilla::ipc::BrowserProcessSubThread;
+
+namespace {
+
+static AtExitManager* sExitManager;
+static MessageLoop* sMessageLoop;
+static bool sCommandLineWasInitialized;
+static BrowserProcessSubThread* sIOThread;
+static BackgroundHangMonitor* sMainHangMonitor;
+
+} /* anonymous namespace */
+
+// Registry Factory creation function defined in nsRegistry.cpp
+// We hook into this function locally to create and register the registry
+// Since noone outside xpcom needs to know about this and nsRegistry.cpp
+// does not have a local include file, we are putting this definition
+// here rather than in nsIRegistry.h
+extern nsresult NS_RegistryGetFactory(nsIFactory** aFactory);
+extern nsresult NS_CategoryManagerGetFactory(nsIFactory**);
+
+#ifdef XP_WIN
+extern nsresult CreateAnonTempFileRemover();
+#endif
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsProcess)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsID)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsString)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsCString)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRBool)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRUint8)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRUint16)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRUint32)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRUint64)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRTime)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsChar)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRInt16)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRInt32)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsPRInt64)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsFloat)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsDouble)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsVoid)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSupportsInterfacePointer)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsConsoleService, Init)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsAtomService)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsTimer)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsBinaryOutputStream)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsBinaryInputStream)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsStorageStream)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsVersionComparatorImpl)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsScriptableBase64Encoder)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsVariantCC)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsHashPropertyBagCC)
+
+NS_GENERIC_AGGREGATED_CONSTRUCTOR(nsProperties)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsUUIDGenerator, Init)
+
+#ifdef MOZ_WIDGET_COCOA
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsMacUtilsImpl)
+#endif
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsSystemInfo, Init)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsMemoryReporterManager, Init)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsMemoryInfoDumper)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsStatusReporterManager, Init)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsIOUtil)
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSecurityConsoleMessage)
+
+static nsresult
+nsThreadManagerGetSingleton(nsISupports* aOuter,
+ const nsIID& aIID,
+ void** aInstancePtr)
+{
+ NS_ASSERTION(aInstancePtr, "null outptr");
+ if (NS_WARN_IF(aOuter)) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ return nsThreadManager::get().QueryInterface(aIID, aInstancePtr);
+}
+
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsThreadPool)
+
+static nsresult
+nsXPTIInterfaceInfoManagerGetSingleton(nsISupports* aOuter,
+ const nsIID& aIID,
+ void** aInstancePtr)
+{
+ NS_ASSERTION(aInstancePtr, "null outptr");
+ if (NS_WARN_IF(aOuter)) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ nsCOMPtr<nsIInterfaceInfoManager> iim(XPTInterfaceInfoManager::GetSingleton());
+ if (!iim) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return iim->QueryInterface(aIID, aInstancePtr);
+}
+
+nsComponentManagerImpl* nsComponentManagerImpl::gComponentManager = nullptr;
+bool gXPCOMShuttingDown = false;
+bool gXPCOMThreadsShutDown = false;
+char16_t* gGREBinPath = nullptr;
+
+static NS_DEFINE_CID(kComponentManagerCID, NS_COMPONENTMANAGER_CID);
+static NS_DEFINE_CID(kINIParserFactoryCID, NS_INIPARSERFACTORY_CID);
+
+NS_DEFINE_NAMED_CID(NS_CHROMEREGISTRY_CID);
+NS_DEFINE_NAMED_CID(NS_CHROMEPROTOCOLHANDLER_CID);
+
+NS_DEFINE_NAMED_CID(NS_SECURITY_CONSOLE_MESSAGE_CID);
+
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsChromeRegistry,
+ nsChromeRegistry::GetSingleton)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsChromeProtocolHandler)
+
+#define NS_PERSISTENTPROPERTIES_CID NS_IPERSISTENTPROPERTIES_CID /* sigh */
+
+static already_AddRefed<nsIFactory>
+CreateINIParserFactory(const mozilla::Module& aModule,
+ const mozilla::Module::CIDEntry& aEntry)
+{
+ nsCOMPtr<nsIFactory> f = new nsINIParserFactory();
+ return f.forget();
+}
+
+#define COMPONENT(NAME, Ctor) static NS_DEFINE_CID(kNS_##NAME##_CID, NS_##NAME##_CID);
+#define COMPONENT_M(NAME, Ctor, Selector) static NS_DEFINE_CID(kNS_##NAME##_CID, NS_##NAME##_CID);
+#include "XPCOMModule.inc"
+#undef COMPONENT
+#undef COMPONENT_M
+
+#define COMPONENT(NAME, Ctor) { &kNS_##NAME##_CID, false, nullptr, Ctor },
+#define COMPONENT_M(NAME, Ctor, Selector) { &kNS_##NAME##_CID, false, nullptr, Ctor, Selector },
+const mozilla::Module::CIDEntry kXPCOMCIDEntries[] = {
+ { &kComponentManagerCID, true, nullptr, nsComponentManagerImpl::Create, Module::ALLOW_IN_GPU_PROCESS },
+ { &kINIParserFactoryCID, false, CreateINIParserFactory },
+#include "XPCOMModule.inc"
+ { &kNS_CHROMEREGISTRY_CID, false, nullptr, nsChromeRegistryConstructor },
+ { &kNS_CHROMEPROTOCOLHANDLER_CID, false, nullptr, nsChromeProtocolHandlerConstructor },
+ { &kNS_SECURITY_CONSOLE_MESSAGE_CID, false, nullptr, nsSecurityConsoleMessageConstructor },
+ { nullptr }
+};
+#undef COMPONENT
+#undef COMPONENT_M
+
+#define COMPONENT(NAME, Ctor) { NS_##NAME##_CONTRACTID, &kNS_##NAME##_CID },
+#define COMPONENT_M(NAME, Ctor, Selector) { NS_##NAME##_CONTRACTID, &kNS_##NAME##_CID, Selector },
+const mozilla::Module::ContractIDEntry kXPCOMContracts[] = {
+#include "XPCOMModule.inc"
+ { NS_CHROMEREGISTRY_CONTRACTID, &kNS_CHROMEREGISTRY_CID },
+ { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "chrome", &kNS_CHROMEPROTOCOLHANDLER_CID },
+ { NS_INIPARSERFACTORY_CONTRACTID, &kINIParserFactoryCID },
+ { NS_SECURITY_CONSOLE_MESSAGE_CONTRACTID, &kNS_SECURITY_CONSOLE_MESSAGE_CID },
+ { nullptr }
+};
+#undef COMPONENT
+#undef COMPONENT_M
+
+const mozilla::Module kXPCOMModule = {
+ mozilla::Module::kVersion, kXPCOMCIDEntries, kXPCOMContracts,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ Module::ALLOW_IN_GPU_PROCESS
+};
+
+// gDebug will be freed during shutdown.
+static nsIDebug2* gDebug = nullptr;
+
+EXPORT_XPCOM_API(nsresult)
+NS_GetDebug(nsIDebug2** aResult)
+{
+ return nsDebugImpl::Create(nullptr, NS_GET_IID(nsIDebug2), (void**)aResult);
+}
+
+EXPORT_XPCOM_API(nsresult)
+NS_InitXPCOM(nsIServiceManager** aResult,
+ nsIFile* aBinDirectory)
+{
+ return NS_InitXPCOM2(aResult, aBinDirectory, nullptr);
+}
+
+class ICUReporter final
+ : public nsIMemoryReporter
+ , public CountingAllocatorBase<ICUReporter>
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ static void* Alloc(const void*, size_t aSize)
+ {
+ return CountingMalloc(aSize);
+ }
+
+ static void* Realloc(const void*, void* aPtr, size_t aSize)
+ {
+ return CountingRealloc(aPtr, aSize);
+ }
+
+ static void Free(const void*, void* aPtr)
+ {
+ return CountingFree(aPtr);
+ }
+
+private:
+ NS_IMETHOD
+ CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+ bool aAnonymize) override
+ {
+ MOZ_COLLECT_REPORT(
+ "explicit/icu", KIND_HEAP, UNITS_BYTES, MemoryAllocated(),
+ "Memory used by ICU, a Unicode and globalization support library.");
+
+ return NS_OK;
+ }
+
+ ~ICUReporter() {}
+};
+
+NS_IMPL_ISUPPORTS(ICUReporter, nsIMemoryReporter)
+
+/* static */ template<> Atomic<size_t>
+CountingAllocatorBase<ICUReporter>::sAmount(0);
+
+class OggReporter final
+ : public nsIMemoryReporter
+ , public CountingAllocatorBase<OggReporter>
+{
+public:
+ NS_DECL_ISUPPORTS
+
+private:
+ NS_IMETHOD
+ CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+ bool aAnonymize) override
+ {
+ MOZ_COLLECT_REPORT(
+ "explicit/media/libogg", KIND_HEAP, UNITS_BYTES, MemoryAllocated(),
+ "Memory allocated through libogg for Ogg, Theora, and related media "
+ "files.");
+
+ return NS_OK;
+ }
+
+ ~OggReporter() {}
+};
+
+NS_IMPL_ISUPPORTS(OggReporter, nsIMemoryReporter)
+
+/* static */ template<> Atomic<size_t>
+CountingAllocatorBase<OggReporter>::sAmount(0);
+
+#ifdef MOZ_VPX
+class VPXReporter final
+ : public nsIMemoryReporter
+ , public CountingAllocatorBase<VPXReporter>
+{
+public:
+ NS_DECL_ISUPPORTS
+
+private:
+ NS_IMETHOD
+ CollectReports(nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+ bool aAnonymize) override
+ {
+ MOZ_COLLECT_REPORT(
+ "explicit/media/libvpx", KIND_HEAP, UNITS_BYTES, MemoryAllocated(),
+ "Memory allocated through libvpx for WebM media files.");
+
+ return NS_OK;
+ }
+
+ ~VPXReporter() {}
+};
+
+NS_IMPL_ISUPPORTS(VPXReporter, nsIMemoryReporter)
+
+/* static */ template<> Atomic<size_t>
+CountingAllocatorBase<VPXReporter>::sAmount(0);
+#endif /* MOZ_VPX */
+
+static double
+TimeSinceProcessCreation()
+{
+ bool ignore;
+ return (TimeStamp::Now() - TimeStamp::ProcessCreation(ignore)).ToMilliseconds();
+}
+
+static bool sInitializedJS = false;
+
+// Note that on OSX, aBinDirectory will point to .app/Contents/Resources/browser
+EXPORT_XPCOM_API(nsresult)
+NS_InitXPCOM2(nsIServiceManager** aResult,
+ nsIFile* aBinDirectory,
+ nsIDirectoryServiceProvider* aAppFileLocationProvider)
+{
+ static bool sInitialized = false;
+ if (sInitialized) {
+ return NS_ERROR_FAILURE;
+ }
+
+ sInitialized = true;
+
+ mozPoisonValueInit();
+
+ NS_LogInit();
+
+ NS_InitAtomTable();
+
+ mozilla::LogModule::Init();
+
+ JS_SetCurrentEmbedderTimeFunction(TimeSinceProcessCreation);
+
+ char aLocal;
+ profiler_init(&aLocal);
+ nsresult rv = NS_OK;
+
+ // We are not shutting down
+ gXPCOMShuttingDown = false;
+
+ // Initialize the available memory tracker before other threads have had a
+ // chance to start up, because the initialization is not thread-safe.
+ mozilla::AvailableMemoryTracker::Init();
+
+#ifdef XP_UNIX
+ // Discover the current value of the umask, and save it where
+ // nsSystemInfo::Init can retrieve it when necessary. There is no way
+ // to read the umask without changing it, and the setting is process-
+ // global, so this must be done while we are still single-threaded; the
+ // nsSystemInfo object is typically created much later, when some piece
+ // of chrome JS wants it. The system call is specified as unable to fail.
+ nsSystemInfo::gUserUmask = ::umask(0777);
+ ::umask(nsSystemInfo::gUserUmask);
+#endif
+
+ // Set up chromium libs
+ NS_ASSERTION(!sExitManager && !sMessageLoop, "Bad logic!");
+
+ if (!AtExitManager::AlreadyRegistered()) {
+ sExitManager = new AtExitManager();
+ }
+
+ MessageLoop* messageLoop = MessageLoop::current();
+ if (!messageLoop) {
+ sMessageLoop = new MessageLoopForUI(MessageLoop::TYPE_MOZILLA_PARENT);
+ sMessageLoop->set_thread_name("Gecko");
+ // Set experimental values for main thread hangs:
+ // 128ms for transient hangs and 8192ms for permanent hangs
+ sMessageLoop->set_hang_timeouts(128, 8192);
+ } else if (messageLoop->type() == MessageLoop::TYPE_MOZILLA_CHILD) {
+ messageLoop->set_thread_name("Gecko_Child");
+ messageLoop->set_hang_timeouts(128, 8192);
+ }
+
+ if (XRE_IsParentProcess() &&
+ !BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO)) {
+ UniquePtr<BrowserProcessSubThread> ioThread = MakeUnique<BrowserProcessSubThread>(BrowserProcessSubThread::IO);
+
+ base::Thread::Options options;
+ options.message_loop_type = MessageLoop::TYPE_IO;
+ if (NS_WARN_IF(!ioThread->StartWithOptions(options))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ sIOThread = ioThread.release();
+ }
+
+ // Establish the main thread here.
+ rv = nsThreadManager::get().Init();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Set up the timer globals/timer thread
+ rv = nsTimerImpl::Startup();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+#ifndef ANDROID
+ // If the locale hasn't already been setup by our embedder,
+ // get us out of the "C" locale and into the system
+ if (strcmp(setlocale(LC_ALL, nullptr), "C") == 0) {
+ setlocale(LC_ALL, "");
+ }
+#endif
+
+#if defined(XP_UNIX)
+ NS_StartupNativeCharsetUtils();
+#endif
+
+ NS_StartupLocalFile();
+
+ StartupSpecialSystemDirectory();
+
+ nsDirectoryService::RealInit();
+
+ bool value;
+
+ if (aBinDirectory) {
+ rv = aBinDirectory->IsDirectory(&value);
+
+ if (NS_SUCCEEDED(rv) && value) {
+ nsDirectoryService::gService->Set(NS_XPCOM_INIT_CURRENT_PROCESS_DIR,
+ aBinDirectory);
+ }
+ }
+
+ if (aAppFileLocationProvider) {
+ rv = nsDirectoryService::gService->RegisterProvider(aAppFileLocationProvider);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ nsCOMPtr<nsIFile> xpcomLib;
+ nsDirectoryService::gService->Get(NS_GRE_BIN_DIR,
+ NS_GET_IID(nsIFile),
+ getter_AddRefs(xpcomLib));
+ MOZ_ASSERT(xpcomLib);
+
+ // set gGREBinPath
+ nsAutoString path;
+ xpcomLib->GetPath(path);
+ gGREBinPath = ToNewUnicode(path);
+
+ xpcomLib->AppendNative(nsDependentCString(XPCOM_DLL));
+ nsDirectoryService::gService->Set(NS_XPCOM_LIBRARY_FILE, xpcomLib);
+
+ if (!mozilla::Omnijar::IsInitialized()) {
+ mozilla::Omnijar::Init();
+ }
+
+ if ((sCommandLineWasInitialized = !CommandLine::IsInitialized())) {
+#ifdef OS_WIN
+ CommandLine::Init(0, nullptr);
+#else
+ nsCOMPtr<nsIFile> binaryFile;
+ nsDirectoryService::gService->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
+ NS_GET_IID(nsIFile),
+ getter_AddRefs(binaryFile));
+ if (NS_WARN_IF(!binaryFile)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = binaryFile->AppendNative(NS_LITERAL_CSTRING("nonexistent-executable"));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCString binaryPath;
+ rv = binaryFile->GetNativePath(binaryPath);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ static char const* const argv = { strdup(binaryPath.get()) };
+ CommandLine::Init(1, &argv);
+#endif
+ }
+
+ NS_ASSERTION(nsComponentManagerImpl::gComponentManager == nullptr,
+ "CompMgr not null at init");
+
+ // Create the Component/Service Manager
+ nsComponentManagerImpl::gComponentManager = new nsComponentManagerImpl();
+ NS_ADDREF(nsComponentManagerImpl::gComponentManager);
+
+ // Global cycle collector initialization.
+ if (!nsCycleCollector_init()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // And start it up for this thread too.
+ nsCycleCollector_startup();
+
+ // Register ICU memory functions. This really shouldn't be necessary: the
+ // JS engine should do this on its own inside JS_Init, and memory-reporting
+ // code should call a JSAPI function to observe ICU memory usage. But we
+ // can't define the alloc/free functions in the JS engine, because it can't
+ // depend on the XPCOM-based memory reporting goop. So for now, we have
+ // this oddness.
+ mozilla::SetICUMemoryFunctions();
+
+ // Do the same for libogg.
+ ogg_set_mem_functions(OggReporter::CountingMalloc,
+ OggReporter::CountingCalloc,
+ OggReporter::CountingRealloc,
+ OggReporter::CountingFree);
+
+#if defined(MOZ_VPX) && !defined(MOZ_VPX_NO_MEM_REPORTING)
+ // And for VPX.
+ vpx_mem_set_functions(VPXReporter::CountingMalloc,
+ VPXReporter::CountingCalloc,
+ VPXReporter::CountingRealloc,
+ VPXReporter::CountingFree,
+ memcpy,
+ memset,
+ memmove);
+#endif
+
+#if EXPOSE_INTL_API && defined(MOZ_ICU_DATA_ARCHIVE)
+ nsCOMPtr<nsIFile> greDir;
+ nsDirectoryService::gService->Get(NS_GRE_DIR,
+ NS_GET_IID(nsIFile),
+ getter_AddRefs(greDir));
+ MOZ_ASSERT(greDir);
+ nsAutoCString nativeGREPath;
+ greDir->GetNativePath(nativeGREPath);
+ u_setDataDirectory(nativeGREPath.get());
+#endif
+
+ // Initialize the JS engine.
+ const char* jsInitFailureReason = JS_InitWithFailureDiagnostic();
+ if (jsInitFailureReason) {
+ NS_RUNTIMEABORT(jsInitFailureReason);
+ }
+ sInitializedJS = true;
+
+ // Init AbstractThread.
+ AbstractThread::InitStatics();
+
+ rv = nsComponentManagerImpl::gComponentManager->Init();
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(nsComponentManagerImpl::gComponentManager);
+ return rv;
+ }
+
+ if (aResult) {
+ NS_ADDREF(*aResult = nsComponentManagerImpl::gComponentManager);
+ }
+
+ // The iimanager constructor searches and registers XPT files.
+ // (We trigger the singleton's lazy construction here to make that happen.)
+ (void)XPTInterfaceInfoManager::GetSingleton();
+
+ // After autoreg, but before we actually instantiate any components,
+ // add any services listed in the "xpcom-directory-providers" category
+ // to the directory service.
+ nsDirectoryService::gService->RegisterCategoryProviders();
+
+ // Init SharedThreadPool (which needs the service manager).
+ SharedThreadPool::InitStatics();
+
+ // Force layout to spin up so that nsContentUtils is available for cx stack
+ // munging. Note that layout registers a number of static atoms, and also
+ // seals the static atom table, so NS_RegisterStaticAtom may not be called
+ // beyond this point.
+ nsCOMPtr<nsISupports> componentLoader =
+ do_GetService("@mozilla.org/moz/jsloader;1");
+
+ mozilla::scache::StartupCache::GetSingleton();
+ mozilla::AvailableMemoryTracker::Activate();
+
+ // Notify observers of xpcom autoregistration start
+ NS_CreateServicesFromCategory(NS_XPCOM_STARTUP_CATEGORY,
+ nullptr,
+ NS_XPCOM_STARTUP_OBSERVER_ID);
+#ifdef XP_WIN
+ CreateAnonTempFileRemover();
+#endif
+
+ // We only want the SystemMemoryReporter running in one process, because it
+ // profiles the entire system. The main process is the obvious place for
+ // it.
+ if (XRE_IsParentProcess()) {
+ mozilla::SystemMemoryReporter::Init();
+ }
+
+ // The memory reporter manager is up and running -- register our reporters.
+ RegisterStrongMemoryReporter(new ICUReporter());
+ RegisterStrongMemoryReporter(new OggReporter());
+#ifdef MOZ_VPX
+ RegisterStrongMemoryReporter(new VPXReporter());
+#endif
+
+ mozilla::Telemetry::Init();
+
+ mozilla::HangMonitor::Startup();
+ mozilla::BackgroundHangMonitor::Startup();
+
+ const MessageLoop* const loop = MessageLoop::current();
+ sMainHangMonitor = new mozilla::BackgroundHangMonitor(
+ loop->thread_name().c_str(),
+ loop->transient_hang_timeout(),
+ loop->permanent_hang_timeout());
+
+ return NS_OK;
+}
+
+EXPORT_XPCOM_API(nsresult)
+NS_InitMinimalXPCOM()
+{
+ mozPoisonValueInit();
+ NS_SetMainThread();
+ mozilla::TimeStamp::Startup();
+ NS_LogInit();
+ NS_InitAtomTable();
+ mozilla::LogModule::Init();
+
+ char aLocal;
+ profiler_init(&aLocal);
+
+ nsresult rv = nsThreadManager::get().Init();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Set up the timer globals/timer thread.
+ rv = nsTimerImpl::Startup();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Create the Component/Service Manager
+ nsComponentManagerImpl::gComponentManager = new nsComponentManagerImpl();
+ NS_ADDREF(nsComponentManagerImpl::gComponentManager);
+
+ rv = nsComponentManagerImpl::gComponentManager->Init();
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(nsComponentManagerImpl::gComponentManager);
+ return rv;
+ }
+
+ // Global cycle collector initialization.
+ if (!nsCycleCollector_init()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ AbstractThread::InitStatics();
+ SharedThreadPool::InitStatics();
+ mozilla::Telemetry::Init();
+ mozilla::HangMonitor::Startup();
+ mozilla::BackgroundHangMonitor::Startup();
+
+ return NS_OK;
+}
+
+//
+// NS_ShutdownXPCOM()
+//
+// The shutdown sequence for xpcom would be
+//
+// - Notify "xpcom-shutdown" for modules to release primary (root) references
+// - Shutdown XPCOM timers
+// - Notify "xpcom-shutdown-threads" for thread joins
+// - Shutdown the event queues
+// - Release the Global Service Manager
+// - Release all service instances held by the global service manager
+// - Release the Global Service Manager itself
+// - Release the Component Manager
+// - Release all factories cached by the Component Manager
+// - Notify module loaders to shut down
+// - Unload Libraries
+// - Release Contractid Cache held by Component Manager
+// - Release dll abstraction held by Component Manager
+// - Release the Registry held by Component Manager
+// - Finally, release the component manager itself
+//
+EXPORT_XPCOM_API(nsresult)
+NS_ShutdownXPCOM(nsIServiceManager* aServMgr)
+{
+ return mozilla::ShutdownXPCOM(aServMgr);
+}
+
+namespace mozilla {
+
+void
+SetICUMemoryFunctions()
+{
+ static bool sICUReporterInitialized = false;
+ if (!sICUReporterInitialized) {
+ if (!JS_SetICUMemoryFunctions(ICUReporter::Alloc, ICUReporter::Realloc,
+ ICUReporter::Free)) {
+ NS_RUNTIMEABORT("JS_SetICUMemoryFunctions failed.");
+ }
+ sICUReporterInitialized = true;
+ }
+}
+
+nsresult
+ShutdownXPCOM(nsIServiceManager* aServMgr)
+{
+ // Make sure the hang monitor is enabled for shutdown.
+ HangMonitor::NotifyActivity();
+
+ if (!NS_IsMainThread()) {
+ NS_RUNTIMEABORT("Shutdown on wrong thread");
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsISimpleEnumerator> moduleLoaders;
+
+ // Notify observers of xpcom shutting down
+ {
+ // Block it so that the COMPtr will get deleted before we hit
+ // servicemanager shutdown
+
+ nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
+ if (NS_WARN_IF(!thread)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ RefPtr<nsObserverService> observerService;
+ CallGetService("@mozilla.org/observer-service;1",
+ (nsObserverService**)getter_AddRefs(observerService));
+
+ if (observerService) {
+ mozilla::KillClearOnShutdown(ShutdownPhase::WillShutdown);
+ observerService->NotifyObservers(nullptr,
+ NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID,
+ nullptr);
+
+ nsCOMPtr<nsIServiceManager> mgr;
+ rv = NS_GetServiceManager(getter_AddRefs(mgr));
+ if (NS_SUCCEEDED(rv)) {
+ mozilla::KillClearOnShutdown(ShutdownPhase::Shutdown);
+ observerService->NotifyObservers(mgr, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
+ nullptr);
+ }
+ }
+
+ // This must happen after the shutdown of media and widgets, which
+ // are triggered by the NS_XPCOM_SHUTDOWN_OBSERVER_ID notification.
+ NS_ProcessPendingEvents(thread);
+ gfxPlatform::ShutdownLayersIPC();
+ mozilla::dom::VideoDecoderManagerChild::Shutdown();
+
+ mozilla::scache::StartupCache::DeleteSingleton();
+ if (observerService)
+ {
+ mozilla::KillClearOnShutdown(ShutdownPhase::ShutdownThreads);
+ observerService->NotifyObservers(nullptr,
+ NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID,
+ nullptr);
+ }
+
+ gXPCOMThreadsShutDown = true;
+ NS_ProcessPendingEvents(thread);
+
+ // Shutdown the timer thread and all timers that might still be alive before
+ // shutting down the component manager
+ nsTimerImpl::Shutdown();
+
+ NS_ProcessPendingEvents(thread);
+
+ // Shutdown all remaining threads. This method does not return until
+ // all threads created using the thread manager (with the exception of
+ // the main thread) have exited.
+ nsThreadManager::get().Shutdown();
+
+ NS_ProcessPendingEvents(thread);
+
+ HangMonitor::NotifyActivity();
+
+ // Late-write checks needs to find the profile directory, so it has to
+ // be initialized before mozilla::services::Shutdown or (because of
+ // xpcshell tests replacing the service) modules being unloaded.
+ mozilla::InitLateWriteChecks();
+
+ // We save the "xpcom-shutdown-loaders" observers to notify after
+ // the observerservice is gone.
+ if (observerService) {
+ mozilla::KillClearOnShutdown(ShutdownPhase::ShutdownLoaders);
+ observerService->EnumerateObservers(NS_XPCOM_SHUTDOWN_LOADERS_OBSERVER_ID,
+ getter_AddRefs(moduleLoaders));
+
+ observerService->Shutdown();
+ }
+ }
+
+ // Free ClearOnShutdown()'ed smart pointers. This needs to happen *after*
+ // we've finished notifying observers of XPCOM shutdown, because shutdown
+ // observers themselves might call ClearOnShutdown().
+ mozilla::KillClearOnShutdown(ShutdownPhase::ShutdownFinal);
+
+ // XPCOM is officially in shutdown mode NOW
+ // Set this only after the observers have been notified as this
+ // will cause servicemanager to become inaccessible.
+ mozilla::services::Shutdown();
+
+ // We may have AddRef'd for the caller of NS_InitXPCOM, so release it
+ // here again:
+ NS_IF_RELEASE(aServMgr);
+
+ // Shutdown global servicemanager
+ if (nsComponentManagerImpl::gComponentManager) {
+ nsComponentManagerImpl::gComponentManager->FreeServices();
+ }
+
+ // Release the directory service
+ nsDirectoryService::gService = nullptr;
+
+ free(gGREBinPath);
+ gGREBinPath = nullptr;
+
+ if (moduleLoaders) {
+ bool more;
+ nsCOMPtr<nsISupports> el;
+ while (NS_SUCCEEDED(moduleLoaders->HasMoreElements(&more)) && more) {
+ moduleLoaders->GetNext(getter_AddRefs(el));
+
+ // Don't worry about weak-reference observers here: there is
+ // no reason for weak-ref observers to register for
+ // xpcom-shutdown-loaders
+
+ // FIXME: This can cause harmless writes from sqlite committing
+ // log files. We have to ignore them before we can move
+ // the mozilla::PoisonWrite call before this point. See bug
+ // 834945 for the details.
+ nsCOMPtr<nsIObserver> obs(do_QueryInterface(el));
+ if (obs) {
+ obs->Observe(nullptr, NS_XPCOM_SHUTDOWN_LOADERS_OBSERVER_ID, nullptr);
+ }
+ }
+
+ moduleLoaders = nullptr;
+ }
+
+ bool shutdownCollect;
+#ifdef NS_FREE_PERMANENT_DATA
+ shutdownCollect = true;
+#else
+ shutdownCollect = !!PR_GetEnv("MOZ_CC_RUN_DURING_SHUTDOWN");
+#endif
+ nsCycleCollector_shutdown(shutdownCollect);
+
+ PROFILER_MARKER("Shutdown xpcom");
+ // If we are doing any shutdown checks, poison writes.
+ if (gShutdownChecks != SCM_NOTHING) {
+#ifdef XP_MACOSX
+ mozilla::OnlyReportDirtyWrites();
+#endif /* XP_MACOSX */
+ mozilla::BeginLateWriteChecks();
+ }
+
+ // Shutdown nsLocalFile string conversion
+ NS_ShutdownLocalFile();
+#ifdef XP_UNIX
+ NS_ShutdownNativeCharsetUtils();
+#endif
+
+ // Shutdown xpcom. This will release all loaders and cause others holding
+ // a refcount to the component manager to release it.
+ if (nsComponentManagerImpl::gComponentManager) {
+ rv = (nsComponentManagerImpl::gComponentManager)->Shutdown();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Component Manager shutdown failed.");
+ } else {
+ NS_WARNING("Component Manager was never created ...");
+ }
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ // In optimized builds we don't do shutdown collections by default, so
+ // uncollected (garbage) objects may keep the nsXPConnect singleton alive,
+ // and its XPCJSContext along with it. However, we still destroy various
+ // bits of state in JS_ShutDown(), so we need to make sure the profiler
+ // can't access them when it shuts down. This call nulls out the
+ // JS pseudo-stack's internal reference to the main thread JSContext,
+ // duplicating the call in XPCJSContext::~XPCJSContext() in case that
+ // never fired.
+ if (PseudoStack* stack = mozilla_get_pseudo_stack()) {
+ stack->sampleContext(nullptr);
+ }
+#endif
+
+ if (sInitializedJS) {
+ // Shut down the JS engine.
+ JS_ShutDown();
+ sInitializedJS = false;
+ }
+
+ // Release our own singletons
+ // Do this _after_ shutting down the component manager, because the
+ // JS component loader will use XPConnect to call nsIModule::canUnload,
+ // and that will spin up the InterfaceInfoManager again -- bad mojo
+ XPTInterfaceInfoManager::FreeInterfaceInfoManager();
+
+ // Finally, release the component manager last because it unloads the
+ // libraries:
+ if (nsComponentManagerImpl::gComponentManager) {
+ nsrefcnt cnt;
+ NS_RELEASE2(nsComponentManagerImpl::gComponentManager, cnt);
+ NS_ASSERTION(cnt == 0, "Component Manager being held past XPCOM shutdown.");
+ }
+ nsComponentManagerImpl::gComponentManager = nullptr;
+ nsCategoryManager::Destroy();
+
+ NS_ShutdownAtomTable();
+
+ NS_IF_RELEASE(gDebug);
+
+ delete sIOThread;
+ sIOThread = nullptr;
+
+ delete sMessageLoop;
+ sMessageLoop = nullptr;
+
+ if (sCommandLineWasInitialized) {
+ CommandLine::Terminate();
+ sCommandLineWasInitialized = false;
+ }
+
+ delete sExitManager;
+ sExitManager = nullptr;
+
+ Omnijar::CleanUp();
+
+ HangMonitor::Shutdown();
+
+ delete sMainHangMonitor;
+ sMainHangMonitor = nullptr;
+
+ BackgroundHangMonitor::Shutdown();
+
+ profiler_shutdown();
+
+ NS_LogTerm();
+
+#if defined(MOZ_WIDGET_GONK)
+ // This _exit(0) call is intended to be temporary, to get shutdown leak
+ // checking working on non-B2G platforms.
+ // On debug B2G, the child process crashes very late. Instead, just
+ // give up so at least we exit cleanly. See bug 1071866.
+ if (XRE_IsContentProcess()) {
+ NS_WARNING("Exiting child process early!");
+ _exit(0);
+ }
+#endif
+
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/xpcom/build/XPCOMModule.inc b/xpcom/build/XPCOMModule.inc
new file mode 100644
index 000000000..5ac2885e2
--- /dev/null
+++ b/xpcom/build/XPCOMModule.inc
@@ -0,0 +1,81 @@
+ COMPONENT_M(MEMORY, nsMemoryImpl::Create, Module::ALLOW_IN_GPU_PROCESS)
+ COMPONENT_M(DEBUG, nsDebugImpl::Create, Module::ALLOW_IN_GPU_PROCESS)
+ COMPONENT(ERRORSERVICE, nsErrorService::Create)
+
+ COMPONENT_M(CATEGORYMANAGER, nsCategoryManager::Create, Module::ALLOW_IN_GPU_PROCESS)
+
+ COMPONENT(SCRIPTABLEINPUTSTREAM, nsScriptableInputStream::Create)
+ COMPONENT(BINARYINPUTSTREAM, nsBinaryInputStreamConstructor)
+ COMPONENT(BINARYOUTPUTSTREAM, nsBinaryOutputStreamConstructor)
+ COMPONENT(STORAGESTREAM, nsStorageStreamConstructor)
+ COMPONENT(VERSIONCOMPARATOR, nsVersionComparatorImplConstructor)
+ COMPONENT(SCRIPTABLEBASE64ENCODER, nsScriptableBase64EncoderConstructor)
+ COMPONENT(PIPE, nsPipeConstructor)
+
+ COMPONENT(PROPERTIES, nsPropertiesConstructor)
+
+ COMPONENT(PERSISTENTPROPERTIES, nsPersistentProperties::Create)
+
+ COMPONENT(SUPPORTSARRAY, nsSupportsArray::Create)
+ COMPONENT(ARRAY, nsArrayBase::XPCOMConstructor)
+ COMPONENT(CONSOLESERVICE, nsConsoleServiceConstructor)
+ COMPONENT(ATOMSERVICE, nsAtomServiceConstructor)
+ COMPONENT_M(OBSERVERSERVICE, nsObserverService::Create, Module::ALLOW_IN_GPU_PROCESS)
+
+ COMPONENT_M(TIMER, nsTimerConstructor, Module::ALLOW_IN_GPU_PROCESS)
+
+#define COMPONENT_SUPPORTS(TYPE, Type) \
+ COMPONENT(SUPPORTS_##TYPE, nsSupports##Type##Constructor)
+
+ COMPONENT_SUPPORTS(ID, ID)
+ COMPONENT_SUPPORTS(STRING, String)
+ COMPONENT_SUPPORTS(CSTRING, CString)
+ COMPONENT_SUPPORTS(PRBOOL, PRBool)
+ COMPONENT_SUPPORTS(PRUINT8, PRUint8)
+ COMPONENT_SUPPORTS(PRUINT16, PRUint16)
+ COMPONENT_SUPPORTS(PRUINT32, PRUint32)
+ COMPONENT_SUPPORTS(PRUINT64, PRUint64)
+ COMPONENT_SUPPORTS(PRTIME, PRTime)
+ COMPONENT_SUPPORTS(CHAR, Char)
+ COMPONENT_SUPPORTS(PRINT16, PRInt16)
+ COMPONENT_SUPPORTS(PRINT32, PRInt32)
+ COMPONENT_SUPPORTS(PRINT64, PRInt64)
+ COMPONENT_SUPPORTS(FLOAT, Float)
+ COMPONENT_SUPPORTS(DOUBLE, Double)
+ COMPONENT_SUPPORTS(VOID, Void)
+ COMPONENT_SUPPORTS(INTERFACE_POINTER, InterfacePointer)
+
+#undef COMPONENT_SUPPORTS
+ COMPONENT(LOCAL_FILE, nsLocalFile::nsLocalFileConstructor)
+ COMPONENT(DIRECTORY_SERVICE, nsDirectoryService::Create)
+ COMPONENT(PROCESS, nsProcessConstructor)
+ COMPONENT(ENVIRONMENT, nsEnvironment::Create)
+
+ COMPONENT(THREADMANAGER, nsThreadManagerGetSingleton)
+ COMPONENT_M(THREADPOOL, nsThreadPoolConstructor, Module::ALLOW_IN_GPU_PROCESS)
+
+ COMPONENT(STRINGINPUTSTREAM, nsStringInputStreamConstructor)
+ COMPONENT(MULTIPLEXINPUTSTREAM, nsMultiplexInputStreamConstructor)
+
+ COMPONENT(VARIANT, nsVariantCCConstructor)
+ COMPONENT(INTERFACEINFOMANAGER_SERVICE, nsXPTIInterfaceInfoManagerGetSingleton)
+
+ COMPONENT(HASH_PROPERTY_BAG, nsHashPropertyBagCCConstructor)
+
+ COMPONENT(UUID_GENERATOR, nsUUIDGeneratorConstructor)
+
+#if defined(XP_WIN)
+ COMPONENT(WINDOWSREGKEY, nsWindowsRegKeyConstructor)
+#endif
+
+#if defined(MOZ_WIDGET_COCOA)
+ COMPONENT(MACUTILSIMPL, nsMacUtilsImplConstructor)
+#endif
+
+ COMPONENT(SYSTEMINFO, nsSystemInfoConstructor)
+ COMPONENT(MEMORY_REPORTER_MANAGER, nsMemoryReporterManagerConstructor)
+ COMPONENT(MEMORY_INFO_DUMPER, nsMemoryInfoDumperConstructor)
+ COMPONENT(IOUTIL, nsIOUtilConstructor)
+ COMPONENT(CYCLE_COLLECTOR_LOGGER, nsCycleCollectorLoggerConstructor)
+ COMPONENT(MESSAGE_LOOP, nsMessageLoopConstructor)
+ COMPONENT(STATUS_REPORTER_MANAGER, nsStatusReporterManagerConstructor)
diff --git a/xpcom/build/XREChildData.h b/xpcom/build/XREChildData.h
new file mode 100644
index 000000000..487fede94
--- /dev/null
+++ b/xpcom/build/XREChildData.h
@@ -0,0 +1,51 @@
+/* -*- 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 XREChildData_h
+#define XREChildData_h
+
+#include "mozilla/UniquePtr.h"
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+#include "mozilla/sandboxing/loggingTypes.h"
+
+namespace sandbox {
+class TargetServices;
+}
+#endif
+
+namespace mozilla {
+namespace gmp {
+class GMPLoader;
+}
+}
+
+/**
+ * Data needed to start a child process.
+ */
+struct XREChildData
+{
+#if !defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_WIDGET_GONK)
+ /**
+ * Used to load the GMP binary.
+ */
+ mozilla::UniquePtr<mozilla::gmp::GMPLoader> gmpLoader;
+#endif
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ /**
+ * Chromium sandbox TargetServices.
+ */
+ sandbox::TargetServices* sandboxTargetServices = nullptr;
+
+ /**
+ * Function to provide a logging function to the chromium sandbox code.
+ */
+ mozilla::sandboxing::ProvideLogFunctionCb ProvideLogFunction = nullptr;
+#endif
+};
+
+#endif // XREChildData_h
diff --git a/xpcom/build/XREShellData.h b/xpcom/build/XREShellData.h
new file mode 100644
index 000000000..11bc162d9
--- /dev/null
+++ b/xpcom/build/XREShellData.h
@@ -0,0 +1,29 @@
+/* -*- 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 XREShellData_h
+#define XREShellData_h
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+namespace sandbox {
+class BrokerServices;
+}
+#endif
+
+/**
+ * Data needed by XRE_XPCShellMain.
+ */
+struct XREShellData
+{
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ /**
+ * Chromium sandbox BrokerServices.
+ */
+ sandbox::BrokerServices* sandboxBrokerServices;
+#endif
+};
+
+#endif // XREShellData_h
diff --git a/xpcom/build/mach_override.c b/xpcom/build/mach_override.c
new file mode 100644
index 000000000..9e4940d29
--- /dev/null
+++ b/xpcom/build/mach_override.c
@@ -0,0 +1,789 @@
+// Copied from upstream at revision 195c13743fe0ebc658714e2a9567d86529f20443.
+// mach_override.c semver:1.2.0
+// Copyright (c) 2003-2012 Jonathan 'Wolf' Rentzsch: http://rentzsch.com
+// Some rights reserved: http://opensource.org/licenses/mit
+// https://github.com/rentzsch/mach_override
+
+#include "mach_override.h"
+
+#include <mach-o/dyld.h>
+#include <mach/mach_host.h>
+#include <mach/mach_init.h>
+#include <mach/vm_map.h>
+#include <sys/mman.h>
+
+#include <CoreServices/CoreServices.h>
+
+/**************************
+*
+* Constants
+*
+**************************/
+#pragma mark -
+#pragma mark (Constants)
+
+#define kPageSize 4096
+#if defined(__ppc__) || defined(__POWERPC__)
+
+long kIslandTemplate[] = {
+ 0x9001FFFC, // stw r0,-4(SP)
+ 0x3C00DEAD, // lis r0,0xDEAD
+ 0x6000BEEF, // ori r0,r0,0xBEEF
+ 0x7C0903A6, // mtctr r0
+ 0x8001FFFC, // lwz r0,-4(SP)
+ 0x60000000, // nop ; optionally replaced
+ 0x4E800420 // bctr
+};
+
+#define kAddressHi 3
+#define kAddressLo 5
+#define kInstructionHi 10
+#define kInstructionLo 11
+
+#elif defined(__i386__)
+
+#define kOriginalInstructionsSize 16
+// On X86 we migh need to instert an add with a 32 bit immediate after the
+// original instructions.
+#define kMaxFixupSizeIncrease 5
+
+unsigned char kIslandTemplate[] = {
+ // kOriginalInstructionsSize nop instructions so that we
+ // should have enough space to host original instructions
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ // Now the real jump instruction
+ 0xE9, 0xEF, 0xBE, 0xAD, 0xDE
+};
+
+#define kInstructions 0
+#define kJumpAddress kInstructions + kOriginalInstructionsSize + 1
+#elif defined(__x86_64__)
+
+#define kOriginalInstructionsSize 32
+// On X86-64 we never need to instert a new instruction.
+#define kMaxFixupSizeIncrease 0
+
+#define kJumpAddress kOriginalInstructionsSize + 6
+
+unsigned char kIslandTemplate[] = {
+ // kOriginalInstructionsSize nop instructions so that we
+ // should have enough space to host original instructions
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+ // Now the real jump instruction
+ 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+#endif
+
+/**************************
+*
+* Data Types
+*
+**************************/
+#pragma mark -
+#pragma mark (Data Types)
+
+typedef struct {
+ char instructions[sizeof(kIslandTemplate)];
+} BranchIsland;
+
+/**************************
+*
+* Funky Protos
+*
+**************************/
+#pragma mark -
+#pragma mark (Funky Protos)
+
+static mach_error_t
+allocateBranchIsland(
+ BranchIsland **island,
+ void *originalFunctionAddress);
+
+ mach_error_t
+freeBranchIsland(
+ BranchIsland *island );
+
+#if defined(__ppc__) || defined(__POWERPC__)
+ mach_error_t
+setBranchIslandTarget(
+ BranchIsland *island,
+ const void *branchTo,
+ long instruction );
+#endif
+
+#if defined(__i386__) || defined(__x86_64__)
+mach_error_t
+setBranchIslandTarget_i386(
+ BranchIsland *island,
+ const void *branchTo,
+ char* instructions );
+void
+atomic_mov64(
+ uint64_t *targetAddress,
+ uint64_t value );
+
+ static Boolean
+eatKnownInstructions(
+ unsigned char *code,
+ uint64_t *newInstruction,
+ int *howManyEaten,
+ char *originalInstructions,
+ int *originalInstructionCount,
+ uint8_t *originalInstructionSizes );
+
+ static void
+fixupInstructions(
+ uint32_t offset,
+ void *instructionsToFix,
+ int instructionCount,
+ uint8_t *instructionSizes );
+#endif
+
+/*******************************************************************************
+*
+* Interface
+*
+*******************************************************************************/
+#pragma mark -
+#pragma mark (Interface)
+
+#if defined(__i386__) || defined(__x86_64__)
+mach_error_t makeIslandExecutable(void *address) {
+ mach_error_t err = err_none;
+ uintptr_t page = (uintptr_t)address & ~(uintptr_t)(kPageSize-1);
+ int e = err_none;
+ e |= mprotect((void *)page, kPageSize, PROT_EXEC | PROT_READ | PROT_WRITE);
+ e |= msync((void *)page, kPageSize, MS_INVALIDATE );
+ if (e) {
+ err = err_cannot_override;
+ }
+ return err;
+}
+#endif
+
+ mach_error_t
+mach_override_ptr(
+ void *originalFunctionAddress,
+ const void *overrideFunctionAddress,
+ void **originalFunctionReentryIsland )
+{
+ assert( originalFunctionAddress );
+ assert( overrideFunctionAddress );
+
+ // this addresses overriding such functions as AudioOutputUnitStart()
+ // test with modified DefaultOutputUnit project
+#if defined(__x86_64__)
+ for(;;){
+ if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp qword near [rip+0x????????]
+ originalFunctionAddress=*(void**)((char*)originalFunctionAddress+6+*(int32_t *)((uint16_t*)originalFunctionAddress+1));
+ else break;
+ }
+#elif defined(__i386__)
+ for(;;){
+ if(*(uint16_t*)originalFunctionAddress==0x25FF) // jmp *0x????????
+ originalFunctionAddress=**(void***)((uint16_t*)originalFunctionAddress+1);
+ else break;
+ }
+#endif
+
+ long *originalFunctionPtr = (long*) originalFunctionAddress;
+ mach_error_t err = err_none;
+
+#if defined(__ppc__) || defined(__POWERPC__)
+ // Ensure first instruction isn't 'mfctr'.
+ #define kMFCTRMask 0xfc1fffff
+ #define kMFCTRInstruction 0x7c0903a6
+
+ long originalInstruction = *originalFunctionPtr;
+ if( !err && ((originalInstruction & kMFCTRMask) == kMFCTRInstruction) )
+ err = err_cannot_override;
+#elif defined(__i386__) || defined(__x86_64__)
+ int eatenCount = 0;
+ int originalInstructionCount = 0;
+ char originalInstructions[kOriginalInstructionsSize];
+ uint8_t originalInstructionSizes[kOriginalInstructionsSize];
+ uint64_t jumpRelativeInstruction = 0; // JMP
+
+ Boolean overridePossible = eatKnownInstructions ((unsigned char *)originalFunctionPtr,
+ &jumpRelativeInstruction, &eatenCount,
+ originalInstructions, &originalInstructionCount,
+ originalInstructionSizes );
+ if (eatenCount + kMaxFixupSizeIncrease > kOriginalInstructionsSize) {
+ //printf ("Too many instructions eaten\n");
+ overridePossible = false;
+ }
+ if (!overridePossible) err = err_cannot_override;
+ if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
+#endif
+
+ // Make the original function implementation writable.
+ if( !err ) {
+ err = vm_protect( mach_task_self(),
+ (vm_address_t) originalFunctionPtr, 8, false,
+ (VM_PROT_ALL | VM_PROT_COPY) );
+ if( err )
+ err = vm_protect( mach_task_self(),
+ (vm_address_t) originalFunctionPtr, 8, false,
+ (VM_PROT_DEFAULT | VM_PROT_COPY) );
+ }
+ if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
+
+ // Allocate and target the escape island to the overriding function.
+ BranchIsland *escapeIsland = NULL;
+ if( !err )
+ err = allocateBranchIsland( &escapeIsland, originalFunctionAddress );
+ if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
+
+
+#if defined(__ppc__) || defined(__POWERPC__)
+ if( !err )
+ err = setBranchIslandTarget( escapeIsland, overrideFunctionAddress, 0 );
+
+ // Build the branch absolute instruction to the escape island.
+ long branchAbsoluteInstruction = 0; // Set to 0 just to silence warning.
+ if( !err ) {
+ long escapeIslandAddress = ((long) escapeIsland) & 0x3FFFFFF;
+ branchAbsoluteInstruction = 0x48000002 | escapeIslandAddress;
+ }
+#elif defined(__i386__) || defined(__x86_64__)
+ if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
+
+ if( !err )
+ err = setBranchIslandTarget_i386( escapeIsland, overrideFunctionAddress, 0 );
+
+ if (err) fprintf(stderr, "err = %x %s:%d\n", err, __FILE__, __LINE__);
+ // Build the jump relative instruction to the escape island
+#endif
+
+
+#if defined(__i386__) || defined(__x86_64__)
+ if (!err) {
+ uint32_t addressOffset = ((char*)escapeIsland - (char*)originalFunctionPtr - 5);
+ addressOffset = OSSwapInt32(addressOffset);
+
+ jumpRelativeInstruction |= 0xE900000000000000LL;
+ jumpRelativeInstruction |= ((uint64_t)addressOffset & 0xffffffff) << 24;
+ jumpRelativeInstruction = OSSwapInt64(jumpRelativeInstruction);
+ }
+#endif
+
+ // Optionally allocate & return the reentry island. This may contain relocated
+ // jmp instructions and so has all the same addressing reachability requirements
+ // the escape island has to the original function, except the escape island is
+ // technically our original function.
+ BranchIsland *reentryIsland = NULL;
+ if( !err && originalFunctionReentryIsland ) {
+ err = allocateBranchIsland( &reentryIsland, escapeIsland);
+ if( !err )
+ *originalFunctionReentryIsland = reentryIsland;
+ }
+
+#if defined(__ppc__) || defined(__POWERPC__)
+ // Atomically:
+ // o If the reentry island was allocated:
+ // o Insert the original instruction into the reentry island.
+ // o Target the reentry island at the 2nd instruction of the
+ // original function.
+ // o Replace the original instruction with the branch absolute.
+ if( !err ) {
+ int escapeIslandEngaged = false;
+ do {
+ if( reentryIsland )
+ err = setBranchIslandTarget( reentryIsland,
+ (void*) (originalFunctionPtr+1), originalInstruction );
+ if( !err ) {
+ escapeIslandEngaged = CompareAndSwap( originalInstruction,
+ branchAbsoluteInstruction,
+ (UInt32*)originalFunctionPtr );
+ if( !escapeIslandEngaged ) {
+ // Someone replaced the instruction out from under us,
+ // re-read the instruction, make sure it's still not
+ // 'mfctr' and try again.
+ originalInstruction = *originalFunctionPtr;
+ if( (originalInstruction & kMFCTRMask) == kMFCTRInstruction)
+ err = err_cannot_override;
+ }
+ }
+ } while( !err && !escapeIslandEngaged );
+ }
+#elif defined(__i386__) || defined(__x86_64__)
+ // Atomically:
+ // o If the reentry island was allocated:
+ // o Insert the original instructions into the reentry island.
+ // o Target the reentry island at the first non-replaced
+ // instruction of the original function.
+ // o Replace the original first instructions with the jump relative.
+ //
+ // Note that on i386, we do not support someone else changing the code under our feet
+ if ( !err ) {
+ uint32_t offset = (uintptr_t)originalFunctionPtr - (uintptr_t)reentryIsland;
+ fixupInstructions(offset, originalInstructions,
+ originalInstructionCount, originalInstructionSizes );
+
+ if( reentryIsland )
+ err = setBranchIslandTarget_i386( reentryIsland,
+ (void*) ((char *)originalFunctionPtr+eatenCount), originalInstructions );
+ // try making islands executable before planting the jmp
+#if defined(__x86_64__) || defined(__i386__)
+ if( !err )
+ err = makeIslandExecutable(escapeIsland);
+ if( !err && reentryIsland )
+ err = makeIslandExecutable(reentryIsland);
+#endif
+ if ( !err )
+ atomic_mov64((uint64_t *)originalFunctionPtr, jumpRelativeInstruction);
+ }
+#endif
+
+ // Clean up on error.
+ if( err ) {
+ if( reentryIsland )
+ freeBranchIsland( reentryIsland );
+ if( escapeIsland )
+ freeBranchIsland( escapeIsland );
+ }
+
+ return err;
+}
+
+/*******************************************************************************
+*
+* Implementation
+*
+*******************************************************************************/
+#pragma mark -
+#pragma mark (Implementation)
+
+static bool jump_in_range(intptr_t from, intptr_t to) {
+ intptr_t field_value = to - from - 5;
+ int32_t field_value_32 = field_value;
+ return field_value == field_value_32;
+}
+
+/*******************************************************************************
+ Implementation: Allocates memory for a branch island.
+
+ @param island <- The allocated island.
+ @result <- mach_error_t
+
+ ***************************************************************************/
+
+static mach_error_t
+allocateBranchIslandAux(
+ BranchIsland **island,
+ void *originalFunctionAddress,
+ bool forward)
+{
+ assert( island );
+ assert( sizeof( BranchIsland ) <= kPageSize );
+
+ vm_map_t task_self = mach_task_self();
+ vm_address_t original_address = (vm_address_t) originalFunctionAddress;
+ vm_address_t address = original_address;
+
+ for (;;) {
+ vm_size_t vmsize = 0;
+ memory_object_name_t object = 0;
+ kern_return_t kr = 0;
+ vm_region_flavor_t flavor = VM_REGION_BASIC_INFO;
+ // Find the region the address is in.
+#if __WORDSIZE == 32
+ vm_region_basic_info_data_t info;
+ mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT;
+ kr = vm_region(task_self, &address, &vmsize, flavor,
+ (vm_region_info_t)&info, &info_count, &object);
+#else
+ vm_region_basic_info_data_64_t info;
+ mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
+ kr = vm_region_64(task_self, &address, &vmsize, flavor,
+ (vm_region_info_t)&info, &info_count, &object);
+#endif
+ if (kr != KERN_SUCCESS)
+ return kr;
+ assert((address & (kPageSize - 1)) == 0);
+
+ // Go to the first page before or after this region
+ vm_address_t new_address = forward ? address + vmsize : address - kPageSize;
+#if __WORDSIZE == 64
+ if(!jump_in_range(original_address, new_address))
+ break;
+#endif
+ address = new_address;
+
+ // Try to allocate this page.
+ kr = vm_allocate(task_self, &address, kPageSize, 0);
+ if (kr == KERN_SUCCESS) {
+ *island = (BranchIsland*) address;
+ return err_none;
+ }
+ if (kr != KERN_NO_SPACE)
+ return kr;
+ }
+
+ return KERN_NO_SPACE;
+}
+
+static mach_error_t
+allocateBranchIsland(
+ BranchIsland **island,
+ void *originalFunctionAddress)
+{
+ mach_error_t err =
+ allocateBranchIslandAux(island, originalFunctionAddress, true);
+ if (!err)
+ return err;
+ return allocateBranchIslandAux(island, originalFunctionAddress, false);
+}
+
+
+/*******************************************************************************
+ Implementation: Deallocates memory for a branch island.
+
+ @param island -> The island to deallocate.
+ @result <- mach_error_t
+
+ ***************************************************************************/
+
+ mach_error_t
+freeBranchIsland(
+ BranchIsland *island )
+{
+ assert( island );
+ assert( (*(long*)&island->instructions[0]) == kIslandTemplate[0] );
+ assert( sizeof( BranchIsland ) <= kPageSize );
+ return vm_deallocate( mach_task_self(), (vm_address_t) island,
+ kPageSize );
+}
+
+/*******************************************************************************
+ Implementation: Sets the branch island's target, with an optional
+ instruction.
+
+ @param island -> The branch island to insert target into.
+ @param branchTo -> The address of the target.
+ @param instruction -> Optional instruction to execute prior to branch. Set
+ to zero for nop.
+ @result <- mach_error_t
+
+ ***************************************************************************/
+#if defined(__ppc__) || defined(__POWERPC__)
+ mach_error_t
+setBranchIslandTarget(
+ BranchIsland *island,
+ const void *branchTo,
+ long instruction )
+{
+ // Copy over the template code.
+ bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
+
+ // Fill in the address.
+ ((short*)island->instructions)[kAddressLo] = ((long) branchTo) & 0x0000FFFF;
+ ((short*)island->instructions)[kAddressHi]
+ = (((long) branchTo) >> 16) & 0x0000FFFF;
+
+ // Fill in the (optional) instuction.
+ if( instruction != 0 ) {
+ ((short*)island->instructions)[kInstructionLo]
+ = instruction & 0x0000FFFF;
+ ((short*)island->instructions)[kInstructionHi]
+ = (instruction >> 16) & 0x0000FFFF;
+ }
+
+ //MakeDataExecutable( island->instructions, sizeof( kIslandTemplate ) );
+ msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
+
+ return err_none;
+}
+#endif
+
+#if defined(__i386__)
+ mach_error_t
+setBranchIslandTarget_i386(
+ BranchIsland *island,
+ const void *branchTo,
+ char* instructions )
+{
+
+ // Copy over the template code.
+ bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
+
+ // copy original instructions
+ if (instructions) {
+ bcopy (instructions, island->instructions + kInstructions, kOriginalInstructionsSize);
+ }
+
+ // Fill in the address.
+ int32_t addressOffset = (char *)branchTo - (island->instructions + kJumpAddress + 4);
+ *((int32_t *)(island->instructions + kJumpAddress)) = addressOffset;
+
+ msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
+ return err_none;
+}
+
+#elif defined(__x86_64__)
+mach_error_t
+setBranchIslandTarget_i386(
+ BranchIsland *island,
+ const void *branchTo,
+ char* instructions )
+{
+ // Copy over the template code.
+ bcopy( kIslandTemplate, island->instructions, sizeof( kIslandTemplate ) );
+
+ // Copy original instructions.
+ if (instructions) {
+ bcopy (instructions, island->instructions, kOriginalInstructionsSize);
+ }
+
+ // Fill in the address.
+ *((uint64_t *)(island->instructions + kJumpAddress)) = (uint64_t)branchTo;
+ msync( island->instructions, sizeof( kIslandTemplate ), MS_INVALIDATE );
+
+ return err_none;
+}
+#endif
+
+
+#if defined(__i386__) || defined(__x86_64__)
+// simplistic instruction matching
+typedef struct {
+ unsigned int length; // max 15
+ unsigned char mask[15]; // sequence of bytes in memory order
+ unsigned char constraint[15]; // sequence of bytes in memory order
+} AsmInstructionMatch;
+
+#if defined(__i386__)
+static AsmInstructionMatch possibleInstructions[] = {
+ { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x????????
+ { 0x5, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0x55, 0x89, 0xe5, 0xc9, 0xc3} }, // push %ebp; mov %esp,%ebp; leave; ret
+ { 0x1, {0xFF}, {0x90} }, // nop
+ { 0x1, {0xFF}, {0x55} }, // push %esp
+ { 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp
+ { 0x1, {0xFF}, {0x53} }, // push %ebx
+ { 0x3, {0xFF, 0xFF, 0x00}, {0x83, 0xEC, 0x00} }, // sub 0x??, %esp
+ { 0x6, {0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x81, 0xEC, 0x00, 0x00, 0x00, 0x00} }, // sub 0x??, %esp with 32bit immediate
+ { 0x1, {0xFF}, {0x57} }, // push %edi
+ { 0x1, {0xFF}, {0x56} }, // push %esi
+ { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax
+ { 0x3, {0xFF, 0x4F, 0x00}, {0x8B, 0x45, 0x00} }, // mov $imm(%ebp), %reg
+ { 0x3, {0xFF, 0x4C, 0x00}, {0x8B, 0x40, 0x00} }, // mov $imm(%eax-%edx), %reg
+ { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x8B, 0x4C, 0x24, 0x00} }, // mov $imm(%esp), %ecx
+ { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %eax
+ { 0x6, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0xE8, 0x00, 0x00, 0x00, 0x00, 0x58} }, // call $imm; pop %eax
+ { 0x0 }
+};
+#elif defined(__x86_64__)
+static AsmInstructionMatch possibleInstructions[] = {
+ { 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE9, 0x00, 0x00, 0x00, 0x00} }, // jmp 0x????????
+ { 0x1, {0xFF}, {0x90} }, // nop
+ { 0x1, {0xF8}, {0x50} }, // push %rX
+ { 0x3, {0xFF, 0xFF, 0xFF}, {0x48, 0x89, 0xE5} }, // mov %rsp,%rbp
+ { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x48, 0x83, 0xEC, 0x00} }, // sub 0x??, %rsp
+ { 0x4, {0xFB, 0xFF, 0x00, 0x00}, {0x48, 0x89, 0x00, 0x00} }, // move onto rbp
+ { 0x4, {0xFF, 0xFF, 0xFF, 0xFF}, {0x40, 0x0f, 0xbe, 0xce} }, // movsbl %sil, %ecx
+ { 0x2, {0xFF, 0x00}, {0x41, 0x00} }, // push %rXX
+ { 0x2, {0xFF, 0x00}, {0x85, 0x00} }, // test %rX,%rX
+ { 0x5, {0xF8, 0x00, 0x00, 0x00, 0x00}, {0xB8, 0x00, 0x00, 0x00, 0x00} }, // mov $imm, %reg
+ { 0x3, {0xFF, 0xFF, 0x00}, {0xFF, 0x77, 0x00} }, // pushq $imm(%rdi)
+ { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax
+ { 0x2, {0xFF, 0xFF}, {0x89, 0xF8} }, // mov %edi, %eax
+
+ //leaq offset(%rip),%rax
+ { 0x7, {0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, {0x48, 0x8d, 0x05, 0x00, 0x00, 0x00, 0x00} },
+
+ { 0x0 }
+};
+#endif
+
+static Boolean codeMatchesInstruction(unsigned char *code, AsmInstructionMatch* instruction)
+{
+ Boolean match = true;
+
+ size_t i;
+ for (i=0; i<instruction->length; i++) {
+ unsigned char mask = instruction->mask[i];
+ unsigned char constraint = instruction->constraint[i];
+ unsigned char codeValue = code[i];
+
+ match = ((codeValue & mask) == constraint);
+ if (!match) break;
+ }
+
+ return match;
+}
+
+#if defined(__i386__) || defined(__x86_64__)
+ static Boolean
+eatKnownInstructions(
+ unsigned char *code,
+ uint64_t *newInstruction,
+ int *howManyEaten,
+ char *originalInstructions,
+ int *originalInstructionCount,
+ uint8_t *originalInstructionSizes )
+{
+ Boolean allInstructionsKnown = true;
+ int totalEaten = 0;
+ unsigned char* ptr = code;
+ int remainsToEat = 5; // a JMP instruction takes 5 bytes
+ int instructionIndex = 0;
+
+ if (howManyEaten) *howManyEaten = 0;
+ if (originalInstructionCount) *originalInstructionCount = 0;
+ while (remainsToEat > 0) {
+ Boolean curInstructionKnown = false;
+
+ // See if instruction matches one we know
+ AsmInstructionMatch* curInstr = possibleInstructions;
+ do {
+ if ((curInstructionKnown = codeMatchesInstruction(ptr, curInstr))) break;
+ curInstr++;
+ } while (curInstr->length > 0);
+
+ // if all instruction matches failed, we don't know current instruction then, stop here
+ if (!curInstructionKnown) {
+ allInstructionsKnown = false;
+ fprintf(stderr, "mach_override: some instructions unknown! Need to update mach_override.c\n");
+ break;
+ }
+
+ // At this point, we've matched curInstr
+ int eaten = curInstr->length;
+ ptr += eaten;
+ remainsToEat -= eaten;
+ totalEaten += eaten;
+
+ if (originalInstructionSizes) originalInstructionSizes[instructionIndex] = eaten;
+ instructionIndex += 1;
+ if (originalInstructionCount) *originalInstructionCount = instructionIndex;
+ }
+
+
+ if (howManyEaten) *howManyEaten = totalEaten;
+
+ if (originalInstructions) {
+ Boolean enoughSpaceForOriginalInstructions = (totalEaten < kOriginalInstructionsSize);
+
+ if (enoughSpaceForOriginalInstructions) {
+ memset(originalInstructions, 0x90 /* NOP */, kOriginalInstructionsSize); // fill instructions with NOP
+ bcopy(code, originalInstructions, totalEaten);
+ } else {
+ // printf ("Not enough space in island to store original instructions. Adapt the island definition and kOriginalInstructionsSize\n");
+ return false;
+ }
+ }
+
+ if (allInstructionsKnown) {
+ // save last 3 bytes of first 64bits of codre we'll replace
+ uint64_t currentFirst64BitsOfCode = *((uint64_t *)code);
+ currentFirst64BitsOfCode = OSSwapInt64(currentFirst64BitsOfCode); // back to memory representation
+ currentFirst64BitsOfCode &= 0x0000000000FFFFFFLL;
+
+ // keep only last 3 instructions bytes, first 5 will be replaced by JMP instr
+ *newInstruction &= 0xFFFFFFFFFF000000LL; // clear last 3 bytes
+ *newInstruction |= (currentFirst64BitsOfCode & 0x0000000000FFFFFFLL); // set last 3 bytes
+ }
+
+ return allInstructionsKnown;
+}
+
+ static void
+fixupInstructions(
+ uint32_t offset,
+ void *instructionsToFix,
+ int instructionCount,
+ uint8_t *instructionSizes )
+{
+ // The start of "leaq offset(%rip),%rax"
+ static const uint8_t LeaqHeader[] = {0x48, 0x8d, 0x05};
+
+ int index;
+ for (index = 0;index < instructionCount;index += 1)
+ {
+ if (*(uint8_t*)instructionsToFix == 0xE9) // 32-bit jump relative
+ {
+ uint32_t *jumpOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 1);
+ *jumpOffsetPtr += offset;
+ }
+
+ // leaq offset(%rip),%rax
+ if (memcmp(instructionsToFix, LeaqHeader, 3) == 0) {
+ uint32_t *LeaqOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 3);
+ *LeaqOffsetPtr += offset;
+ }
+
+ // 32-bit call relative to the next addr; pop %eax
+ if (*(uint8_t*)instructionsToFix == 0xE8)
+ {
+ // Just this call is larger than the jump we use, so we
+ // know this is the last instruction.
+ assert(index == (instructionCount - 1));
+ assert(instructionSizes[index] == 6);
+
+ // Insert "addl $offset, %eax" in the end so that when
+ // we jump to the rest of the function %eax has the
+ // value it would have if eip had been pushed by the
+ // call in its original position.
+ uint8_t *op = instructionsToFix;
+ op += 6;
+ *op = 0x05; // addl
+ uint32_t *addImmPtr = (uint32_t*)(op + 1);
+ *addImmPtr = offset;
+ }
+
+ instructionsToFix = (void*)((uintptr_t)instructionsToFix + instructionSizes[index]);
+ }
+}
+#endif
+
+#if defined(__i386__)
+__asm(
+ ".text;"
+ ".align 2, 0x90;"
+ "_atomic_mov64:;"
+ " pushl %ebp;"
+ " movl %esp, %ebp;"
+ " pushl %esi;"
+ " pushl %ebx;"
+ " pushl %ecx;"
+ " pushl %eax;"
+ " pushl %edx;"
+
+ // atomic push of value to an address
+ // we use cmpxchg8b, which compares content of an address with
+ // edx:eax. If they are equal, it atomically puts 64bit value
+ // ecx:ebx in address.
+ // We thus put contents of address in edx:eax to force ecx:ebx
+ // in address
+ " mov 8(%ebp), %esi;" // esi contains target address
+ " mov 12(%ebp), %ebx;"
+ " mov 16(%ebp), %ecx;" // ecx:ebx now contains value to put in target address
+ " mov (%esi), %eax;"
+ " mov 4(%esi), %edx;" // edx:eax now contains value currently contained in target address
+ " lock; cmpxchg8b (%esi);" // atomic move.
+
+ // restore registers
+ " popl %edx;"
+ " popl %eax;"
+ " popl %ecx;"
+ " popl %ebx;"
+ " popl %esi;"
+ " popl %ebp;"
+ " ret"
+);
+#elif defined(__x86_64__)
+void atomic_mov64(
+ uint64_t *targetAddress,
+ uint64_t value )
+{
+ *targetAddress = value;
+}
+#endif
+#endif
diff --git a/xpcom/build/mach_override.h b/xpcom/build/mach_override.h
new file mode 100644
index 000000000..d9be988a3
--- /dev/null
+++ b/xpcom/build/mach_override.h
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ mach_override.h
+ Copyright (c) 2003-2009 Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
+ Some rights reserved: <http://opensource.org/licenses/mit-license.php>
+
+ ***************************************************************************/
+
+/***************************************************************************//**
+ @mainpage mach_override
+ @author Jonathan 'Wolf' Rentzsch: <http://rentzsch.com>
+
+ This package, coded in C to the Mach API, allows you to override ("patch")
+ program- and system-supplied functions at runtime. You can fully replace
+ functions with your implementations, or merely head- or tail-patch the
+ original implementations.
+
+ Use it by #include'ing mach_override.h from your .c, .m or .mm file(s).
+
+ @todo Discontinue use of Carbon's MakeDataExecutable() and
+ CompareAndSwap() calls and start using the Mach equivalents, if they
+ exist. If they don't, write them and roll them in. That way, this
+ code will be pure Mach, which will make it easier to use everywhere.
+ Update: MakeDataExecutable() has been replaced by
+ msync(MS_INVALIDATE). There is an OSCompareAndSwap in libkern, but
+ I'm currently unsure if I can link against it. May have to roll in
+ my own version...
+ @todo Stop using an entire 4K high-allocated VM page per 28-byte escape
+ branch island. Done right, this will dramatically speed up escape
+ island allocations when they number over 250. Then again, if you're
+ overriding more than 250 functions, maybe speed isn't your main
+ concern...
+ @todo Add detection of: b, bl, bla, bc, bcl, bcla, bcctrl, bclrl
+ first-instructions. Initially, we should refuse to override
+ functions beginning with these instructions. Eventually, we should
+ dynamically rewrite them to make them position-independent.
+ @todo Write mach_unoverride(), which would remove an override placed on a
+ function. Must be multiple-override aware, which means an almost
+ complete rewrite under the covers, because the target address can't
+ be spread across two load instructions like it is now since it will
+ need to be atomically updatable.
+ @todo Add non-rentry variants of overrides to test_mach_override.
+
+ ***************************************************************************/
+
+#ifndef _mach_override_
+#define _mach_override_
+
+#include <sys/types.h>
+#include <mach/error.h>
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/**
+ Returned if the function to be overrided begins with a 'mfctr' instruction.
+*/
+#define err_cannot_override (err_local|1)
+
+/************************************************************************************//**
+ Dynamically overrides the function implementation referenced by
+ originalFunctionAddress with the implentation pointed to by overrideFunctionAddress.
+ Optionally returns a pointer to a "reentry island" which, if jumped to, will resume
+ the original implementation.
+
+ @param originalFunctionAddress -> Required address of the function to
+ override (with overrideFunctionAddress).
+ @param overrideFunctionAddress -> Required address to the overriding
+ function.
+ @param originalFunctionReentryIsland <- Optional pointer to pointer to the
+ reentry island. Can be nullptr.
+ @result <- err_cannot_override if the original
+ function's implementation begins with
+ the 'mfctr' instruction.
+
+ ************************************************************************************/
+
+ mach_error_t
+mach_override_ptr(
+ void *originalFunctionAddress,
+ const void *overrideFunctionAddress,
+ void **originalFunctionReentryIsland );
+
+/************************************************************************************//**
+
+
+ ************************************************************************************/
+
+#ifdef __cplusplus
+
+#define MACH_OVERRIDE( ORIGINAL_FUNCTION_RETURN_TYPE, ORIGINAL_FUNCTION_NAME, ORIGINAL_FUNCTION_ARGS, ERR ) \
+ { \
+ static ORIGINAL_FUNCTION_RETURN_TYPE (*ORIGINAL_FUNCTION_NAME##_reenter)ORIGINAL_FUNCTION_ARGS; \
+ static bool ORIGINAL_FUNCTION_NAME##_overriden = false; \
+ class mach_override_class__##ORIGINAL_FUNCTION_NAME { \
+ public: \
+ static kern_return_t override(void *originalFunctionPtr) { \
+ kern_return_t result = err_none; \
+ if (!ORIGINAL_FUNCTION_NAME##_overriden) { \
+ ORIGINAL_FUNCTION_NAME##_overriden = true; \
+ result = mach_override_ptr( (void*)originalFunctionPtr, \
+ (void*)mach_override_class__##ORIGINAL_FUNCTION_NAME::replacement, \
+ (void**)&ORIGINAL_FUNCTION_NAME##_reenter ); \
+ } \
+ return result; \
+ } \
+ static ORIGINAL_FUNCTION_RETURN_TYPE replacement ORIGINAL_FUNCTION_ARGS {
+
+#define END_MACH_OVERRIDE( ORIGINAL_FUNCTION_NAME ) \
+ } \
+ }; \
+ \
+ err = mach_override_class__##ORIGINAL_FUNCTION_NAME::override((void*)ORIGINAL_FUNCTION_NAME); \
+ }
+
+#endif
+
+#ifdef __cplusplus
+ }
+#endif
+#endif // _mach_override_
diff --git a/xpcom/build/moz.build b/xpcom/build/moz.build
new file mode 100644
index 000000000..68bd001a2
--- /dev/null
+++ b/xpcom/build/moz.build
@@ -0,0 +1,102 @@
+# -*- 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/.
+
+EXPORTS += [
+ 'nsXPCOM.h',
+ 'nsXPCOMCID.h',
+ 'nsXPCOMCIDInternal.h',
+ 'nsXREAppData.h',
+ 'nsXULAppAPI.h',
+ 'XREChildData.h',
+ 'xrecore.h',
+ 'XREShellData.h',
+]
+
+EXPORTS.mozilla += [
+ 'FileLocation.h',
+ 'IOInterposer.h',
+ 'LateWriteChecks.h',
+ 'Omnijar.h',
+ 'PoisonIOInterposer.h',
+ 'ServiceList.h',
+ 'Services.h',
+ 'XPCOM.h',
+]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ EXPORTS += ['nsWindowsDllInterceptor.h']
+ EXPORTS.mozilla += ['perfprobe.h']
+ SOURCES += [
+ 'perfprobe.cpp',
+ 'PoisonIOInterposerBase.cpp',
+ 'PoisonIOInterposerWin.cpp',
+ ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ UNIFIED_SOURCES += [
+ 'PoisonIOInterposerBase.cpp',
+ 'PoisonIOInterposerMac.cpp',
+ ]
+ SOURCES += ['mach_override.c']
+ SOURCES['mach_override.c'].flags += ['-Wno-unused-function']
+else:
+ SOURCES += ['PoisonIOInterposerStub.cpp']
+
+include('../glue/objs.mozbuild')
+
+UNIFIED_SOURCES += xpcom_gluens_src_cppsrcs
+UNIFIED_SOURCES += xpcom_glue_src_cppsrcs
+
+UNIFIED_SOURCES += [
+ 'FrozenFunctions.cpp',
+ 'IOInterposer.cpp',
+ 'LateWriteChecks.cpp',
+ 'MainThreadIOLogger.cpp',
+ 'nsXPCOMStrings.cpp',
+ 'Services.cpp',
+ 'XPCOMInit.cpp',
+]
+
+if CONFIG['OS_ARCH'] != 'WINNT':
+ SOURCES += [
+ 'NSPRInterposer.cpp',
+ ]
+
+# FileLocation.cpp and Omnijar.cpp cannot be built in unified mode because they
+# use plarena.h.
+SOURCES += [
+ 'FileLocation.cpp',
+ 'Omnijar.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+DEFINES['_IMPL_NS_STRINGAPI'] = True
+DEFINES['OMNIJAR_NAME'] = CONFIG['OMNIJAR_NAME']
+if CONFIG['MOZ_ICU_DATA_ARCHIVE']:
+ DEFINES['MOZ_ICU_DATA_ARCHIVE'] = True
+
+LOCAL_INCLUDES += [
+ '!..',
+ '../base',
+ '../components',
+ '../ds',
+ '../glue',
+ '../io',
+ '../reflect/xptinfo',
+ '../threads',
+ '/chrome',
+ '/docshell/base',
+]
+
+if CONFIG['MOZ_VPX']:
+ LOCAL_INCLUDES += [
+ '/media/libvpx',
+ ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ CXXFLAGS += CONFIG['TK_CFLAGS']
diff --git a/xpcom/build/nsWindowsDllInterceptor.h b/xpcom/build/nsWindowsDllInterceptor.h
new file mode 100644
index 000000000..f7b7c93fb
--- /dev/null
+++ b/xpcom/build/nsWindowsDllInterceptor.h
@@ -0,0 +1,1127 @@
+/* -*- 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 NS_WINDOWS_DLL_INTERCEPTOR_H_
+#define NS_WINDOWS_DLL_INTERCEPTOR_H_
+
+#include "mozilla/Assertions.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/UniquePtr.h"
+#include "nsWindowsHelpers.h"
+
+#include <wchar.h>
+#include <windows.h>
+#include <winternl.h>
+
+/*
+ * Simple function interception.
+ *
+ * We have two separate mechanisms for intercepting a function: We can use the
+ * built-in nop space, if it exists, or we can create a detour.
+ *
+ * Using the built-in nop space works as follows: On x86-32, DLL functions
+ * begin with a two-byte nop (mov edi, edi) and are preceeded by five bytes of
+ * NOP instructions.
+ *
+ * When we detect a function with this prelude, we do the following:
+ *
+ * 1. Write a long jump to our interceptor function into the five bytes of NOPs
+ * before the function.
+ *
+ * 2. Write a short jump -5 into the two-byte nop at the beginning of the function.
+ *
+ * This mechanism is nice because it's thread-safe. It's even safe to do if
+ * another thread is currently running the function we're modifying!
+ *
+ * When the WindowsDllNopSpacePatcher is destroyed, we overwrite the short jump
+ * but not the long jump, so re-intercepting the same function won't work,
+ * because its prelude won't match.
+ *
+ *
+ * Unfortunately nop space patching doesn't work on functions which don't have
+ * this magic prelude (and in particular, x86-64 never has the prelude). So
+ * when we can't use the built-in nop space, we fall back to using a detour,
+ * which works as follows:
+ *
+ * 1. Save first N bytes of OrigFunction to trampoline, where N is a
+ * number of bytes >= 5 that are instruction aligned.
+ *
+ * 2. Replace first 5 bytes of OrigFunction with a jump to the Hook
+ * function.
+ *
+ * 3. After N bytes of the trampoline, add a jump to OrigFunction+N to
+ * continue original program flow.
+ *
+ * 4. Hook function needs to call the trampoline during its execution,
+ * to invoke the original function (so address of trampoline is
+ * returned).
+ *
+ * When the WindowsDllDetourPatcher object is destructed, OrigFunction is
+ * patched again to jump directly to the trampoline instead of going through
+ * the hook function. As such, re-intercepting the same function won't work, as
+ * jump instructions are not supported.
+ *
+ * Note that this is not thread-safe. Sad day.
+ *
+ */
+
+#include <stdint.h>
+
+namespace mozilla {
+namespace internal {
+
+class AutoVirtualProtect
+{
+public:
+ AutoVirtualProtect(void* aFunc, size_t aSize, DWORD aProtect)
+ : mFunc(aFunc), mSize(aSize), mNewProtect(aProtect), mOldProtect(0),
+ mSuccess(false)
+ {}
+
+ ~AutoVirtualProtect()
+ {
+ if (mSuccess) {
+ VirtualProtectEx(GetCurrentProcess(), mFunc, mSize, mOldProtect,
+ &mOldProtect);
+ }
+ }
+
+ bool Protect()
+ {
+ mSuccess = !!VirtualProtectEx(GetCurrentProcess(), mFunc, mSize,
+ mNewProtect, &mOldProtect);
+ if (!mSuccess) {
+ // printf("VirtualProtectEx failed! %d\n", GetLastError());
+ }
+ return mSuccess;
+ }
+
+private:
+ void* const mFunc;
+ size_t const mSize;
+ DWORD const mNewProtect;
+ DWORD mOldProtect;
+ bool mSuccess;
+};
+
+class WindowsDllNopSpacePatcher
+{
+ typedef uint8_t* byteptr_t;
+ HMODULE mModule;
+
+ // Dumb array for remembering the addresses of functions we've patched.
+ // (This should be nsTArray, but non-XPCOM code uses this class.)
+ static const size_t maxPatchedFns = 128;
+ byteptr_t mPatchedFns[maxPatchedFns];
+ int mPatchedFnsLen;
+
+public:
+ WindowsDllNopSpacePatcher()
+ : mModule(0)
+ , mPatchedFnsLen(0)
+ {}
+
+#if defined(_M_IX86)
+ ~WindowsDllNopSpacePatcher()
+ {
+ // Restore the mov edi, edi to the beginning of each function we patched.
+
+ for (int i = 0; i < mPatchedFnsLen; i++) {
+ byteptr_t fn = mPatchedFns[i];
+
+ // Ensure we can write to the code.
+ AutoVirtualProtect protect(fn, 2, PAGE_EXECUTE_READWRITE);
+ if (!protect.Protect()) {
+ continue;
+ }
+
+ // mov edi, edi
+ *((uint16_t*)fn) = 0xff8b;
+
+ // I don't think this is actually necessary, but it can't hurt.
+ FlushInstructionCache(GetCurrentProcess(),
+ /* ignored */ nullptr,
+ /* ignored */ 0);
+ }
+ }
+
+ void Init(const char* aModuleName)
+ {
+ if (!IsCompatible()) {
+#if defined(MOZILLA_INTERNAL_API)
+ NS_WARNING("NOP space patching is unavailable for compatibility reasons");
+#endif
+ return;
+ }
+
+ mModule = LoadLibraryExA(aModuleName, nullptr, 0);
+ if (!mModule) {
+ //printf("LoadLibraryEx for '%s' failed\n", aModuleName);
+ return;
+ }
+ }
+
+ /**
+ * NVIDIA Optimus drivers utilize Microsoft Detours 2.x to patch functions
+ * in our address space. There is a bug in Detours 2.x that causes it to
+ * patch at the wrong address when attempting to detour code that is already
+ * NOP space patched. This function is an effort to detect the presence of
+ * this NVIDIA code in our address space and disable NOP space patching if it
+ * is. We also check AppInit_DLLs since this is the mechanism that the Optimus
+ * drivers use to inject into our process.
+ */
+ static bool IsCompatible()
+ {
+ // These DLLs are known to have bad interactions with this style of patching
+ const wchar_t* kIncompatibleDLLs[] = {
+ L"detoured.dll",
+ L"_etoured.dll",
+ L"nvd3d9wrap.dll",
+ L"nvdxgiwrap.dll"
+ };
+ // See if the infringing DLLs are already loaded
+ for (unsigned int i = 0; i < mozilla::ArrayLength(kIncompatibleDLLs); ++i) {
+ if (GetModuleHandleW(kIncompatibleDLLs[i])) {
+ return false;
+ }
+ }
+ if (GetModuleHandleW(L"user32.dll")) {
+ // user32 is loaded but the infringing DLLs are not, assume we're safe to
+ // proceed.
+ return true;
+ }
+ // If user32 has not loaded yet, check AppInit_DLLs to ensure that Optimus
+ // won't be loaded once user32 is initialized.
+ HKEY hkey = NULL;
+ if (!RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+ L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows",
+ 0, KEY_QUERY_VALUE, &hkey)) {
+ nsAutoRegKey key(hkey);
+ DWORD numBytes = 0;
+ const wchar_t kAppInitDLLs[] = L"AppInit_DLLs";
+ // Query for required buffer size
+ LONG status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr,
+ nullptr, nullptr, &numBytes);
+ mozilla::UniquePtr<wchar_t[]> data;
+ if (!status) {
+ // Allocate the buffer and query for the actual data
+ data = mozilla::MakeUnique<wchar_t[]>(numBytes / sizeof(wchar_t));
+ status = RegQueryValueExW(hkey, kAppInitDLLs, nullptr,
+ nullptr, (LPBYTE)data.get(), &numBytes);
+ }
+ if (!status) {
+ // For each token, split up the filename components and then check the
+ // name of the file.
+ const wchar_t kDelimiters[] = L", ";
+ wchar_t* tokenContext = nullptr;
+ wchar_t* token = wcstok_s(data.get(), kDelimiters, &tokenContext);
+ while (token) {
+ wchar_t fname[_MAX_FNAME] = {0};
+ if (!_wsplitpath_s(token, nullptr, 0, nullptr, 0,
+ fname, mozilla::ArrayLength(fname),
+ nullptr, 0)) {
+ // nvinit.dll is responsible for bootstrapping the DLL injection, so
+ // that is the library that we check for here
+ const wchar_t kNvInitName[] = L"nvinit";
+ if (!_wcsnicmp(fname, kNvInitName,
+ mozilla::ArrayLength(kNvInitName))) {
+ return false;
+ }
+ }
+ token = wcstok_s(nullptr, kDelimiters, &tokenContext);
+ }
+ }
+ }
+ return true;
+ }
+
+ bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
+ {
+ if (!mModule) {
+ return false;
+ }
+
+ if (!IsCompatible()) {
+#if defined(MOZILLA_INTERNAL_API)
+ NS_WARNING("NOP space patching is unavailable for compatibility reasons");
+#endif
+ return false;
+ }
+
+ if (mPatchedFnsLen == maxPatchedFns) {
+ // printf ("No space for hook in mPatchedFns.\n");
+ return false;
+ }
+
+ byteptr_t fn = reinterpret_cast<byteptr_t>(GetProcAddress(mModule, aName));
+ if (!fn) {
+ //printf ("GetProcAddress failed\n");
+ return false;
+ }
+
+ fn = ResolveRedirectedAddress(fn);
+
+ // Ensure we can read and write starting at fn - 5 (for the long jmp we're
+ // going to write) and ending at fn + 2 (for the short jmp up to the long
+ // jmp). These bytes may span two pages with different protection.
+ AutoVirtualProtect protectBefore(fn - 5, 5, PAGE_EXECUTE_READWRITE);
+ AutoVirtualProtect protectAfter(fn, 2, PAGE_EXECUTE_READWRITE);
+ if (!protectBefore.Protect() || !protectAfter.Protect()) {
+ return false;
+ }
+
+ bool rv = WriteHook(fn, aHookDest, aOrigFunc);
+
+ if (rv) {
+ mPatchedFns[mPatchedFnsLen] = fn;
+ mPatchedFnsLen++;
+ }
+
+ return rv;
+ }
+
+ bool WriteHook(byteptr_t aFn, intptr_t aHookDest, void** aOrigFunc)
+ {
+ // Check that the 5 bytes before aFn are NOP's or INT 3's,
+ // and that the 2 bytes after aFn are mov(edi, edi).
+ //
+ // It's safe to read aFn[-5] because we set it to PAGE_EXECUTE_READWRITE
+ // before calling WriteHook.
+
+ for (int i = -5; i <= -1; i++) {
+ if (aFn[i] != 0x90 && aFn[i] != 0xcc) { // nop or int 3
+ return false;
+ }
+ }
+
+ // mov edi, edi. Yes, there are two ways to encode the same thing:
+ //
+ // 0x89ff == mov r/m, r
+ // 0x8bff == mov r, r/m
+ //
+ // where "r" is register and "r/m" is register or memory. Windows seems to
+ // use 8bff; I include 89ff out of paranoia.
+ if ((aFn[0] != 0x8b && aFn[0] != 0x89) || aFn[1] != 0xff) {
+ return false;
+ }
+
+ // Write a long jump into the space above the function.
+ aFn[-5] = 0xe9; // jmp
+ *((intptr_t*)(aFn - 4)) = aHookDest - (uintptr_t)(aFn); // target displacement
+
+ // Set aOrigFunc here, because after this point, aHookDest might be called,
+ // and aHookDest might use the aOrigFunc pointer.
+ *aOrigFunc = aFn + 2;
+
+ // Short jump up into our long jump.
+ *((uint16_t*)(aFn)) = 0xf9eb; // jmp $-5
+
+ // I think this routine is safe without this, but it can't hurt.
+ FlushInstructionCache(GetCurrentProcess(),
+ /* ignored */ nullptr,
+ /* ignored */ 0);
+
+ return true;
+ }
+
+private:
+ static byteptr_t ResolveRedirectedAddress(const byteptr_t aOriginalFunction)
+ {
+ // If function entry is jmp [disp32] such as used by kernel32,
+ // we resolve redirected address from import table.
+ if (aOriginalFunction[0] == 0xff && aOriginalFunction[1] == 0x25) {
+ return (byteptr_t)(**((uint32_t**) (aOriginalFunction + 2)));
+ }
+
+ return aOriginalFunction;
+ }
+#else
+ void Init(const char* aModuleName)
+ {
+ // Not implemented except on x86-32.
+ }
+
+ bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
+ {
+ // Not implemented except on x86-32.
+ return false;
+ }
+#endif
+};
+
+class WindowsDllDetourPatcher
+{
+ typedef unsigned char* byteptr_t;
+public:
+ WindowsDllDetourPatcher()
+ : mModule(0), mHookPage(0), mMaxHooks(0), mCurHooks(0)
+ {
+ }
+
+ ~WindowsDllDetourPatcher()
+ {
+ int i;
+ byteptr_t p;
+ for (i = 0, p = mHookPage; i < mCurHooks; i++, p += kHookSize) {
+#if defined(_M_IX86)
+ size_t nBytes = 1 + sizeof(intptr_t);
+#elif defined(_M_X64)
+ size_t nBytes = 2 + sizeof(intptr_t);
+#else
+#error "Unknown processor type"
+#endif
+ byteptr_t origBytes = (byteptr_t)DecodePointer(*((byteptr_t*)p));
+
+ // ensure we can modify the original code
+ AutoVirtualProtect protect(origBytes, nBytes, PAGE_EXECUTE_READWRITE);
+ if (!protect.Protect()) {
+ continue;
+ }
+
+ // Remove the hook by making the original function jump directly
+ // in the trampoline.
+ intptr_t dest = (intptr_t)(p + sizeof(void*));
+#if defined(_M_IX86)
+ // Ensure the JMP from CreateTrampoline is where we expect it to be.
+ if (origBytes[0] != 0xE9)
+ continue;
+ *((intptr_t*)(origBytes + 1)) =
+ dest - (intptr_t)(origBytes + 5); // target displacement
+#elif defined(_M_X64)
+ // Ensure the MOV R11 from CreateTrampoline is where we expect it to be.
+ if (origBytes[0] != 0x49 || origBytes[1] != 0xBB)
+ continue;
+ *((intptr_t*)(origBytes + 2)) = dest;
+#else
+#error "Unknown processor type"
+#endif
+ }
+ }
+
+ void Init(const char* aModuleName, int aNumHooks = 0)
+ {
+ if (mModule) {
+ return;
+ }
+
+ mModule = LoadLibraryExA(aModuleName, nullptr, 0);
+ if (!mModule) {
+ //printf("LoadLibraryEx for '%s' failed\n", aModuleName);
+ return;
+ }
+
+ int hooksPerPage = 4096 / kHookSize;
+ if (aNumHooks == 0) {
+ aNumHooks = hooksPerPage;
+ }
+
+ mMaxHooks = aNumHooks + (hooksPerPage % aNumHooks);
+
+ mHookPage = (byteptr_t)VirtualAllocEx(GetCurrentProcess(), nullptr,
+ mMaxHooks * kHookSize,
+ MEM_COMMIT | MEM_RESERVE,
+ PAGE_EXECUTE_READ);
+ if (!mHookPage) {
+ mModule = 0;
+ return;
+ }
+ }
+
+ bool Initialized() { return !!mModule; }
+
+ bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
+ {
+ if (!mModule) {
+ return false;
+ }
+
+ void* pAddr = (void*)GetProcAddress(mModule, aName);
+ if (!pAddr) {
+ //printf ("GetProcAddress failed\n");
+ return false;
+ }
+
+ pAddr = ResolveRedirectedAddress((byteptr_t)pAddr);
+
+ CreateTrampoline(pAddr, aHookDest, aOrigFunc);
+ if (!*aOrigFunc) {
+ //printf ("CreateTrampoline failed\n");
+ return false;
+ }
+
+ return true;
+ }
+
+protected:
+ const static int kPageSize = 4096;
+ const static int kHookSize = 128;
+
+ HMODULE mModule;
+ byteptr_t mHookPage;
+ int mMaxHooks;
+ int mCurHooks;
+
+ // rex bits
+ static const BYTE kMaskHighNibble = 0xF0;
+ static const BYTE kRexOpcode = 0x40;
+ static const BYTE kMaskRexW = 0x08;
+ static const BYTE kMaskRexR = 0x04;
+ static const BYTE kMaskRexX = 0x02;
+ static const BYTE kMaskRexB = 0x01;
+
+ // mod r/m bits
+ static const BYTE kRegFieldShift = 3;
+ static const BYTE kMaskMod = 0xC0;
+ static const BYTE kMaskReg = 0x38;
+ static const BYTE kMaskRm = 0x07;
+ static const BYTE kRmNeedSib = 0x04;
+ static const BYTE kModReg = 0xC0;
+ static const BYTE kModDisp32 = 0x80;
+ static const BYTE kModDisp8 = 0x40;
+ static const BYTE kModNoRegDisp = 0x00;
+ static const BYTE kRmNoRegDispDisp32 = 0x05;
+
+ // sib bits
+ static const BYTE kMaskSibScale = 0xC0;
+ static const BYTE kMaskSibIndex = 0x38;
+ static const BYTE kMaskSibBase = 0x07;
+ static const BYTE kSibBaseEbp = 0x05;
+
+ int CountModRmSib(const BYTE *aModRm, BYTE* aSubOpcode = nullptr)
+ {
+ if (!aModRm) {
+ return -1;
+ }
+ int numBytes = 1; // Start with 1 for mod r/m byte itself
+ switch (*aModRm & kMaskMod) {
+ case kModReg:
+ return numBytes;
+ case kModDisp8:
+ numBytes += 1;
+ break;
+ case kModDisp32:
+ numBytes += 4;
+ break;
+ case kModNoRegDisp:
+ if ((*aModRm & kMaskRm) == kRmNoRegDispDisp32) {
+#if defined(_M_X64)
+ // RIP-relative on AMD64, currently unsupported
+ return -1;
+#else
+ // On IA-32, all ModR/M instruction modes address memory relative to 0
+ numBytes += 4;
+#endif
+ } else if (((*aModRm & kMaskRm) == kRmNeedSib &&
+ (*(aModRm + 1) & kMaskSibBase) == kSibBaseEbp)) {
+ numBytes += 4;
+ }
+ break;
+ default:
+ // This should not be reachable
+ MOZ_ASSERT_UNREACHABLE("Impossible value for modr/m byte mod bits");
+ return -1;
+ }
+ if ((*aModRm & kMaskRm) == kRmNeedSib) {
+ // SIB byte
+ numBytes += 1;
+ }
+ if (aSubOpcode) {
+ *aSubOpcode = (*aModRm & kMaskReg) >> kRegFieldShift;
+ }
+ return numBytes;
+ }
+
+#if defined(_M_X64)
+ // To patch for JMP and JE
+
+ enum JumpType {
+ Je,
+ Jmp
+ };
+
+ struct JumpPatch {
+ JumpPatch()
+ : mHookOffset(0), mJumpAddress(0), mType(JumpType::Jmp)
+ {
+ }
+
+ JumpPatch(size_t aOffset, intptr_t aAddress, JumpType aType = JumpType::Jmp)
+ : mHookOffset(aOffset), mJumpAddress(aAddress), mType(aType)
+ {
+ }
+
+ void AddJumpPatch(size_t aHookOffset, intptr_t aAbsJumpAddress,
+ JumpType aType = JumpType::Jmp)
+ {
+ mHookOffset = aHookOffset;
+ mJumpAddress = aAbsJumpAddress;
+ mType = aType;
+ }
+
+ size_t GenerateJump(uint8_t* aCode)
+ {
+ size_t offset = mHookOffset;
+ if (mType == JumpType::Je) {
+ // JNE RIP+14
+ aCode[offset] = 0x75;
+ aCode[offset + 1] = 14;
+ offset += 2;
+ }
+
+ // JMP [RIP+0]
+ aCode[offset] = 0xff;
+ aCode[offset + 1] = 0x25;
+ *reinterpret_cast<int32_t*>(aCode + offset + 2) = 0;
+
+ // Jump table
+ *reinterpret_cast<int64_t*>(aCode + offset + 2 + 4) = mJumpAddress;
+
+ return offset + 2 + 4 + 8;
+ }
+
+ bool HasJumpPatch() const
+ {
+ return !!mJumpAddress;
+ }
+
+ size_t mHookOffset;
+ intptr_t mJumpAddress;
+ JumpType mType;
+ };
+
+#endif
+
+ enum ePrefixGroupBits
+ {
+ eNoPrefixes = 0,
+ ePrefixGroup1 = (1 << 0),
+ ePrefixGroup2 = (1 << 1),
+ ePrefixGroup3 = (1 << 2),
+ ePrefixGroup4 = (1 << 3)
+ };
+
+ int CountPrefixBytes(byteptr_t aBytes, const int aBytesIndex,
+ unsigned char* aOutGroupBits)
+ {
+ unsigned char& groupBits = *aOutGroupBits;
+ groupBits = eNoPrefixes;
+ int index = aBytesIndex;
+ while (true) {
+ switch (aBytes[index]) {
+ // Group 1
+ case 0xF0: // LOCK
+ case 0xF2: // REPNZ
+ case 0xF3: // REP / REPZ
+ if (groupBits & ePrefixGroup1) {
+ return -1;
+ }
+ groupBits |= ePrefixGroup1;
+ ++index;
+ break;
+
+ // Group 2
+ case 0x2E: // CS override / branch not taken
+ case 0x36: // SS override
+ case 0x3E: // DS override / branch taken
+ case 0x64: // FS override
+ case 0x65: // GS override
+ if (groupBits & ePrefixGroup2) {
+ return -1;
+ }
+ groupBits |= ePrefixGroup2;
+ ++index;
+ break;
+
+ // Group 3
+ case 0x66: // operand size override
+ if (groupBits & ePrefixGroup3) {
+ return -1;
+ }
+ groupBits |= ePrefixGroup3;
+ ++index;
+ break;
+
+ // Group 4
+ case 0x67: // Address size override
+ if (groupBits & ePrefixGroup4) {
+ return -1;
+ }
+ groupBits |= ePrefixGroup4;
+ ++index;
+ break;
+
+ default:
+ return index - aBytesIndex;
+ }
+ }
+ }
+
+ void CreateTrampoline(void* aOrigFunction, intptr_t aDest, void** aOutTramp)
+ {
+ *aOutTramp = nullptr;
+
+ AutoVirtualProtect protectHookPage(mHookPage, mMaxHooks * kHookSize,
+ PAGE_EXECUTE_READWRITE);
+ if (!protectHookPage.Protect()) {
+ return;
+ }
+
+ byteptr_t tramp = FindTrampolineSpace();
+ if (!tramp) {
+ return;
+ }
+
+ byteptr_t origBytes = (byteptr_t)aOrigFunction;
+
+ int nBytes = 0;
+
+#if defined(_M_IX86)
+ int pJmp32 = -1;
+ while (nBytes < 5) {
+ // Understand some simple instructions that might be found in a
+ // prologue; we might need to extend this as necessary.
+ //
+ // Note! If we ever need to understand jump instructions, we'll
+ // need to rewrite the displacement argument.
+ unsigned char prefixGroups;
+ int numPrefixBytes = CountPrefixBytes(origBytes, nBytes, &prefixGroups);
+ if (numPrefixBytes < 0 || (prefixGroups & (ePrefixGroup3 | ePrefixGroup4))) {
+ // Either the prefix sequence was bad, or there are prefixes that
+ // we don't currently support (groups 3 and 4)
+ return;
+ }
+ nBytes += numPrefixBytes;
+ if (origBytes[nBytes] >= 0x88 && origBytes[nBytes] <= 0x8B) {
+ // various MOVs
+ ++nBytes;
+ int len = CountModRmSib(origBytes + nBytes);
+ if (len < 0) {
+ return;
+ }
+ nBytes += len;
+ } else if (origBytes[nBytes] == 0xA1) {
+ // MOV eax, [seg:offset]
+ nBytes += 5;
+ } else if (origBytes[nBytes] == 0xB8) {
+ // MOV 0xB8: http://ref.x86asm.net/coder32.html#xB8
+ nBytes += 5;
+ } else if (origBytes[nBytes] == 0x83) {
+ // ADD|ODR|ADC|SBB|AND|SUB|XOR|CMP r/m, imm8
+ unsigned char b = origBytes[nBytes + 1];
+ if ((b & 0xc0) == 0xc0) {
+ // ADD|ODR|ADC|SBB|AND|SUB|XOR|CMP r, imm8
+ nBytes += 3;
+ } else {
+ // bail
+ return;
+ }
+ } else if (origBytes[nBytes] == 0x68) {
+ // PUSH with 4-byte operand
+ nBytes += 5;
+ } else if ((origBytes[nBytes] & 0xf0) == 0x50) {
+ // 1-byte PUSH/POP
+ nBytes++;
+ } else if (origBytes[nBytes] == 0x6A) {
+ // PUSH imm8
+ nBytes += 2;
+ } else if (origBytes[nBytes] == 0xe9) {
+ pJmp32 = nBytes;
+ // jmp 32bit offset
+ nBytes += 5;
+ } else if (origBytes[nBytes] == 0xff && origBytes[nBytes + 1] == 0x25) {
+ // jmp [disp32]
+ nBytes += 6;
+ } else {
+ //printf ("Unknown x86 instruction byte 0x%02x, aborting trampoline\n", origBytes[nBytes]);
+ return;
+ }
+ }
+#elif defined(_M_X64)
+ JumpPatch jump;
+
+ while (nBytes < 13) {
+
+ // if found JMP 32bit offset, next bytes must be NOP or INT3
+ if (jump.HasJumpPatch()) {
+ if (origBytes[nBytes] == 0x90 || origBytes[nBytes] == 0xcc) {
+ nBytes++;
+ continue;
+ }
+ return;
+ }
+ if (origBytes[nBytes] == 0x0f) {
+ nBytes++;
+ if (origBytes[nBytes] == 0x1f) {
+ // nop (multibyte)
+ nBytes++;
+ if ((origBytes[nBytes] & 0xc0) == 0x40 &&
+ (origBytes[nBytes] & 0x7) == 0x04) {
+ nBytes += 3;
+ } else {
+ return;
+ }
+ } else if (origBytes[nBytes] == 0x05) {
+ // syscall
+ nBytes++;
+ } else if (origBytes[nBytes] == 0x84) {
+ // je rel32
+ jump.AddJumpPatch(nBytes - 1,
+ (intptr_t)
+ origBytes + nBytes + 5 +
+ *(reinterpret_cast<int32_t*>(origBytes +
+ nBytes + 1)),
+ JumpType::Je);
+ nBytes += 5;
+ } else {
+ return;
+ }
+ } else if (origBytes[nBytes] == 0x40 ||
+ origBytes[nBytes] == 0x41) {
+ // Plain REX or REX.B
+ nBytes++;
+
+ if ((origBytes[nBytes] & 0xf0) == 0x50) {
+ // push/pop with Rx register
+ nBytes++;
+ } else if (origBytes[nBytes] >= 0xb8 && origBytes[nBytes] <= 0xbf) {
+ // mov r32, imm32
+ nBytes += 5;
+ } else {
+ return;
+ }
+ } else if (origBytes[nBytes] == 0x45) {
+ // REX.R & REX.B
+ nBytes++;
+
+ if (origBytes[nBytes] == 0x33) {
+ // xor r32, r32
+ nBytes += 2;
+ } else {
+ return;
+ }
+ } else if ((origBytes[nBytes] & 0xfb) == 0x48) {
+ // REX.W | REX.WR
+ nBytes++;
+
+ if (origBytes[nBytes] == 0x81 &&
+ (origBytes[nBytes + 1] & 0xf8) == 0xe8) {
+ // sub r, dword
+ nBytes += 6;
+ } else if (origBytes[nBytes] == 0x83 &&
+ (origBytes[nBytes + 1] & 0xf8) == 0xe8) {
+ // sub r, byte
+ nBytes += 3;
+ } else if (origBytes[nBytes] == 0x83 &&
+ (origBytes[nBytes + 1] & 0xf8) == 0x60) {
+ // and [r+d], imm8
+ nBytes += 5;
+ } else if (origBytes[nBytes] == 0x85) {
+ // 85 /r => TEST r/m32, r32
+ if ((origBytes[nBytes + 1] & 0xc0) == 0xc0) {
+ nBytes += 2;
+ } else {
+ return;
+ }
+ } else if ((origBytes[nBytes] & 0xfd) == 0x89) {
+ ++nBytes;
+ // MOV r/m64, r64 | MOV r64, r/m64
+ int len = CountModRmSib(origBytes + nBytes);
+ if (len < 0) {
+ return;
+ }
+ nBytes += len;
+ } else if (origBytes[nBytes] == 0xc7) {
+ // MOV r/m64, imm32
+ if (origBytes[nBytes + 1] == 0x44) {
+ // MOV [r64+disp8], imm32
+ // ModR/W + SIB + disp8 + imm32
+ nBytes += 8;
+ } else {
+ return;
+ }
+ } else if (origBytes[nBytes] == 0xff) {
+ // JMP /4
+ if ((origBytes[nBytes + 1] & 0xc0) == 0x0 &&
+ (origBytes[nBytes + 1] & 0x07) == 0x5) {
+ // [rip+disp32]
+ // convert JMP 32bit offset to JMP 64bit direct
+ jump.AddJumpPatch(nBytes - 1,
+ *reinterpret_cast<intptr_t*>(
+ origBytes + nBytes + 6 +
+ *reinterpret_cast<int32_t*>(origBytes + nBytes +
+ 2)));
+ nBytes += 6;
+ } else {
+ // not support yet!
+ return;
+ }
+ } else {
+ // not support yet!
+ return;
+ }
+ } else if (origBytes[nBytes] == 0x66) {
+ // operand override prefix
+ nBytes += 1;
+ // This is the same as the x86 version
+ if (origBytes[nBytes] >= 0x88 && origBytes[nBytes] <= 0x8B) {
+ // various MOVs
+ unsigned char b = origBytes[nBytes + 1];
+ if (((b & 0xc0) == 0xc0) ||
+ (((b & 0xc0) == 0x00) &&
+ ((b & 0x07) != 0x04) && ((b & 0x07) != 0x05))) {
+ // REG=r, R/M=r or REG=r, R/M=[r]
+ nBytes += 2;
+ } else if ((b & 0xc0) == 0x40) {
+ if ((b & 0x07) == 0x04) {
+ // REG=r, R/M=[SIB + disp8]
+ nBytes += 4;
+ } else {
+ // REG=r, R/M=[r + disp8]
+ nBytes += 3;
+ }
+ } else {
+ // complex MOV, bail
+ return;
+ }
+ }
+ } else if ((origBytes[nBytes] & 0xf0) == 0x50) {
+ // 1-byte push/pop
+ nBytes++;
+ } else if (origBytes[nBytes] == 0x65) {
+ // GS prefix
+ //
+ // The entry of GetKeyState on Windows 10 has the following code.
+ // 65 48 8b 04 25 30 00 00 00 mov rax,qword ptr gs:[30h]
+ // (GS prefix + REX + MOV (0x8b) ...)
+ if (origBytes[nBytes + 1] == 0x48 &&
+ (origBytes[nBytes + 2] >= 0x88 && origBytes[nBytes + 2] <= 0x8b)) {
+ nBytes += 3;
+ int len = CountModRmSib(origBytes + nBytes);
+ if (len < 0) {
+ // no way to support this yet.
+ return;
+ }
+ nBytes += len;
+ } else {
+ return;
+ }
+ } else if (origBytes[nBytes] == 0x90) {
+ // nop
+ nBytes++;
+ } else if (origBytes[nBytes] == 0xb8) {
+ // MOV 0xB8: http://ref.x86asm.net/coder32.html#xB8
+ nBytes += 5;
+ } else if (origBytes[nBytes] == 0x33) {
+ // xor r32, r/m32
+ nBytes += 2;
+ } else if (origBytes[nBytes] == 0xf6) {
+ // test r/m8, imm8 (used by ntdll on Windows 10 x64)
+ // (no flags are affected by near jmp since there is no task switch,
+ // so it is ok for a jmp to be written immediately after a test)
+ BYTE subOpcode = 0;
+ int nModRmSibBytes = CountModRmSib(&origBytes[nBytes + 1], &subOpcode);
+ if (nModRmSibBytes < 0 || subOpcode != 0) {
+ // Unsupported
+ return;
+ }
+ nBytes += 2 + nModRmSibBytes;
+ } else if (origBytes[nBytes] == 0xc3) {
+ // ret
+ nBytes++;
+ } else if (origBytes[nBytes] == 0xcc) {
+ // int 3
+ nBytes++;
+ } else if (origBytes[nBytes] == 0xe9) {
+ // jmp 32bit offset
+ jump.AddJumpPatch(nBytes,
+ // convert JMP 32bit offset to JMP 64bit direct
+ (intptr_t)
+ origBytes + nBytes + 5 +
+ *(reinterpret_cast<int32_t*>(origBytes + nBytes + 1)));
+ nBytes += 5;
+ } else if (origBytes[nBytes] == 0xff) {
+ nBytes++;
+ if ((origBytes[nBytes] & 0xf8) == 0xf0) {
+ // push r64
+ nBytes++;
+ } else {
+ return;
+ }
+ } else {
+ return;
+ }
+ }
+#else
+#error "Unknown processor type"
+#endif
+
+ if (nBytes > 100) {
+ //printf ("Too big!");
+ return;
+ }
+
+ // We keep the address of the original function in the first bytes of
+ // the trampoline buffer
+ *((void**)tramp) = EncodePointer(aOrigFunction);
+ tramp += sizeof(void*);
+
+ memcpy(tramp, aOrigFunction, nBytes);
+
+ // OrigFunction+N, the target of the trampoline
+ byteptr_t trampDest = origBytes + nBytes;
+
+#if defined(_M_IX86)
+ if (pJmp32 >= 0) {
+ // Jump directly to the original target of the jump instead of jumping to the
+ // original function.
+ // Adjust jump target displacement to jump location in the trampoline.
+ *((intptr_t*)(tramp + pJmp32 + 1)) += origBytes - tramp;
+ } else {
+ tramp[nBytes] = 0xE9; // jmp
+ *((intptr_t*)(tramp + nBytes + 1)) =
+ (intptr_t)trampDest - (intptr_t)(tramp + nBytes + 5); // target displacement
+ }
+#elif defined(_M_X64)
+ // If JMP/JE opcode found, we don't insert to trampoline jump
+ if (jump.HasJumpPatch()) {
+ size_t offset = jump.GenerateJump(tramp);
+ if (jump.mType != JumpType::Jmp) {
+ JumpPatch patch(offset, reinterpret_cast<intptr_t>(trampDest));
+ patch.GenerateJump(tramp);
+ }
+ } else {
+ JumpPatch patch(nBytes, reinterpret_cast<intptr_t>(trampDest));
+ patch.GenerateJump(tramp);
+ }
+#endif
+
+ // The trampoline is now valid.
+ *aOutTramp = tramp;
+
+ // ensure we can modify the original code
+ AutoVirtualProtect protect(aOrigFunction, nBytes, PAGE_EXECUTE_READWRITE);
+ if (!protect.Protect()) {
+ return;
+ }
+
+#if defined(_M_IX86)
+ // now modify the original bytes
+ origBytes[0] = 0xE9; // jmp
+ *((intptr_t*)(origBytes + 1)) =
+ aDest - (intptr_t)(origBytes + 5); // target displacement
+#elif defined(_M_X64)
+ // mov r11, address
+ origBytes[0] = 0x49;
+ origBytes[1] = 0xbb;
+
+ *((intptr_t*)(origBytes + 2)) = aDest;
+
+ // jmp r11
+ origBytes[10] = 0x41;
+ origBytes[11] = 0xff;
+ origBytes[12] = 0xe3;
+#endif
+ }
+
+ byteptr_t FindTrampolineSpace()
+ {
+ if (mCurHooks >= mMaxHooks) {
+ return 0;
+ }
+
+ byteptr_t p = mHookPage + mCurHooks * kHookSize;
+
+ mCurHooks++;
+
+ return p;
+ }
+
+ static void* ResolveRedirectedAddress(const byteptr_t aOriginalFunction)
+ {
+#if defined(_M_IX86)
+ // If function entry is jmp [disp32] such as used by kernel32,
+ // we resolve redirected address from import table.
+ if (aOriginalFunction[0] == 0xff && aOriginalFunction[1] == 0x25) {
+ return (void*)(**((uint32_t**) (aOriginalFunction + 2)));
+ }
+#elif defined(_M_X64)
+ if (aOriginalFunction[0] == 0xe9) {
+ // require for TestDllInterceptor with --disable-optimize
+ int32_t offset = *((int32_t*)(aOriginalFunction + 1));
+ return aOriginalFunction + 5 + offset;
+ }
+#endif
+
+ return aOriginalFunction;
+ }
+};
+
+} // namespace internal
+
+class WindowsDllInterceptor
+{
+ internal::WindowsDllNopSpacePatcher mNopSpacePatcher;
+ internal::WindowsDllDetourPatcher mDetourPatcher;
+
+ const char* mModuleName;
+ int mNHooks;
+
+public:
+ WindowsDllInterceptor()
+ : mModuleName(nullptr)
+ , mNHooks(0)
+ {}
+
+ void Init(const char* aModuleName, int aNumHooks = 0)
+ {
+ if (mModuleName) {
+ return;
+ }
+
+ mModuleName = aModuleName;
+ mNHooks = aNumHooks;
+ mNopSpacePatcher.Init(aModuleName);
+
+ // Lazily initialize mDetourPatcher, since it allocates memory and we might
+ // not need it.
+ }
+
+ bool AddHook(const char* aName, intptr_t aHookDest, void** aOrigFunc)
+ {
+ // Use a nop space patch if possible, otherwise fall back to a detour.
+ // This should be the preferred method for adding hooks.
+
+ if (!mModuleName) {
+ return false;
+ }
+
+ if (mNopSpacePatcher.AddHook(aName, aHookDest, aOrigFunc)) {
+ return true;
+ }
+
+ return AddDetour(aName, aHookDest, aOrigFunc);
+ }
+
+ bool AddDetour(const char* aName, intptr_t aHookDest, void** aOrigFunc)
+ {
+ // Generally, code should not call this method directly. Use AddHook unless
+ // there is a specific need to avoid nop space patches.
+
+ if (!mModuleName) {
+ return false;
+ }
+
+ if (!mDetourPatcher.Initialized()) {
+ mDetourPatcher.Init(mModuleName, mNHooks);
+ }
+
+ return mDetourPatcher.AddHook(aName, aHookDest, aOrigFunc);
+ }
+};
+
+} // namespace mozilla
+
+#endif /* NS_WINDOWS_DLL_INTERCEPTOR_H_ */
diff --git a/xpcom/build/nsXPCOM.h b/xpcom/build/nsXPCOM.h
new file mode 100644
index 000000000..925ca6901
--- /dev/null
+++ b/xpcom/build/nsXPCOM.h
@@ -0,0 +1,433 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsXPCOM_h__
+#define nsXPCOM_h__
+
+#include "nscore.h"
+#include "nsXPCOMCID.h"
+
+#ifdef __cplusplus
+#define DECL_CLASS(c) class c
+#define DECL_STRUCT(c) struct c
+#else
+#define DECL_CLASS(c) typedef struct c c
+#define DECL_STRUCT(c) typedef struct c c
+#endif
+
+DECL_CLASS(nsAString);
+DECL_CLASS(nsACString);
+
+DECL_CLASS(nsISupports);
+DECL_CLASS(nsIModule);
+DECL_CLASS(nsIComponentManager);
+DECL_CLASS(nsIComponentRegistrar);
+DECL_CLASS(nsIServiceManager);
+DECL_CLASS(nsIFile);
+DECL_CLASS(nsILocalFile);
+DECL_CLASS(nsIDirectoryServiceProvider);
+DECL_CLASS(nsIMemory);
+DECL_CLASS(nsIDebug2);
+
+#ifdef __cplusplus
+namespace mozilla {
+struct Module;
+} // namespace mozilla
+#endif
+
+/**
+ * Initialises XPCOM. You must call one of the NS_InitXPCOM methods
+ * before proceeding to use xpcom. The one exception is that you may
+ * call NS_NewLocalFile to create a nsIFile.
+ *
+ * @note Use <CODE>NS_NewLocalFile</CODE> or <CODE>NS_NewNativeLocalFile</CODE>
+ * to create the file object you supply as the bin directory path in this
+ * call. The function may be safely called before the rest of XPCOM or
+ * embedding has been initialised.
+ *
+ * @param aResult The service manager. You may pass null.
+ *
+ * @param aBinDirectory The directory containing the component
+ * registry and runtime libraries;
+ * or use <CODE>nullptr</CODE> to use the working
+ * directory.
+ *
+ * @param aAppFileLocationProvider The object to be used by Gecko that
+ * specifies to Gecko where to find profiles, the
+ * component registry preferences and so on; or use
+ * <CODE>nullptr</CODE> for the default behaviour.
+ *
+ * @see NS_NewLocalFile
+ * @see nsIFile
+ * @see nsIDirectoryServiceProvider
+ *
+ * @return NS_OK for success;
+ * NS_ERROR_NOT_INITIALIZED if static globals were not initialized,
+ * which can happen if XPCOM is reloaded, but did not completly
+ * shutdown. Other error codes indicate a failure during
+ * initialisation.
+ */
+XPCOM_API(nsresult)
+NS_InitXPCOM2(nsIServiceManager** aResult,
+ nsIFile* aBinDirectory,
+ nsIDirectoryServiceProvider* aAppFileLocationProvider);
+
+/**
+ * Initialize only minimal components of XPCOM. This ensures nsThreadManager,
+ * logging, and timers will work.
+ */
+XPCOM_API(nsresult)
+NS_InitMinimalXPCOM();
+
+/**
+ * Shutdown XPCOM. You must call this method after you are finished
+ * using xpcom.
+ *
+ * @param aServMgr The service manager which was returned by NS_InitXPCOM.
+ * This will release servMgr. You may pass null.
+ *
+ * @return NS_OK for success;
+ * other error codes indicate a failure during initialisation.
+ */
+XPCOM_API(nsresult) NS_ShutdownXPCOM(nsIServiceManager* aServMgr);
+
+
+/**
+ * Public Method to access to the service manager.
+ *
+ * @param aResult Interface pointer to the service manager
+ *
+ * @return NS_OK for success;
+ * other error codes indicate a failure during initialisation.
+ */
+XPCOM_API(nsresult) NS_GetServiceManager(nsIServiceManager** aResult);
+
+/**
+ * Public Method to access to the component manager.
+ *
+ * @param aResult Interface pointer to the service
+ *
+ * @return NS_OK for success;
+ * other error codes indicate a failure during initialisation.
+ */
+XPCOM_API(nsresult) NS_GetComponentManager(nsIComponentManager** aResult);
+
+
+/**
+ * Public Method to access to the component registration manager.
+ *
+ * @param aResult Interface pointer to the service
+ *
+ * @return NS_OK for success;
+ * other error codes indicate a failure during initialisation.
+ */
+XPCOM_API(nsresult) NS_GetComponentRegistrar(nsIComponentRegistrar** aResult);
+
+/**
+ * Public Method to access to the memory manager. See nsIMemory
+ *
+ * @param aResult Interface pointer to the memory manager
+ *
+ * @return NS_OK for success;
+ * other error codes indicate a failure during initialisation.
+ */
+XPCOM_API(nsresult) NS_GetMemoryManager(nsIMemory** aResult);
+
+/**
+ * Public Method to create an instance of a nsIFile. This function
+ * may be called prior to NS_InitXPCOM.
+ *
+ * @param aPath
+ * A string which specifies a full file path to a
+ * location. Relative paths will be treated as an
+ * error (NS_ERROR_FILE_UNRECOGNIZED_PATH).
+ * |NS_NewNativeLocalFile|'s path must be in the
+ * filesystem charset.
+ * @param aFollowLinks
+ * This attribute will determine if the nsLocalFile will auto
+ * resolve symbolic links. By default, this value will be false
+ * on all non unix systems. On unix, this attribute is effectively
+ * a noop.
+ * @param aResult Interface pointer to a new instance of an nsIFile
+ *
+ * @return NS_OK for success;
+ * other error codes indicate a failure.
+ */
+
+#ifdef __cplusplus
+
+XPCOM_API(nsresult) NS_NewLocalFile(const nsAString& aPath,
+ bool aFollowLinks,
+ nsIFile** aResult);
+
+XPCOM_API(nsresult) NS_NewNativeLocalFile(const nsACString& aPath,
+ bool aFollowLinks,
+ nsIFile** aResult);
+
+#endif
+
+/**
+ * Allocator functions for the standalone glue.
+ * Do not use outside the xpcom glue code.
+ * Use moz_xmalloc/moz_xrealloc/free, or new/delete instead.
+ */
+#ifdef XPCOM_GLUE
+/**
+ * Allocates a block of memory of a particular size. If the memory cannot
+ * be allocated (because of an out-of-memory condition), the process aborts.
+ *
+ * @param aSize The size of the block to allocate
+ * @result The block of memory
+ * @note This function is thread-safe.
+ */
+XPCOM_API(void*) NS_Alloc(size_t aSize);
+
+/**
+ * Reallocates a block of memory to a new size.
+ *
+ * @param aPtr The block of memory to reallocate. This block must originally
+ have been allocated by NS_Alloc or NS_Realloc
+ * @param aSize The new size. If 0, frees the block like NS_Free
+ * @result The reallocated block of memory
+ * @note This function is thread-safe.
+ *
+ * If aPtr is null, this function behaves like NS_Alloc.
+ * If s is the size of the block to which aPtr points, the first min(s, size)
+ * bytes of aPtr's block are copied to the new block. If the allocation
+ * succeeds, aPtr is freed and a pointer to the new block is returned. If the
+ * allocation fails, the process aborts.
+ */
+XPCOM_API(void*) NS_Realloc(void* aPtr, size_t aSize);
+
+/**
+ * Frees a block of memory. Null is a permissible value, in which case no
+ * action is taken.
+ *
+ * @param aPtr The block of memory to free. This block must originally have
+ * been allocated by NS_Alloc or NS_Realloc
+ * @note This function is thread-safe.
+ */
+XPCOM_API(void) NS_Free(void* aPtr);
+#else
+#define NS_Alloc moz_xmalloc
+#define NS_Realloc moz_xrealloc
+#define NS_Free free
+#endif
+
+/**
+ * Support for warnings, assertions, and debugging breaks.
+ */
+
+enum
+{
+ NS_DEBUG_WARNING = 0,
+ NS_DEBUG_ASSERTION = 1,
+ NS_DEBUG_BREAK = 2,
+ NS_DEBUG_ABORT = 3
+};
+
+/**
+ * Print a runtime assertion. This function is available in both debug and
+ * release builds.
+ *
+ * @note Based on the value of aSeverity and the XPCOM_DEBUG_BREAK
+ * environment variable, this function may cause the application to
+ * print the warning, print a stacktrace, break into a debugger, or abort
+ * immediately.
+ *
+ * @param aSeverity A NS_DEBUG_* value
+ * @param aStr A readable error message (ASCII, may be null)
+ * @param aExpr The expression evaluated (may be null)
+ * @param aFile The source file containing the assertion (may be null)
+ * @param aLine The source file line number (-1 indicates no line number)
+ */
+XPCOM_API(void) NS_DebugBreak(uint32_t aSeverity,
+ const char* aStr, const char* aExpr,
+ const char* aFile, int32_t aLine);
+
+/**
+ * Perform a stack-walk to a debugging log under various
+ * circumstances. Used to aid debugging of leaked object graphs.
+ *
+ * The NS_Log* functions are available in both debug and release
+ * builds of XPCOM, but the output will be useless unless binary
+ * debugging symbols for all modules in the stacktrace are available.
+ */
+
+/**
+ * By default, refcount logging is enabled at NS_InitXPCOM and
+ * refcount statistics are printed at NS_ShutdownXPCOM. NS_LogInit and
+ * NS_LogTerm allow applications to enable logging earlier and delay
+ * printing of logging statistics. They should always be used as a
+ * matched pair.
+ */
+XPCOM_API(void) NS_LogInit();
+
+XPCOM_API(void) NS_LogTerm();
+
+#ifdef __cplusplus
+/**
+ * A helper class that calls NS_LogInit in its constructor and
+ * NS_LogTerm in its destructor.
+ */
+
+class ScopedLogging
+{
+public:
+ ScopedLogging()
+ {
+ NS_LogInit();
+ }
+
+ ~ScopedLogging()
+ {
+ NS_LogTerm();
+ }
+};
+#endif
+
+/**
+ * Log construction and destruction of objects. Processing tools can use the
+ * stacktraces printed by these functions to identify objects that are being
+ * leaked.
+ *
+ * @param aPtr A pointer to the concrete object.
+ * @param aTypeName The class name of the type
+ * @param aInstanceSize The size of the type
+ */
+
+XPCOM_API(void) NS_LogCtor(void* aPtr, const char* aTypeName,
+ uint32_t aInstanceSize);
+
+XPCOM_API(void) NS_LogDtor(void* aPtr, const char* aTypeName,
+ uint32_t aInstanceSize);
+
+/**
+ * Log a stacktrace when an XPCOM object's refcount is incremented or
+ * decremented. Processing tools can use the stacktraces printed by these
+ * functions to identify objects that were leaked due to XPCOM references.
+ *
+ * @param aPtr A pointer to the concrete object
+ * @param aNewRefCnt The new reference count.
+ * @param aTypeName The class name of the type
+ * @param aInstanceSize The size of the type
+ */
+XPCOM_API(void) NS_LogAddRef(void* aPtr, nsrefcnt aNewRefCnt,
+ const char* aTypeName, uint32_t aInstanceSize);
+
+XPCOM_API(void) NS_LogRelease(void* aPtr, nsrefcnt aNewRefCnt,
+ const char* aTypeName);
+
+/**
+ * Log reference counting performed by COMPtrs. Processing tools can
+ * use the stacktraces printed by these functions to simplify reports
+ * about leaked objects generated from the data printed by
+ * NS_LogAddRef/NS_LogRelease.
+ *
+ * @param aCOMPtr the address of the COMPtr holding a strong reference
+ * @param aObject the object being referenced by the COMPtr
+ */
+
+XPCOM_API(void) NS_LogCOMPtrAddRef(void* aCOMPtr, nsISupports* aObject);
+
+XPCOM_API(void) NS_LogCOMPtrRelease(void* aCOMPtr, nsISupports* aObject);
+
+/**
+ * The XPCOM cycle collector analyzes and breaks reference cycles between
+ * participating XPCOM objects. All objects in the cycle must implement
+ * nsCycleCollectionParticipant to break cycles correctly.
+ */
+
+#ifdef __cplusplus
+
+class nsCycleCollectionParticipant;
+class nsCycleCollectingAutoRefCnt;
+
+XPCOM_API(void) NS_CycleCollectorSuspect3(void* aPtr,
+ nsCycleCollectionParticipant* aCp,
+ nsCycleCollectingAutoRefCnt* aRefCnt,
+ bool* aShouldDelete);
+
+#endif
+
+/**
+ * Categories (in the category manager service) used by XPCOM:
+ */
+
+/**
+ * A category which is read after component registration but before
+ * the "xpcom-startup" notifications. Each category entry is treated
+ * as the contract ID of a service which implements
+ * nsIDirectoryServiceProvider. Each directory service provider is
+ * installed in the global directory service.
+ */
+#define XPCOM_DIRECTORY_PROVIDER_CATEGORY "xpcom-directory-providers"
+
+/**
+ * A category which is read after component registration but before
+ * NS_InitXPCOM returns. Each category entry is treated as the contractID of
+ * a service: each service is instantiated, and if it implements nsIObserver
+ * the nsIObserver.observe method is called with the "xpcom-startup" topic.
+ */
+#define NS_XPCOM_STARTUP_CATEGORY "xpcom-startup"
+
+
+/**
+ * Observer topics (in the observer service) used by XPCOM:
+ */
+
+/**
+ * At XPCOM startup after component registration is complete, the
+ * following topic is notified. In order to receive this notification,
+ * component must register their contract ID in the category manager,
+ *
+ * @see NS_XPCOM_STARTUP_CATEGORY
+ */
+#define NS_XPCOM_STARTUP_OBSERVER_ID "xpcom-startup"
+
+/**
+ * At XPCOM shutdown, this topic is notified just before "xpcom-shutdown".
+ * Components should only use this to mark themselves as 'being destroyed'.
+ * Nothing should be dispatched to any event loop.
+ */
+#define NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID "xpcom-will-shutdown"
+
+/**
+ * At XPCOM shutdown, this topic is notified. All components must
+ * release any interface references to objects in other modules when
+ * this topic is notified.
+ */
+#define NS_XPCOM_SHUTDOWN_OBSERVER_ID "xpcom-shutdown"
+
+/**
+ * This topic is notified when an entry was added to a category in the
+ * category manager. The subject of the notification will be the name of
+ * the added entry as an nsISupportsCString, and the data will be the
+ * name of the category. The notification will occur on the main thread.
+ */
+#define NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID \
+ "xpcom-category-entry-added"
+
+/**
+ * This topic is notified when an entry was removed from a category in the
+ * category manager. The subject of the notification will be the name of
+ * the removed entry as an nsISupportsCString, and the data will be the
+ * name of the category. The notification will occur on the main thread.
+ */
+#define NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID \
+ "xpcom-category-entry-removed"
+
+/**
+ * This topic is notified when an a category was cleared in the category
+ * manager. The subject of the notification will be the category manager,
+ * and the data will be the name of the cleared category.
+ * The notification will occur on the main thread.
+ */
+#define NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID "xpcom-category-cleared"
+
+XPCOM_API(nsresult) NS_GetDebug(nsIDebug2** aResult);
+
+#endif
diff --git a/xpcom/build/nsXPCOMCID.h b/xpcom/build/nsXPCOMCID.h
new file mode 100644
index 000000000..7219babe4
--- /dev/null
+++ b/xpcom/build/nsXPCOMCID.h
@@ -0,0 +1,185 @@
+/* -*- 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 nsXPCOMCID_h__
+#define nsXPCOMCID_h__
+
+/**
+ * XPCOM Directory Service Contract ID
+ * The directory service provides ways to obtain file system locations. The
+ * directory service is a singleton.
+ *
+ * This contract supports the nsIDirectoryService and the nsIProperties
+ * interfaces.
+ *
+ */
+#define NS_DIRECTORY_SERVICE_CONTRACTID "@mozilla.org/file/directory_service;1"
+
+/**
+ * XPCOM File
+ * The file abstraction provides ways to obtain and access files and
+ * directories located on the local system.
+ *
+ * This contract supports the nsIFile interface.
+ * This contract may also support platform specific interfaces such as
+ * nsILocalFileMac on platforms where additional interfaces are required.
+ *
+ */
+#define NS_LOCAL_FILE_CONTRACTID "@mozilla.org/file/local;1"
+
+/**
+ * XPCOM Category Manager Contract ID
+ * The contract supports the nsICategoryManager interface. The
+ * category manager is a singleton.
+ * The "enumerateCategory" method of nsICategoryManager will return an object
+ * that implements nsIUTF8StringEnumerator. In addition, the enumerator will
+ * return the entries in sorted order (sorted by byte comparison).
+ */
+#define NS_CATEGORYMANAGER_CONTRACTID "@mozilla.org/categorymanager;1"
+
+/**
+ * XPCOM Properties Object Contract ID
+ * Simple mapping object which supports the nsIProperties interface.
+ */
+#define NS_PROPERTIES_CONTRACTID "@mozilla.org/properties;1"
+
+/**
+ * XPCOM Array Object ContractID
+ * Simple array implementation which supports the nsIArray and
+ * nsIMutableArray interfaces.
+ */
+#define NS_ARRAY_CONTRACTID "@mozilla.org/array;1"
+
+/**
+ * Observer Service ContractID
+ * The observer service implements the global nsIObserverService object.
+ * It should be used from the main thread only.
+ */
+#define NS_OBSERVERSERVICE_CONTRACTID "@mozilla.org/observer-service;1"
+
+/**
+ * IO utilities service contract id.
+ * This guarantees implementation of nsIIOUtil. Usable from any thread.
+ */
+#define NS_IOUTIL_CONTRACTID "@mozilla.org/io-util;1"
+
+/**
+ * Memory reporter service CID
+ */
+#define NS_MEMORY_REPORTER_MANAGER_CONTRACTID "@mozilla.org/memory-reporter-manager;1"
+
+/**
+ * Memory info dumper service CID
+ */
+#define NS_MEMORY_INFO_DUMPER_CONTRACTID "@mozilla.org/memory-info-dumper;1"
+
+/**
+ * Status reporter service CID
+ */
+#define NS_STATUS_REPORTER_MANAGER_CONTRACTID "@mozilla.org/status-reporter-manager;1"
+
+/**
+ * Cycle collector logger contract id
+ */
+#define NS_CYCLE_COLLECTOR_LOGGER_CONTRACTID "@mozilla.org/cycle-collector-logger;1"
+
+/**
+ * nsMessageLoop contract id
+ */
+#define NS_MESSAGE_LOOP_CONTRACTID "@mozilla.org/message-loop;1"
+
+#define NS_COMPARTMENT_INFO_CONTRACTID "@mozilla.org/compartment-info;1"
+
+/**
+ * The following are the CIDs and Contract IDs of the nsISupports wrappers for
+ * primative types.
+ */
+#define NS_SUPPORTS_ID_CID \
+{ 0xacf8dc40, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_ID_CONTRACTID "@mozilla.org/supports-id;1"
+
+#define NS_SUPPORTS_CSTRING_CID \
+{ 0xacf8dc41, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_CSTRING_CONTRACTID "@mozilla.org/supports-cstring;1"
+
+#define NS_SUPPORTS_STRING_CID \
+{ 0xacf8dc42, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_STRING_CONTRACTID "@mozilla.org/supports-string;1"
+
+#define NS_SUPPORTS_PRBOOL_CID \
+{ 0xacf8dc43, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_PRBOOL_CONTRACTID "@mozilla.org/supports-PRBool;1"
+
+#define NS_SUPPORTS_PRUINT8_CID \
+{ 0xacf8dc44, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_PRUINT8_CONTRACTID "@mozilla.org/supports-PRUint8;1"
+
+#define NS_SUPPORTS_PRUINT16_CID \
+{ 0xacf8dc46, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_PRUINT16_CONTRACTID "@mozilla.org/supports-PRUint16;1"
+
+#define NS_SUPPORTS_PRUINT32_CID \
+{ 0xacf8dc47, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_PRUINT32_CONTRACTID "@mozilla.org/supports-PRUint32;1"
+
+#define NS_SUPPORTS_PRUINT64_CID \
+{ 0xacf8dc48, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_PRUINT64_CONTRACTID "@mozilla.org/supports-PRUint64;1"
+
+#define NS_SUPPORTS_PRTIME_CID \
+{ 0xacf8dc49, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_PRTIME_CONTRACTID "@mozilla.org/supports-PRTime;1"
+
+#define NS_SUPPORTS_CHAR_CID \
+{ 0xacf8dc4a, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_CHAR_CONTRACTID "@mozilla.org/supports-char;1"
+
+#define NS_SUPPORTS_PRINT16_CID \
+{ 0xacf8dc4b, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_PRINT16_CONTRACTID "@mozilla.org/supports-PRInt16;1"
+
+#define NS_SUPPORTS_PRINT32_CID \
+{ 0xacf8dc4c, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_PRINT32_CONTRACTID "@mozilla.org/supports-PRInt32;1"
+
+#define NS_SUPPORTS_PRINT64_CID \
+{ 0xacf8dc4d, 0x4a25, 0x11d3, \
+{ 0x98, 0x90, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22 } }
+#define NS_SUPPORTS_PRINT64_CONTRACTID "@mozilla.org/supports-PRInt64;1"
+
+#define NS_SUPPORTS_FLOAT_CID \
+{ 0xcbf86870, 0x4ac0, 0x11d3, \
+{ 0xba, 0xea, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } }
+#define NS_SUPPORTS_FLOAT_CONTRACTID "@mozilla.org/supports-float;1"
+
+#define NS_SUPPORTS_DOUBLE_CID \
+{ 0xcbf86871, 0x4ac0, 0x11d3, \
+{ 0xba, 0xea, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } }
+#define NS_SUPPORTS_DOUBLE_CONTRACTID "@mozilla.org/supports-double;1"
+
+#define NS_SUPPORTS_VOID_CID \
+{ 0xaf10f3e0, 0x568d, 0x11d3, \
+{ 0xba, 0xf8, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } }
+#define NS_SUPPORTS_VOID_CONTRACTID "@mozilla.org/supports-void;1"
+
+#define NS_SUPPORTS_INTERFACE_POINTER_CID \
+{ 0xA99FEBBA, 0x1DD1, 0x11B2, \
+{ 0xA9, 0x43, 0xB0, 0x23, 0x34, 0xA6, 0xD0, 0x83 } }
+#define NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID "@mozilla.org/supports-interface-pointer;1"
+
+#endif
diff --git a/xpcom/build/nsXPCOMCIDInternal.h b/xpcom/build/nsXPCOMCIDInternal.h
new file mode 100644
index 000000000..c9f7fb6f3
--- /dev/null
+++ b/xpcom/build/nsXPCOMCIDInternal.h
@@ -0,0 +1,54 @@
+/* -*- 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 nsXPCOMCIDInternal_h__
+#define nsXPCOMCIDInternal_h__
+
+#include "nsXPCOMCID.h"
+
+/**
+ * A hashtable-based property bag component.
+ * @implements nsIWritablePropertyBag, nsIWritablePropertyBag2
+ */
+#define NS_HASH_PROPERTY_BAG_CID \
+{ 0x678c50b8, 0x6bcb, 0x4ad0, \
+{ 0xb9, 0xb8, 0xc8, 0x11, 0x75, 0x95, 0x51, 0x99 } }
+#define NS_HASH_PROPERTY_BAG_CONTRACTID "@mozilla.org/hash-property-bag;1"
+
+/**
+ * Factory for creating nsIUnicharInputStream
+ * @implements nsIUnicharInputStreamFactory
+ * @note nsIUnicharInputStream instances cannot be created via
+ * createInstance. Code must use one of the custom factory methods.
+ */
+#define NS_SIMPLE_UNICHAR_STREAM_FACTORY_CONTRACTID \
+ "@mozilla.org/xpcom/simple-unichar-stream-factory;1"
+
+/**
+ * The global thread manager service. This component is a singleton.
+ * @implements nsIThreadManager
+ */
+#define NS_THREADMANAGER_CONTRACTID "@mozilla.org/thread-manager;1"
+
+/**
+ * A thread pool component.
+ * @implements nsIThreadPool
+ */
+#define NS_THREADPOOL_CONTRACTID "@mozilla.org/thread-pool;1"
+
+/**
+ * The contract id for the nsIXULAppInfo service.
+ */
+#define XULAPPINFO_SERVICE_CONTRACTID \
+ "@mozilla.org/xre/app-info;1"
+
+/**
+ * The contract id for the nsIXULRuntime service.
+ */
+#define XULRUNTIME_SERVICE_CONTRACTID \
+ "@mozilla.org/xre/runtime;1"
+
+#endif // nsXPCOMCIDInternal_h__
diff --git a/xpcom/build/nsXPCOMPrivate.h b/xpcom/build/nsXPCOMPrivate.h
new file mode 100644
index 000000000..99a994013
--- /dev/null
+++ b/xpcom/build/nsXPCOMPrivate.h
@@ -0,0 +1,317 @@
+/* -*- 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 nsXPCOMPrivate_h__
+#define nsXPCOMPrivate_h__
+
+#include "nscore.h"
+#include "nsXPCOM.h"
+#include "nsXPCOMStrings.h"
+#include "xptcall.h"
+
+class nsStringContainer;
+class nsCStringContainer;
+class nsPurpleBufferEntry;
+
+/**
+ * During this shutdown notification all threads which run XPCOM code must
+ * be joined.
+ */
+#define NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID "xpcom-shutdown-threads"
+
+/**
+ * During this shutdown notification all module loaders must unload XPCOM
+ * modules.
+ */
+#define NS_XPCOM_SHUTDOWN_LOADERS_OBSERVER_ID "xpcom-shutdown-loaders"
+
+// PUBLIC
+typedef nsresult (*InitFunc)(nsIServiceManager** aResult,
+ nsIFile* aBinDirectory,
+ nsIDirectoryServiceProvider* aAppFileLocationProvider);
+typedef nsresult (*ShutdownFunc)(nsIServiceManager* aServMgr);
+typedef nsresult (*GetServiceManagerFunc)(nsIServiceManager** aResult);
+typedef nsresult (*GetComponentManagerFunc)(nsIComponentManager** aResult);
+typedef nsresult (*GetComponentRegistrarFunc)(nsIComponentRegistrar** aResult);
+typedef nsresult (*GetMemoryManagerFunc)(nsIMemory** aResult);
+typedef nsresult (*NewLocalFileFunc)(const nsAString& aPath,
+ bool aFollowLinks, nsIFile** aResult);
+typedef nsresult (*NewNativeLocalFileFunc)(const nsACString& aPath,
+ bool aFollowLinks,
+ nsIFile** aResult);
+
+typedef nsresult (*GetDebugFunc)(nsIDebug2** aResult);
+
+typedef nsresult (*StringContainerInitFunc)(nsStringContainer&);
+typedef nsresult (*StringContainerInit2Func)(nsStringContainer&,
+ const char16_t*,
+ uint32_t, uint32_t);
+typedef void (*StringContainerFinishFunc)(nsStringContainer&);
+typedef uint32_t (*StringGetDataFunc)(const nsAString&, const char16_t**,
+ bool*);
+typedef uint32_t (*StringGetMutableDataFunc)(nsAString&, uint32_t,
+ char16_t**);
+typedef char16_t* (*StringCloneDataFunc)(const nsAString&);
+typedef nsresult (*StringSetDataFunc)(nsAString&, const char16_t*, uint32_t);
+typedef nsresult (*StringSetDataRangeFunc)(nsAString&, uint32_t, uint32_t,
+ const char16_t*, uint32_t);
+typedef nsresult (*StringCopyFunc)(nsAString&, const nsAString&);
+typedef void (*StringSetIsVoidFunc)(nsAString&, const bool);
+typedef bool (*StringGetIsVoidFunc)(const nsAString&);
+
+typedef nsresult (*CStringContainerInitFunc)(nsCStringContainer&);
+typedef nsresult (*CStringContainerInit2Func)(nsCStringContainer&,
+ const char*,
+ uint32_t, uint32_t);
+typedef void (*CStringContainerFinishFunc)(nsCStringContainer&);
+typedef uint32_t (*CStringGetDataFunc)(const nsACString&, const char**,
+ bool*);
+typedef uint32_t (*CStringGetMutableDataFunc)(nsACString&, uint32_t, char**);
+typedef char* (*CStringCloneDataFunc)(const nsACString&);
+typedef nsresult (*CStringSetDataFunc)(nsACString&, const char*, uint32_t);
+typedef nsresult (*CStringSetDataRangeFunc)(nsACString&, uint32_t, uint32_t,
+ const char*, uint32_t);
+typedef nsresult (*CStringCopyFunc)(nsACString&, const nsACString&);
+typedef void (*CStringSetIsVoidFunc)(nsACString&, const bool);
+typedef bool (*CStringGetIsVoidFunc)(const nsACString&);
+
+typedef nsresult (*CStringToUTF16)(const nsACString&, nsCStringEncoding,
+ nsAString&);
+typedef nsresult (*UTF16ToCString)(const nsAString&, nsCStringEncoding,
+ nsACString&);
+
+typedef void* (*AllocFunc)(size_t aSize);
+typedef void* (*ReallocFunc)(void* aPtr, size_t aSize);
+typedef void (*FreeFunc)(void* aPtr);
+
+typedef void (*DebugBreakFunc)(uint32_t aSeverity,
+ const char* aStr, const char* aExpr,
+ const char* aFile, int32_t aLine);
+
+typedef void (*xpcomVoidFunc)();
+typedef void (*LogAddRefFunc)(void*, nsrefcnt, const char*, uint32_t);
+typedef void (*LogReleaseFunc)(void*, nsrefcnt, const char*);
+typedef void (*LogCtorFunc)(void*, const char*, uint32_t);
+typedef void (*LogCOMPtrFunc)(void*, nsISupports*);
+
+typedef nsresult (*GetXPTCallStubFunc)(REFNSIID, nsIXPTCProxy*,
+ nsISomeInterface**);
+typedef void (*DestroyXPTCallStubFunc)(nsISomeInterface*);
+typedef nsresult (*InvokeByIndexFunc)(nsISupports*, uint32_t, uint32_t,
+ nsXPTCVariant*);
+typedef bool (*CycleCollectorFunc)(nsISupports*);
+typedef nsPurpleBufferEntry*
+ (*CycleCollectorSuspect2Func)(void*,
+ nsCycleCollectionParticipant*);
+typedef bool (*CycleCollectorForget2Func)(nsPurpleBufferEntry*);
+typedef void (*CycleCollectorSuspect3Func)(void*,
+ nsCycleCollectionParticipant*,
+ nsCycleCollectingAutoRefCnt*,
+ bool*);
+// PRIVATE AND DEPRECATED
+typedef NS_CALLBACK(XPCOMExitRoutine)(void);
+
+typedef nsresult (*RegisterXPCOMExitRoutineFunc)(XPCOMExitRoutine aExitRoutine,
+ uint32_t aPriority);
+typedef nsresult (*UnregisterXPCOMExitRoutineFunc)(XPCOMExitRoutine aExitRoutine);
+
+typedef struct XPCOMFunctions
+{
+ uint32_t version;
+ uint32_t size;
+
+ InitFunc init;
+ ShutdownFunc shutdown;
+ GetServiceManagerFunc getServiceManager;
+ GetComponentManagerFunc getComponentManager;
+ GetComponentRegistrarFunc getComponentRegistrar;
+ GetMemoryManagerFunc getMemoryManager;
+ NewLocalFileFunc newLocalFile;
+ NewNativeLocalFileFunc newNativeLocalFile;
+
+ RegisterXPCOMExitRoutineFunc registerExitRoutine;
+ UnregisterXPCOMExitRoutineFunc unregisterExitRoutine;
+
+ // Added for Mozilla 1.5
+ GetDebugFunc getDebug;
+ void* getTraceRefcnt;
+
+ // Added for Mozilla 1.7
+ StringContainerInitFunc stringContainerInit;
+ StringContainerFinishFunc stringContainerFinish;
+ StringGetDataFunc stringGetData;
+ StringSetDataFunc stringSetData;
+ StringSetDataRangeFunc stringSetDataRange;
+ StringCopyFunc stringCopy;
+ CStringContainerInitFunc cstringContainerInit;
+ CStringContainerFinishFunc cstringContainerFinish;
+ CStringGetDataFunc cstringGetData;
+ CStringSetDataFunc cstringSetData;
+ CStringSetDataRangeFunc cstringSetDataRange;
+ CStringCopyFunc cstringCopy;
+ CStringToUTF16 cstringToUTF16;
+ UTF16ToCString utf16ToCString;
+ StringCloneDataFunc stringCloneData;
+ CStringCloneDataFunc cstringCloneData;
+
+ // Added for Mozilla 1.8
+ AllocFunc allocFunc;
+ ReallocFunc reallocFunc;
+ FreeFunc freeFunc;
+ StringContainerInit2Func stringContainerInit2;
+ CStringContainerInit2Func cstringContainerInit2;
+ StringGetMutableDataFunc stringGetMutableData;
+ CStringGetMutableDataFunc cstringGetMutableData;
+ void* init3; // obsolete
+
+ // Added for Mozilla 1.9
+ DebugBreakFunc debugBreakFunc;
+ xpcomVoidFunc logInitFunc;
+ xpcomVoidFunc logTermFunc;
+ LogAddRefFunc logAddRefFunc;
+ LogReleaseFunc logReleaseFunc;
+ LogCtorFunc logCtorFunc;
+ LogCtorFunc logDtorFunc;
+ LogCOMPtrFunc logCOMPtrAddRefFunc;
+ LogCOMPtrFunc logCOMPtrReleaseFunc;
+ GetXPTCallStubFunc getXPTCallStubFunc;
+ DestroyXPTCallStubFunc destroyXPTCallStubFunc;
+ InvokeByIndexFunc invokeByIndexFunc;
+ CycleCollectorFunc cycleSuspectFunc; // obsolete: use cycleSuspect3Func
+ CycleCollectorFunc cycleForgetFunc; // obsolete
+ StringSetIsVoidFunc stringSetIsVoid;
+ StringGetIsVoidFunc stringGetIsVoid;
+ CStringSetIsVoidFunc cstringSetIsVoid;
+ CStringGetIsVoidFunc cstringGetIsVoid;
+
+ // Added for Mozilla 1.9.1
+ CycleCollectorSuspect2Func cycleSuspect2Func; // obsolete: use cycleSuspect3Func
+ CycleCollectorForget2Func cycleForget2Func; // obsolete
+
+ CycleCollectorSuspect3Func cycleSuspect3Func;
+
+} XPCOMFunctions;
+
+typedef nsresult (*GetFrozenFunctionsFunc)(XPCOMFunctions* aEntryPoints,
+ const char* aLibraryPath);
+XPCOM_API(nsresult) NS_GetFrozenFunctions(XPCOMFunctions* aEntryPoints,
+ const char* aLibraryPath);
+
+
+namespace mozilla {
+
+/**
+ * Shutdown XPCOM. You must call this method after you are finished
+ * using xpcom.
+ *
+ * @param aServMgr The service manager which was returned by NS_InitXPCOM.
+ * This will release servMgr. You may pass null.
+ *
+ * @return NS_OK for success;
+ * other error codes indicate a failure during shutdown
+ *
+ */
+nsresult
+ShutdownXPCOM(nsIServiceManager* aServMgr);
+
+void SetICUMemoryFunctions();
+
+/**
+ * C++ namespaced version of NS_LogTerm.
+ */
+void LogTerm();
+
+} // namespace mozilla
+
+
+// think hard before changing this
+#define XPCOM_GLUE_VERSION 1
+
+
+/* XPCOM Specific Defines
+ *
+ * XPCOM_DLL - name of the loadable xpcom library on disk.
+ * XUL_DLL - name of the loadable XUL library on disk
+ * XPCOM_SEARCH_KEY - name of the environment variable that can be
+ * modified to include additional search paths.
+ * GRE_CONF_NAME - Name of the GRE Configuration file
+ */
+
+#if defined(XP_WIN32)
+
+#define XPCOM_SEARCH_KEY "PATH"
+#define GRE_CONF_NAME "gre.config"
+#define GRE_WIN_REG_LOC L"Software\\mozilla.org\\GRE"
+#define XPCOM_DLL XUL_DLL
+#define LXPCOM_DLL LXUL_DLL
+#define XUL_DLL "xul.dll"
+#define LXUL_DLL L"xul.dll"
+
+#else // Unix
+#include <limits.h> // for PATH_MAX
+
+#define XPCOM_DLL XUL_DLL
+
+// you have to love apple..
+#ifdef XP_MACOSX
+#define XPCOM_SEARCH_KEY "DYLD_LIBRARY_PATH"
+#define GRE_FRAMEWORK_NAME "XUL.framework"
+#define XUL_DLL "XUL"
+#else
+#define XPCOM_SEARCH_KEY "LD_LIBRARY_PATH"
+#define XUL_DLL "libxul" MOZ_DLL_SUFFIX
+#endif
+
+#define GRE_CONF_NAME ".gre.config"
+#define GRE_CONF_PATH "/etc/gre.conf"
+#define GRE_CONF_DIR "/etc/gre.d"
+#define GRE_USER_CONF_DIR ".gre.d"
+#endif
+
+#if defined(XP_WIN)
+ #define XPCOM_FILE_PATH_SEPARATOR "\\"
+ #define XPCOM_ENV_PATH_SEPARATOR ";"
+#elif defined(XP_UNIX)
+ #define XPCOM_FILE_PATH_SEPARATOR "/"
+ #define XPCOM_ENV_PATH_SEPARATOR ":"
+#else
+ #error need_to_define_your_file_path_separator_and_illegal_characters
+#endif
+
+#ifdef AIX
+#include <sys/param.h>
+#endif
+
+#ifndef MAXPATHLEN
+#ifdef PATH_MAX
+#define MAXPATHLEN PATH_MAX
+#elif defined(_MAX_PATH)
+#define MAXPATHLEN _MAX_PATH
+#elif defined(CCHMAXPATH)
+#define MAXPATHLEN CCHMAXPATH
+#else
+#define MAXPATHLEN 1024
+#endif
+#endif
+
+extern bool gXPCOMShuttingDown;
+extern bool gXPCOMThreadsShutDown;
+
+// Needed by the IPC layer from off the main thread
+extern char16_t* gGREBinPath;
+
+namespace mozilla {
+namespace services {
+
+/**
+ * Clears service cache, sets gXPCOMShuttingDown
+ */
+void Shutdown();
+
+} // namespace services
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/build/nsXPCOMStrings.cpp b/xpcom/build/nsXPCOMStrings.cpp
new file mode 100644
index 000000000..617edbab2
--- /dev/null
+++ b/xpcom/build/nsXPCOMStrings.cpp
@@ -0,0 +1,366 @@
+/* -*- 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 "nsString.h"
+#include "nsCharTraits.h"
+
+#include "nsXPCOMStrings.h"
+#include "nsNativeCharsetUtils.h"
+
+/* ------------------------------------------------------------------------- */
+
+XPCOM_API(nsresult)
+NS_StringContainerInit(nsStringContainer& aContainer)
+{
+ NS_ASSERTION(sizeof(nsStringContainer_base) >= sizeof(nsString),
+ "nsStringContainer is not large enough");
+
+ // use placement new to avoid heap allocating nsString object
+ new (&aContainer) nsString();
+
+ return NS_OK;
+}
+
+XPCOM_API(nsresult)
+NS_StringContainerInit2(nsStringContainer& aContainer,
+ const char16_t* aData,
+ uint32_t aDataLength,
+ uint32_t aFlags)
+{
+ NS_ASSERTION(sizeof(nsStringContainer_base) >= sizeof(nsString),
+ "nsStringContainer is not large enough");
+
+ if (!aData) {
+ new (&aContainer) nsString();
+ } else {
+ if (aDataLength == UINT32_MAX) {
+ if (NS_WARN_IF(aFlags & NS_STRING_CONTAINER_INIT_SUBSTRING)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ aDataLength = nsCharTraits<char16_t>::length(aData);
+ }
+
+ if (aFlags & (NS_STRING_CONTAINER_INIT_DEPEND |
+ NS_STRING_CONTAINER_INIT_ADOPT)) {
+ uint32_t flags;
+ if (aFlags & NS_STRING_CONTAINER_INIT_SUBSTRING) {
+ flags = nsSubstring::F_NONE;
+ } else {
+ flags = nsSubstring::F_TERMINATED;
+ }
+
+ if (aFlags & NS_STRING_CONTAINER_INIT_ADOPT) {
+ flags |= nsSubstring::F_OWNED;
+ }
+
+ new (&aContainer) nsSubstring(const_cast<char16_t*>(aData),
+ aDataLength, flags);
+ } else {
+ new (&aContainer) nsString(aData, aDataLength);
+ }
+ }
+
+ return NS_OK;
+}
+
+XPCOM_API(void)
+NS_StringContainerFinish(nsStringContainer& aContainer)
+{
+ // call the nsString dtor
+ reinterpret_cast<nsString*>(&aContainer)->~nsString();
+}
+
+/* ------------------------------------------------------------------------- */
+
+XPCOM_API(uint32_t)
+NS_StringGetData(const nsAString& aStr, const char16_t** aData,
+ bool* aTerminated)
+{
+ if (aTerminated) {
+ *aTerminated = aStr.IsTerminated();
+ }
+
+ *aData = aStr.BeginReading();
+ return aStr.Length();
+}
+
+XPCOM_API(uint32_t)
+NS_StringGetMutableData(nsAString& aStr, uint32_t aDataLength,
+ char16_t** aData)
+{
+ if (aDataLength != UINT32_MAX) {
+ aStr.SetLength(aDataLength);
+ if (aStr.Length() != aDataLength) {
+ *aData = nullptr;
+ return 0;
+ }
+ }
+
+ *aData = aStr.BeginWriting();
+ return aStr.Length();
+}
+
+XPCOM_API(char16_t*)
+NS_StringCloneData(const nsAString& aStr)
+{
+ return ToNewUnicode(aStr);
+}
+
+XPCOM_API(nsresult)
+NS_StringSetData(nsAString& aStr, const char16_t* aData, uint32_t aDataLength)
+{
+ aStr.Assign(aData, aDataLength);
+ return NS_OK; // XXX report errors
+}
+
+XPCOM_API(nsresult)
+NS_StringSetDataRange(nsAString& aStr,
+ uint32_t aCutOffset, uint32_t aCutLength,
+ const char16_t* aData, uint32_t aDataLength)
+{
+ if (aCutOffset == UINT32_MAX) {
+ // append case
+ if (aData) {
+ aStr.Append(aData, aDataLength);
+ }
+ return NS_OK; // XXX report errors
+ }
+
+ if (aCutLength == UINT32_MAX) {
+ aCutLength = aStr.Length() - aCutOffset;
+ }
+
+ if (aData) {
+ if (aDataLength == UINT32_MAX) {
+ aStr.Replace(aCutOffset, aCutLength, nsDependentString(aData));
+ } else {
+ aStr.Replace(aCutOffset, aCutLength, Substring(aData, aDataLength));
+ }
+ } else {
+ aStr.Cut(aCutOffset, aCutLength);
+ }
+
+ return NS_OK; // XXX report errors
+}
+
+XPCOM_API(nsresult)
+NS_StringCopy(nsAString& aDest, const nsAString& aSrc)
+{
+ aDest.Assign(aSrc);
+ return NS_OK; // XXX report errors
+}
+
+XPCOM_API(void)
+NS_StringSetIsVoid(nsAString& aStr, const bool aIsVoid)
+{
+ aStr.SetIsVoid(aIsVoid);
+}
+
+XPCOM_API(bool)
+NS_StringGetIsVoid(const nsAString& aStr)
+{
+ return aStr.IsVoid();
+}
+
+/* ------------------------------------------------------------------------- */
+
+XPCOM_API(nsresult)
+NS_CStringContainerInit(nsCStringContainer& aContainer)
+{
+ NS_ASSERTION(sizeof(nsStringContainer_base) >= sizeof(nsCString),
+ "nsCStringContainer is not large enough");
+
+ // use placement new to avoid heap allocating nsCString object
+ new (&aContainer) nsCString();
+
+ return NS_OK;
+}
+
+XPCOM_API(nsresult)
+NS_CStringContainerInit2(nsCStringContainer& aContainer,
+ const char* aData,
+ uint32_t aDataLength,
+ uint32_t aFlags)
+{
+ NS_ASSERTION(sizeof(nsStringContainer_base) >= sizeof(nsCString),
+ "nsStringContainer is not large enough");
+
+ if (!aData) {
+ new (&aContainer) nsCString();
+ } else {
+ if (aDataLength == UINT32_MAX) {
+ if (NS_WARN_IF(aFlags & NS_CSTRING_CONTAINER_INIT_SUBSTRING)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ aDataLength = nsCharTraits<char>::length(aData);
+ }
+
+ if (aFlags & (NS_CSTRING_CONTAINER_INIT_DEPEND |
+ NS_CSTRING_CONTAINER_INIT_ADOPT)) {
+ uint32_t flags;
+ if (aFlags & NS_CSTRING_CONTAINER_INIT_SUBSTRING) {
+ flags = nsCSubstring::F_NONE;
+ } else {
+ flags = nsCSubstring::F_TERMINATED;
+ }
+
+ if (aFlags & NS_CSTRING_CONTAINER_INIT_ADOPT) {
+ flags |= nsCSubstring::F_OWNED;
+ }
+
+ new (&aContainer) nsCSubstring(const_cast<char*>(aData),
+ aDataLength, flags);
+ } else {
+ new (&aContainer) nsCString(aData, aDataLength);
+ }
+ }
+
+ return NS_OK;
+}
+
+XPCOM_API(void)
+NS_CStringContainerFinish(nsCStringContainer& aContainer)
+{
+ // call the nsCString dtor
+ reinterpret_cast<nsCString*>(&aContainer)->~nsCString();
+}
+
+/* ------------------------------------------------------------------------- */
+
+XPCOM_API(uint32_t)
+NS_CStringGetData(const nsACString& aStr, const char** aData,
+ bool* aTerminated)
+{
+ if (aTerminated) {
+ *aTerminated = aStr.IsTerminated();
+ }
+
+ *aData = aStr.BeginReading();
+ return aStr.Length();
+}
+
+XPCOM_API(uint32_t)
+NS_CStringGetMutableData(nsACString& aStr, uint32_t aDataLength, char** aData)
+{
+ if (aDataLength != UINT32_MAX) {
+ aStr.SetLength(aDataLength);
+ if (aStr.Length() != aDataLength) {
+ *aData = nullptr;
+ return 0;
+ }
+ }
+
+ *aData = aStr.BeginWriting();
+ return aStr.Length();
+}
+
+XPCOM_API(char*)
+NS_CStringCloneData(const nsACString& aStr)
+{
+ return ToNewCString(aStr);
+}
+
+XPCOM_API(nsresult)
+NS_CStringSetData(nsACString& aStr, const char* aData, uint32_t aDataLength)
+{
+ aStr.Assign(aData, aDataLength);
+ return NS_OK; // XXX report errors
+}
+
+XPCOM_API(nsresult)
+NS_CStringSetDataRange(nsACString& aStr,
+ uint32_t aCutOffset, uint32_t aCutLength,
+ const char* aData, uint32_t aDataLength)
+{
+ if (aCutOffset == UINT32_MAX) {
+ // append case
+ if (aData) {
+ aStr.Append(aData, aDataLength);
+ }
+ return NS_OK; // XXX report errors
+ }
+
+ if (aCutLength == UINT32_MAX) {
+ aCutLength = aStr.Length() - aCutOffset;
+ }
+
+ if (aData) {
+ if (aDataLength == UINT32_MAX) {
+ aStr.Replace(aCutOffset, aCutLength, nsDependentCString(aData));
+ } else {
+ aStr.Replace(aCutOffset, aCutLength, Substring(aData, aDataLength));
+ }
+ } else {
+ aStr.Cut(aCutOffset, aCutLength);
+ }
+
+ return NS_OK; // XXX report errors
+}
+
+XPCOM_API(nsresult)
+NS_CStringCopy(nsACString& aDest, const nsACString& aSrc)
+{
+ aDest.Assign(aSrc);
+ return NS_OK; // XXX report errors
+}
+
+XPCOM_API(void)
+NS_CStringSetIsVoid(nsACString& aStr, const bool aIsVoid)
+{
+ aStr.SetIsVoid(aIsVoid);
+}
+
+XPCOM_API(bool)
+NS_CStringGetIsVoid(const nsACString& aStr)
+{
+ return aStr.IsVoid();
+}
+
+/* ------------------------------------------------------------------------- */
+
+XPCOM_API(nsresult)
+NS_CStringToUTF16(const nsACString& aSrc,
+ nsCStringEncoding aSrcEncoding,
+ nsAString& aDest)
+{
+ switch (aSrcEncoding) {
+ case NS_CSTRING_ENCODING_ASCII:
+ CopyASCIItoUTF16(aSrc, aDest);
+ break;
+ case NS_CSTRING_ENCODING_UTF8:
+ CopyUTF8toUTF16(aSrc, aDest);
+ break;
+ case NS_CSTRING_ENCODING_NATIVE_FILESYSTEM:
+ NS_CopyNativeToUnicode(aSrc, aDest);
+ break;
+ default:
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ return NS_OK; // XXX report errors
+}
+
+XPCOM_API(nsresult)
+NS_UTF16ToCString(const nsAString& aSrc,
+ nsCStringEncoding aDestEncoding,
+ nsACString& aDest)
+{
+ switch (aDestEncoding) {
+ case NS_CSTRING_ENCODING_ASCII:
+ LossyCopyUTF16toASCII(aSrc, aDest);
+ break;
+ case NS_CSTRING_ENCODING_UTF8:
+ CopyUTF16toUTF8(aSrc, aDest);
+ break;
+ case NS_CSTRING_ENCODING_NATIVE_FILESYSTEM:
+ NS_CopyUnicodeToNative(aSrc, aDest);
+ break;
+ default:
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ return NS_OK; // XXX report errors
+}
diff --git a/xpcom/build/nsXREAppData.h b/xpcom/build/nsXREAppData.h
new file mode 100644
index 000000000..fbc7adb8f
--- /dev/null
+++ b/xpcom/build/nsXREAppData.h
@@ -0,0 +1,164 @@
+/* -*- 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 nsXREAppData_h
+#define nsXREAppData_h
+
+#include <stdint.h>
+#include "mozilla/Attributes.h"
+
+class nsIFile;
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+namespace sandbox {
+class BrokerServices;
+}
+#endif
+
+/**
+ * Application-specific data needed to start the apprunner.
+ *
+ * @note When this structure is allocated and manipulated by XRE_CreateAppData,
+ * string fields will be allocated with moz_xmalloc, and interface pointers
+ * are strong references.
+ */
+struct nsXREAppData
+{
+ /**
+ * This should be set to sizeof(nsXREAppData). This structure may be
+ * extended in future releases, and this ensures that binary compatibility
+ * is maintained.
+ */
+ uint32_t size;
+
+ /**
+ * The directory of the application to be run. May be null if the
+ * xulrunner and the app are installed into the same directory.
+ */
+ nsIFile* MOZ_NON_OWNING_REF directory;
+
+ /**
+ * The name of the application vendor. This must be ASCII, and is normally
+ * mixed-case, e.g. "Mozilla". Optional (may be null), but highly
+ * recommended. Must not be the empty string.
+ */
+ const char* vendor;
+
+ /**
+ * The name of the application. This must be ASCII, and is normally
+ * mixed-case, e.g. "Firefox". Required (must not be null or an empty
+ * string).
+ */
+ const char* name;
+
+ /**
+ * The internal name of the application for remoting purposes. When left
+ * unspecified, "name" is used instead. This must be ASCII, and is normally
+ * lowercase, e.g. "firefox". Optional (may be null but not an empty string).
+ */
+ const char* remotingName;
+
+ /**
+ * The major version, e.g. "0.8.0+". Optional (may be null), but
+ * required for advanced application features such as the extension
+ * manager and update service. Must not be the empty string.
+ */
+ const char* version;
+
+ /**
+ * The application's build identifier, e.g. "2004051604"
+ */
+ const char* buildID;
+
+ /**
+ * The application's UUID. Used by the extension manager to determine
+ * compatible extensions. Optional, but required for advanced application
+ * features such as the extension manager and update service.
+ *
+ * This has traditionally been in the form
+ * "{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}" but for new applications
+ * a more readable form is encouraged: "appname@vendor.tld". Only
+ * the following characters are allowed: a-z A-Z 0-9 - . @ _ { } *
+ */
+ const char* ID;
+
+ /**
+ * The copyright information to print for the -h commandline flag,
+ * e.g. "Copyright (c) 2003 mozilla.org".
+ */
+ const char* copyright;
+
+ /**
+ * Combination of NS_XRE_ prefixed flags (defined below).
+ */
+ uint32_t flags;
+
+ /**
+ * The location of the XRE. XRE_main may not be able to figure this out
+ * programatically.
+ */
+ nsIFile* MOZ_NON_OWNING_REF xreDirectory;
+
+ /**
+ * The minimum/maximum compatible XRE version.
+ */
+ const char* minVersion;
+ const char* maxVersion;
+
+ /**
+ * The server URL to send crash reports to.
+ */
+ const char* crashReporterURL;
+
+ /**
+ * The profile directory that will be used. Optional (may be null). Must not
+ * be the empty string, must be ASCII. The path is split into components
+ * along the path separator characters '/' and '\'.
+ *
+ * The application data directory ("UAppData", see below) is normally
+ * composed as follows, where $HOME is platform-specific:
+ *
+ * UAppData = $HOME[/$vendor]/$name
+ *
+ * If present, the 'profile' string will be used instead of the combination of
+ * vendor and name as follows:
+ *
+ * UAppData = $HOME/$profile
+ */
+ const char* profile;
+
+ /**
+ * The application name to use in the User Agent string.
+ */
+ const char* UAName;
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ /**
+ * Chromium sandbox BrokerServices.
+ */
+ sandbox::BrokerServices* sandboxBrokerServices;
+#endif
+};
+
+/**
+ * Indicates whether or not the profile migrator service may be
+ * invoked at startup when creating a profile.
+ */
+#define NS_XRE_ENABLE_PROFILE_MIGRATOR (1 << 1)
+
+/**
+ * Indicates the Windows DLL Blocklist initialized properly. For testing
+ * purposes only. Set in nsBrowserApp on startup, automated tests then
+ * check the result.
+ */
+#define NS_XRE_DLL_BLOCKLIST_ENABLED (1 << 2)
+
+/**
+ * Indicates whether or not to use Breakpad crash reporting.
+ */
+#define NS_XRE_ENABLE_CRASH_REPORTER (1 << 3)
+
+#endif // nsXREAppData_h
diff --git a/xpcom/build/nsXULAppAPI.h b/xpcom/build/nsXULAppAPI.h
new file mode 100644
index 000000000..426a58f06
--- /dev/null
+++ b/xpcom/build/nsXULAppAPI.h
@@ -0,0 +1,538 @@
+/* -*- 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 _nsXULAppAPI_h__
+#define _nsXULAppAPI_h__
+
+#include "nsID.h"
+#include "xrecore.h"
+#include "nsXPCOM.h"
+#include "nsISupports.h"
+#include "mozilla/Logging.h"
+#include "nsXREAppData.h"
+#include "js/TypeDecls.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Vector.h"
+#include "mozilla/TimeStamp.h"
+#include "XREChildData.h"
+#include "XREShellData.h"
+
+/**
+ * A directory service key which provides the platform-correct "application
+ * data" directory as follows, where $name and $vendor are as defined above and
+ * $vendor is optional:
+ *
+ * Windows:
+ * HOME = Documents and Settings\$USER\Application Data
+ * UAppData = $HOME[\$vendor]\$name
+ *
+ * Unix:
+ * HOME = ~
+ * UAppData = $HOME/.[$vendor/]$name
+ *
+ * Mac:
+ * HOME = ~
+ * UAppData = $HOME/Library/Application Support/$name
+ *
+ * Note that the "profile" member above will change the value of UAppData as
+ * follows:
+ *
+ * Windows:
+ * UAppData = $HOME\$profile
+ *
+ * Unix:
+ * UAppData = $HOME/.$profile
+ *
+ * Mac:
+ * UAppData = $HOME/Library/Application Support/$profile
+ */
+#define XRE_USER_APP_DATA_DIR "UAppData"
+
+/**
+ * A directory service key which provides a list of all enabled extension
+ * directories and files (packed XPIs). The list includes compatible
+ * platform-specific extension subdirectories.
+ *
+ * @note The directory list will have no members when the application is
+ * launched in safe mode.
+ */
+#define XRE_EXTENSIONS_DIR_LIST "XREExtDL"
+
+/**
+ * A directory service key which provides the executable file used to
+ * launch the current process. This is the same value returned by the
+ * XRE_GetBinaryPath function defined below.
+ */
+#define XRE_EXECUTABLE_FILE "XREExeF"
+
+/**
+ * A directory service key which specifies the profile
+ * directory. Unlike the NS_APP_USER_PROFILE_50_DIR key, this key may
+ * be available when the profile hasn't been "started", or after is
+ * has been shut down. If the application is running without a
+ * profile, such as when showing the profile manager UI, this key will
+ * not be available. This key is provided by the XUL apprunner or by
+ * the aAppDirProvider object passed to XRE_InitEmbedding.
+ */
+#define NS_APP_PROFILE_DIR_STARTUP "ProfDS"
+
+/**
+ * A directory service key which specifies the profile
+ * directory. Unlike the NS_APP_USER_PROFILE_LOCAL_50_DIR key, this key may
+ * be available when the profile hasn't been "started", or after is
+ * has been shut down. If the application is running without a
+ * profile, such as when showing the profile manager UI, this key will
+ * not be available. This key is provided by the XUL apprunner or by
+ * the aAppDirProvider object passed to XRE_InitEmbedding.
+ */
+#define NS_APP_PROFILE_LOCAL_DIR_STARTUP "ProfLDS"
+
+/**
+ * A directory service key which specifies the system extension
+ * parent directory containing platform-specific extensions.
+ * This key may not be available on all platforms.
+ */
+#define XRE_SYS_LOCAL_EXTENSION_PARENT_DIR "XRESysLExtPD"
+
+/**
+ * A directory service key which specifies the system extension
+ * parent directory containing platform-independent extensions.
+ * This key may not be available on all platforms.
+ * Additionally, the directory may be equal to that returned by
+ * XRE_SYS_LOCAL_EXTENSION_PARENT_DIR on some platforms.
+ */
+#define XRE_SYS_SHARE_EXTENSION_PARENT_DIR "XRESysSExtPD"
+
+#if defined(XP_UNIX) || defined(XP_MACOSX)
+/**
+ * Directory service keys for the system-wide and user-specific
+ * directories where host manifests used by the WebExtensions
+ * native messaging feature are found.
+ */
+#define XRE_SYS_NATIVE_MESSAGING_MANIFESTS "XRESysNativeMessaging"
+#define XRE_USER_NATIVE_MESSAGING_MANIFESTS "XREUserNativeMessaging"
+#endif
+
+/**
+ * A directory service key which specifies the user system extension
+ * parent directory.
+ */
+#define XRE_USER_SYS_EXTENSION_DIR "XREUSysExt"
+
+/**
+ * A directory service key which specifies the distribution specific files for
+ * the application.
+ */
+#define XRE_APP_DISTRIBUTION_DIR "XREAppDist"
+
+/**
+ * A directory service key which specifies the location for system add-ons.
+ */
+#define XRE_APP_FEATURES_DIR "XREAppFeat"
+
+/**
+ * A directory service key which specifies the location for app dir add-ons.
+ * Should be a synonym for XCurProcD everywhere except in tests.
+ */
+#define XRE_ADDON_APP_DIR "XREAddonAppDir"
+
+/**
+ * A directory service key which provides the update directory. Callers should
+ * fall back to appDir.
+ * Windows: If vendor name exists:
+ * Documents and Settings\<User>\Local Settings\Application Data\
+ * <vendor name>\updates\
+ * <hash of the path to XRE_EXECUTABLE_FILE’s parent directory>
+ *
+ * If vendor name doesn't exist, but product name exists:
+ * Documents and Settings\<User>\Local Settings\Application Data\
+ * <product name>\updates\
+ * <hash of the path to XRE_EXECUTABLE_FILE’s parent directory>
+ *
+ * If neither vendor nor product name exists:
+ * If app dir is under Program Files:
+ * Documents and Settings\<User>\Local Settings\Application Data\
+ * <relative path to app dir from Program Files>
+ *
+ * If app dir isn’t under Program Files:
+ * Documents and Settings\<User>\Local Settings\Application Data\
+ * <MOZ_APP_NAME>
+ *
+ * Mac: ~/Library/Caches/Mozilla/updates/<absolute path to app dir>
+ *
+ * Gonk: /data/local
+ *
+ * All others: Parent directory of XRE_EXECUTABLE_FILE.
+ */
+#define XRE_UPDATE_ROOT_DIR "UpdRootD"
+
+/**
+ * A directory service key which provides an alternate location
+ * to UpdRootD to to store large files. This key is currently
+ * only implemented in the Gonk directory service provider.
+ */
+
+#define XRE_UPDATE_ARCHIVE_DIR "UpdArchD"
+
+/**
+ * A directory service key which provides the directory where an OS update is
+* applied.
+ * At present this is supported only in Gonk.
+ */
+#define XRE_OS_UPDATE_APPLY_TO_DIR "OSUpdApplyToD"
+
+/**
+ * Begin an XUL application. Does not return until the user exits the
+ * application.
+ *
+ * @param argc/argv Command-line parameters to pass to the application. On
+ * Windows, these should be in UTF8. On unix-like platforms
+ * these are in the "native" character set.
+ *
+ * @param aAppData Information about the application to be run.
+ *
+ * @param aFlags Platform specific flags.
+ *
+ * @return A native result code suitable for returning from main().
+ *
+ * @note If the binary is linked against the standalone XPCOM glue,
+ * XPCOMGlueStartup() should be called before this method.
+ */
+XRE_API(int,
+ XRE_main, (int argc, char* argv[], const nsXREAppData* aAppData,
+ uint32_t aFlags))
+
+/**
+ * Given a path relative to the current working directory (or an absolute
+ * path), return an appropriate nsIFile object.
+ *
+ * @note Pass UTF8 strings on Windows... native charset on other platforms.
+ */
+XRE_API(nsresult,
+ XRE_GetFileFromPath, (const char* aPath, nsIFile** aResult))
+
+/**
+ * Get the path of the running application binary and store it in aResult.
+ * @param aArgv0 The value passed as argv[0] of main(). This value is only
+ * used on *nix, and only when other methods of determining
+ * the binary path have failed.
+ */
+XRE_API(nsresult,
+ XRE_GetBinaryPath, (const char* aArgv0, nsIFile** aResult))
+
+/**
+ * Get the static module built in to libxul.
+ */
+XRE_API(const mozilla::Module*,
+ XRE_GetStaticModule, ())
+
+/**
+ * Lock a profile directory using platform-specific semantics.
+ *
+ * @param aDirectory The profile directory to lock.
+ * @param aLockObject An opaque lock object. The directory will remain locked
+ * as long as the XPCOM reference is held.
+ */
+XRE_API(nsresult,
+ XRE_LockProfileDirectory, (nsIFile* aDirectory,
+ nsISupports** aLockObject))
+
+/**
+ * Initialize libXUL for embedding purposes.
+ *
+ * @param aLibXULDirectory The directory in which the libXUL shared library
+ * was found.
+ * @param aAppDirectory The directory in which the application components
+ * and resources can be found. This will map to
+ * the NS_OS_CURRENT_PROCESS_DIR directory service
+ * key.
+ * @param aAppDirProvider A directory provider for the application. This
+ * provider will be aggregated by a libxul provider
+ * which will provide the base required GRE keys.
+ *
+ * @note This function must be called from the "main" thread.
+ *
+ * @note At the present time, this function may only be called once in
+ * a given process. Use XRE_TermEmbedding to clean up and free
+ * resources allocated by XRE_InitEmbedding.
+ */
+
+XRE_API(nsresult,
+ XRE_InitEmbedding2, (nsIFile* aLibXULDirectory,
+ nsIFile* aAppDirectory,
+ nsIDirectoryServiceProvider* aAppDirProvider))
+
+/**
+ * Register static XPCOM component information.
+ * This method may be called at any time before or after XRE_main or
+ * XRE_InitEmbedding.
+ */
+XRE_API(nsresult,
+ XRE_AddStaticComponent, (const mozilla::Module* aComponent))
+
+/**
+ * Register XPCOM components found in an array of files/directories.
+ * This method may be called at any time before or after XRE_main or
+ * XRE_InitEmbedding.
+ *
+ * @param aFiles An array of files or directories.
+ * @param aFileCount the number of items in the aFiles array.
+ * @note appdir/components is registered automatically.
+ *
+ * NS_APP_LOCATION specifies a location to search for binary XPCOM
+ * components as well as component/chrome manifest files.
+ *
+ * NS_EXTENSION_LOCATION excludes binary XPCOM components but allows other
+ * manifest instructions.
+ *
+ * NS_SKIN_LOCATION specifies a location to search for chrome manifest files
+ * which are only allowed to register only skin packages and style overlays.
+ */
+enum NSLocationType
+{
+ NS_APP_LOCATION,
+ NS_EXTENSION_LOCATION,
+ NS_SKIN_LOCATION,
+ NS_BOOTSTRAPPED_LOCATION
+};
+
+XRE_API(nsresult,
+ XRE_AddManifestLocation, (NSLocationType aType,
+ nsIFile* aLocation))
+
+/**
+ * Register XPCOM components found in a JAR.
+ * This is similar to XRE_AddManifestLocation except the file specified
+ * must be a zip archive with a manifest named chrome.manifest
+ * This method may be called at any time before or after XRE_main or
+ * XRE_InitEmbedding.
+ *
+ * @param aFiles An array of files or directories.
+ * @param aFileCount the number of items in the aFiles array.
+ * @note appdir/components is registered automatically.
+ *
+ * NS_COMPONENT_LOCATION specifies a location to search for binary XPCOM
+ * components as well as component/chrome manifest files.
+ *
+ * NS_SKIN_LOCATION specifies a location to search for chrome manifest files
+ * which are only allowed to register only skin packages and style overlays.
+ */
+XRE_API(nsresult,
+ XRE_AddJarManifestLocation, (NSLocationType aType,
+ nsIFile* aLocation))
+
+/**
+ * Fire notifications to inform the toolkit about a new profile. This
+ * method should be called after XRE_InitEmbedding if the embedder
+ * wishes to run with a profile. Normally the embedder should call
+ * XRE_LockProfileDirectory to lock the directory before calling this
+ * method.
+ *
+ * @note There are two possibilities for selecting a profile:
+ *
+ * 1) Select the profile before calling XRE_InitEmbedding. The aAppDirProvider
+ * object passed to XRE_InitEmbedding should provide the
+ * NS_APP_USER_PROFILE_50_DIR key, and may also provide the following keys:
+ * - NS_APP_USER_PROFILE_LOCAL_50_DIR
+ * - NS_APP_PROFILE_DIR_STARTUP
+ * - NS_APP_PROFILE_LOCAL_DIR_STARTUP
+ * In this scenario XRE_NotifyProfile should be called immediately after
+ * XRE_InitEmbedding. Component registration information will be stored in
+ * the profile and JS components may be stored in the fastload cache.
+ *
+ * 2) Select a profile some time after calling XRE_InitEmbedding. In this case
+ * the embedder must install a directory service provider which provides
+ * NS_APP_USER_PROFILE_50_DIR and optionally
+ * NS_APP_USER_PROFILE_LOCAL_50_DIR. Component registration information
+ * will be stored in the application directory and JS components will not
+ * fastload.
+ */
+XRE_API(void,
+ XRE_NotifyProfile, ())
+
+/**
+ * Terminate embedding started with XRE_InitEmbedding or XRE_InitEmbedding2
+ */
+XRE_API(void,
+ XRE_TermEmbedding, ())
+
+/**
+ * Create a new nsXREAppData structure from an application.ini file.
+ *
+ * @param aINIFile The application.ini file to parse.
+ * @param aAppData A newly-allocated nsXREAppData structure. The caller is
+ * responsible for freeing this structure using
+ * XRE_FreeAppData.
+ */
+XRE_API(nsresult,
+ XRE_CreateAppData, (nsIFile* aINIFile,
+ nsXREAppData** aAppData))
+
+/**
+ * Parse an INI file (application.ini or override.ini) into an existing
+ * nsXREAppData structure.
+ *
+ * @param aINIFile The INI file to parse
+ * @param aAppData The nsXREAppData structure to fill.
+ */
+XRE_API(nsresult,
+ XRE_ParseAppData, (nsIFile* aINIFile,
+ nsXREAppData* aAppData))
+
+/**
+ * Free a nsXREAppData structure that was allocated with XRE_CreateAppData.
+ */
+XRE_API(void,
+ XRE_FreeAppData, (nsXREAppData* aAppData))
+
+enum GeckoProcessType
+{
+ GeckoProcessType_Default = 0,
+
+ GeckoProcessType_Plugin,
+ GeckoProcessType_Content,
+
+ GeckoProcessType_IPDLUnitTest,
+
+ GeckoProcessType_GMPlugin, // Gecko Media Plugin
+
+ GeckoProcessType_GPU, // GPU and compositor process
+
+ GeckoProcessType_End,
+ GeckoProcessType_Invalid = GeckoProcessType_End
+};
+
+static const char* const kGeckoProcessTypeString[] = {
+ "default",
+ "plugin",
+ "tab",
+ "ipdlunittest",
+ "geckomediaplugin",
+ "gpu"
+};
+
+static_assert(MOZ_ARRAY_LENGTH(kGeckoProcessTypeString) ==
+ GeckoProcessType_End,
+ "Array length mismatch");
+
+XRE_API(const char*,
+ XRE_ChildProcessTypeToString, (GeckoProcessType aProcessType))
+
+XRE_API(void,
+ XRE_SetProcessType, (const char* aProcessTypeString))
+
+#if defined(MOZ_CRASHREPORTER)
+// Used in the "master" parent process hosting the crash server
+XRE_API(bool,
+ XRE_TakeMinidumpForChild, (uint32_t aChildPid, nsIFile** aDump,
+ uint32_t* aSequence))
+
+// Used in child processes.
+XRE_API(bool,
+ XRE_SetRemoteExceptionHandler, (const char* aPipe))
+#endif
+
+namespace mozilla {
+namespace gmp {
+class GMPLoader;
+} // namespace gmp
+} // namespace mozilla
+
+XRE_API(nsresult,
+ XRE_InitChildProcess, (int aArgc,
+ char* aArgv[],
+ const XREChildData* aChildData))
+
+XRE_API(GeckoProcessType,
+ XRE_GetProcessType, ())
+
+XRE_API(bool,
+ XRE_IsParentProcess, ())
+
+XRE_API(bool,
+ XRE_IsContentProcess, ())
+
+XRE_API(bool,
+ XRE_IsGPUProcess, ())
+
+typedef void (*MainFunction)(void* aData);
+
+XRE_API(nsresult,
+ XRE_InitParentProcess, (int aArgc,
+ char* aArgv[],
+ MainFunction aMainFunction,
+ void* aMainFunctionExtraData))
+
+XRE_API(int,
+ XRE_RunIPDLTest, (int aArgc,
+ char* aArgv[]))
+
+XRE_API(nsresult,
+ XRE_RunAppShell, ())
+
+XRE_API(nsresult,
+ XRE_InitCommandLine, (int aArgc, char* aArgv[]))
+
+XRE_API(nsresult,
+ XRE_DeinitCommandLine, ())
+
+class MessageLoop;
+
+XRE_API(void,
+ XRE_ShutdownChildProcess, ())
+
+XRE_API(MessageLoop*,
+ XRE_GetIOMessageLoop, ())
+
+XRE_API(bool,
+ XRE_SendTestShellCommand, (JSContext* aCx,
+ JSString* aCommand,
+ void* aCallback))
+XRE_API(bool,
+ XRE_ShutdownTestShell, ())
+
+XRE_API(void,
+ XRE_InstallX11ErrorHandler, ())
+
+XRE_API(void,
+ XRE_TelemetryAccumulate, (int aID, uint32_t aSample))
+
+XRE_API(void,
+ XRE_StartupTimelineRecord, (int aEvent, mozilla::TimeStamp aWhen))
+
+XRE_API(void,
+ XRE_InitOmnijar, (nsIFile* aGreOmni,
+ nsIFile* aAppOmni))
+XRE_API(void,
+ XRE_StopLateWriteChecks, (void))
+
+XRE_API(void,
+ XRE_EnableSameExecutableForContentProc, ())
+
+XRE_API(int,
+ XRE_XPCShellMain, (int argc, char** argv, char** envp,
+ const XREShellData* aShellData))
+
+#if MOZ_WIDGET_GTK == 2
+XRE_API(void,
+ XRE_GlibInit, ())
+#endif
+
+
+#ifdef LIBFUZZER
+#include "LibFuzzerRegistry.h"
+
+XRE_API(void,
+ XRE_LibFuzzerSetMain, (int, char**, LibFuzzerMain))
+
+XRE_API(void,
+ XRE_LibFuzzerGetFuncs, (const char*, LibFuzzerInitFunc*,
+ LibFuzzerTestingFunc*))
+#endif // LIBFUZZER
+
+#endif // _nsXULAppAPI_h__
diff --git a/xpcom/build/perfprobe.cpp b/xpcom/build/perfprobe.cpp
new file mode 100644
index 000000000..118e73fc8
--- /dev/null
+++ b/xpcom/build/perfprobe.cpp
@@ -0,0 +1,242 @@
+/* -*- 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/. */
+
+/*****************************
+ Windows implementation of probes, using xperf
+ *****************************/
+#include <windows.h>
+#include <wmistr.h>
+#include <evntrace.h>
+
+#include "perfprobe.h"
+#include "nsAutoPtr.h"
+
+namespace mozilla {
+namespace probes {
+
+#if defined(MOZ_LOGGING)
+static LazyLogModule sProbeLog("SysProbe");
+#define LOG(x) MOZ_LOG(sProbeLog, mozilla::LogLevel::Debug, x)
+#else
+#define LOG(x)
+#endif
+
+// Utility function
+GUID
+CID_to_GUID(const nsCID& aCID)
+{
+ GUID result;
+ result.Data1 = aCID.m0;
+ result.Data2 = aCID.m1;
+ result.Data3 = aCID.m2;
+ for (int i = 0; i < 8; ++i) {
+ result.Data4[i] = aCID.m3[i];
+ }
+ return result;
+}
+
+
+// Implementation of Probe
+
+Probe::Probe(const nsCID& aGUID,
+ const nsACString& aName,
+ ProbeManager* aManager)
+ : mGUID(CID_to_GUID(aGUID))
+ , mName(aName)
+ , mManager(aManager)
+{
+}
+
+nsresult
+Probe::Trigger()
+{
+ if (!(mManager->mIsActive)) {
+ //Do not trigger if there is no session
+ return NS_OK;
+ }
+
+ _EVENT_TRACE_HEADER event;
+ ZeroMemory(&event, sizeof(event));
+ event.Size = sizeof(event);
+ event.Flags = WNODE_FLAG_TRACED_GUID ;
+ event.Guid = (const GUID)mGUID;
+ event.Class.Type = 1;
+ event.Class.Version = 0;
+ event.Class.Level = TRACE_LEVEL_INFORMATION;
+
+ ULONG result = TraceEvent(mManager->mSessionHandle, &event);
+
+ LOG(("Probes: Triggered %s, %s, %ld",
+ mName.Data(),
+ result == ERROR_SUCCESS ? "success" : "failure",
+ result));
+
+ nsresult rv;
+ switch (result) {
+ case ERROR_SUCCESS:
+ rv = NS_OK;
+ break;
+ case ERROR_INVALID_FLAG_NUMBER:
+ case ERROR_MORE_DATA:
+ case ERROR_INVALID_PARAMETER:
+ rv = NS_ERROR_INVALID_ARG;
+ break;
+ case ERROR_INVALID_HANDLE:
+ rv = NS_ERROR_FAILURE;
+ break;
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_OUTOFMEMORY:
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ break;
+ default:
+ rv = NS_ERROR_UNEXPECTED;
+ }
+ return rv;
+}
+
+
+// Implementation of ProbeManager
+
+ProbeManager::~ProbeManager()
+{
+ //If the manager goes out of scope, stop the session.
+ if (mIsActive && mRegistrationHandle) {
+ StopSession();
+ }
+}
+
+ProbeManager::ProbeManager(const nsCID& aApplicationUID,
+ const nsACString& aApplicationName)
+ : mIsActive(false)
+ , mApplicationUID(aApplicationUID)
+ , mApplicationName(aApplicationName)
+ , mSessionHandle(0)
+ , mRegistrationHandle(0)
+ , mInitialized(false)
+{
+#if defined(MOZ_LOGGING)
+ char cidStr[NSID_LENGTH];
+ aApplicationUID.ToProvidedString(cidStr);
+ LOG(("ProbeManager::Init for application %s, %s",
+ aApplicationName.Data(), cidStr));
+#endif
+}
+
+//Note: The Windows API is just a little bit scary there.
+//The only way to obtain the session handle is to
+//- ignore the session handle obtained from RegisterTraceGuids
+//- pass a callback
+//- in that callback, request the session handle through
+// GetTraceLoggerHandle and some opaque value received by the callback
+
+ULONG WINAPI
+ControlCallback(WMIDPREQUESTCODE aRequestCode,
+ PVOID aContext,
+ ULONG* aReserved,
+ PVOID aBuffer)
+{
+ ProbeManager* context = (ProbeManager*)aContext;
+ switch (aRequestCode) {
+ case WMI_ENABLE_EVENTS: {
+ context->mIsActive = true;
+ TRACEHANDLE sessionHandle = GetTraceLoggerHandle(aBuffer);
+ //Note: We only accept one handle
+ if ((HANDLE)sessionHandle == INVALID_HANDLE_VALUE) {
+ ULONG result = GetLastError();
+ LOG(("Probes: ControlCallback failed, %ul", result));
+ return result;
+ } else if (context->mIsActive && context->mSessionHandle &&
+ context->mSessionHandle != sessionHandle) {
+ LOG(("Probes: Can only handle one context at a time, "
+ "ignoring activation"));
+ return ERROR_SUCCESS;
+ } else {
+ context->mSessionHandle = sessionHandle;
+ LOG(("Probes: ControlCallback activated"));
+ return ERROR_SUCCESS;
+ }
+ }
+
+ case WMI_DISABLE_EVENTS:
+ context->mIsActive = false;
+ context->mSessionHandle = 0;
+ LOG(("Probes: ControlCallback deactivated"));
+ return ERROR_SUCCESS;
+
+ default:
+ LOG(("Probes: ControlCallback does not know what to do with %d",
+ aRequestCode));
+ return ERROR_INVALID_PARAMETER;
+ }
+}
+
+already_AddRefed<Probe>
+ProbeManager::GetProbe(const nsCID& aEventUID, const nsACString& aEventName)
+{
+ RefPtr<Probe> result(new Probe(aEventUID, aEventName, this));
+ mAllProbes.AppendElement(result);
+ return result.forget();
+}
+
+nsresult
+ProbeManager::StartSession()
+{
+ return StartSession(mAllProbes);
+}
+
+nsresult
+ProbeManager::StartSession(nsTArray<RefPtr<Probe>>& aProbes)
+{
+ const size_t probesCount = aProbes.Length();
+ _TRACE_GUID_REGISTRATION* probes = new _TRACE_GUID_REGISTRATION[probesCount];
+ for (unsigned int i = 0; i < probesCount; ++i) {
+ const Probe* probe = aProbes[i];
+ const Probe* probeX = static_cast<const Probe*>(probe);
+ probes[i].Guid = (LPCGUID)&probeX->mGUID;
+ }
+ ULONG result =
+ RegisterTraceGuids(&ControlCallback
+ /*RequestAddress: Sets mSessions appropriately.*/,
+ this
+ /*RequestContext: Passed to ControlCallback*/,
+ (LPGUID)&mApplicationUID
+ /*ControlGuid: Tracing GUID
+ the cast comes from MSDN examples*/,
+ probesCount
+ /*GuidCount: Number of probes*/,
+ probes
+ /*TraceGuidReg: Probes registration*/,
+ nullptr
+ /*MofImagePath: Must be nullptr, says MSDN*/,
+ nullptr
+ /*MofResourceName:Must be nullptr, says MSDN*/,
+ &mRegistrationHandle
+ /*RegistrationHandle: Handler.
+ used only for unregistration*/
+ );
+ delete[] probes;
+ if (NS_WARN_IF(result != ERROR_SUCCESS)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ return NS_OK;
+}
+
+nsresult
+ProbeManager::StopSession()
+{
+ LOG(("Probes: Stopping measures"));
+ if (mSessionHandle != 0) {
+ ULONG result = UnregisterTraceGuids(mSessionHandle);
+ mSessionHandle = 0;
+ if (result != ERROR_SUCCESS) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ }
+ return NS_OK;
+}
+
+} // namespace probes
+} // namespace mozilla
diff --git a/xpcom/build/perfprobe.h b/xpcom/build/perfprobe.h
new file mode 100644
index 000000000..bc3563654
--- /dev/null
+++ b/xpcom/build/perfprobe.h
@@ -0,0 +1,204 @@
+/* -*- 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/. */
+
+/**
+ * A mechanism for interacting with operating system-provided
+ * debugging/profiling tools such as Microsoft EWT/Windows Performance Toolkit.
+ */
+
+#ifndef mozilla_perfprobe_h
+#define mozilla_perfprobe_h
+
+#if !defined(XP_WIN)
+#error "For the moment, perfprobe.h is defined only for Windows platforms"
+#endif
+
+#include "nsError.h"
+#include "nsString.h"
+#include "mozilla/Logging.h"
+#include "nsTArray.h"
+#include "nsAutoPtr.h"
+#include <windows.h>
+#undef GetStartupInfo //Prevent Windows from polluting global namespace
+#include <wmistr.h>
+#include <evntrace.h>
+
+namespace mozilla {
+namespace probes {
+
+class ProbeManager;
+
+/**
+ * A data structure supporting a trigger operation that can be used to
+ * send information to the operating system.
+ */
+
+class Probe
+{
+public:
+ NS_INLINE_DECL_REFCOUNTING(Probe)
+
+ /**
+ * Trigger the event.
+ *
+ * Note: Can be called from any thread.
+ */
+ nsresult Trigger();
+
+protected:
+ ~Probe() {};
+
+ Probe(const nsCID& aGUID, const nsACString& aName, ProbeManager* aManager);
+ friend class ProbeManager;
+
+protected:
+
+ /**
+ * The system GUID associated to this probe. See the documentation
+ * of |ProbeManager::Make| for more details.
+ */
+ const GUID mGUID;
+
+ /**
+ * The name of this probe. See the documentation
+ * of |ProbeManager::Make| for more details.
+ */
+ const nsCString mName;
+
+ /**
+ * The ProbeManager managing this probe.
+ *
+ * Note: This is a weak reference to avoid a useless cycle.
+ */
+ class ProbeManager* mManager;
+};
+
+
+/**
+ * A manager for a group of probes.
+ *
+ * You can have several managers in one application, provided that they all
+ * have distinct IDs and names. However, having more than 2 is considered a bad
+ * practice.
+ */
+class ProbeManager
+{
+public:
+ NS_INLINE_DECL_REFCOUNTING(ProbeManager)
+
+ /**
+ * Create a new probe manager.
+ *
+ * This constructor should be called from the main thread.
+ *
+ * @param aApplicationUID The unique ID of the probe. Under Windows, this
+ * unique ID must have been previously registered using an external tool.
+ * See MyCategory on http://msdn.microsoft.com/en-us/library/aa364100.aspx
+ * @param aApplicationName A name for the probe. Currently used only for
+ * logging purposes. In the future, may be attached to the data sent to the
+ * operating system.
+ *
+ * Note: If two ProbeManagers are constructed with the same uid and/or name,
+ * behavior is unspecified.
+ */
+ ProbeManager(const nsCID& aApplicationUID,
+ const nsACString& aApplicationName);
+
+ /**
+ * Acquire a probe.
+ *
+ * Note: Only probes acquired before the call to SetReady are taken into
+ * account
+ * Note: Can be called only from the main thread.
+ *
+ * @param aEventUID The unique ID of the probe. Under Windows, this unique
+ * ID must have been previously registered using an external tool.
+ * See MyCategory on http://msdn.microsoft.com/en-us/library/aa364100.aspx
+ * @param aEventName A name for the probe. Currently used only for logging
+ * purposes. In the
+ * future, may be attached to the data sent to the operating system.
+ * @return Either |null| in case of error or a valid |Probe*|.
+ *
+ * Note: If this method is called twice with the same uid and/or name,
+ * behavior is undefined.
+ */
+ already_AddRefed<Probe> GetProbe(const nsCID& aEventUID,
+ const nsACString& aEventName);
+
+ /**
+ * Start/stop the measuring session.
+ *
+ * This method should be called from the main thread.
+ *
+ * Note that starting an already started probe manager has no effect,
+ * nor does stopping an already stopped probe manager.
+ */
+ nsresult StartSession();
+ nsresult StopSession();
+
+ /**
+ * @return true If measures are currently on, i.e. if triggering probes is any
+ * is useful. You do not have to check this before triggering a probe, unless
+ * this can avoid complex computations.
+ */
+ bool IsActive();
+
+protected:
+ ~ProbeManager();
+
+ nsresult StartSession(nsTArray<RefPtr<Probe>>& aProbes);
+ nsresult Init(const nsCID& aApplicationUID,
+ const nsACString& aApplicationName);
+
+protected:
+ /**
+ * `true` if a session is in activity, `false` otherwise.
+ */
+ bool mIsActive;
+
+ /**
+ * The UID of this manager.
+ * See documentation above for registration steps that you
+ * may have to take.
+ */
+ nsCID mApplicationUID;
+
+ /**
+ * The name of the application.
+ */
+ nsCString mApplicationName;
+
+ /**
+ * All the probes that have been created for this manager.
+ */
+ nsTArray<RefPtr<Probe>> mAllProbes;
+
+ /**
+ * Handle used for triggering events
+ */
+ TRACEHANDLE mSessionHandle;
+
+ /**
+ * Handle used for registration/unregistration
+ */
+ TRACEHANDLE mRegistrationHandle;
+
+ /**
+ * `true` if initialization has been performed, `false` until then.
+ */
+ bool mInitialized;
+
+ friend class Probe; // Needs to access |mSessionHandle|
+ friend ULONG WINAPI ControlCallback(WMIDPREQUESTCODE aRequestCode,
+ PVOID aContext,
+ ULONG* aReserved,
+ PVOID aBuffer); // Sets |mSessionHandle|
+};
+
+} // namespace probes
+} // namespace mozilla
+
+#endif //mozilla_perfprobe_h
diff --git a/xpcom/build/xpcom_alpha.def b/xpcom/build/xpcom_alpha.def
new file mode 100644
index 000000000..38fedfa17
--- /dev/null
+++ b/xpcom/build/xpcom_alpha.def
@@ -0,0 +1,256 @@
+;+# 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/.
+
+LIBRARY xpcom
+DESCRIPTION "xpcom library"
+
+EXPORTS
+ ?Stub3@nsXPTCStubBase@@UAAIXZ
+ ?Stub4@nsXPTCStubBase@@UAAIXZ
+ ?Stub5@nsXPTCStubBase@@UAAIXZ
+ ?Stub6@nsXPTCStubBase@@UAAIXZ
+ ?Stub7@nsXPTCStubBase@@UAAIXZ
+ ?Stub8@nsXPTCStubBase@@UAAIXZ
+ ?Stub9@nsXPTCStubBase@@UAAIXZ
+ ?Stub10@nsXPTCStubBase@@UAAIXZ
+ ?Stub11@nsXPTCStubBase@@UAAIXZ
+ ?Stub12@nsXPTCStubBase@@UAAIXZ
+ ?Stub13@nsXPTCStubBase@@UAAIXZ
+ ?Stub14@nsXPTCStubBase@@UAAIXZ
+ ?Stub15@nsXPTCStubBase@@UAAIXZ
+ ?Stub16@nsXPTCStubBase@@UAAIXZ
+ ?Stub17@nsXPTCStubBase@@UAAIXZ
+ ?Stub18@nsXPTCStubBase@@UAAIXZ
+ ?Stub19@nsXPTCStubBase@@UAAIXZ
+ ?Stub20@nsXPTCStubBase@@UAAIXZ
+ ?Stub21@nsXPTCStubBase@@UAAIXZ
+ ?Stub22@nsXPTCStubBase@@UAAIXZ
+ ?Stub23@nsXPTCStubBase@@UAAIXZ
+ ?Stub24@nsXPTCStubBase@@UAAIXZ
+ ?Stub25@nsXPTCStubBase@@UAAIXZ
+ ?Stub26@nsXPTCStubBase@@UAAIXZ
+ ?Stub27@nsXPTCStubBase@@UAAIXZ
+ ?Stub28@nsXPTCStubBase@@UAAIXZ
+ ?Stub29@nsXPTCStubBase@@UAAIXZ
+ ?Stub30@nsXPTCStubBase@@UAAIXZ
+ ?Stub31@nsXPTCStubBase@@UAAIXZ
+ ?Stub32@nsXPTCStubBase@@UAAIXZ
+ ?Stub33@nsXPTCStubBase@@UAAIXZ
+ ?Stub34@nsXPTCStubBase@@UAAIXZ
+ ?Stub35@nsXPTCStubBase@@UAAIXZ
+ ?Stub36@nsXPTCStubBase@@UAAIXZ
+ ?Stub37@nsXPTCStubBase@@UAAIXZ
+ ?Stub38@nsXPTCStubBase@@UAAIXZ
+ ?Stub39@nsXPTCStubBase@@UAAIXZ
+ ?Stub40@nsXPTCStubBase@@UAAIXZ
+ ?Stub41@nsXPTCStubBase@@UAAIXZ
+ ?Stub42@nsXPTCStubBase@@UAAIXZ
+ ?Stub43@nsXPTCStubBase@@UAAIXZ
+ ?Stub44@nsXPTCStubBase@@UAAIXZ
+ ?Stub45@nsXPTCStubBase@@UAAIXZ
+ ?Stub46@nsXPTCStubBase@@UAAIXZ
+ ?Stub47@nsXPTCStubBase@@UAAIXZ
+ ?Stub48@nsXPTCStubBase@@UAAIXZ
+ ?Stub49@nsXPTCStubBase@@UAAIXZ
+ ?Stub50@nsXPTCStubBase@@UAAIXZ
+ ?Stub51@nsXPTCStubBase@@UAAIXZ
+ ?Stub52@nsXPTCStubBase@@UAAIXZ
+ ?Stub53@nsXPTCStubBase@@UAAIXZ
+ ?Stub54@nsXPTCStubBase@@UAAIXZ
+ ?Stub55@nsXPTCStubBase@@UAAIXZ
+ ?Stub56@nsXPTCStubBase@@UAAIXZ
+ ?Stub57@nsXPTCStubBase@@UAAIXZ
+ ?Stub58@nsXPTCStubBase@@UAAIXZ
+ ?Stub59@nsXPTCStubBase@@UAAIXZ
+ ?Stub60@nsXPTCStubBase@@UAAIXZ
+ ?Stub61@nsXPTCStubBase@@UAAIXZ
+ ?Stub62@nsXPTCStubBase@@UAAIXZ
+ ?Stub63@nsXPTCStubBase@@UAAIXZ
+ ?Stub64@nsXPTCStubBase@@UAAIXZ
+ ?Stub65@nsXPTCStubBase@@UAAIXZ
+ ?Stub66@nsXPTCStubBase@@UAAIXZ
+ ?Stub67@nsXPTCStubBase@@UAAIXZ
+ ?Stub68@nsXPTCStubBase@@UAAIXZ
+ ?Stub69@nsXPTCStubBase@@UAAIXZ
+ ?Stub70@nsXPTCStubBase@@UAAIXZ
+ ?Stub71@nsXPTCStubBase@@UAAIXZ
+ ?Stub72@nsXPTCStubBase@@UAAIXZ
+ ?Stub73@nsXPTCStubBase@@UAAIXZ
+ ?Stub74@nsXPTCStubBase@@UAAIXZ
+ ?Stub75@nsXPTCStubBase@@UAAIXZ
+ ?Stub76@nsXPTCStubBase@@UAAIXZ
+ ?Stub77@nsXPTCStubBase@@UAAIXZ
+ ?Stub78@nsXPTCStubBase@@UAAIXZ
+ ?Stub79@nsXPTCStubBase@@UAAIXZ
+ ?Stub80@nsXPTCStubBase@@UAAIXZ
+ ?Stub81@nsXPTCStubBase@@UAAIXZ
+ ?Stub82@nsXPTCStubBase@@UAAIXZ
+ ?Stub83@nsXPTCStubBase@@UAAIXZ
+ ?Stub84@nsXPTCStubBase@@UAAIXZ
+ ?Stub85@nsXPTCStubBase@@UAAIXZ
+ ?Stub86@nsXPTCStubBase@@UAAIXZ
+ ?Stub87@nsXPTCStubBase@@UAAIXZ
+ ?Stub88@nsXPTCStubBase@@UAAIXZ
+ ?Stub89@nsXPTCStubBase@@UAAIXZ
+ ?Stub90@nsXPTCStubBase@@UAAIXZ
+ ?Stub91@nsXPTCStubBase@@UAAIXZ
+ ?Stub92@nsXPTCStubBase@@UAAIXZ
+ ?Stub93@nsXPTCStubBase@@UAAIXZ
+ ?Stub94@nsXPTCStubBase@@UAAIXZ
+ ?Stub95@nsXPTCStubBase@@UAAIXZ
+ ?Stub96@nsXPTCStubBase@@UAAIXZ
+ ?Stub97@nsXPTCStubBase@@UAAIXZ
+ ?Stub98@nsXPTCStubBase@@UAAIXZ
+ ?Stub99@nsXPTCStubBase@@UAAIXZ
+ ?Stub100@nsXPTCStubBase@@UAAIXZ
+ ?Stub101@nsXPTCStubBase@@UAAIXZ
+ ?Stub102@nsXPTCStubBase@@UAAIXZ
+ ?Stub103@nsXPTCStubBase@@UAAIXZ
+ ?Stub104@nsXPTCStubBase@@UAAIXZ
+ ?Stub105@nsXPTCStubBase@@UAAIXZ
+ ?Stub106@nsXPTCStubBase@@UAAIXZ
+ ?Stub107@nsXPTCStubBase@@UAAIXZ
+ ?Stub108@nsXPTCStubBase@@UAAIXZ
+ ?Stub109@nsXPTCStubBase@@UAAIXZ
+ ?Stub110@nsXPTCStubBase@@UAAIXZ
+ ?Stub111@nsXPTCStubBase@@UAAIXZ
+ ?Stub112@nsXPTCStubBase@@UAAIXZ
+ ?Stub113@nsXPTCStubBase@@UAAIXZ
+ ?Stub114@nsXPTCStubBase@@UAAIXZ
+ ?Stub115@nsXPTCStubBase@@UAAIXZ
+ ?Stub116@nsXPTCStubBase@@UAAIXZ
+ ?Stub117@nsXPTCStubBase@@UAAIXZ
+ ?Stub118@nsXPTCStubBase@@UAAIXZ
+ ?Stub119@nsXPTCStubBase@@UAAIXZ
+ ?Stub120@nsXPTCStubBase@@UAAIXZ
+ ?Stub121@nsXPTCStubBase@@UAAIXZ
+ ?Stub122@nsXPTCStubBase@@UAAIXZ
+ ?Stub123@nsXPTCStubBase@@UAAIXZ
+ ?Stub124@nsXPTCStubBase@@UAAIXZ
+ ?Stub125@nsXPTCStubBase@@UAAIXZ
+ ?Stub126@nsXPTCStubBase@@UAAIXZ
+ ?Stub127@nsXPTCStubBase@@UAAIXZ
+ ?Stub128@nsXPTCStubBase@@UAAIXZ
+ ?Stub129@nsXPTCStubBase@@UAAIXZ
+ ?Stub130@nsXPTCStubBase@@UAAIXZ
+ ?Stub131@nsXPTCStubBase@@UAAIXZ
+ ?Stub132@nsXPTCStubBase@@UAAIXZ
+ ?Stub133@nsXPTCStubBase@@UAAIXZ
+ ?Stub134@nsXPTCStubBase@@UAAIXZ
+ ?Stub135@nsXPTCStubBase@@UAAIXZ
+ ?Stub136@nsXPTCStubBase@@UAAIXZ
+ ?Stub137@nsXPTCStubBase@@UAAIXZ
+ ?Stub138@nsXPTCStubBase@@UAAIXZ
+ ?Stub139@nsXPTCStubBase@@UAAIXZ
+ ?Stub140@nsXPTCStubBase@@UAAIXZ
+ ?Stub141@nsXPTCStubBase@@UAAIXZ
+ ?Stub142@nsXPTCStubBase@@UAAIXZ
+ ?Stub143@nsXPTCStubBase@@UAAIXZ
+ ?Stub144@nsXPTCStubBase@@UAAIXZ
+ ?Stub145@nsXPTCStubBase@@UAAIXZ
+ ?Stub146@nsXPTCStubBase@@UAAIXZ
+ ?Stub147@nsXPTCStubBase@@UAAIXZ
+ ?Stub148@nsXPTCStubBase@@UAAIXZ
+ ?Stub149@nsXPTCStubBase@@UAAIXZ
+ ?Stub150@nsXPTCStubBase@@UAAIXZ
+ ?Stub151@nsXPTCStubBase@@UAAIXZ
+ ?Stub152@nsXPTCStubBase@@UAAIXZ
+ ?Stub153@nsXPTCStubBase@@UAAIXZ
+ ?Stub154@nsXPTCStubBase@@UAAIXZ
+ ?Stub155@nsXPTCStubBase@@UAAIXZ
+ ?Stub156@nsXPTCStubBase@@UAAIXZ
+ ?Stub157@nsXPTCStubBase@@UAAIXZ
+ ?Stub158@nsXPTCStubBase@@UAAIXZ
+ ?Stub159@nsXPTCStubBase@@UAAIXZ
+ ?Stub160@nsXPTCStubBase@@UAAIXZ
+ ?Stub161@nsXPTCStubBase@@UAAIXZ
+ ?Stub162@nsXPTCStubBase@@UAAIXZ
+ ?Stub163@nsXPTCStubBase@@UAAIXZ
+ ?Stub164@nsXPTCStubBase@@UAAIXZ
+ ?Stub165@nsXPTCStubBase@@UAAIXZ
+ ?Stub166@nsXPTCStubBase@@UAAIXZ
+ ?Stub167@nsXPTCStubBase@@UAAIXZ
+ ?Stub168@nsXPTCStubBase@@UAAIXZ
+ ?Stub169@nsXPTCStubBase@@UAAIXZ
+ ?Stub170@nsXPTCStubBase@@UAAIXZ
+ ?Stub171@nsXPTCStubBase@@UAAIXZ
+ ?Stub172@nsXPTCStubBase@@UAAIXZ
+ ?Stub173@nsXPTCStubBase@@UAAIXZ
+ ?Stub174@nsXPTCStubBase@@UAAIXZ
+ ?Stub175@nsXPTCStubBase@@UAAIXZ
+ ?Stub176@nsXPTCStubBase@@UAAIXZ
+ ?Stub177@nsXPTCStubBase@@UAAIXZ
+ ?Stub178@nsXPTCStubBase@@UAAIXZ
+ ?Stub179@nsXPTCStubBase@@UAAIXZ
+ ?Stub180@nsXPTCStubBase@@UAAIXZ
+ ?Stub181@nsXPTCStubBase@@UAAIXZ
+ ?Stub182@nsXPTCStubBase@@UAAIXZ
+ ?Stub183@nsXPTCStubBase@@UAAIXZ
+ ?Stub184@nsXPTCStubBase@@UAAIXZ
+ ?Stub185@nsXPTCStubBase@@UAAIXZ
+ ?Stub186@nsXPTCStubBase@@UAAIXZ
+ ?Stub187@nsXPTCStubBase@@UAAIXZ
+ ?Stub188@nsXPTCStubBase@@UAAIXZ
+ ?Stub189@nsXPTCStubBase@@UAAIXZ
+ ?Stub190@nsXPTCStubBase@@UAAIXZ
+ ?Stub191@nsXPTCStubBase@@UAAIXZ
+ ?Stub192@nsXPTCStubBase@@UAAIXZ
+ ?Stub193@nsXPTCStubBase@@UAAIXZ
+ ?Stub194@nsXPTCStubBase@@UAAIXZ
+ ?Stub195@nsXPTCStubBase@@UAAIXZ
+ ?Stub196@nsXPTCStubBase@@UAAIXZ
+ ?Stub197@nsXPTCStubBase@@UAAIXZ
+ ?Stub198@nsXPTCStubBase@@UAAIXZ
+ ?Stub199@nsXPTCStubBase@@UAAIXZ
+ ?Stub200@nsXPTCStubBase@@UAAIXZ
+ ?Stub201@nsXPTCStubBase@@UAAIXZ
+ ?Stub202@nsXPTCStubBase@@UAAIXZ
+ ?Stub203@nsXPTCStubBase@@UAAIXZ
+ ?Stub204@nsXPTCStubBase@@UAAIXZ
+ ?Stub205@nsXPTCStubBase@@UAAIXZ
+ ?Stub206@nsXPTCStubBase@@UAAIXZ
+ ?Stub207@nsXPTCStubBase@@UAAIXZ
+ ?Stub208@nsXPTCStubBase@@UAAIXZ
+ ?Stub209@nsXPTCStubBase@@UAAIXZ
+ ?Stub210@nsXPTCStubBase@@UAAIXZ
+ ?Stub211@nsXPTCStubBase@@UAAIXZ
+ ?Stub212@nsXPTCStubBase@@UAAIXZ
+ ?Stub213@nsXPTCStubBase@@UAAIXZ
+ ?Stub214@nsXPTCStubBase@@UAAIXZ
+ ?Stub215@nsXPTCStubBase@@UAAIXZ
+ ?Stub216@nsXPTCStubBase@@UAAIXZ
+ ?Stub217@nsXPTCStubBase@@UAAIXZ
+ ?Stub218@nsXPTCStubBase@@UAAIXZ
+ ?Stub219@nsXPTCStubBase@@UAAIXZ
+ ?Stub220@nsXPTCStubBase@@UAAIXZ
+ ?Stub221@nsXPTCStubBase@@UAAIXZ
+ ?Stub222@nsXPTCStubBase@@UAAIXZ
+ ?Stub223@nsXPTCStubBase@@UAAIXZ
+ ?Stub224@nsXPTCStubBase@@UAAIXZ
+ ?Stub225@nsXPTCStubBase@@UAAIXZ
+ ?Stub226@nsXPTCStubBase@@UAAIXZ
+ ?Stub227@nsXPTCStubBase@@UAAIXZ
+ ?Stub228@nsXPTCStubBase@@UAAIXZ
+ ?Stub229@nsXPTCStubBase@@UAAIXZ
+ ?Stub230@nsXPTCStubBase@@UAAIXZ
+ ?Stub231@nsXPTCStubBase@@UAAIXZ
+ ?Stub232@nsXPTCStubBase@@UAAIXZ
+ ?Stub233@nsXPTCStubBase@@UAAIXZ
+ ?Stub234@nsXPTCStubBase@@UAAIXZ
+ ?Stub235@nsXPTCStubBase@@UAAIXZ
+ ?Stub236@nsXPTCStubBase@@UAAIXZ
+ ?Stub237@nsXPTCStubBase@@UAAIXZ
+ ?Stub238@nsXPTCStubBase@@UAAIXZ
+ ?Stub239@nsXPTCStubBase@@UAAIXZ
+ ?Stub240@nsXPTCStubBase@@UAAIXZ
+ ?Stub241@nsXPTCStubBase@@UAAIXZ
+ ?Stub242@nsXPTCStubBase@@UAAIXZ
+ ?Stub243@nsXPTCStubBase@@UAAIXZ
+ ?Stub244@nsXPTCStubBase@@UAAIXZ
+ ?Stub245@nsXPTCStubBase@@UAAIXZ
+ ?Stub246@nsXPTCStubBase@@UAAIXZ
+ ?Stub247@nsXPTCStubBase@@UAAIXZ
+ ?Stub248@nsXPTCStubBase@@UAAIXZ
+ ?Stub249@nsXPTCStubBase@@UAAIXZ
+
diff --git a/xpcom/build/xrecore.h b/xpcom/build/xrecore.h
new file mode 100644
index 000000000..9749a8e90
--- /dev/null
+++ b/xpcom/build/xrecore.h
@@ -0,0 +1,25 @@
+/* -*- 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 xrecore_h__
+#define xrecore_h__
+
+#include "nscore.h"
+
+/**
+ * Import/export macros for libXUL APIs.
+ */
+#ifdef XPCOM_GLUE
+#define XRE_API(type, name, params) \
+ typedef type (NS_FROZENCALL * name##Type) params; \
+ extern name##Type name NS_HIDDEN;
+#elif defined(IMPL_LIBXUL)
+#define XRE_API(type, name, params) EXPORT_XPCOM_API(type) name params;
+#else
+#define XRE_API(type, name, params) IMPORT_XPCOM_API(type) name params;
+#endif
+
+#endif // xrecore_h__