summaryrefslogtreecommitdiffstats
path: root/xpcom/build
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /xpcom/build
parentInitial commit. (diff)
downloadfirefox-e51783d008170d9ab27d25da98ca3a38b0a41b67.tar.xz
firefox-e51783d008170d9ab27d25da98ca3a38b0a41b67.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xpcom/build')
-rw-r--r--xpcom/build/BinaryPath.h321
-rw-r--r--xpcom/build/FileLocation.cpp215
-rw-r--r--xpcom/build/FileLocation.h140
-rw-r--r--xpcom/build/IOInterposer.cpp532
-rw-r--r--xpcom/build/IOInterposer.h290
-rw-r--r--xpcom/build/IOInterposerPrivate.h117
-rw-r--r--xpcom/build/LateWriteChecks.cpp262
-rw-r--r--xpcom/build/LateWriteChecks.h78
-rw-r--r--xpcom/build/MainThreadIOLogger.cpp206
-rw-r--r--xpcom/build/MainThreadIOLogger.h18
-rw-r--r--xpcom/build/NSPRInterposer.cpp207
-rw-r--r--xpcom/build/NSPRInterposer.h28
-rw-r--r--xpcom/build/Omnijar.cpp231
-rw-r--r--xpcom/build/Omnijar.h180
-rw-r--r--xpcom/build/PoisonIOInterposer.h93
-rw-r--r--xpcom/build/PoisonIOInterposerBase.cpp244
-rw-r--r--xpcom/build/PoisonIOInterposerMac.cpp348
-rw-r--r--xpcom/build/PoisonIOInterposerStub.cpp16
-rw-r--r--xpcom/build/PoisonIOInterposerWin.cpp510
-rw-r--r--xpcom/build/Services.py170
-rw-r--r--xpcom/build/SmallArrayLRUCache.h199
-rw-r--r--xpcom/build/XPCOM.h167
-rw-r--r--xpcom/build/XPCOMInit.cpp819
-rw-r--r--xpcom/build/XPCOMModule.h20
-rw-r--r--xpcom/build/XPCOMModule.inc3
-rw-r--r--xpcom/build/XREAppData.h231
-rw-r--r--xpcom/build/XREChildData.h44
-rw-r--r--xpcom/build/XREShellData.h39
-rw-r--r--xpcom/build/components.conf267
-rw-r--r--xpcom/build/gen_process_types.py34
-rw-r--r--xpcom/build/mach_override.c793
-rw-r--r--xpcom/build/mach_override.h121
-rw-r--r--xpcom/build/moz.build108
-rw-r--r--xpcom/build/nsXPCOM.h377
-rw-r--r--xpcom/build/nsXPCOMCID.h220
-rw-r--r--xpcom/build/nsXPCOMCIDInternal.h49
-rw-r--r--xpcom/build/nsXPCOMPrivate.h126
-rw-r--r--xpcom/build/nsXULAppAPI.h386
-rw-r--r--xpcom/build/perfprobe.cpp215
-rw-r--r--xpcom/build/perfprobe.h198
-rw-r--r--xpcom/build/xpcom_alpha.def256
41 files changed, 8878 insertions, 0 deletions
diff --git a/xpcom/build/BinaryPath.h b/xpcom/build/BinaryPath.h
new file mode 100644
index 0000000000..47bd6940cb
--- /dev/null
+++ b/xpcom/build/BinaryPath.h
@@ -0,0 +1,321 @@
+/* -*- 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_DARWIN)
+# include <CoreFoundation/CoreFoundation.h>
+#elif defined(XP_UNIX)
+# include <unistd.h>
+# include <stdlib.h>
+# include <string.h>
+#endif
+#if defined(__FreeBSD__) || defined(__DragonFly__) || \
+ defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
+# include <sys/sysctl.h>
+#endif
+#if defined(__OpenBSD__)
+# include <sys/stat.h>
+#endif
+#include "mozilla/UniquePtr.h"
+#include "mozilla/UniquePtrExtensions.h"
+
+#ifdef MOZILLA_INTERNAL_API
+# include "nsCOMPtr.h"
+# include "nsIFile.h"
+# include "nsString.h"
+#endif
+
+namespace mozilla {
+
+class BinaryPath {
+ public:
+#ifdef XP_WIN
+ static nsresult Get(char aResult[MAXPATHLEN]) {
+ wchar_t wide_path[MAXPATHLEN];
+ nsresult rv = GetW(wide_path);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ WideCharToMultiByte(CP_UTF8, 0, wide_path, -1, aResult, MAXPATHLEN, nullptr,
+ nullptr);
+ return NS_OK;
+ }
+
+ static nsresult GetLong(wchar_t aResult[MAXPATHLEN]) {
+ static bool cached = false;
+ static wchar_t exeLongPath[MAXPATHLEN] = L"";
+
+ if (!cached) {
+ nsresult rv = GetW(exeLongPath);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (!::GetLongPathNameW(exeLongPath, exeLongPath, MAXPATHLEN)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ cached = true;
+ }
+
+ if (wcscpy_s(aResult, MAXPATHLEN, exeLongPath)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+ }
+
+ private:
+ static nsresult GetW(wchar_t aResult[MAXPATHLEN]) {
+ static bool cached = false;
+ static wchar_t moduleFileName[MAXPATHLEN] = L"";
+
+ if (!cached) {
+ if (!::GetModuleFileNameW(0, moduleFileName, MAXPATHLEN)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ cached = true;
+ }
+
+ if (wcscpy_s(aResult, MAXPATHLEN, moduleFileName)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+ }
+
+#elif defined(XP_DARWIN)
+ static nsresult Get(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(char aResult[MAXPATHLEN]) {
+ // On Android, we use the MOZ_ANDROID_LIBDIR variable that is set by the
+ // Java bootstrap code.
+ const char* libDir = getenv("MOZ_ANDROID_LIBDIR");
+ if (!libDir) {
+ return NS_ERROR_FAILURE;
+ }
+
+ snprintf(aResult, MAXPATHLEN, "%s/%s", libDir, "dummy");
+ aResult[MAXPATHLEN - 1] = '\0';
+ return NS_OK;
+ }
+
+#elif defined(XP_LINUX) || defined(XP_SOLARIS)
+ static nsresult Get(char aResult[MAXPATHLEN]) {
+# if defined(XP_SOLARIS)
+ const char path[] = "/proc/self/path/a.out";
+# else
+ const char path[] = "/proc/self/exe";
+# endif
+
+ ssize_t len = readlink(path, aResult, MAXPATHLEN - 1);
+ if (len < 0) {
+ return NS_ERROR_FAILURE;
+ }
+ aResult[len] = '\0';
+# if defined(XP_LINUX)
+ // Removing suffix " (deleted)" from the binary path
+ const char suffix[] = " (deleted)";
+ const ssize_t suffix_len = sizeof(suffix);
+ if (len >= suffix_len) {
+ char* result_end = &aResult[len - (suffix_len - 1)];
+ if (memcmp(result_end, suffix, suffix_len) == 0) {
+ *result_end = '\0';
+ }
+ }
+# endif
+ return NS_OK;
+ }
+
+#elif defined(__FreeBSD__) || defined(__DragonFly__) || \
+ defined(__FreeBSD_kernel__) || defined(__NetBSD__)
+ static nsresult Get(char aResult[MAXPATHLEN]) {
+ int mib[4];
+ mib[0] = CTL_KERN;
+# ifdef __NetBSD__
+ mib[1] = KERN_PROC_ARGS;
+ mib[2] = -1;
+ mib[3] = KERN_PROC_PATHNAME;
+# else
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PATHNAME;
+ mib[3] = -1;
+# endif
+
+ size_t len = MAXPATHLEN;
+ if (sysctl(mib, 4, aResult, &len, nullptr, 0) < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+ }
+
+#elif defined(__OpenBSD__)
+ static nsresult Get(char aResult[MAXPATHLEN]) {
+ static bool cached = false;
+ static char cachedPath[MAXPATHLEN];
+ nsresult r;
+ int mib[4];
+ if (cached) {
+ if (strlcpy(aResult, cachedPath, MAXPATHLEN) >= MAXPATHLEN) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+ }
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC_ARGS;
+ mib[2] = getpid();
+ mib[3] = KERN_PROC_ARGV;
+
+ size_t len = 0;
+ if (sysctl(mib, 4, nullptr, &len, nullptr, 0) < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ auto argv = MakeUnique<const char*[]>(len / sizeof(const char*));
+ if (sysctl(mib, 4, argv.get(), &len, nullptr, 0) < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ r = GetFromArgv0(argv[0], aResult);
+ if (NS_SUCCEEDED(r)) {
+ if (strlcpy(cachedPath, aResult, MAXPATHLEN) >= MAXPATHLEN) {
+ return NS_ERROR_FAILURE;
+ }
+ cached = true;
+ }
+ return r;
+ }
+
+ static nsresult GetFromArgv0(const char* aArgv0, char aResult[MAXPATHLEN]) {
+ struct stat fileStat;
+ // 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 UniqueFreePtr<char> Get() {
+ char path[MAXPATHLEN];
+ if (NS_FAILED(Get(path))) {
+ return nullptr;
+ }
+ UniqueFreePtr<char> result;
+ result.reset(strdup(path));
+ return result;
+ }
+
+#ifdef MOZILLA_INTERNAL_API
+ static nsresult GetFile(nsIFile** aResult) {
+ nsCOMPtr<nsIFile> lf;
+# ifdef XP_WIN
+ wchar_t exePath[MAXPATHLEN];
+ nsresult rv = GetW(exePath);
+# else
+ char exePath[MAXPATHLEN];
+ nsresult rv = Get(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;
+ }
+#endif
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_BinaryPath_h */
diff --git a/xpcom/build/FileLocation.cpp b/xpcom/build/FileLocation.cpp
new file mode 100644
index 0000000000..5162546f1d
--- /dev/null
+++ b/xpcom/build/FileLocation.cpp
@@ -0,0 +1,215 @@
+/* -*- 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"
+
+#include "mozilla/UniquePtrExtensions.h"
+
+namespace mozilla {
+
+FileLocation::FileLocation() = default;
+
+FileLocation::~FileLocation() = default;
+
+FileLocation::FileLocation(nsIFile* aFile) { Init(aFile); }
+
+FileLocation::FileLocation(nsIFile* aFile, const char* aPath) {
+ Init(aFile, aPath);
+}
+
+FileLocation::FileLocation(nsZipArchive* aZip, const char* aPath) {
+ Init(aZip, aPath);
+}
+
+FileLocation::FileLocation(const FileLocation& aOther)
+
+ = default;
+
+FileLocation::FileLocation(FileLocation&& aOther)
+ : mBaseFile(std::move(aOther.mBaseFile)),
+ mBaseZip(std::move(aOther.mBaseZip)),
+ mPath(std::move(aOther.mPath)) {
+ aOther.mPath.Truncate();
+}
+
+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.InsertLiteral("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,
+ getter_Transfers(aData.mFd));
+ }
+ aData.mZip = mBaseZip;
+ if (!aData.mZip) {
+ // this can return nullptr
+ aData.mZip = nsZipArchive::OpenArchive(mBaseFile);
+ }
+ if (aData.mZip) {
+ 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.get(), &fileInfo)) {
+ return NS_ErrorAccordingToNSPR();
+ }
+
+ if (fileInfo.size > int64_t(UINT32_MAX)) {
+ return NS_ERROR_FILE_TOO_BIG;
+ }
+
+ *aResult = fileInfo.size;
+ return NS_OK;
+ }
+ 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.get(), aBuf + totalRead,
+ XPCOM_MIN(aLen - totalRead, uint32_t(INT32_MAX)));
+ if (read < 0) {
+ return NS_ErrorAccordingToNSPR();
+ }
+ totalRead += read;
+ }
+ return NS_OK;
+ }
+ if (mItem) {
+ nsZipCursor cursor(mItem, mZip, reinterpret_cast<uint8_t*>(aBuf), aLen,
+ true);
+ uint32_t readLen;
+ cursor.Copy(&readLen);
+ if (readLen != aLen) {
+ 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 0000000000..0988a9595f
--- /dev/null
+++ b/xpcom/build/FileLocation.h
@@ -0,0 +1,140 @@
+/* -*- 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 "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();
+
+ FileLocation(const FileLocation& aOther);
+ FileLocation(FileLocation&& aOther);
+
+ FileLocation& operator=(const FileLocation&) = default;
+
+ /**
+ * 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);
+
+ /**
+ * 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();
+
+ nsZipArchive* GetBaseZip() { return mBaseZip; }
+
+ /**
+ * 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/IOInterposer.cpp b/xpcom/build/IOInterposer.cpp
new file mode 100644
index 0000000000..0420965b91
--- /dev/null
+++ b/xpcom/build/IOInterposer.cpp
@@ -0,0 +1,532 @@
+/* -*- 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"
+#include "prenv.h"
+
+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() = default;
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ObserverLists)
+
+ ObserverLists() = default;
+
+ 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<mozilla::IOInterposeObserver*> mCreateObservers;
+ std::vector<mozilla::IOInterposeObserver*> mReadObservers;
+ std::vector<mozilla::IOInterposeObserver*> mWriteObservers;
+ std::vector<mozilla::IOInterposeObserver*> mFSyncObservers;
+ std::vector<mozilla::IOInterposeObserver*> mStatObservers;
+ std::vector<mozilla::IOInterposeObserver*> mCloseObservers;
+ std::vector<mozilla::IOInterposeObserver*> mStageObservers;
+};
+
+class PerThreadData {
+ public:
+ explicit PerThreadData(bool aIsMainThread = false)
+ : mIsMainThread(aIsMainThread),
+ mIsHandlingObservation(false),
+ mCurrentGeneration(0) {
+ MOZ_COUNT_CTOR(PerThreadData);
+ }
+
+ MOZ_COUNTED_DTOR(PerThreadData)
+
+ void CallObservers(mozilla::IOInterposeObserver::Observation& aObservation) {
+ // Prevent recursive reporting.
+ if (mIsHandlingObservation) {
+ return;
+ }
+
+ mIsHandlingObservation = true;
+ // Decide which list of observers to inform
+ const std::vector<mozilla::IOInterposeObserver*>* observers = nullptr;
+ switch (aObservation.ObservedOperation()) {
+ case mozilla::IOInterposeObserver::OpCreateOrOpen:
+ observers = &mObserverLists->mCreateObservers;
+ break;
+ case mozilla::IOInterposeObserver::OpRead:
+ observers = &mObserverLists->mReadObservers;
+ break;
+ case mozilla::IOInterposeObserver::OpWrite:
+ observers = &mObserverLists->mWriteObservers;
+ break;
+ case mozilla::IOInterposeObserver::OpFSync:
+ observers = &mObserverLists->mFSyncObservers;
+ break;
+ case mozilla::IOInterposeObserver::OpStat:
+ observers = &mObserverLists->mStatObservers;
+ break;
+ case mozilla::IOInterposeObserver::OpClose:
+ observers = &mObserverLists->mCloseObservers;
+ break;
+ case mozilla::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<const ObserverLists>& aNewLists) {
+ mCurrentGeneration = aNewGeneration;
+ mObserverLists = aNewLists;
+ }
+
+ inline void ClearObserverLists() {
+ if (mObserverLists) {
+ mCurrentGeneration = 0;
+ mObserverLists = nullptr;
+ }
+ }
+
+ private:
+ bool mIsMainThread;
+ bool mIsHandlingObservation;
+ uint32_t mCurrentGeneration;
+ RefPtr<const ObserverLists> mObserverLists;
+};
+
+// Thread-safe list of observers, from which `PerThreadData` sources its own
+// local list when needed.
+class SourceList {
+ public:
+ SourceList()
+ : mObservedOperations(mozilla::IOInterposeObserver::OpNone),
+ mIsEnabled(true) {
+ MOZ_COUNT_CTOR(SourceList);
+ }
+
+ MOZ_COUNTED_DTOR(SourceList)
+
+ inline void Disable() { mIsEnabled = false; }
+ inline void Enable() { mIsEnabled = true; }
+
+ void Register(mozilla::IOInterposeObserver::Operation aOp,
+ mozilla::IOInterposeObserver* aStaticObserver) {
+ mozilla::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 & mozilla::IOInterposeObserver::OpCreateOrOpen &&
+ !VectorContains(newLists->mCreateObservers, aStaticObserver)) {
+ newLists->mCreateObservers.push_back(aStaticObserver);
+ }
+ if (aOp & mozilla::IOInterposeObserver::OpRead &&
+ !VectorContains(newLists->mReadObservers, aStaticObserver)) {
+ newLists->mReadObservers.push_back(aStaticObserver);
+ }
+ if (aOp & mozilla::IOInterposeObserver::OpWrite &&
+ !VectorContains(newLists->mWriteObservers, aStaticObserver)) {
+ newLists->mWriteObservers.push_back(aStaticObserver);
+ }
+ if (aOp & mozilla::IOInterposeObserver::OpFSync &&
+ !VectorContains(newLists->mFSyncObservers, aStaticObserver)) {
+ newLists->mFSyncObservers.push_back(aStaticObserver);
+ }
+ if (aOp & mozilla::IOInterposeObserver::OpStat &&
+ !VectorContains(newLists->mStatObservers, aStaticObserver)) {
+ newLists->mStatObservers.push_back(aStaticObserver);
+ }
+ if (aOp & mozilla::IOInterposeObserver::OpClose &&
+ !VectorContains(newLists->mCloseObservers, aStaticObserver)) {
+ newLists->mCloseObservers.push_back(aStaticObserver);
+ }
+ if (aOp & mozilla::IOInterposeObserver::OpNextStage &&
+ !VectorContains(newLists->mStageObservers, aStaticObserver)) {
+ newLists->mStageObservers.push_back(aStaticObserver);
+ }
+ mObserverLists = newLists;
+ mObservedOperations =
+ (mozilla::IOInterposeObserver::Operation)(mObservedOperations | aOp);
+
+ mCurrentGeneration++;
+ }
+
+ void Unregister(mozilla::IOInterposeObserver::Operation aOp,
+ mozilla::IOInterposeObserver* aStaticObserver) {
+ mozilla::IOInterposer::AutoLock lock(mLock);
+
+ ObserverLists* newLists = nullptr;
+ if (mObserverLists) {
+ newLists = new ObserverLists(*mObserverLists);
+ } else {
+ newLists = new ObserverLists();
+ }
+
+ if (aOp & mozilla::IOInterposeObserver::OpCreateOrOpen) {
+ VectorRemove(newLists->mCreateObservers, aStaticObserver);
+ if (newLists->mCreateObservers.empty()) {
+ mObservedOperations = (mozilla::IOInterposeObserver::Operation)(
+ mObservedOperations &
+ ~mozilla::IOInterposeObserver::OpCreateOrOpen);
+ }
+ }
+ if (aOp & mozilla::IOInterposeObserver::OpRead) {
+ VectorRemove(newLists->mReadObservers, aStaticObserver);
+ if (newLists->mReadObservers.empty()) {
+ mObservedOperations = (mozilla::IOInterposeObserver::Operation)(
+ mObservedOperations & ~mozilla::IOInterposeObserver::OpRead);
+ }
+ }
+ if (aOp & mozilla::IOInterposeObserver::OpWrite) {
+ VectorRemove(newLists->mWriteObservers, aStaticObserver);
+ if (newLists->mWriteObservers.empty()) {
+ mObservedOperations = (mozilla::IOInterposeObserver::Operation)(
+ mObservedOperations & ~mozilla::IOInterposeObserver::OpWrite);
+ }
+ }
+ if (aOp & mozilla::IOInterposeObserver::OpFSync) {
+ VectorRemove(newLists->mFSyncObservers, aStaticObserver);
+ if (newLists->mFSyncObservers.empty()) {
+ mObservedOperations = (mozilla::IOInterposeObserver::Operation)(
+ mObservedOperations & ~mozilla::IOInterposeObserver::OpFSync);
+ }
+ }
+ if (aOp & mozilla::IOInterposeObserver::OpStat) {
+ VectorRemove(newLists->mStatObservers, aStaticObserver);
+ if (newLists->mStatObservers.empty()) {
+ mObservedOperations = (mozilla::IOInterposeObserver::Operation)(
+ mObservedOperations & ~mozilla::IOInterposeObserver::OpStat);
+ }
+ }
+ if (aOp & mozilla::IOInterposeObserver::OpClose) {
+ VectorRemove(newLists->mCloseObservers, aStaticObserver);
+ if (newLists->mCloseObservers.empty()) {
+ mObservedOperations = (mozilla::IOInterposeObserver::Operation)(
+ mObservedOperations & ~mozilla::IOInterposeObserver::OpClose);
+ }
+ }
+ if (aOp & mozilla::IOInterposeObserver::OpNextStage) {
+ VectorRemove(newLists->mStageObservers, aStaticObserver);
+ if (newLists->mStageObservers.empty()) {
+ mObservedOperations = (mozilla::IOInterposeObserver::Operation)(
+ mObservedOperations & ~mozilla::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 source list.
+ mozilla::IOInterposer::AutoLock lock(mLock);
+ aPtd.SetObserverLists(mCurrentGeneration, mObserverLists);
+ }
+
+ inline bool IsObservedOperation(mozilla::IOInterposeObserver::Operation aOp) {
+ // This does not occur inside of a lock, so this makes no guarantees that
+ // the observers are in sync with this. That is acceptable; it is not a
+ // problem if we occasionally report more or less IO than is actually
+ // occurring.
+ return mIsEnabled && !!(mObservedOperations & aOp);
+ }
+
+ private:
+ RefPtr<const ObserverLists> mObserverLists MOZ_GUARDED_BY(mLock);
+ // 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.
+ mozilla::IOInterposer::Mutex mLock;
+ // Flags tracking which operations are being observed
+ mozilla::Atomic<mozilla::IOInterposeObserver::Operation,
+ mozilla::MemoryOrdering::Relaxed>
+ mObservedOperations;
+ // Used for quickly disabling everything by IOInterposer::Disable()
+ mozilla::Atomic<bool> mIsEnabled;
+ // Used to inform threads that the source observer list has changed
+ mozilla::Atomic<uint32_t> mCurrentGeneration;
+};
+
+// Special observation used by IOInterposer::EnteringNextStage()
+class NextStageObservation : public mozilla::IOInterposeObserver::Observation {
+ public:
+ NextStageObservation()
+ : mozilla::IOInterposeObserver::Observation(
+ mozilla::IOInterposeObserver::OpNextStage, "IOInterposer", false) {
+ mStart = mozilla::TimeStamp::Now();
+ mEnd = mStart;
+ }
+};
+
+// List of observers registered
+static mozilla::StaticAutoPtr<SourceList> sSourceList;
+static MOZ_THREAD_LOCAL(PerThreadData*) sThreadLocalData;
+static bool sThreadLocalDataInitialized;
+
+} // anonymous namespace
+
+namespace mozilla {
+
+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 (sSourceList) {
+ return true;
+ }
+ if (!sThreadLocalData.init()) {
+ return false;
+ }
+ sThreadLocalDataInitialized = true;
+ bool isMainThread = true;
+ RegisterCurrentThread(isMainThread);
+ sSourceList = new SourceList();
+
+ MainThreadIOLogger::Init();
+
+ // Now we initialize the various interposers depending on platform
+
+ // Under certain conditions it may be unsafe to initialize PoisonIOInterposer,
+ // such as when a background thread is already running. We set this variable
+ // elsewhere when such a condition applies.
+ if (!PR_GetEnv("MOZ_DISABLE_POISON_IO_INTERPOSER")) {
+ 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();
+ sSourceList = nullptr;
+#endif
+}
+
+void IOInterposer::Disable() {
+ if (!sSourceList) {
+ return;
+ }
+ sSourceList->Disable();
+}
+
+void IOInterposer::Enable() {
+ if (!sSourceList) {
+ return;
+ }
+ sSourceList->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 (!sSourceList) {
+ // If there is no longer a source list then we should clear the local one.
+ ptd->ClearObserverLists();
+ return;
+ }
+
+ sSourceList->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 sSourceList && sSourceList->IsObservedOperation(aOp);
+}
+
+void IOInterposer::Register(IOInterposeObserver::Operation aOp,
+ IOInterposeObserver* aStaticObserver) {
+ MOZ_ASSERT(aStaticObserver);
+ if (!sSourceList || !aStaticObserver) {
+ return;
+ }
+
+ sSourceList->Register(aOp, aStaticObserver);
+}
+
+void IOInterposer::Unregister(IOInterposeObserver::Operation aOp,
+ IOInterposeObserver* aStaticObserver) {
+ if (!sSourceList) {
+ return;
+ }
+
+ sSourceList->Unregister(aOp, aStaticObserver);
+}
+
+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;
+ }
+ if (PerThreadData* curThreadData = sThreadLocalData.get()) {
+ sThreadLocalData.set(nullptr);
+ delete curThreadData;
+ }
+}
+
+void IOInterposer::EnteringNextStage() {
+ if (!sSourceList) {
+ return;
+ }
+ NextStageObservation observation;
+ Report(observation);
+}
+
+} // namespace mozilla
diff --git a/xpcom/build/IOInterposer.h b/xpcom/build/IOInterposer.h
new file mode 100644
index 0000000000..34dd337e11
--- /dev/null
+++ b/xpcom/build/IOInterposer.h
@@ -0,0 +1,290 @@
+/* -*- 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/TimeStamp.h"
+#include "nsString.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; }
+
+ virtual const char* FileType() const { return "File"; }
+
+ /** Request filename associated with the I/O operation, empty if unknown */
+ virtual void Filename(nsAString& aString) { aString.Truncate(); }
+
+ virtual ~Observation() = default;
+
+ 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() = default;
+
+ 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.
+ *
+ * Remarks:
+ * - Init() must be called before observers are registered.
+ * - The IOInterposeObserver object should be static, because it could still be
+ * used on another thread shortly after Unregister().
+ */
+void Register(IOInterposeObserver::Operation aOp,
+ IOInterposeObserver* aStaticObserver);
+
+/**
+ * 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)
+ *
+ * Remarks:
+ * - Init() must be called before observers are registered.
+ * - The IOInterposeObserver object should be static, because it could still be
+ * used on another thread shortly after this Unregister() call.
+ */
+void Unregister(IOInterposeObserver::Operation aOp,
+ IOInterposeObserver* aStaticObserver);
+
+/**
+ * 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 MOZ_RAII AutoIOInterposer {
+ public:
+ AutoIOInterposer() = default;
+
+ void Init() {
+#if defined(EARLY_BETA_OR_EARLIER)
+ IOInterposer::Init();
+#endif
+ }
+
+ ~AutoIOInterposer() {
+#if defined(EARLY_BETA_OR_EARLIER)
+ IOInterposer::Clear();
+#endif
+ }
+};
+
+class MOZ_RAII AutoIOInterposerDisable final {
+ public:
+ explicit AutoIOInterposerDisable() { IOInterposer::Disable(); }
+ ~AutoIOInterposerDisable() { IOInterposer::Enable(); }
+
+ private:
+};
+
+} // namespace mozilla
+
+#endif // mozilla_IOInterposer_h
diff --git a/xpcom/build/IOInterposerPrivate.h b/xpcom/build/IOInterposerPrivate.h
new file mode 100644
index 0000000000..14f71603b8
--- /dev/null
+++ b/xpcom/build/IOInterposerPrivate.h
@@ -0,0 +1,117 @@
+/* -*- 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>
+
+#include "mozilla/ThreadSafety.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 MOZ_CAPABILITY("monitor") Monitor {
+ public:
+ Monitor() : mLock(PR_NewLock()), mCondVar(PR_NewCondVar(mLock)) {}
+
+ ~Monitor() {
+ PR_DestroyCondVar(mCondVar);
+ mCondVar = nullptr;
+ PR_DestroyLock(mLock);
+ mLock = nullptr;
+ }
+
+ void Lock() MOZ_CAPABILITY_ACQUIRE() { PR_Lock(mLock); }
+
+ void Unlock() MOZ_CAPABILITY_RELEASE() { PR_Unlock(mLock); }
+
+ bool Wait(PRIntervalTime aTimeout = PR_INTERVAL_NO_TIMEOUT)
+ MOZ_REQUIRES(this) {
+ return PR_WaitCondVar(mCondVar, aTimeout) == PR_SUCCESS;
+ }
+
+ bool Notify() { return PR_NotifyCondVar(mCondVar) == PR_SUCCESS; }
+
+ private:
+ PRLock* mLock;
+ PRCondVar* mCondVar;
+};
+
+class MOZ_SCOPED_CAPABILITY MonitorAutoLock {
+ public:
+ explicit MonitorAutoLock(Monitor& aMonitor) MOZ_CAPABILITY_ACQUIRE(aMonitor)
+ : mMonitor(aMonitor) {
+ mMonitor.Lock();
+ }
+
+ ~MonitorAutoLock() MOZ_CAPABILITY_RELEASE() { mMonitor.Unlock(); }
+
+ private:
+ Monitor& mMonitor;
+};
+
+class MOZ_SCOPED_CAPABILITY MonitorAutoUnlock {
+ public:
+ explicit MonitorAutoUnlock(Monitor& aMonitor)
+ MOZ_SCOPED_UNLOCK_RELEASE(aMonitor)
+ : mMonitor(aMonitor) {
+ mMonitor.Unlock();
+ }
+
+ ~MonitorAutoUnlock() MOZ_SCOPED_UNLOCK_REACQUIRE() { mMonitor.Lock(); }
+
+ private:
+ Monitor& mMonitor;
+};
+
+class MOZ_CAPABILITY("mutex") Mutex {
+ public:
+ Mutex() : mPRLock(PR_NewLock()) {}
+
+ ~Mutex() {
+ PR_DestroyLock(mPRLock);
+ mPRLock = nullptr;
+ }
+
+ void Lock() MOZ_CAPABILITY_ACQUIRE() { PR_Lock(mPRLock); }
+
+ void Unlock() MOZ_CAPABILITY_RELEASE() { PR_Unlock(mPRLock); }
+
+ private:
+ PRLock* mPRLock;
+};
+
+class MOZ_SCOPED_CAPABILITY AutoLock {
+ public:
+ explicit AutoLock(Mutex& aLock) MOZ_CAPABILITY_ACQUIRE(aLock) : mLock(aLock) {
+ mLock.Lock();
+ }
+
+ ~AutoLock() MOZ_CAPABILITY_RELEASE() { 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 0000000000..515b484aef
--- /dev/null
+++ b/xpcom/build/LateWriteChecks.cpp
@@ -0,0 +1,262 @@
+/* -*- 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/StaticPtr.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Unused.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsLocalFile.h"
+#include "nsPrintfCString.h"
+#include "mozilla/StackWalk.h"
+#include "prio.h"
+
+#ifdef XP_WIN
+# 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"
+
+/*************************** Auxiliary Declarations ***************************/
+
+static MOZ_THREAD_LOCAL(int) tlsSuspendLateWriteChecks;
+
+bool SuspendingLateWriteChecksForCurrentThread() {
+ if (!tlsSuspendLateWriteChecks.init()) {
+ return true;
+ }
+ return tlsSuspendLateWriteChecks.get() > 0;
+}
+
+// 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_FORMAT_PRINTF(2, 3) {
+ MOZ_ASSERT(mFile);
+ va_list list;
+ va_start(list, aFormat);
+ nsAutoCString str;
+ str.AppendVprintf(aFormat, list);
+ va_end(list);
+ mSHA1.update(str.get(), str.Length());
+ mozilla::Unused << fwrite(str.get(), 1, str.Length(), mFile);
+ }
+ void Finish(mozilla::SHA1Sum::Hash& aHash) {
+ int fd = fileno(mFile);
+ fflush(mFile);
+ MozillaUnRegisterDebugFD(fd);
+ fclose(mFile);
+ mSHA1.finish(aHash);
+ mFile = nullptr;
+ }
+
+ private:
+ FILE* mFile;
+ mozilla::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 mozilla::IOInterposeObserver {
+ using char_type = mozilla::filesystem::Path::value_type;
+
+ public:
+ explicit LateWriteObserver(const char_type* aProfileDirectory)
+ : mProfileDirectory(NS_xstrdup(aProfileDirectory)) {}
+ ~LateWriteObserver() {
+ free(mProfileDirectory);
+ mProfileDirectory = nullptr;
+ }
+
+ void Observe(
+ mozilla::IOInterposeObserver::Observation& aObservation) override;
+
+ private:
+ char_type* mProfileDirectory;
+};
+
+void LateWriteObserver::Observe(
+ mozilla::IOInterposeObserver::Observation& aOb) {
+ if (SuspendingLateWriteChecksForCurrentThread()) {
+ return;
+ }
+
+#ifdef DEBUG
+ MOZ_CRASH();
+#endif
+
+ // If we can't record then abort
+ if (!mozilla::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, nullptr, /* maxFrames */ 0, &rawStack);
+ mozilla::Telemetry::ProcessedStack stack =
+ mozilla::Telemetry::GetStackAndModules(rawStack);
+
+ nsTAutoString<char_type> nameAux(mProfileDirectory);
+ nameAux.AppendLiteral(NS_SLASH "Telemetry.LateWriteTmpXXXXXX");
+ char_type* name = nameAux.BeginWriting();
+
+ // 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
+ _wmktemp_s(char16ptr_t(name), NS_strlen(name) + 1);
+ hFile = CreateFileW(char16ptr_t(name), GENERIC_WRITE, 0, nullptr,
+ CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
+ } while (GetLastError() == ERROR_FILE_EXISTS);
+
+ if (hFile == INVALID_HANDLE_VALUE) {
+ MOZ_CRASH("Um, how did we get here?");
+ }
+
+ // http://support.microsoft.com/kb/139640
+ int fd = _open_osfhandle((intptr_t)hFile, _O_APPEND);
+ if (fd == -1) {
+ MOZ_CRASH("Um, how did we get here?");
+ }
+
+ stream = _fdopen(fd, "w");
+#else
+ int fd = mkstemp(name);
+ if (fd == -1) {
+ MOZ_CRASH("mkstemp failed");
+ }
+ 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) {
+ mozilla::Telemetry::ProcessedStack::Module module = stack.GetModule(i);
+ sha1Stream.Printf("%s %s\n", module.mBreakpadId.get(),
+ NS_ConvertUTF16toUTF8(module.mName).get());
+ }
+
+ size_t numFrames = stack.GetStackSize();
+ sha1Stream.Printf("%u\n", (unsigned)numFrames);
+ for (size_t i = 0; i < numFrames; ++i) {
+ const mozilla::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);
+ }
+
+ mozilla::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.
+ nsAutoString finalName(u"Telemetry.LateWriteFinal-"_ns);
+ for (int i = 0; i < 20; ++i) {
+ finalName.AppendPrintf("%02x", sha1[i]);
+ }
+ RefPtr<nsLocalFile> file = new nsLocalFile(nameAux);
+ file->RenameTo(nullptr, finalName);
+}
+
+/******************************* Setup/Teardown *******************************/
+
+static mozilla::StaticAutoPtr<LateWriteObserver> sLateWriteObserver;
+
+namespace mozilla {
+
+void InitLateWriteChecks() {
+ nsCOMPtr<nsIFile> mozFile;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mozFile));
+ if (mozFile) {
+ PathString nativePath = mozFile->NativePath();
+ if (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;
+ }
+}
+
+void PushSuspendLateWriteChecks() {
+ if (!tlsSuspendLateWriteChecks.init()) {
+ return;
+ }
+ tlsSuspendLateWriteChecks.set(tlsSuspendLateWriteChecks.get() + 1);
+}
+
+void PopSuspendLateWriteChecks() {
+ if (!tlsSuspendLateWriteChecks.init()) {
+ return;
+ }
+ int current = tlsSuspendLateWriteChecks.get();
+ MOZ_ASSERT(current > 0);
+ tlsSuspendLateWriteChecks.set(current - 1);
+}
+
+} // namespace mozilla
diff --git a/xpcom/build/LateWriteChecks.h b/xpcom/build/LateWriteChecks.h
new file mode 100644
index 0000000000..6c9ee419f9
--- /dev/null
+++ b/xpcom/build/LateWriteChecks.h
@@ -0,0 +1,78 @@
+/* -*- 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();
+
+/**
+ * Temporarily suspend late write checks for the current thread. This is useful
+ * if you're about to perform a write, but it would be fine if this write were
+ * interrupted or skipped during a fast shutdown.
+ */
+void PushSuspendLateWriteChecks();
+
+/**
+ * Resume late write checks for the current thread, assuming an ancestor in the
+ * call stack hasn't also pushed a suspension.
+ */
+void PopSuspendLateWriteChecks();
+
+class MOZ_RAII AutoSuspendLateWriteChecks {
+ public:
+ AutoSuspendLateWriteChecks() { PushSuspendLateWriteChecks(); }
+ ~AutoSuspendLateWriteChecks() { PopSuspendLateWriteChecks(); }
+};
+
+} // namespace mozilla
+
+#endif // mozilla_LateWriteChecks_h
diff --git a/xpcom/build/MainThreadIOLogger.cpp b/xpcom/build/MainThreadIOLogger.cpp
new file mode 100644
index 0000000000..8fe2d9eba5
--- /dev/null
+++ b/xpcom/build/MainThreadIOLogger.cpp
@@ -0,0 +1,206 @@
+/* -*- 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 "mozilla/UniquePtr.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsThreadUtils.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 {
+ explicit ObservationWithStack(mozilla::IOInterposeObserver::Observation& aObs,
+ ProfilerBacktrace* aStack)
+ : mObservation(aObs), mStack(aStack) {
+ aObs.Filename(mFilename);
+ }
+
+ mozilla::IOInterposeObserver::Observation mObservation;
+ ProfilerBacktrace* mStack;
+ nsString mFilename;
+};
+
+class MainThreadIOLoggerImpl final : public mozilla::IOInterposeObserver {
+ public:
+ MainThreadIOLoggerImpl();
+ ~MainThreadIOLoggerImpl();
+
+ bool Init();
+
+ void Observe(Observation& aObservation) override;
+
+ private:
+ static void sIOThreadFunc(void* aArg);
+ void IOThreadFunc();
+
+ mozilla::TimeStamp mLogStartTime;
+ const char* mFileName;
+ PRThread* mIOThread;
+ mozilla::IOInterposer::Monitor mMonitor;
+ bool mShutdownRequired MOZ_GUARDED_BY(mMonitor);
+ std::vector<ObservationWithStack> mObservations MOZ_GUARDED_BY(mMonitor);
+};
+
+static mozilla::StaticAutoPtr<MainThreadIOLoggerImpl> sImpl;
+
+MainThreadIOLoggerImpl::MainThreadIOLoggerImpl()
+ : mFileName(nullptr), mIOThread(nullptr), mShutdownRequired(false) {}
+
+MainThreadIOLoggerImpl::~MainThreadIOLoggerImpl() {
+ if (!mIOThread) {
+ return;
+ }
+ {
+ // Scope for lock
+ mozilla::IOInterposer::MonitorAutoLock lock(mMonitor);
+ mShutdownRequired = true;
+ mMonitor.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) {
+ AUTO_PROFILER_REGISTER_THREAD("MainThreadIOLogger");
+
+ NS_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) {
+ mozilla::IOInterposer::MonitorAutoLock lock(mMonitor);
+ mShutdownRequired = true;
+ std::vector<ObservationWithStack>().swap(mObservations);
+ return;
+ }
+ mLogStartTime = mozilla::TimeStamp::Now();
+ {
+ // Scope for lock
+ mozilla::IOInterposer::MonitorAutoLock lock(mMonitor);
+ while (true) {
+ while (!mShutdownRequired && mObservations.empty()) {
+ mMonitor.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
+ mozilla::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",
+ (mozilla::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)");
+ }
+ }
+ // clang-format off
+ /**
+ * Format:
+ * Start Timestamp (Milliseconds), Operation, Duration (Milliseconds), Event Source, Filename
+ */
+ // clang-format on
+ 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) {
+ // TODO: Write out the callstack
+ i->mStack = nullptr;
+ }
+ }
+ }
+ }
+ PR_Close(fd);
+}
+
+void MainThreadIOLoggerImpl::Observe(Observation& aObservation) {
+ if (!mFileName || !IsMainThread()) {
+ return;
+ }
+ mozilla::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));
+ mMonitor.Notify();
+}
+
+} // namespace
+
+namespace mozilla {
+
+namespace MainThreadIOLogger {
+
+bool Init() {
+ auto impl = MakeUnique<MainThreadIOLoggerImpl>();
+ if (!impl->Init()) {
+ return false;
+ }
+ sImpl = impl.release();
+ 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 0000000000..cdf3b8728a
--- /dev/null
+++ b/xpcom/build/MainThreadIOLogger.h
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef 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 0000000000..184c3793e7
--- /dev/null
+++ b/xpcom/build/NSPRInterposer.cpp
@@ -0,0 +1,207 @@
+/* -*- 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"
+
+#include <sys/param.h>
+#ifdef XP_MACOSX
+# include <fcntl.h>
+#else
+# include "prprf.h"
+# include <unistd.h>
+#endif
+
+namespace {
+
+/* Original IO methods */
+PRCloseFN sCloseFn = nullptr;
+PRReadFN sReadFn = nullptr;
+PRWriteFN sWriteFn = nullptr;
+PRFsyncFN sFSyncFn = nullptr;
+PRFileInfoFN sFileInfoFn = nullptr;
+PRFileInfo64FN sFileInfo64Fn = nullptr;
+
+static int32_t GetPathFromFd(int32_t aFd, char* aBuf, size_t aBufSize) {
+#ifdef XP_MACOSX
+ NS_ASSERTION(aBufSize >= MAXPATHLEN,
+ "aBufSize should be a least MAXPATHLEN long");
+
+ return fcntl(aFd, F_GETPATH, aBuf);
+#else
+ char procPath[32];
+ if (PR_snprintf(procPath, sizeof(procPath), "/proc/self/fd/%i", aFd) ==
+ (PRUint32)-1) {
+ return -1;
+ }
+
+ int32_t ret = readlink(procPath, aBuf, aBufSize - 1);
+ if (ret > -1) {
+ aBuf[ret] = '\0';
+ }
+
+ return ret;
+#endif
+}
+
+/**
+ * RAII class for timing the duration of an NSPR I/O call and reporting the
+ * result to the mozilla::IOInterposeObserver API.
+ */
+class NSPRIOAutoObservation : public mozilla::IOInterposeObserver::Observation {
+ public:
+ explicit NSPRIOAutoObservation(mozilla::IOInterposeObserver::Operation aOp,
+ PRFileDesc* aFd)
+ : mozilla::IOInterposeObserver::Observation(aOp, "NSPRIOInterposer") {
+ char filename[MAXPATHLEN];
+ if (mShouldReport && aFd &&
+ GetPathFromFd(PR_FileDesc2NativeHandle(aFd), filename,
+ sizeof(filename)) != -1) {
+ CopyUTF8toUTF16(mozilla::MakeStringSpan(filename), mFilename);
+ } else {
+ mFilename.Truncate();
+ }
+ }
+
+ void Filename(nsAString& aFilename) override { aFilename = mFilename; }
+
+ ~NSPRIOAutoObservation() override { Report(); }
+
+ private:
+ nsString mFilename;
+};
+
+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(mozilla::IOInterposeObserver::OpClose, aFd);
+ 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(mozilla::IOInterposeObserver::OpRead, aFd);
+ 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(mozilla::IOInterposeObserver::OpWrite, aFd);
+ 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(mozilla::IOInterposeObserver::OpFSync, aFd);
+ 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(mozilla::IOInterposeObserver::OpStat, aFd);
+ 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(mozilla::IOInterposeObserver::OpStat, aFd);
+ 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 0000000000..3f811df60c
--- /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 0000000000..a816cc083f
--- /dev/null
+++ b/xpcom/build/Omnijar.cpp
@@ -0,0 +1,231 @@
+/* -*- 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 "mozilla/GeckoArgs.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] = nullptr;
+ }
+ if (sOuterReader[aType]) {
+ 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));
+ constexpr auto kOmnijarName = nsLiteralCString{MOZ_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 = nsZipArchive::OpenArchive(file);
+ if (!zipReader) {
+ return;
+ }
+
+ RefPtr<nsZipArchive> outerReader;
+ RefPtr<nsZipHandle> handle;
+ if (NS_SUCCEEDED(nsZipHandle::Init(zipReader, MOZ_STRINGIFY(OMNIJAR_NAME),
+ getter_AddRefs(handle)))) {
+ outerReader = zipReader;
+ zipReader = nsZipArchive::OpenArchive(handle);
+ if (!zipReader) {
+ 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;
+}
+
+already_AddRefed<nsZipArchive> Omnijar::GetInnerReader(
+ nsIFile* aPath, const nsACString& aEntry) {
+ MOZ_ASSERT(IsInitialized(), "Omnijar not initialized");
+
+ if (!aEntry.EqualsLiteral(MOZ_STRINGIFY(OMNIJAR_NAME))) {
+ return nullptr;
+ }
+
+ bool equals;
+ nsresult rv;
+
+ if (sPath[GRE]) {
+ rv = sPath[GRE]->Equals(aPath, &equals);
+ if (NS_SUCCEEDED(rv) && equals) {
+ return IsNested(GRE) ? GetReader(GRE) : nullptr;
+ }
+ }
+ if (sPath[APP]) {
+ rv = sPath[APP]->Equals(aPath, &equals);
+ if (NS_SUCCEEDED(rv) && equals) {
+ return IsNested(APP) ? GetReader(APP) : nullptr;
+ }
+ }
+ 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 += "/" MOZ_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;
+}
+
+void Omnijar::ChildProcessInit(int& aArgc, char** aArgv) {
+ nsCOMPtr<nsIFile> greOmni, appOmni;
+
+ if (auto greOmniStr = geckoargs::sGREOmni.Get(aArgc, aArgv)) {
+ if (NS_WARN_IF(NS_FAILED(
+ XRE_GetFileFromPath(*greOmniStr, getter_AddRefs(greOmni))))) {
+ greOmni = nullptr;
+ }
+ }
+ if (auto appOmniStr = geckoargs::sAppOmni.Get(aArgc, aArgv)) {
+ if (NS_WARN_IF(NS_FAILED(
+ XRE_GetFileFromPath(*appOmniStr, getter_AddRefs(appOmni))))) {
+ appOmni = nullptr;
+ }
+ }
+
+ // If we're unified, then only the -greomni flag is present
+ // (reflecting the state of sPath in the parent process) but that
+ // path should be used for both (not nullptr, which will try to
+ // invoke the directory service, which probably isn't up yet.)
+ if (!appOmni) {
+ appOmni = greOmni;
+ }
+
+ if (greOmni) {
+ Init(greOmni, appOmni);
+ } else {
+ // We should never have an appOmni without a greOmni.
+ MOZ_ASSERT(!appOmni);
+ }
+}
+
+} /* namespace mozilla */
diff --git a/xpcom/build/Omnijar.h b/xpcom/build/Omnijar.h
new file mode 100644
index 0000000000..e334c55478
--- /dev/null
+++ b/xpcom/build/Omnijar.h
@@ -0,0 +1,180 @@
+/* -*- 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);
+
+ /**
+ * Initializes the Omnijar API for a child process, given its argument
+ * list, if the `-greomni` flag and optionally also the `-appomni` flag
+ * is present. (`-appomni` is absent in the case of a unified jar.) If
+ * neither flag is present, the Omnijar API is not initialized. The
+ * flags, if present, will be removed from the argument list.
+ */
+ static void ChildProcessInit(int& aArgc, char** aArgv);
+
+ /**
+ * 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);
+
+ /**
+ * In the case of a nested omnijar, this returns the inner reader for the
+ * omnijar if aPath points to the outer archive and aEntry is the omnijar
+ * entry name. Returns null otherwise.
+ * In concrete terms: On Android the omnijar is nested inside the apk archive.
+ * GetReader("path/to.apk") returns the outer reader and GetInnerReader(
+ * "path/to.apk", "assets/omni.ja") returns the inner reader.
+ */
+ static already_AddRefed<nsZipArchive> GetInnerReader(
+ nsIFile* aPath, const nsACString& aEntry);
+
+ /**
+ * 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 */
+
+/**
+ * Returns whether or not the currently running build is an unpackaged
+ * developer build. This check is implemented by looking for omni.ja in the
+ * the obj/dist dir. We use this routine to detect when the build dir will
+ * use symlinks to the repo and object dir.
+ */
+inline bool IsPackagedBuild() {
+ return Omnijar::HasOmnijar(mozilla::Omnijar::GRE);
+}
+
+} /* namespace mozilla */
+
+#endif /* mozilla_Omnijar_h */
diff --git a/xpcom/build/PoisonIOInterposer.h b/xpcom/build/PoisonIOInterposer.h
new file mode 100644
index 0000000000..20adeb835b
--- /dev/null
+++ b/xpcom/build/PoisonIOInterposer.h
@@ -0,0 +1,93 @@
+/* -*- 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_MACOSX) || defined(XP_WIN)
+
+# 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.
+ * Never called! See bug 1647107.
+ */
+void ClearPoisonIOInterposer();
+
+} // namespace mozilla
+# endif /* __cplusplus */
+
+#else /* defined(XP_MACOSX) || defined(XP_WIN) */
+
+# 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 0000000000..0a25a3d1f8
--- /dev/null
+++ b/xpcom/build/PoisonIOInterposerBase.cpp
@@ -0,0 +1,244 @@
+/* -*- 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/Maybe.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/UniquePtr.h"
+
+#include <algorithm>
+
+#include "PoisonIOInterposer.h"
+
+#include "prlock.h"
+
+#ifdef MOZ_REPLACE_MALLOC
+# include "replace_malloc_bridge.h"
+#endif
+
+// Auxiliary method to convert file descriptors to ids
+#if defined(XP_WIN)
+# include <io.h>
+inline mozilla::Maybe<intptr_t> FileDescriptorToHandle(int aFd) {
+ intptr_t handle = _get_osfhandle(aFd);
+ if ((handle == -1) || (handle == -2)) {
+ // -1: Invalid handle. -2: stdin/out/err not associated with a stream.
+ return mozilla::Nothing();
+ }
+ return mozilla::Some(handle);
+}
+#else
+inline mozilla::Maybe<intptr_t> FileDescriptorToHandle(int aFd) {
+ return mozilla::Some<intptr_t>(aFd);
+}
+#endif /* if not XP_WIN */
+
+namespace {
+
+class DebugFilesAutoLock final {
+ public:
+ static PRLock* getDebugFileIDsLock() {
+ static PRLock* sLock = PR_NewLock();
+ return sLock;
+ }
+
+ DebugFilesAutoLock() { PR_Lock(getDebugFileIDsLock()); }
+
+ ~DebugFilesAutoLock() { PR_Unlock(getDebugFileIDsLock()); }
+};
+
+// 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) {
+ mozilla::Maybe<intptr_t> handle = FileDescriptorToHandle(aFd);
+ if (!handle.isSome()) {
+ return;
+ }
+ MozillaRegisterDebugHandle(handle.value());
+}
+
+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) {
+ mozilla::Maybe<intptr_t> handle = FileDescriptorToHandle(aFd);
+ if (!handle.isSome()) {
+ return;
+ }
+ MozillaUnRegisterDebugHandle(handle.value());
+}
+
+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 mozilla::DebugFdRegistry::RegisterHandle(intptr_t aHandle) {
+ MozillaRegisterDebugHandle(aHandle);
+}
+
+void mozilla::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 0000000000..21eca58757
--- /dev/null
+++ b/xpcom/build/PoisonIOInterposerMac.cpp
@@ -0,0 +1,348 @@
+/* -*- 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"
+// Disabled until bug 1658385 is fixed.
+#ifndef __aarch64__
+# include "mach_override.h"
+#endif
+
+#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 "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 {
+
+// 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 mozilla::IOInterposeObserver API.
+ */
+class MacIOAutoObservation : public mozilla::IOInterposeObserver::Observation {
+ public:
+ MacIOAutoObservation(mozilla::IOInterposeObserver::Operation aOp, int aFd)
+ : mozilla::IOInterposeObserver::Observation(
+ aOp, sReference, sIsEnabled && !mozilla::IsDebugFile(aFd)),
+ mFd(aFd),
+ mHasQueriedFilename(false) {}
+
+ MacIOAutoObservation(mozilla::IOInterposeObserver::Operation aOp, int aFd,
+ const void* aBuf, size_t aCount)
+ : mozilla::IOInterposeObserver::Observation(
+ aOp, sReference,
+ sIsEnabled && !mozilla::IsDebugFile(aFd) &&
+ IsValidWrite(aFd, aBuf, aCount)),
+ mFd(aFd),
+ mHasQueriedFilename(false) {}
+
+ // Custom implementation of
+ // mozilla::IOInterposeObserver::Observation::Filename
+ void Filename(nsAString& aFilename) override;
+
+ ~MacIOAutoObservation() { Report(); }
+
+ private:
+ int mFd;
+ bool mHasQueriedFilename;
+ nsString mFilename;
+ static const char* sReference;
+};
+
+const char* MacIOAutoObservation::sReference = "PoisonIOInterposer";
+
+// Get filename for this observation
+void MacIOAutoObservation::Filename(nsAString& aFilename) {
+ // If mHasQueriedFilename is true, then we already have it
+ if (mHasQueriedFilename) {
+ aFilename = mFilename;
+ return;
+ }
+
+ char filename[MAXPATHLEN];
+ if (fcntl(mFd, F_GETPATH, filename) != -1) {
+ CopyUTF8toUTF16(filename, mFilename);
+ } else {
+ mFilename.Truncate();
+ }
+ mHasQueriedFilename = true;
+
+ aFilename = 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 = mozilla::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(mozilla::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(mozilla::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(mozilla::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(mozilla::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 = mozilla::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;
+ }
+#ifndef __aarch64__
+ DebugOnly<mach_error_t> t =
+ mach_override_ptr(d->Function, d->Wrapper, &d->Buffer);
+ MOZ_ASSERT(t == err_none);
+#endif
+ }
+}
+
+void OnlyReportDirtyWrites() { sOnlyReportDirtyWrites = true; }
+
+// Never called! See bug 1647107.
+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 0000000000..1f3daa3539
--- /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 0000000000..ad9a11dbb1
--- /dev/null
+++ b/xpcom/build/PoisonIOInterposerWin.cpp
@@ -0,0 +1,510 @@
+/* -*- 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/ClearOnShutdown.h"
+#include "mozilla/FileUtilsWin.h"
+#include "mozilla/IOInterposer.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/NativeNt.h"
+#include "mozilla/SmallArrayLRUCache.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
+#include "nsTArray.h"
+#include "nsWindowsDllInterceptor.h"
+
+#ifdef MOZ_REPLACE_MALLOC
+# include "replace_malloc_bridge.h"
+#endif
+
+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 ***************************/
+
+// Cache of filenames associated with handles.
+// `static` to be shared between all calls to `Filename()`.
+// This assumes handles are not reused, at least within a windows of 32
+// handles.
+// Profiling showed that during startup, around half of `Filename()` calls are
+// resolved with the first entry (best case), and 32 entries cover >95% of
+// cases, reducing the average `Filename()` cost by 5-10x.
+using HandleToFilenameCache = mozilla::SmallArrayLRUCache<HANDLE, nsString, 32>;
+static mozilla::UniquePtr<HandleToFilenameCache> sHandleToFilenameCache;
+
+/**
+ * RAII class for timing the duration of an I/O call and reporting the result
+ * to the mozilla::IOInterposeObserver API.
+ */
+class WinIOAutoObservation : public mozilla::IOInterposeObserver::Observation {
+ public:
+ WinIOAutoObservation(mozilla::IOInterposeObserver::Operation aOp,
+ HANDLE aFileHandle, const LARGE_INTEGER* aOffset)
+ : mozilla::IOInterposeObserver::Observation(
+ aOp, sReference,
+ !mozilla::IsDebugFile(reinterpret_cast<intptr_t>(aFileHandle))),
+ mFileHandle(aFileHandle),
+ mFileHandleType(GetFileType(aFileHandle)),
+ mHasQueriedFilename(false) {
+ if (mShouldReport) {
+ mOffset.QuadPart = aOffset ? aOffset->QuadPart : 0;
+ }
+ }
+
+ WinIOAutoObservation(mozilla::IOInterposeObserver::Operation aOp,
+ nsAString& aFilename)
+ : mozilla::IOInterposeObserver::Observation(aOp, sReference),
+ mFileHandle(nullptr),
+ mFileHandleType(FILE_TYPE_UNKNOWN),
+ mHasQueriedFilename(false) {
+ if (mShouldReport) {
+ nsAutoString dosPath;
+ if (mozilla::NtPathToDosPath(aFilename, dosPath)) {
+ mFilename = dosPath;
+ } else {
+ // If we can't get a dosPath, what we have is better than nothing.
+ mFilename = aFilename;
+ }
+ mHasQueriedFilename = true;
+ mOffset.QuadPart = 0;
+ }
+ }
+
+ void SetHandle(HANDLE aFileHandle) {
+ mFileHandle = aFileHandle;
+ if (aFileHandle) {
+ // Note: `GetFileType()` is fast enough that we don't need to cache it.
+ mFileHandleType = GetFileType(aFileHandle);
+
+ if (mHasQueriedFilename) {
+ // `mHasQueriedFilename` indicates we already have a filename, add it to
+ // the cache with the now-known handle.
+ sHandleToFilenameCache->Add(aFileHandle, mFilename);
+ }
+ }
+ }
+
+ const char* FileType() const override;
+
+ void Filename(nsAString& aFilename) override;
+
+ ~WinIOAutoObservation() { Report(); }
+
+ private:
+ HANDLE mFileHandle;
+ DWORD mFileHandleType;
+ LARGE_INTEGER mOffset;
+ bool mHasQueriedFilename;
+ nsString mFilename;
+ static const char* sReference;
+};
+
+const char* WinIOAutoObservation::sReference = "PoisonIOInterposer";
+
+// Get filename for this observation
+void WinIOAutoObservation::Filename(nsAString& aFilename) {
+ // If mHasQueriedFilename is true, then filename is already stored in
+ // mFilename
+ if (mHasQueriedFilename) {
+ aFilename = mFilename;
+ return;
+ }
+
+ if (mFileHandle) {
+ mFilename = sHandleToFilenameCache->FetchOrAdd(mFileHandle, [&]() {
+ nsString filename;
+ if (!mozilla::HandleToFilename(mFileHandle, mOffset, filename)) {
+ // HandleToFilename could fail (return false) but still have added
+ // something to `filename`, so it should be cleared in this case.
+ filename.Truncate();
+ }
+ return filename;
+ });
+ }
+ mHasQueriedFilename = true;
+
+ aFilename = mFilename;
+}
+
+const char* WinIOAutoObservation::FileType() const {
+ if (mFileHandle) {
+ switch (mFileHandleType) {
+ case FILE_TYPE_CHAR:
+ return "Char";
+ case FILE_TYPE_DISK:
+ return "File";
+ case FILE_TYPE_PIPE:
+ return "Pipe";
+ case FILE_TYPE_REMOTE:
+ return "Remote";
+ case FILE_TYPE_UNKNOWN:
+ default:
+ break;
+ }
+ }
+ // Fallback to base class default implementation.
+ return mozilla::IOInterposeObserver::Observation::FileType();
+}
+
+/*************************** IO Interposing Methods ***************************/
+
+// Function pointers to original functions
+static mozilla::WindowsDllInterceptor::FuncHookType<NtCreateFileFn>
+ gOriginalNtCreateFile;
+static mozilla::WindowsDllInterceptor::FuncHookType<NtReadFileFn>
+ gOriginalNtReadFile;
+static mozilla::WindowsDllInterceptor::FuncHookType<NtReadFileScatterFn>
+ gOriginalNtReadFileScatter;
+static mozilla::WindowsDllInterceptor::FuncHookType<NtWriteFileFn>
+ gOriginalNtWriteFile;
+static mozilla::WindowsDllInterceptor::FuncHookType<NtWriteFileGatherFn>
+ gOriginalNtWriteFileGather;
+static mozilla::WindowsDllInterceptor::FuncHookType<NtFlushBuffersFileFn>
+ gOriginalNtFlushBuffersFile;
+static mozilla::WindowsDllInterceptor::FuncHookType<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) {
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtCreateFile);
+
+ if (!mozilla::nt::RtlGetThreadLocalStoragePointer()) {
+ return gOriginalNtCreateFile(
+ aFileHandle, aDesiredAccess, aObjectAttributes, aIoStatusBlock,
+ aAllocationSize, aFileAttributes, aShareAccess, aCreateDisposition,
+ aCreateOptions, aEaBuffer, 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(mozilla::IOInterposeObserver::OpCreateOrOpen,
+ filename);
+
+ // Execute original function
+ NTSTATUS status = gOriginalNtCreateFile(
+ aFileHandle, aDesiredAccess, aObjectAttributes, aIoStatusBlock,
+ aAllocationSize, aFileAttributes, aShareAccess, aCreateDisposition,
+ aCreateOptions, aEaBuffer, aEaLength);
+ if (NT_SUCCESS(status) && aFileHandle) {
+ timer.SetHandle(*aFileHandle);
+ }
+ return status;
+}
+
+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) {
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtReadFile);
+
+ if (!mozilla::nt::RtlGetThreadLocalStoragePointer()) {
+ return gOriginalNtReadFile(aFileHandle, aEvent, aApc, aApcCtx, aIoStatus,
+ aBuffer, aLength, aOffset, aKey);
+ }
+
+ // Report IO
+ WinIOAutoObservation timer(mozilla::IOInterposeObserver::OpRead, aFileHandle,
+ aOffset);
+
+ // 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) {
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtReadFileScatter);
+
+ if (!mozilla::nt::RtlGetThreadLocalStoragePointer()) {
+ return gOriginalNtReadFileScatter(aFileHandle, aEvent, aApc, aApcCtx,
+ aIoStatus, aSegments, aLength, aOffset,
+ aKey);
+ }
+
+ // Report IO
+ WinIOAutoObservation timer(mozilla::IOInterposeObserver::OpRead, aFileHandle,
+ aOffset);
+
+ // 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) {
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtWriteFile);
+
+ if (!mozilla::nt::RtlGetThreadLocalStoragePointer()) {
+ return gOriginalNtWriteFile(aFileHandle, aEvent, aApc, aApcCtx, aIoStatus,
+ aBuffer, aLength, aOffset, aKey);
+ }
+
+ // Report IO
+ WinIOAutoObservation timer(mozilla::IOInterposeObserver::OpWrite, aFileHandle,
+ aOffset);
+
+ // 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) {
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtWriteFileGather);
+
+ if (!mozilla::nt::RtlGetThreadLocalStoragePointer()) {
+ return gOriginalNtWriteFileGather(aFileHandle, aEvent, aApc, aApcCtx,
+ aIoStatus, aSegments, aLength, aOffset,
+ aKey);
+ }
+
+ // Report IO
+ WinIOAutoObservation timer(mozilla::IOInterposeObserver::OpWrite, aFileHandle,
+ aOffset);
+
+ // Execute original function
+ return gOriginalNtWriteFileGather(aFileHandle, aEvent, aApc, aApcCtx,
+ aIoStatus, aSegments, aLength, aOffset,
+ aKey);
+}
+
+static NTSTATUS NTAPI InterposedNtFlushBuffersFile(
+ HANDLE aFileHandle, PIO_STATUS_BLOCK aIoStatusBlock) {
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtFlushBuffersFile);
+
+ if (!mozilla::nt::RtlGetThreadLocalStoragePointer()) {
+ return gOriginalNtFlushBuffersFile(aFileHandle, aIoStatusBlock);
+ }
+
+ // Report IO
+ WinIOAutoObservation timer(mozilla::IOInterposeObserver::OpFSync, aFileHandle,
+ nullptr);
+
+ // Execute original function
+ return gOriginalNtFlushBuffersFile(aFileHandle, aIoStatusBlock);
+}
+
+static NTSTATUS NTAPI InterposedNtQueryFullAttributesFile(
+ POBJECT_ATTRIBUTES aObjectAttributes,
+ PFILE_NETWORK_OPEN_INFORMATION aFileInformation) {
+ // Something is badly wrong if this function is undefined
+ MOZ_ASSERT(gOriginalNtQueryFullAttributesFile);
+
+ if (!mozilla::nt::RtlGetThreadLocalStoragePointer()) {
+ return gOriginalNtQueryFullAttributesFile(aObjectAttributes,
+ 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(mozilla::IOInterposeObserver::OpStat, filename);
+
+ // Execute original function
+ return gOriginalNtQueryFullAttributesFile(aObjectAttributes,
+ aFileInformation);
+}
+
+} // namespace
+
+/******************************** IO Poisoning ********************************/
+
+// Windows DLL interceptor
+static mozilla::WindowsDllInterceptor sNtDllInterceptor;
+
+namespace mozilla {
+
+void InitPoisonIOInterposer() {
+ // Currently we hook the functions not early enough to precede third-party
+ // injections. Until we implement a compatible way e.g. applying a hook
+ // in the parent process (bug 1646804), we skip interposing functions under
+ // the known condition(s).
+
+ // Bug 1679741: Kingsoft Internet Security calls NtReadFile in their thread
+ // simultaneously when we're applying a hook on NtReadFile.
+ // Bug 1705042: Symantec applies its own hook on NtReadFile, and ends up
+ // overwriting part of ours in an incompatible way.
+ if (::GetModuleHandleW(L"kwsui64.dll") || ::GetModuleHandleW(L"ffm64.dll")) {
+ return;
+ }
+
+ // 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;
+
+ MOZ_RELEASE_ASSERT(!sHandleToFilenameCache);
+ sHandleToFilenameCache = mozilla::MakeUnique<HandleToFilenameCache>();
+ mozilla::RunOnShutdown([]() {
+ // The interposer may still be active after the final shutdown phase
+ // (especially since ClearPoisonIOInterposer() is never called, see bug
+ // 1647107), so we cannot just reset the pointer. Instead we put the cache
+ // in shutdown mode, to clear its memory and stop caching operations.
+ sHandleToFilenameCache->Shutdown();
+ });
+
+ // Stdout and Stderr are OK.
+ MozillaRegisterDebugFD(1);
+ if (::GetStdHandle(STD_OUTPUT_HANDLE) != ::GetStdHandle(STD_ERROR_HANDLE)) {
+ 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");
+ gOriginalNtCreateFile.Set(sNtDllInterceptor, "NtCreateFile",
+ &InterposedNtCreateFile);
+ gOriginalNtReadFile.Set(sNtDllInterceptor, "NtReadFile",
+ &InterposedNtReadFile);
+ gOriginalNtReadFileScatter.Set(sNtDllInterceptor, "NtReadFileScatter",
+ &InterposedNtReadFileScatter);
+ gOriginalNtWriteFile.Set(sNtDllInterceptor, "NtWriteFile",
+ &InterposedNtWriteFile);
+ gOriginalNtWriteFileGather.Set(sNtDllInterceptor, "NtWriteFileGather",
+ &InterposedNtWriteFileGather);
+ gOriginalNtFlushBuffersFile.Set(sNtDllInterceptor, "NtFlushBuffersFile",
+ &InterposedNtFlushBuffersFile);
+ gOriginalNtQueryFullAttributesFile.Set(sNtDllInterceptor,
+ "NtQueryFullAttributesFile",
+ &InterposedNtQueryFullAttributesFile);
+}
+
+void ClearPoisonIOInterposer() {
+ MOZ_ASSERT(false, "Never called! See bug 1647107");
+ if (sIOPoisoned) {
+ // Destroy the DLL interceptor
+ sIOPoisoned = false;
+ sNtDllInterceptor.Clear();
+ sHandleToFilenameCache->Clear();
+ }
+}
+
+} // namespace mozilla
diff --git a/xpcom/build/Services.py b/xpcom/build/Services.py
new file mode 100644
index 0000000000..43f010d05b
--- /dev/null
+++ b/xpcom/build/Services.py
@@ -0,0 +1,170 @@
+# 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/.
+
+# NOTE: Although this generates headers and code for C++, using Services.h
+# is deprecated in favour of Components.h.
+
+
+services = []
+
+
+def service(name, iface, contractid):
+ """Define a convenient service getter"""
+ services.append((name, iface, contractid))
+
+
+# The `name` parameter is derived from the `iface` by removing the `nsI`
+# prefix. (This often matches the `contractid`, but not always.)
+service("ChromeRegistry", "nsIChromeRegistry", "@mozilla.org/chrome/chrome-registry;1")
+service("IOService", "nsIIOService", "@mozilla.org/network/io-service;1")
+service("ObserverService", "nsIObserverService", "@mozilla.org/observer-service;1")
+service("PermissionManager", "nsIPermissionManager", "@mozilla.org/permissionmanager;1")
+service(
+ "AsyncShutdownService",
+ "nsIAsyncShutdownService",
+ "@mozilla.org/async-shutdown-service;1",
+)
+
+# The definition file needs access to the definitions of the particular
+# interfaces. If you add a new interface here, make sure the necessary includes
+# are also listed in the following code snippet.
+CPP_INCLUDES = """
+#include "mozilla/Likely.h"
+#include "mozilla/Services.h"
+#include "mozIThirdPartyUtil.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 "IHistory.h"
+#include "nsIXPConnect.h"
+#include "nsIPermissionManager.h"
+#include "nsIPrefService.h"
+#include "nsIServiceWorkerManager.h"
+#include "nsICacheStorageService.h"
+#include "nsIStreamTransportService.h"
+#include "nsISocketTransportService.h"
+#include "nsIURIClassifier.h"
+#include "nsIHttpActivityObserver.h"
+#include "nsIAsyncShutdown.h"
+#include "nsIUUIDGenerator.h"
+#include "nsIGfxInfo.h"
+#include "nsIURIFixup.h"
+#include "nsIBits.h"
+#include "nsIXULRuntime.h"
+"""
+
+
+#####
+# Codegen Logic
+#
+# The following code consumes the data listed above to generate the files
+# Services.h, and Services.cpp which provide access to these service getters in
+# C++ code.
+
+
+def services_h(output):
+ output.write(
+ """\
+/* THIS FILE IS GENERATED BY Services.py - DO NOT EDIT */
+
+#ifndef mozilla_Services_h
+#define mozilla_Services_h
+
+#include "nscore.h"
+#include "nsCOMPtr.h"
+"""
+ )
+
+ for name, iface, contractid in services:
+ # Write out a forward declaration for the type in question
+ segs = iface.split("::")
+ for namespace in segs[:-1]:
+ output.write("namespace %s {\n" % namespace)
+ output.write("class %s;\n" % segs[-1])
+ for namespace in reversed(segs[:-1]):
+ output.write("} // namespace %s\n" % namespace)
+
+ # Write out the C-style function signature, and the C++ wrapper
+ output.write(
+ """
+#ifdef MOZILLA_INTERNAL_API
+namespace mozilla {
+namespace services {
+/**
+ * Fetch a cached instance of the %(name)s.
+ * This function will return nullptr during XPCOM shutdown.
+ * Prefer using static components to this method.
+ * WARNING: This method is _not_ threadsafe!
+ */
+already_AddRefed<%(type)s> Get%(name)s();
+} // namespace services
+} // namespace mozilla
+#endif // defined(MOZILLA_INTERNAL_API)
+"""
+ % {
+ "name": name,
+ "type": iface,
+ }
+ )
+
+ output.write("#endif // !defined(mozilla_Services_h)\n")
+
+
+def services_cpp(output):
+ output.write(
+ """\
+/* THIS FILE IS GENERATED BY Services.py - DO NOT EDIT */
+"""
+ )
+ output.write(CPP_INCLUDES)
+
+ for name, iface, contractid in services:
+ output.write(
+ """
+static %(type)s* g%(name)s = nullptr;
+
+namespace mozilla {
+namespace services {
+already_AddRefed<%(type)s> Get%(name)s()
+{
+ if (MOZ_UNLIKELY(gXPCOMShuttingDown)) {
+ return nullptr;
+ }
+ if (!g%(name)s) {
+ nsCOMPtr<%(type)s> os = do_GetService("%(contractid)s");
+ os.swap(g%(name)s);
+ }
+ return do_AddRef(g%(name)s);
+}
+}
+}
+"""
+ % {
+ "name": name,
+ "type": iface,
+ "contractid": contractid,
+ }
+ )
+
+ output.write(
+ """
+/**
+ * Clears service cache, sets gXPCOMShuttingDown
+ */
+void
+mozilla::services::Shutdown()
+{
+ gXPCOMShuttingDown = true;
+"""
+ )
+ for name, iface, contractid in services:
+ output.write(" NS_IF_RELEASE(g%s);\n" % name)
+ output.write("}\n")
diff --git a/xpcom/build/SmallArrayLRUCache.h b/xpcom/build/SmallArrayLRUCache.h
new file mode 100644
index 0000000000..e7ac727652
--- /dev/null
+++ b/xpcom/build/SmallArrayLRUCache.h
@@ -0,0 +1,199 @@
+/* -*- 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 SmallArrayLRUCache_h
+#define SmallArrayLRUCache_h
+
+#include "mozilla/Mutex.h"
+
+#include <algorithm>
+#include <type_traits>
+#include <utility>
+
+// Uncomment the next line to get shutdown stats about cache usage.
+// #define SMALLARRAYLRUCACHE_STATS
+
+#ifdef SMALLARRAYLRUCACHE_STATS
+# include <cstdio>
+#endif
+
+namespace mozilla {
+
+// Array-based LRU cache, thread-safe.
+// Optimized for cases where `FetchOrAdd` is used with the same key most
+// recently, and assuming the cost of running the value-builder function is much
+// more expensive than going through the whole list.
+// Note: No time limits on keeping items.
+// TODO: Move to more public place, if this could be used elsewhere; make sure
+// the cost/benefits are highlighted.
+template <typename Key, typename Value, unsigned LRUCapacity>
+class SmallArrayLRUCache {
+ public:
+ static_assert(std::is_default_constructible_v<Key>);
+ static_assert(std::is_trivially_constructible_v<Key>);
+ static_assert(std::is_trivially_copyable_v<Key>);
+ static_assert(std::is_default_constructible_v<Value>);
+ static_assert(LRUCapacity >= 2);
+ static_assert(LRUCapacity <= 1024,
+ "This seems a bit big, is this the right cache for your use?");
+
+ // Reset all stored values to their default, and set cache size to zero.
+ void Clear() {
+ mozilla::OffTheBooksMutexAutoLock lock(mMutex);
+ if (mSize == ShutdownSize) {
+ return;
+ }
+ Clear(lock);
+ }
+
+ // Clear the cache, and then prevent further uses (other functions will do
+ // nothing).
+ void Shutdown() {
+ mozilla::OffTheBooksMutexAutoLock lock(mMutex);
+ if (mSize == ShutdownSize) {
+ return;
+ }
+ Clear(lock);
+ mSize = ShutdownSize;
+ }
+
+ // Add a key-value.
+ template <typename ToValue>
+ void Add(Key aKey, ToValue&& aValue) {
+ mozilla::OffTheBooksMutexAutoLock lock(mMutex);
+
+ if (mSize == ShutdownSize) {
+ return;
+ }
+
+ // Quick add to the front, don't remove possible duplicate handles later in
+ // the list, they will eventually drop off the end.
+ KeyAndValue* const item0 = &mLRUArray[0];
+ mSize = std::min(mSize + 1, LRUCapacity);
+ if (MOZ_LIKELY(mSize != 1)) {
+ // List is not empty.
+ // Make a hole at the start.
+ std::move_backward(item0, item0 + mSize - 1, item0 + mSize);
+ }
+ item0->mKey = aKey;
+ item0->mValue = std::forward<ToValue>(aValue);
+ return;
+ }
+
+ // Look for the value associated with `aKey` in the cache.
+ // If not found, run `aValueFunction()`, add it in the cache before returning.
+ // After shutdown, just run `aValueFunction()`.
+ template <typename ValueFunction>
+ Value FetchOrAdd(Key aKey, ValueFunction&& aValueFunction) {
+ Value value;
+ mozilla::OffTheBooksMutexAutoLock lock(mMutex);
+
+ if (mSize == ShutdownSize) {
+ value = std::forward<ValueFunction>(aValueFunction)();
+ return value;
+ }
+
+ KeyAndValue* const item0 = &mLRUArray[0];
+ if (MOZ_UNLIKELY(mSize == 0)) {
+ // List is empty.
+ value = std::forward<ValueFunction>(aValueFunction)();
+ item0->mKey = aKey;
+ item0->mValue = value;
+ mSize = 1;
+ return value;
+ }
+
+ if (MOZ_LIKELY(item0->mKey == aKey)) {
+ // This is already at the beginning of the list, we're done.
+#ifdef SMALLARRAYLRUCACHE_STATS
+ ++mCacheFoundAt[0];
+#endif // SMALLARRAYLRUCACHE_STATS
+ value = item0->mValue;
+ return value;
+ }
+
+ for (KeyAndValue* item = item0 + 1; item != item0 + mSize; ++item) {
+ if (item->mKey == aKey) {
+ // Found handle in the middle.
+#ifdef SMALLARRAYLRUCACHE_STATS
+ ++mCacheFoundAt[unsigned(item - item0)];
+#endif // SMALLARRAYLRUCACHE_STATS
+ value = item->mValue;
+ // Move this item to the start of the list.
+ std::rotate(item0, item, item + 1);
+ return value;
+ }
+ }
+
+ // Handle was not in the list.
+#ifdef SMALLARRAYLRUCACHE_STATS
+ ++mCacheFoundAt[LRUCapacity];
+#endif // SMALLARRAYLRUCACHE_STATS
+ {
+ // Don't lock while doing the potentially-expensive ValueFunction().
+ // This means that the list could change when we lock again, but
+ // it's okay because we'll want to add the new entry at the beginning
+ // anyway, whatever else is in the list then.
+ // In the worst case, it could be the same handle as another `FetchOrAdd`
+ // in parallel, it just means it will be duplicated, so it's a little bit
+ // less efficient (using the extra space), but not wrong (the next
+ // `FetchOrAdd` will find the first one).
+ mozilla::OffTheBooksMutexAutoUnlock unlock(mMutex);
+ value = std::forward<ValueFunction>(aValueFunction)();
+ }
+ // Make a hole at the start, and put the value there.
+ mSize = std::min(mSize + 1, LRUCapacity);
+ std::move_backward(item0, item0 + mSize - 1, item0 + mSize);
+ item0->mKey = aKey;
+ item0->mValue = value;
+ return value;
+ }
+
+#ifdef SMALLARRAYLRUCACHE_STATS
+ ~SmallArrayLRUCache() {
+ if (mSize != 0 && mSize != ShutdownSize) {
+ fprintf(stderr,
+ "***** SmallArrayLRUCache stats: (position -> hit count)\n");
+ for (unsigned i = 0; i < mSize; ++i) {
+ fprintf(stderr, "***** %3u -> %6u\n", i, mCacheFoundAt[i]);
+ }
+ fprintf(stderr, "***** not found -> %6u\n", mCacheFoundAt[LRUCapacity]);
+ }
+ }
+#endif // SMALLARRAYLRUCACHE_STATS
+
+ private:
+ void Clear(const mozilla::OffTheBooksMutexAutoLock&) MOZ_REQUIRES(mMutex) {
+ for (KeyAndValue* item = &mLRUArray[0]; item != &mLRUArray[mSize]; ++item) {
+ item->mValue = Value{};
+ }
+ mSize = 0;
+ }
+
+ struct KeyAndValue {
+ Key mKey;
+ Value mValue;
+
+ KeyAndValue() = default;
+ KeyAndValue(KeyAndValue&&) = default;
+ KeyAndValue& operator=(KeyAndValue&&) = default;
+ };
+
+ // Special size value that indicates the cache is in shutdown mode.
+ constexpr static unsigned ShutdownSize = unsigned(-1);
+
+ mozilla::OffTheBooksMutex mMutex{"LRU cache"};
+ unsigned mSize MOZ_GUARDED_BY(mMutex) = 0;
+ KeyAndValue mLRUArray[LRUCapacity] MOZ_GUARDED_BY(mMutex);
+#ifdef SMALLARRAYLRUCACHE_STATS
+ // Hit count for each position in the case. +1 for counting not-found cases.
+ unsigned mCacheFoundAt[LRUCapacity + 1] MOZ_GUARDED_BY(mMutex) = {0u};
+#endif // SMALLARRAYLRUCACHE_STATS
+};
+
+} // namespace mozilla
+
+#endif // SmallArrayLRUCache_h
diff --git a/xpcom/build/XPCOM.h b/xpcom/build/XPCOM.h
new file mode 100644
index 0000000000..1a04091dc4
--- /dev/null
+++ b/xpcom/build/XPCOM.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 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>
+
+#ifndef MOZILLA_INTERNAL_API
+# error "MOZILLA_INTERNAL_API must be defined"
+#endif
+
+// core headers required by pretty much everything else
+
+#include "nscore.h"
+
+#include "nsXPCOMCID.h"
+#include "nsXPCOM.h"
+
+#include "nsError.h"
+#include "nsDebug.h"
+
+#include "nsID.h"
+
+#include "nsISupports.h"
+
+#include "nsTArray.h"
+
+#include "nsCOMPtr.h"
+#include "nsCOMArray.h"
+
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsNativeCharsetUtils.h"
+
+#include "nsISupportsUtils.h"
+#include "nsISupportsImpl.h"
+
+// core data structures
+
+#include "nsTHashtable.h"
+#include "nsHashKeys.h"
+#include "nsBaseHashtable.h"
+#include "nsTHashMap.h"
+#include "nsInterfaceHashtable.h"
+#include "nsClassHashtable.h"
+#include "nsRefPtrHashtable.h"
+
+// interfaces that inherit directly from nsISupports
+
+#include "nsIArray.h"
+#include "nsAtom.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 "nsIEventTarget.h"
+#include "nsIException.h"
+#include "nsIFactory.h"
+#include "nsIFile.h"
+#include "nsIINIParser.h"
+#include "nsIInputStream.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsILineInputStream.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsIOutputStream.h"
+#include "nsIProcess.h"
+#include "nsIProperties.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"
+#endif
+
+// xpcom/glue utility headers
+
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.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 "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 0000000000..4d6572a501
--- /dev/null
+++ b/xpcom/build/XPCOMInit.cpp
@@ -0,0 +1,819 @@
+/* -*- 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 "ThreadEventTarget.h"
+#include "XPCOMModule.h"
+
+#include "base/basictypes.h"
+
+#include "mozilla/AbstractThread.h"
+#include "mozilla/AppShutdown.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Poison.h"
+#include "mozilla/SharedThreadPool.h"
+#include "mozilla/TaskController.h"
+#include "mozilla/Unused.h"
+#include "mozilla/XPCOM.h"
+#include "mozJSModuleLoader.h"
+#include "nsXULAppAPI.h"
+
+#ifndef ANDROID
+# include "nsTerminator.h"
+#endif
+
+#include "nsXPCOMPrivate.h"
+#include "nsXPCOMCIDInternal.h"
+
+#include "mozilla/dom/JSExecutionManager.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+
+#include "prlink.h"
+
+#include "nsCycleCollector.h"
+#include "nsObserverService.h"
+
+#include "nsDebugImpl.h"
+#include "nsSystemInfo.h"
+
+#include "nsComponentManager.h"
+#include "nsCategoryManagerUtils.h"
+#include "nsIServiceManager.h"
+
+#include "nsThreadManager.h"
+#include "nsThreadPool.h"
+
+#include "nsTimerImpl.h"
+#include "TimerThread.h"
+
+#include "nsThread.h"
+#include "nsVersionComparatorImpl.h"
+
+#include "nsIFile.h"
+#include "nsLocalFile.h"
+#include "nsDirectoryService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsCategoryManager.h"
+#include "nsMultiplexInputStream.h"
+
+#include "nsAtomTable.h"
+#include "nsISupportsImpl.h"
+#include "nsLanguageAtomService.h"
+
+#include "nsSystemInfo.h"
+#include "nsMemoryReporterManager.h"
+#include "nss.h"
+#include "nsNSSComponent.h"
+
+#include <locale.h>
+#include "mozilla/Services.h"
+#include "mozilla/Omnijar.h"
+#include "mozilla/ScriptPreloader.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/BackgroundHangMonitor.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"
+#ifdef MOZ_PHC
+# include "mozilla/PHCManager.h"
+#endif
+#include "mozilla/UniquePtr.h"
+#include "mozilla/ServoStyleConsts.h"
+
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+
+#include "ogg/ogg.h"
+
+#include "GeckoProfiler.h"
+#include "ProfilerControl.h"
+
+#include "jsapi.h"
+#include "js/Initialization.h"
+#include "js/Prefs.h"
+#include "mozilla/StaticPrefs_javascript.h"
+#include "XPCSelfHostedShmem.h"
+
+#include "gfxPlatform.h"
+
+using base::AtExitManager;
+using mozilla::ipc::BrowserProcessSubThread;
+
+// From toolkit/library/rust/lib.rs
+extern "C" void GkRust_Init();
+extern "C" void GkRust_Shutdown();
+
+namespace {
+
+static AtExitManager* sExitManager;
+static MessageLoop* sMessageLoop;
+static bool sCommandLineWasInitialized;
+static BrowserProcessSubThread* sIOThread;
+static mozilla::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
+
+nsresult nsThreadManagerGetSingleton(const nsIID& aIID, void** aInstancePtr) {
+ NS_ASSERTION(aInstancePtr, "null outptr");
+ return nsThreadManager::get().QueryInterface(aIID, aInstancePtr);
+}
+
+nsresult nsLocalFileConstructor(const nsIID& aIID, void** aInstancePtr) {
+ return nsLocalFile::nsLocalFileConstructor(aIID, aInstancePtr);
+}
+
+nsComponentManagerImpl* nsComponentManagerImpl::gComponentManager = nullptr;
+bool gXPCOMShuttingDown = false;
+bool gXPCOMMainThreadEventsAreDoomed = false;
+char16_t* gGREBinPath = nullptr;
+
+// gDebug will be freed during shutdown.
+static nsIDebug2* gDebug = nullptr;
+
+EXPORT_XPCOM_API(nsresult)
+NS_GetDebug(nsIDebug2** aResult) {
+ return nsDebugImpl::Create(NS_GET_IID(nsIDebug2), (void**)aResult);
+}
+
+class ICUReporter final : public nsIMemoryReporter,
+ public mozilla::CountingAllocatorBase<ICUReporter> {
+ public:
+ NS_DECL_ISUPPORTS
+
+ static void* Alloc(const void*, size_t aSize) {
+ void* result = CountingMalloc(aSize);
+ if (result == nullptr) {
+ MOZ_CRASH("Ran out of memory while allocating for ICU");
+ }
+ return result;
+ }
+
+ static void* Realloc(const void*, void* aPtr, size_t aSize) {
+ void* result = CountingRealloc(aPtr, aSize);
+ if (result == nullptr) {
+ MOZ_CRASH("Ran out of memory while reallocating for ICU");
+ }
+ return result;
+ }
+
+ 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() = default;
+};
+
+NS_IMPL_ISUPPORTS(ICUReporter, nsIMemoryReporter)
+
+class OggReporter final : public nsIMemoryReporter,
+ public mozilla::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() = default;
+};
+
+NS_IMPL_ISUPPORTS(OggReporter, nsIMemoryReporter)
+
+static bool sInitializedJS = false;
+
+static void InitializeJS() {
+#if defined(ENABLE_WASM_SIMD) && \
+ (defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86))
+ // Update static engine preferences, such as AVX, before
+ // `JS_InitWithFailureDiagnostic` is called.
+ JS::SetAVXEnabled(mozilla::StaticPrefs::javascript_options_wasm_simd_avx());
+#endif
+
+ // Set all JS::Prefs.
+ SET_JS_PREFS_FROM_BROWSER_PREFS;
+
+ const char* jsInitFailureReason = JS_InitWithFailureDiagnostic();
+ if (jsInitFailureReason) {
+ MOZ_CRASH_UNSAFE(jsInitFailureReason);
+ }
+}
+
+// Note that on OSX, aBinDirectory will point to .app/Contents/Resources/browser
+EXPORT_XPCOM_API(nsresult)
+NS_InitXPCOM(nsIServiceManager** aResult, nsIFile* aBinDirectory,
+ nsIDirectoryServiceProvider* aAppFileLocationProvider,
+ bool aInitJSContext) {
+ static bool sInitialized = false;
+ if (sInitialized) {
+ return NS_ERROR_FAILURE;
+ }
+
+ sInitialized = true;
+
+ NS_LogInit();
+
+ NS_InitAtomTable();
+
+ // We don't have the arguments by hand here. If logging has already been
+ // initialized by a previous call to LogModule::Init with the arguments
+ // passed, passing (0, nullptr) is alright here.
+ mozilla::LogModule::Init(0, nullptr);
+
+ GkRust_Init();
+
+ nsresult rv = NS_OK;
+
+ // We are not shutting down
+ gXPCOMShuttingDown = false;
+
+#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)) {
+ mozilla::UniquePtr<BrowserProcessSubThread> ioThread =
+ mozilla::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;
+ }
+ AUTO_PROFILER_INIT2;
+
+ // 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
+
+ nsDirectoryService::RealInit();
+
+ bool value;
+
+ if (aBinDirectory) {
+ rv = aBinDirectory->IsDirectory(&value);
+
+ if (NS_SUCCEEDED(rv) && value) {
+ nsDirectoryService::gService->SetCurrentProcessDirectory(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()) {
+ // If you added a new process type that uses NS_InitXPCOM, and you're
+ // *sure* you don't want NS_InitMinimalXPCOM: in addition to everything
+ // else you'll probably have to do, please add it to the case in
+ // GeckoChildProcessHost.cpp which sets the greomni/appomni flags.
+ MOZ_ASSERT(XRE_IsParentProcess() || XRE_IsContentProcess());
+ mozilla::Omnijar::Init();
+ }
+
+ if ((sCommandLineWasInitialized = !CommandLine::IsInitialized())) {
+#ifdef XP_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("nonexistent-executable"_ns);
+ 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);
+
+ // Initialize the JS engine.
+ InitializeJS();
+ sInitializedJS = true;
+
+ rv = nsComponentManagerImpl::gComponentManager->Init();
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(nsComponentManagerImpl::gComponentManager);
+ return rv;
+ }
+
+ if (aResult) {
+ NS_ADDREF(*aResult = nsComponentManagerImpl::gComponentManager);
+ }
+
+#ifdef MOZ_PHC
+ // This is the earliest possible moment we can start PHC while still being
+ // able to read prefs.
+ mozilla::InitPHCState();
+#endif
+
+ // 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 mozilla::SharedThreadPool (which needs the service manager).
+ mozilla::SharedThreadPool::InitStatics();
+
+ mozilla::scache::StartupCache::GetSingleton();
+ mozilla::AvailableMemoryTracker::Init();
+
+ // Notify observers of xpcom autoregistration start
+ NS_CreateServicesFromCategory(NS_XPCOM_STARTUP_CATEGORY, nullptr,
+ NS_XPCOM_STARTUP_OBSERVER_ID);
+#ifdef XP_WIN
+ CreateAnonTempFileRemover();
+#endif
+
+ // The memory reporter manager is up and running -- register our reporters.
+ RegisterStrongMemoryReporter(new ICUReporter());
+ RegisterStrongMemoryReporter(new OggReporter());
+ xpc::SelfHostedShmem::GetSingleton().InitMemoryReporter();
+
+ mozilla::Telemetry::Init();
+
+ 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());
+
+ mozilla::dom::JSExecutionManager::Initialize();
+
+ if (aInitJSContext) {
+ xpc::InitializeJSContext();
+ }
+
+ return NS_OK;
+}
+
+EXPORT_XPCOM_API(nsresult)
+NS_InitMinimalXPCOM() {
+ NS_SetMainThread();
+ mozilla::TimeStamp::Startup();
+ NS_LogInit();
+ NS_InitAtomTable();
+
+ // We don't have the arguments by hand here. If logging has already been
+ // initialized by a previous call to LogModule::Init with the arguments
+ // passed, passing (0, nullptr) is alright here.
+ mozilla::LogModule::Init(0, nullptr);
+
+ GkRust_Init();
+
+ 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;
+ }
+
+ mozilla::SharedThreadPool::InitStatics();
+ mozilla::Telemetry::Init();
+ 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)) {
+ MOZ_CRASH("JS_SetICUMemoryFunctions failed.");
+ }
+ sICUReporterInitialized = true;
+ }
+}
+
+nsresult ShutdownXPCOM(nsIServiceManager* aServMgr) {
+ // Make sure the hang monitor is enabled for shutdown.
+ BackgroundHangMonitor().NotifyActivity();
+
+ if (!NS_IsMainThread()) {
+ MOZ_CRASH("Shutdown on wrong thread");
+ }
+
+ // 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;
+ }
+
+ mozilla::AppShutdown::AdvanceShutdownPhase(
+ mozilla::ShutdownPhase::XPCOMWillShutdown);
+
+ // We want the service manager to be the subject of notifications
+ nsCOMPtr<nsIServiceManager> mgr;
+ Unused << NS_GetServiceManager(getter_AddRefs(mgr));
+ MOZ_DIAGNOSTIC_ASSERT(mgr != nullptr, "Service manager not present!");
+ mozilla::AppShutdown::AdvanceShutdownPhase(
+ mozilla::ShutdownPhase::XPCOMShutdown, nullptr, do_QueryInterface(mgr));
+
+ // This must happen after the shutdown of media and widgets, which
+ // are triggered by the NS_XPCOM_SHUTDOWN_OBSERVER_ID notification.
+ gfxPlatform::ShutdownLayersIPC();
+
+ mozilla::AppShutdown::AdvanceShutdownPhase(
+ mozilla::ShutdownPhase::XPCOMShutdownThreads);
+#ifdef DEBUG
+ // Prime an assertion at ThreadEventTarget::Dispatch to avoid late
+ // dispatches to non main-thread threads.
+ ThreadEventTarget::XPCOMShutdownThreadsNotificationFinished();
+#endif
+
+ // Shutdown the timer thread and all timers that might still be alive
+ nsTimerImpl::Shutdown();
+
+ // Have an extra round of processing after the timers went away.
+ 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().ShutdownNonMainThreads();
+
+ RefPtr<nsObserverService> observerService;
+ CallGetService("@mozilla.org/observer-service;1",
+ (nsObserverService**)getter_AddRefs(observerService));
+ if (observerService) {
+ observerService->Shutdown();
+ }
+
+#ifdef NS_FREE_PERMANENT_DATA
+ // In leak-checking / ASAN / etc. builds, shut down the Servo thread-pool,
+ // which will wait for all the work to be done. For other builds, we don't
+ // really want to wait on shutdown for possibly slow tasks.
+ Servo_ShutdownThreadPool();
+#endif
+
+ // XPCOMShutdownFinal is the default phase for ClearOnShutdown.
+ // This AdvanceShutdownPhase will thus free most ClearOnShutdown()'ed
+ // smart pointers. Some destructors may fire extra main thread runnables
+ // that will be processed inside AdvanceShutdownPhase.
+ AppShutdown::AdvanceShutdownPhase(ShutdownPhase::XPCOMShutdownFinal);
+
+ // Shutdown the main thread, processing our very last round of events, and
+ // then mark that we've finished main thread event processing.
+ nsThreadManager::get().ShutdownMainThread();
+ gXPCOMMainThreadEventsAreDoomed = true;
+
+ BackgroundHangMonitor().NotifyActivity();
+
+ mozilla::dom::JSExecutionManager::Shutdown();
+ }
+
+ // 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();
+ }
+
+ // Remove the remaining main thread representations
+ nsThreadManager::get().ReleaseMainThread();
+ AbstractThread::ShutdownMainThread();
+
+ // Release the directory service
+ nsDirectoryService::gService = nullptr;
+
+ free(gGREBinPath);
+ gGREBinPath = nullptr;
+
+ // 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.
+ mozJSModuleLoader::UnloadLoaders();
+
+ // Clear the profiler's JS context before cycle collection. The profiler will
+ // notify the JS engine that it can let go of any data it's holding on to for
+ // profiling purposes.
+ PROFILER_CLEAR_JS_CONTEXT();
+
+ bool shutdownCollect;
+#ifdef NS_FREE_PERMANENT_DATA
+ shutdownCollect = true;
+#else
+ shutdownCollect = !!PR_GetEnv("MOZ_CC_RUN_DURING_SHUTDOWN");
+#endif
+ nsCycleCollector_shutdown(shutdownCollect);
+
+ // There can be code trying to refer to global objects during the final cc
+ // shutdown. This is the phase for such global objects to correctly release.
+ AppShutdown::AdvanceShutdownPhase(ShutdownPhase::CCPostLastCycleCollection);
+
+ mozilla::scache::StartupCache::DeleteSingleton();
+ mozilla::ScriptPreloader::DeleteSingleton();
+
+ PROFILER_MARKER_UNTYPED("Shutdown xpcom", OTHER);
+
+ // Shutdown xpcom. This will release all loaders and cause others holding
+ // a refcount to the component manager to release it.
+ if (nsComponentManagerImpl::gComponentManager) {
+ DebugOnly<nsresult> rv =
+ (nsComponentManagerImpl::gComponentManager)->Shutdown();
+ NS_ASSERTION(NS_SUCCEEDED(rv.value), "Component Manager shutdown failed.");
+ } else {
+ NS_WARNING("Component Manager was never created ...");
+ }
+
+ if (sInitializedJS) {
+ // Shut down the JS engine.
+ JS_ShutDown();
+ sInitializedJS = false;
+ }
+
+ mozilla::ScriptPreloader::DeleteCacheDataSingleton();
+
+ // Release shared memory which might be borrowed by the JS engine.
+ xpc::SelfHostedShmem::Shutdown();
+
+ // After all threads have been joined and the component manager has been shut
+ // down, any remaining objects that could be holding NSS resources (should)
+ // have been released, so we can safely shut down NSS.
+ if (NSS_IsInitialized()) {
+ nsNSSComponent::DoClearSSLExternalAndInternalSessionCache();
+ if (NSS_Shutdown() != SECSuccess) {
+ // If you're seeing this crash and/or warning, some NSS resources are
+ // still in use (see bugs 1417680 and 1230312). Set the environment
+ // variable 'MOZ_IGNORE_NSS_SHUTDOWN_LEAKS' to some value to ignore this.
+ // Also, if leak checking is enabled, report this as a fake leak instead
+ // of crashing.
+#if defined(DEBUG) && !defined(ANDROID)
+ if (!getenv("MOZ_IGNORE_NSS_SHUTDOWN_LEAKS") &&
+ !getenv("XPCOM_MEM_BLOAT_LOG") && !getenv("XPCOM_MEM_LEAK_LOG") &&
+ !getenv("XPCOM_MEM_REFCNT_LOG") && !getenv("XPCOM_MEM_COMPTR_LOG")) {
+ MOZ_CRASH("NSS_Shutdown failed");
+ } else {
+# ifdef NS_BUILD_REFCNT_LOGGING
+ // Create a fake leak.
+ NS_LogCtor((void*)0x100, "NSSShutdownFailed", 100);
+# endif // NS_BUILD_REFCNT_LOGGING
+ NS_WARNING("NSS_Shutdown failed");
+ }
+#else
+ NS_WARNING("NSS_Shutdown failed");
+#endif // defined(DEBUG) && !defined(ANDROID)
+ }
+ }
+
+ // 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();
+
+ nsLanguageAtomService::Shutdown();
+
+ GkRust_Shutdown();
+
+#ifdef NS_FREE_PERMANENT_DATA
+ // As we do shutdown Servo only in leak-checking builds, there may still
+ // be async parse tasks going on in the Servo thread-pool in other builds.
+ // CSS parsing heavily uses the atom table, so we can safely drop it only
+ // if Servo has been stopped, too.
+ NS_ShutdownAtomTable();
+#endif
+
+ NS_IF_RELEASE(gDebug);
+
+ delete sIOThread;
+ sIOThread = nullptr;
+
+ delete sMessageLoop;
+ sMessageLoop = nullptr;
+
+ mozilla::TaskController::Shutdown();
+
+ if (sCommandLineWasInitialized) {
+ CommandLine::Terminate();
+ sCommandLineWasInitialized = false;
+ }
+
+ delete sExitManager;
+ sExitManager = nullptr;
+
+ Omnijar::CleanUp();
+
+ BackgroundHangMonitor::Shutdown();
+
+ delete sMainHangMonitor;
+ sMainHangMonitor = nullptr;
+
+ NS_LogTerm();
+
+ return NS_OK;
+}
+
+} // namespace mozilla
diff --git a/xpcom/build/XPCOMModule.h b/xpcom/build/XPCOMModule.h
new file mode 100644
index 0000000000..e98afc21c0
--- /dev/null
+++ b/xpcom/build/XPCOMModule.h
@@ -0,0 +1,20 @@
+/* -*- 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 XPCOMModule_h
+#define XPCOMModule_h
+
+#include "nsID.h"
+
+class nsISupports;
+
+nsresult nsThreadManagerGetSingleton(const nsIID& aIID, void** aInstancePtr);
+
+nsresult nsLocalFileConstructor(const nsIID& aIID, void** aInstancePtr);
+
+extern nsresult nsStringInputStreamConstructor(const nsID&, void**);
+
+#endif // XPCOMModule_h
diff --git a/xpcom/build/XPCOMModule.inc b/xpcom/build/XPCOMModule.inc
new file mode 100644
index 0000000000..4f860fd08e
--- /dev/null
+++ b/xpcom/build/XPCOMModule.inc
@@ -0,0 +1,3 @@
+#if defined(XP_WIN)
+ COMPONENT(WINDOWSREGKEY, nsWindowsRegKeyConstructor)
+#endif
diff --git a/xpcom/build/XREAppData.h b/xpcom/build/XREAppData.h
new file mode 100644
index 0000000000..61572dfc76
--- /dev/null
+++ b/xpcom/build/XREAppData.h
@@ -0,0 +1,231 @@
+/* -*- 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"
+#include "mozilla/UniquePtrExtensions.h"
+#include "nsCOMPtr.h"
+#include "nsCRTGlue.h"
+#include "nsStringFwd.h"
+#include "nsIFile.h"
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+namespace sandbox {
+class BrokerServices;
+}
+#endif
+
+namespace mozilla {
+
+struct StaticXREAppData;
+
+/**
+ * Application-specific data needed to start the apprunner.
+ */
+class XREAppData {
+ public:
+ XREAppData() = default;
+ ~XREAppData() = default;
+ XREAppData(const XREAppData& aOther) { *this = aOther; }
+
+ explicit XREAppData(const StaticXREAppData& aOther) { *this = aOther; }
+
+ XREAppData& operator=(const StaticXREAppData& aOther);
+ XREAppData& operator=(const XREAppData& aOther);
+ XREAppData& operator=(XREAppData&& aOther) = default;
+
+ // Lots of code reads these fields directly like a struct, so rather
+ // than using UniquePtr directly, use an auto-converting wrapper.
+ class CharPtr {
+ public:
+ explicit CharPtr() = default;
+ explicit CharPtr(const char* v) { *this = v; }
+ CharPtr(CharPtr&&) = default;
+ ~CharPtr() = default;
+
+ CharPtr& operator=(const char* v) {
+ if (v) {
+ mValue.reset(NS_xstrdup(v));
+ } else {
+ mValue = nullptr;
+ }
+ return *this;
+ }
+ CharPtr& operator=(const CharPtr& v) {
+ *this = (const char*)v;
+ return *this;
+ }
+
+ operator const char*() const { return mValue.get(); }
+
+ private:
+ UniqueFreePtr<const char> mValue;
+ };
+
+ /**
+ * The directory of the application to be run. May be null if the
+ * xulrunner and the app are installed into the same directory.
+ */
+ nsCOMPtr<nsIFile> 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.
+ */
+ CharPtr 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).
+ */
+ CharPtr 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).
+ */
+ CharPtr 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.
+ */
+ CharPtr version;
+
+ /**
+ * The application's build identifier, e.g. "2004051604"
+ */
+ CharPtr 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 - . @ _ { } *
+ */
+ CharPtr ID;
+
+ /**
+ * The copyright information to print for the -h commandline flag,
+ * e.g. "Copyright (c) 2003 mozilla.org".
+ */
+ CharPtr copyright;
+
+ /**
+ * Combination of NS_XRE_ prefixed flags (defined below).
+ */
+ uint32_t flags = 0;
+
+ /**
+ * The location of the XRE. XRE_main may not be able to figure this out
+ * programatically.
+ */
+ nsCOMPtr<nsIFile> xreDirectory;
+
+ /**
+ * The minimum/maximum compatible XRE version.
+ */
+ CharPtr minVersion;
+ CharPtr maxVersion;
+
+ /**
+ * The server URL to send crash reports to.
+ */
+ CharPtr 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
+ */
+ CharPtr profile;
+
+ /**
+ * The application name to use in the User Agent string.
+ */
+ CharPtr UAName;
+
+ /**
+ * The URL to the source revision for this build of the application.
+ */
+ CharPtr sourceURL;
+
+ /**
+ * The URL to use to check for updates.
+ */
+ CharPtr updateURL;
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ /**
+ * Chromium sandbox BrokerServices.
+ */
+ sandbox::BrokerServices* sandboxBrokerServices = nullptr;
+#endif
+
+ // Returns a name suitable for DBUS services.
+ static void SanitizeNameForDBus(nsACString&);
+ void GetDBusAppName(nsACString&) const;
+};
+
+/**
+ * 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 whether or not to use Breakpad crash reporting.
+ */
+#define NS_XRE_ENABLE_CRASH_REPORTER (1 << 3)
+
+/**
+ * A static version of the XRE app data is compiled into the application
+ * so that it is not necessary to read application.ini at startup.
+ *
+ * This structure is initialized into and matches nsXREAppData
+ */
+struct StaticXREAppData {
+ const char* vendor;
+ const char* name;
+ const char* remotingName;
+ const char* version;
+ const char* buildID;
+ const char* ID;
+ const char* copyright;
+ uint32_t flags;
+ const char* minVersion;
+ const char* maxVersion;
+ const char* crashReporterURL;
+ const char* profile;
+ const char* UAName;
+ const char* sourceURL;
+ const char* updateURL;
+};
+
+} // namespace mozilla
+
+#endif // XREAppData_h
diff --git a/xpcom/build/XREChildData.h b/xpcom/build/XREChildData.h
new file mode 100644
index 0000000000..8050fb8afc
--- /dev/null
+++ b/xpcom/build/XREChildData.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef XREChildData_h
+#define XREChildData_h
+
+#include "mozilla/UniquePtr.h"
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+# include "mozilla/sandboxing/loggingTypes.h"
+
+namespace sandbox {
+class BrokerServices;
+class TargetServices;
+} // namespace sandbox
+#endif
+
+/**
+ * Data needed to start a child process.
+ */
+struct XREChildData {
+#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;
+
+ /**
+ * Chromium sandbox broker services; needed by the remote sandbox
+ * launcher process.
+ */
+ sandbox::BrokerServices* sandboxBrokerServices = nullptr;
+#endif
+};
+
+#endif // XREChildData_h
diff --git a/xpcom/build/XREShellData.h b/xpcom/build/XREShellData.h
new file mode 100644
index 0000000000..a0f736f658
--- /dev/null
+++ b/xpcom/build/XREShellData.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef XREShellData_h
+#define XREShellData_h
+
+#if defined(LIBFUZZER)
+# include "FuzzerRegistry.h" // LibFuzzerDriver
+#endif
+
+#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
+#if defined(ANDROID)
+ FILE* outFile;
+ FILE* errFile;
+#endif
+#if defined(LIBFUZZER)
+ LibFuzzerDriver fuzzerDriver;
+#endif
+};
+
+#endif // XREShellData_h
diff --git a/xpcom/build/components.conf b/xpcom/build/components.conf
new file mode 100644
index 0000000000..a34076a525
--- /dev/null
+++ b/xpcom/build/components.conf
@@ -0,0 +1,267 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+Classes = [
+ {
+ 'cid': '{c521a612-2aad-46db-b6ab-3b821fb150b1}',
+ 'contract_ids': ['@mozilla.org/binaryinputstream;1'],
+ 'type': 'nsBinaryInputStream',
+ 'headers': ['/xpcom/io/nsBinaryStream.h'],
+ },
+ {
+ 'cid': '{86c37b9a-74e7-4672-844e-6e7dd83ba484}',
+ 'contract_ids': ['@mozilla.org/binaryoutputstream;1'],
+ 'type': 'nsBinaryOutputStream',
+ 'headers': ['/xpcom/io/nsBinaryStream.h'],
+ },
+ {
+ 'cid': '{61ba33c0-3031-11d3-8cd0-0060b0fc14a3}',
+ 'contract_ids': ['@mozilla.org/network/protocol;1?name=chrome'],
+ 'type': 'nsChromeProtocolHandler',
+ 'headers': ['/chrome/nsChromeProtocolHandler.h'],
+ 'protocol_config': {
+ 'scheme': 'chrome',
+ 'flags': [
+ 'URI_STD',
+ 'URI_IS_UI_RESOURCE',
+ 'URI_IS_LOCAL_RESOURCE',
+ 'URI_HAS_WEB_EXPOSED_ORIGIN',
+ ],
+ },
+ },
+ {
+ 'name': 'ChromeRegistry',
+ 'cid': '{47049e42-1d87-482a-984d-56ae185e367a}',
+ 'contract_ids': ['@mozilla.org/chrome/chrome-registry;1'],
+ 'singleton': True,
+ 'type': 'nsChromeRegistry',
+ 'headers': ['/chrome/nsChromeRegistry.h'],
+ 'constructor': 'nsChromeRegistry::GetSingleton',
+ },
+ {
+ 'js_name': 'console',
+ 'cid': '{7e3ff85c-1dd2-11b2-8d4b-eb452cb0ff40}',
+ 'contract_ids': ['@mozilla.org/consoleservice;1'],
+ 'interfaces': ['nsIConsoleService'],
+ 'type': 'nsConsoleService',
+ 'headers': ['/xpcom/base/nsConsoleService.h'],
+ 'init_method': 'Init',
+ },
+ {
+ 'cid': '{678c50b8-6bcb-4ad0-b9b8-c81175955199}',
+ 'contract_ids': ['@mozilla.org/hash-property-bag;1'],
+ 'type': 'nsHashPropertyBagCC',
+ 'headers': ['nsHashPropertyBag.h'],
+ },
+ {
+ 'cid': '{eb833911-4f49-4623-845f-e58a8e6de4c2}',
+ 'contract_ids': ['@mozilla.org/io-util;1'],
+ 'type': 'nsIOUtil',
+ 'headers': ['/xpcom/io/nsIOUtil.h'],
+ },
+ {
+ 'cid': '{2e23e220-60be-11d3-8c4a-000064657374}',
+ 'contract_ids': ['@mozilla.org/file/local;1'],
+ 'legacy_constructor': 'nsLocalFileConstructor',
+ },
+ {
+ 'cid': '{00bd71fb-7f09-4ec3-96af-a0b522b77969}',
+ 'contract_ids': ['@mozilla.org/memory-info-dumper;1'],
+ 'type': 'nsMemoryInfoDumper',
+ 'headers': ['mozilla/nsMemoryInfoDumper.h'],
+ },
+ {
+ 'cid': '{fb97e4f5-32dd-497a-baa2-7d1e55079910}',
+ 'contract_ids': ['@mozilla.org/memory-reporter-manager;1'],
+ 'type': 'nsMemoryReporterManager',
+ 'headers': ['/xpcom/base/nsMemoryReporterManager.h'],
+ 'init_method': 'Init',
+ 'processes': ProcessSelector.ALLOW_IN_GPU_RDD_VR_SOCKET_UTILITY_AND_GMPLUGIN_PROCESS,
+ },
+ {
+ 'cid': '{7b4eeb20-d781-11d4-8a83-0010a4e0c9ca}',
+ 'contract_ids': ['@mozilla.org/process/util;1'],
+ 'type': 'nsProcess',
+ 'headers': ['nsProcess.h'],
+ },
+ {
+ 'cid': '{aaf68860-f849-40ee-bb7a-b229bce036a3}',
+ 'contract_ids': ['@mozilla.org/scriptablebase64encoder;1'],
+ 'type': 'nsScriptableBase64Encoder',
+ 'headers': ['/xpcom/io/nsScriptableBase64Encoder.h'],
+ },
+ {
+ 'cid': '{43ebf210-8a7b-4ddb-a83d-b87c51a058db}',
+ 'contract_ids': ['@mozilla.org/securityconsole/message;1'],
+ 'type': 'nsSecurityConsoleMessage',
+ 'headers': ['/xpcom/base/nsSecurityConsoleMessage.h'],
+ },
+ {
+ 'cid': '{669a9795-6ff7-4ed4-9150-c34ce2971b63}',
+ 'contract_ids': ['@mozilla.org/storagestream;1'],
+ 'type': 'nsStorageStream',
+ 'headers': ['nsStorageStream.h'],
+ },
+ {
+ 'cid': '{acf8dc41-4a25-11d3-9890-006008962422}',
+ 'contract_ids': ['@mozilla.org/supports-cstring;1'],
+ 'type': 'nsSupportsCString',
+ 'headers': ['nsSupportsPrimitives.h'],
+ },
+ {
+ 'cid': '{acf8dc4a-4a25-11d3-9890-006008962422}',
+ 'contract_ids': ['@mozilla.org/supports-char;1'],
+ 'type': 'nsSupportsChar',
+ 'headers': ['nsSupportsPrimitives.h'],
+ },
+ {
+ 'cid': '{cbf86871-4ac0-11d3-baea-00805f8a5dd7}',
+ 'contract_ids': ['@mozilla.org/supports-double;1'],
+ 'type': 'nsSupportsDouble',
+ 'headers': ['nsSupportsPrimitives.h'],
+ },
+ {
+ 'cid': '{cbf86870-4ac0-11d3-baea-00805f8a5dd7}',
+ 'contract_ids': ['@mozilla.org/supports-float;1'],
+ 'type': 'nsSupportsFloat',
+ 'headers': ['nsSupportsPrimitives.h'],
+ },
+ {
+ 'cid': '{a99febba-1dd1-11b2-a943-b02334a6d083}',
+ 'contract_ids': ['@mozilla.org/supports-interface-pointer;1'],
+ 'type': 'nsSupportsInterfacePointer',
+ 'headers': ['nsSupportsPrimitives.h'],
+ },
+ {
+ 'cid': '{acf8dc43-4a25-11d3-9890-006008962422}',
+ 'contract_ids': ['@mozilla.org/supports-PRBool;1'],
+ 'type': 'nsSupportsPRBool',
+ 'headers': ['nsSupportsPrimitives.h'],
+ },
+ {
+ 'cid': '{acf8dc4b-4a25-11d3-9890-006008962422}',
+ 'contract_ids': ['@mozilla.org/supports-PRInt16;1'],
+ 'type': 'nsSupportsPRInt16',
+ 'headers': ['nsSupportsPrimitives.h'],
+ },
+ {
+ 'cid': '{acf8dc4c-4a25-11d3-9890-006008962422}',
+ 'contract_ids': ['@mozilla.org/supports-PRInt32;1'],
+ 'type': 'nsSupportsPRInt32',
+ 'headers': ['nsSupportsPrimitives.h'],
+ },
+ {
+ 'cid': '{acf8dc4d-4a25-11d3-9890-006008962422}',
+ 'contract_ids': ['@mozilla.org/supports-PRInt64;1'],
+ 'type': 'nsSupportsPRInt64',
+ 'headers': ['nsSupportsPrimitives.h'],
+ },
+ {
+ 'cid': '{acf8dc49-4a25-11d3-9890-006008962422}',
+ 'contract_ids': ['@mozilla.org/supports-PRTime;1'],
+ 'type': 'nsSupportsPRTime',
+ 'headers': ['nsSupportsPrimitives.h'],
+ },
+ {
+ 'cid': '{acf8dc46-4a25-11d3-9890-006008962422}',
+ 'contract_ids': ['@mozilla.org/supports-PRUint16;1'],
+ 'type': 'nsSupportsPRUint16',
+ 'headers': ['nsSupportsPrimitives.h'],
+ },
+ {
+ 'cid': '{acf8dc47-4a25-11d3-9890-006008962422}',
+ 'contract_ids': ['@mozilla.org/supports-PRUint32;1'],
+ 'type': 'nsSupportsPRUint32',
+ 'headers': ['nsSupportsPrimitives.h'],
+ },
+ {
+ 'cid': '{acf8dc48-4a25-11d3-9890-006008962422}',
+ 'contract_ids': ['@mozilla.org/supports-PRUint64;1'],
+ 'type': 'nsSupportsPRUint64',
+ 'headers': ['nsSupportsPrimitives.h'],
+ },
+ {
+ 'cid': '{acf8dc44-4a25-11d3-9890-006008962422}',
+ 'contract_ids': ['@mozilla.org/supports-PRUint8;1'],
+ 'type': 'nsSupportsPRUint8',
+ 'headers': ['nsSupportsPrimitives.h'],
+ },
+ {
+ 'cid': '{acf8dc42-4a25-11d3-9890-006008962422}',
+ 'contract_ids': ['@mozilla.org/supports-string;1'],
+ 'type': 'nsSupportsString',
+ 'headers': ['nsSupportsPrimitives.h'],
+ },
+ {
+ 'js_name': 'sysinfo',
+ 'cid': '{d962398a-99e5-49b2-857a-c159049c7f6c}',
+ 'contract_ids': ['@mozilla.org/system-info;1'],
+ 'interfaces': ['nsIPropertyBag2', 'nsISystemInfo'],
+ 'type': 'nsSystemInfo',
+ 'headers': ['nsSystemInfo.h'],
+ 'init_method': 'Init',
+ 'overridable': True,
+ },
+ {
+ 'js_name': 'tm',
+ 'cid': '{7a4204c6-e45a-4c37-8ebb-6709a22c917c}',
+ 'contract_ids': ['@mozilla.org/thread-manager;1'],
+ 'interfaces': ['nsIThreadManager'],
+ 'legacy_constructor': 'nsThreadManagerGetSingleton',
+ 'headers': ['/xpcom/build/XPCOMModule.h'],
+ },
+ {
+ 'js_name': 'uuid',
+ 'name': 'UUIDGenerator',
+ 'cid': '{706d36bb-bf79-4293-81f2-8f6828c18f9d}',
+ 'contract_ids': ['@mozilla.org/uuid-generator;1'],
+ 'interfaces': ['nsIUUIDGenerator'],
+ 'type': 'nsUUIDGenerator',
+ 'headers': ['/xpcom/base/nsUUIDGenerator.h'],
+ 'processes': ProcessSelector.ALLOW_IN_SOCKET_PROCESS,
+ },
+ {
+ 'cid': '{0d6ea1d0-879c-11d5-90ef-0010a4e73d9a}',
+ 'contract_ids': ['@mozilla.org/variant;1'],
+ 'type': 'nsVariantCC',
+ 'headers': ['nsVariant.h'],
+ },
+ {
+ 'js_name': 'vc',
+ 'cid': '{c6e47036-ca94-4be3-963a-9abd8705f7a8}',
+ 'contract_ids': ['@mozilla.org/xpcom/version-comparator;1'],
+ 'interfaces': ['nsIVersionComparator'],
+ 'type': 'nsVersionComparatorImpl',
+ 'headers': ['/xpcom/base/nsVersionComparatorImpl.h'],
+ },
+ {
+ 'cid': '{dfac10a9-dd24-43cf-a095-6ffa2e4b6a6c}',
+ 'contract_ids': ['@mozilla.org/xpcom/ini-parser-factory;1'],
+ 'type': 'nsINIParserFactory',
+ 'headers': ['/xpcom/ds/nsINIParserImpl.h'],
+ 'processes': ProcessSelector.ALLOW_IN_GPU_RDD_VR_SOCKET_AND_UTILITY_PROCESS,
+ },
+]
+
+if buildconfig.substs['OS_ARCH'] == 'WINNT':
+ Classes += [
+ {
+ 'cid': '{a53bc624-d577-4839-b8ec-bb5040a52ff4}',
+ 'contract_ids': ['@mozilla.org/windows-registry-key;1'],
+ 'legacy_constructor': 'nsWindowsRegKeyConstructor',
+ 'headers': ['nsWindowsRegKey.h'],
+ },
+ ]
+
+if buildconfig.substs['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ Classes += [
+ {
+ 'cid': '{b0f20595-88ce-4738-a1a4-24de78eb8051}',
+ 'contract_ids': ['@mozilla.org/mac-preferences-reader;1'],
+ 'type': 'nsMacPreferencesReader',
+ 'headers': ['mozilla/nsMacPreferencesReader.h'],
+ },
+ ]
diff --git a/xpcom/build/gen_process_types.py b/xpcom/build/gen_process_types.py
new file mode 100644
index 0000000000..9e16ecfd4c
--- /dev/null
+++ b/xpcom/build/gen_process_types.py
@@ -0,0 +1,34 @@
+# 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/.
+
+from geckoprocesstypes import process_types
+
+
+def main(output):
+ output.write(
+ """\
+/* THIS FILE IS GENERATED BY gen_process_types.py - DO NOT EDIT */
+"""
+ )
+
+ for p in process_types:
+ output.write(
+ """
+#ifndef SKIP_PROCESS_TYPE_%(allcaps_name)s
+GECKO_PROCESS_TYPE(%(enum_value)d, %(enum_name)s, "%(string_name)s", """
+ """%(proc_typename)s, %(process_bin_type)s, %(procinfo_typename)s, """
+ """%(webidl_typename)s, %(allcaps_name)s)
+#endif // SKIP_PROCESS_TYPE_%(allcaps_name)s
+"""
+ % {
+ "enum_value": p.enum_value,
+ "enum_name": p.enum_name,
+ "string_name": p.string_name,
+ "proc_typename": p.proc_typename,
+ "process_bin_type": p.process_bin_type,
+ "procinfo_typename": p.procinfo_typename,
+ "webidl_typename": p.webidl_typename,
+ "allcaps_name": p.allcaps_name,
+ }
+ )
diff --git a/xpcom/build/mach_override.c b/xpcom/build/mach_override.c
new file mode 100644
index 0000000000..1ece045e2c
--- /dev/null
+++ b/xpcom/build/mach_override.c
@@ -0,0 +1,793 @@
+// 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[] = {
+ // clang-format off
+ { 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 }
+ // clang-format on
+};
+#elif defined(__x86_64__)
+static AsmInstructionMatch possibleInstructions[] = {
+ // clang-format off
+ { 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 }
+ // clang-format on
+};
+#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 0000000000..21fbc7ff4d
--- /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 0000000000..5261c04523
--- /dev/null
+++ b/xpcom/build/moz.build
@@ -0,0 +1,108 @@
+# -*- 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",
+ "nsXULAppAPI.h",
+ "XREChildData.h",
+ "XREShellData.h",
+]
+
+EXPORTS.mozilla += [
+ "!GeckoProcessTypes.h",
+ "!Services.h",
+ "FileLocation.h",
+ "IOInterposer.h",
+ "LateWriteChecks.h",
+ "Omnijar.h",
+ "PoisonIOInterposer.h",
+ "SmallArrayLRUCache.h",
+ "XPCOM.h",
+ "XREAppData.h",
+]
+
+if CONFIG["OS_ARCH"] == "WINNT":
+ EXPORTS.mozilla += [
+ "perfprobe.h",
+ ]
+ SOURCES += [
+ "perfprobe.cpp",
+ "PoisonIOInterposerBase.cpp",
+ "PoisonIOInterposerWin.cpp",
+ ]
+elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
+ UNIFIED_SOURCES += [
+ "PoisonIOInterposerBase.cpp",
+ "PoisonIOInterposerMac.cpp",
+ ]
+ if CONFIG["TARGET_CPU"] != "aarch64":
+ SOURCES += ["mach_override.c"]
+ SOURCES["mach_override.c"].flags += ["-Wno-unused-function"]
+else:
+ SOURCES += ["PoisonIOInterposerStub.cpp"]
+
+include("../glue/objs.mozbuild")
+
+XPCOM_MANIFESTS += [
+ "components.conf",
+]
+
+UNIFIED_SOURCES += xpcom_gluens_src_cppsrcs
+UNIFIED_SOURCES += xpcom_glue_src_cppsrcs
+
+UNIFIED_SOURCES += [
+ "FileLocation.cpp",
+ "IOInterposer.cpp",
+ "LateWriteChecks.cpp",
+ "MainThreadIOLogger.cpp",
+ "Omnijar.cpp",
+ "XPCOMInit.cpp",
+]
+
+SOURCES += ["!Services.cpp"]
+
+if CONFIG["OS_ARCH"] != "WINNT":
+ SOURCES += [
+ "NSPRInterposer.cpp",
+ ]
+
+GeneratedFile("Services.cpp", script="Services.py", entry_point="services_cpp")
+GeneratedFile("Services.h", script="Services.py", entry_point="services_h")
+GeneratedFile(
+ "GeckoProcessTypes.h",
+ script="gen_process_types.py",
+ entry_point="main",
+)
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
+
+DEFINES["_IMPL_NS_STRINGAPI"] = True
+DEFINES["OMNIJAR_NAME"] = CONFIG["OMNIJAR_NAME"]
+
+LOCAL_INCLUDES += [
+ "!..",
+ "../base",
+ "../components",
+ "../ds",
+ "../glue",
+ "../io",
+ "../threads",
+ "/chrome",
+ "/docshell/base",
+ "/js/xpconnect/loader",
+]
+
+if CONFIG["MOZ_VPX"]:
+ LOCAL_INCLUDES += [
+ "/media/libvpx",
+ ]
+
+if CONFIG["MOZ_PHC"]:
+ DEFINES["MOZ_PHC"] = 1
diff --git a/xpcom/build/nsXPCOM.h b/xpcom/build/nsXPCOM.h
new file mode 100644
index 0000000000..8c3d66fd10
--- /dev/null
+++ b/xpcom/build/nsXPCOM.h
@@ -0,0 +1,377 @@
+/* -*- 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"
+#include "mozilla/Attributes.h"
+#include "mozilla/Atomics.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(nsISupports);
+DECL_CLASS(nsIComponentManager);
+DECL_CLASS(nsIComponentRegistrar);
+DECL_CLASS(nsIServiceManager);
+DECL_CLASS(nsIFile);
+DECL_CLASS(nsIDirectoryServiceProvider);
+DECL_CLASS(nsIMemory);
+DECL_CLASS(nsIDebug2);
+
+#ifdef __cplusplus
+extern bool gXPCOMShuttingDown;
+extern bool gXPCOMMainThreadEventsAreDoomed;
+#endif
+
+#ifdef __cplusplus
+# include "nsStringFwd.h"
+#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.
+ *
+ * @param aInitJSContext Whether the nsXPCJSContext should be initialized at
+ * this point.
+ *
+ * @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_InitXPCOM(nsIServiceManager** aResult, nsIFile* aBinDirectory,
+ nsIDirectoryServiceProvider* aAppFileLocationProvider,
+ bool aInitJSContext = true);
+
+/**
+ * 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.
+ *
+ * MOZ_CAN_RUN_SCRIPT_BOUNDARY for now, but really it should maybe be
+ * MOZ_CAN_RUN_SCRIPT.
+ */
+XPCOM_API(MOZ_CAN_RUN_SCRIPT_BOUNDARY 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 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);
+
+// Use NS_NewLocalFile if you already have a UTF-16 string.
+// Otherwise non-ASCII paths will break on some platforms
+// including Windows.
+class NS_ConvertUTF16toUTF8;
+nsresult NS_NewNativeLocalFile(const NS_ConvertUTF16toUTF8& aPath,
+ bool aFollowLinks, nsIFile** aResult) = delete;
+
+#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 0000000000..a5df26717a
--- /dev/null
+++ b/xpcom/build/nsXPCOMCID.h
@@ -0,0 +1,220 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#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"
+
+/**
+ * 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_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 0000000000..d07fcb1c4d
--- /dev/null
+++ b/xpcom/build/nsXPCOMCIDInternal.h
@@ -0,0 +1,49 @@
+/* -*- 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"
+
+/**
+ * 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 0000000000..49f36c709c
--- /dev/null
+++ b/xpcom/build/nsXPCOMPrivate.h
@@ -0,0 +1,126 @@
+/* -*- 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 "mozilla/Attributes.h"
+
+/**
+ * During this shutdown notification all threads which run XPCOM code must
+ * be joined.
+ */
+#define NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID "xpcom-shutdown-threads"
+
+// PUBLIC
+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
+ *
+ */
+MOZ_CAN_RUN_SCRIPT
+nsresult ShutdownXPCOM(nsIServiceManager* aServMgr);
+
+void SetICUMemoryFunctions();
+
+/**
+ * C++ namespaced version of NS_LogTerm.
+ */
+void LogTerm();
+
+} // namespace mozilla
+
+/* 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_WIN)
+
+# 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
+
+// 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/nsXULAppAPI.h b/xpcom/build/nsXULAppAPI.h
new file mode 100644
index 0000000000..d15944f8e4
--- /dev/null
+++ b/xpcom/build/nsXULAppAPI.h
@@ -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/. */
+
+#ifndef _nsXULAppAPI_h__
+#define _nsXULAppAPI_h__
+
+#include "js/TypeDecls.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/ProcessType.h"
+#include "mozilla/TimeStamp.h"
+#include "nscore.h"
+
+#if defined(MOZ_WIDGET_ANDROID)
+# include <jni.h>
+#endif
+
+class JSString;
+class MessageLoop;
+class nsIDirectoryServiceProvider;
+class nsIFile;
+class nsISupports;
+struct JSContext;
+struct XREChildData;
+struct XREShellData;
+
+namespace mozilla {
+class XREAppData;
+struct BootstrapConfig;
+} // namespace mozilla
+
+/**
+ * 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 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 native manifests used by the WebExtensions
+ * native messaging and managed storage features are found.
+ */
+# define XRE_SYS_NATIVE_MANIFESTS "XRESysNativeManifests"
+# define XRE_USER_NATIVE_MANIFESTS "XREUserNativeManifests"
+#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 specifies the distribution specific files for
+ * the application unique for each user.
+ * It's located at /run/user/$UID/<product name>/
+ */
+#define XRE_USER_RUNTIME_DIR "XREUserRunTimeDir"
+
+/**
+ * A directory service key which provides the update directory. Callers should
+ * fall back to appDir.
+ * Windows: If vendor name exists:
+ * ProgramData\<vendor name>\updates\
+ * <hash of the path to XRE_EXECUTABLE_FILE's parent directory>
+ *
+ * If vendor name doesn't exist, but product name exists:
+ * ProgramData\<product name>\updates\
+ * <hash of the path to XRE_EXECUTABLE_FILE's parent directory>
+ *
+ * If neither vendor nor product name exists:
+ * ProgramData\Mozilla\updates
+ *
+ * Mac: ~/Library/Caches/Mozilla/updates/<absolute path to app dir>
+ *
+ * All others: Parent directory of XRE_EXECUTABLE_FILE.
+ */
+#define XRE_UPDATE_ROOT_DIR "UpdRootD"
+
+/**
+ * A directory service key which provides the *old* update directory. This
+ * path should only be used when data needs to be migrated from the old update
+ * directory.
+ * 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:
+ * Documents and Settings\<User>\Local Settings\Application Data\
+ * Mozilla\updates
+ *
+ * This path does not exist on other operating systems
+ */
+#define XRE_OLD_UPDATE_ROOT_DIR "OldUpdRootD"
+
+/**
+ * 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 aConfig Information about the application to be run.
+ *
+ * @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.
+ */
+int XRE_main(int argc, char* argv[], const mozilla::BootstrapConfig& aConfig);
+
+/**
+ * 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.
+ */
+nsresult XRE_GetFileFromPath(const char* aPath, nsIFile** aResult);
+
+/**
+ * Get the path of the running application binary and store it in aResult.
+ */
+nsresult XRE_GetBinaryPath(nsIFile** aResult);
+
+/**
+ * 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 skin packages.
+ */
+enum NSLocationType {
+ NS_APP_LOCATION,
+ NS_EXTENSION_LOCATION,
+ NS_SKIN_LOCATION,
+ NS_BOOTSTRAPPED_LOCATION
+};
+
+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 skin packages.
+ */
+nsresult XRE_AddJarManifestLocation(NSLocationType aType, nsIFile* aLocation);
+
+/**
+ * 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.
+ */
+nsresult XRE_ParseAppData(nsIFile* aINIFile, mozilla::XREAppData& aAppData);
+
+const char* XRE_GeckoProcessTypeToString(GeckoProcessType aProcessType);
+const char* XRE_ChildProcessTypeToAnnotation(GeckoProcessType aProcessType);
+
+#if defined(MOZ_WIDGET_ANDROID)
+struct XRE_AndroidChildFds {
+ int mPrefsFd;
+ int mPrefMapFd;
+ int mIpcFd;
+ int mCrashFd;
+};
+
+void XRE_SetAndroidChildFds(JNIEnv* env, const XRE_AndroidChildFds& fds);
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+void XRE_SetProcessType(const char* aProcessTypeString);
+
+nsresult XRE_InitChildProcess(int aArgc, char* aArgv[],
+ const XREChildData* aChildData);
+
+/**
+ * Return the GeckoProcessType of the current process.
+ */
+GeckoProcessType XRE_GetProcessType();
+
+/**
+ * Return the string representation of the GeckoProcessType of the current
+ * process.
+ */
+const char* XRE_GetProcessTypeString();
+
+/**
+ * Returns true when called in the e10s parent process. Does *NOT* return true
+ * when called in the main process if e10s is disabled.
+ */
+bool XRE_IsE10sParentProcess();
+
+/**
+ * Defines XRE_IsParentProcess, XRE_IsContentProcess, etc.
+ *
+ * XRE_IsParentProcess is unique in that it returns true when called in
+ * the e10s parent process or called in the main process when e10s is
+ * disabled.
+ */
+#define GECKO_PROCESS_TYPE(enum_value, enum_name, string_name, proc_typename, \
+ process_bin_type, procinfo_typename, \
+ webidl_typename, allcaps_name) \
+ bool XRE_Is##proc_typename##Process();
+#include "mozilla/GeckoProcessTypes.h"
+#undef GECKO_PROCESS_TYPE
+
+bool XRE_IsSocketProcess();
+
+/**
+ * Returns true if the appshell should run its own native event loop. Returns
+ * false if we should rely solely on the Gecko event loop.
+ */
+bool XRE_UseNativeEventProcessing();
+
+typedef void (*MainFunction)(void* aData);
+
+int XRE_RunIPDLTest(int aArgc, char* aArgv[]);
+
+nsresult XRE_RunAppShell();
+
+nsresult XRE_InitCommandLine(int aArgc, char* aArgv[]);
+
+nsresult XRE_DeinitCommandLine();
+
+void XRE_ShutdownChildProcess();
+
+MessageLoop* XRE_GetIOMessageLoop();
+
+bool XRE_SendTestShellCommand(JSContext* aCx, JSString* aCommand,
+ JS::Value* aCallback);
+bool XRE_ShutdownTestShell();
+
+void XRE_InstallX11ErrorHandler();
+void XRE_CleanupX11ErrorHandler();
+
+void XRE_TelemetryAccumulate(int aID, uint32_t aSample);
+
+void XRE_StartupTimelineRecord(int aEvent, mozilla::TimeStamp aWhen);
+
+void XRE_InitOmnijar(nsIFile* aGreOmni, nsIFile* aAppOmni);
+void XRE_StopLateWriteChecks(void);
+
+void XRE_EnableSameExecutableForContentProc();
+
+namespace mozilla {
+enum class BinPathType { Self, PluginContainer };
+}
+mozilla::BinPathType XRE_GetChildProcBinPathType(GeckoProcessType aProcessType);
+
+int XRE_XPCShellMain(int argc, char** argv, char** envp,
+ const XREShellData* aShellData);
+
+#ifdef LIBFUZZER
+# include "FuzzerRegistry.h"
+
+void XRE_LibFuzzerSetDriver(LibFuzzerDriver);
+
+#endif // LIBFUZZER
+
+#ifdef MOZ_ENABLE_FORKSERVER
+
+int XRE_ForkServer(int* aArgc, char*** aArgv);
+
+#endif // MOZ_ENABLE_FORKSERVER
+
+#endif // _nsXULAppAPI_h__
diff --git a/xpcom/build/perfprobe.cpp b/xpcom/build/perfprobe.cpp
new file mode 100644
index 0000000000..d5a82be59d
--- /dev/null
+++ b/xpcom/build/perfprobe.cpp
@@ -0,0 +1,215 @@
+/* -*- 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"
+
+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, %lu", 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 0000000000..e5a0382322
--- /dev/null
+++ b/xpcom/build/perfprobe.h
@@ -0,0 +1,198 @@
+/* -*- 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 <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 0000000000..38fedfa17f
--- /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
+