summaryrefslogtreecommitdiffstats
path: root/xpcom/glue
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/glue')
-rw-r--r--xpcom/glue/FileUtils.cpp579
-rw-r--r--xpcom/glue/FileUtils.h140
-rw-r--r--xpcom/glue/MemUtils.cpp60
-rw-r--r--xpcom/glue/MemUtils.h20
-rw-r--r--xpcom/glue/XREAppData.cpp60
-rw-r--r--xpcom/glue/moz.build12
-rw-r--r--xpcom/glue/objs.mozbuild17
-rw-r--r--xpcom/glue/standalone/moz.build36
-rw-r--r--xpcom/glue/standalone/nsXPCOMGlue.cpp407
9 files changed, 1331 insertions, 0 deletions
diff --git a/xpcom/glue/FileUtils.cpp b/xpcom/glue/FileUtils.cpp
new file mode 100644
index 0000000000..537c060bf7
--- /dev/null
+++ b/xpcom/glue/FileUtils.cpp
@@ -0,0 +1,579 @@
+/* -*- 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/FileUtils.h"
+
+#include "nscore.h"
+#include "private/pprio.h"
+#include "prmem.h"
+#include "mozilla/BaseProfilerMarkers.h"
+#include "mozilla/MemUtils.h"
+
+#if defined(XP_MACOSX)
+# include <fcntl.h>
+# include <unistd.h>
+# include <mach/machine.h>
+# include <mach-o/fat.h>
+# include <mach-o/loader.h>
+# include <sys/mman.h>
+# include <sys/stat.h>
+# include <limits.h>
+#elif defined(XP_UNIX)
+# include <fcntl.h>
+# include <unistd.h>
+# if defined(LINUX)
+# include <elf.h>
+# endif
+# include <sys/types.h>
+# include <sys/stat.h>
+#elif defined(XP_WIN)
+# include <nsWindowsHelpers.h>
+# include <mozilla/NativeNt.h>
+# include <mozilla/ScopeExit.h>
+#endif
+
+// Functions that are not to be used in standalone glue must be implemented
+// within this #if block
+#if defined(MOZILLA_INTERNAL_API)
+
+# include "nsString.h"
+
+bool mozilla::fallocate(PRFileDesc* aFD, int64_t aLength) {
+# if defined(HAVE_POSIX_FALLOCATE)
+ return posix_fallocate(PR_FileDesc2NativeHandle(aFD), 0, aLength) == 0;
+# elif defined(XP_WIN)
+ int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR);
+ if (oldpos == -1) {
+ return false;
+ }
+
+ if (PR_Seek64(aFD, aLength, PR_SEEK_SET) != aLength) {
+ return false;
+ }
+
+ bool retval = (0 != SetEndOfFile((HANDLE)PR_FileDesc2NativeHandle(aFD)));
+
+ PR_Seek64(aFD, oldpos, PR_SEEK_SET);
+ return retval;
+# elif defined(XP_MACOSX)
+ int fd = PR_FileDesc2NativeHandle(aFD);
+ fstore_t store = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, aLength};
+ // Try to get a continous chunk of disk space
+ int ret = fcntl(fd, F_PREALLOCATE, &store);
+ if (ret == -1) {
+ // OK, perhaps we are too fragmented, allocate non-continuous
+ store.fst_flags = F_ALLOCATEALL;
+ ret = fcntl(fd, F_PREALLOCATE, &store);
+ if (ret == -1) {
+ return false;
+ }
+ }
+ return ftruncate(fd, aLength) == 0;
+# elif defined(XP_UNIX)
+ // The following is copied from fcntlSizeHint in sqlite
+ /* If the OS does not have posix_fallocate(), fake it. First use
+ ** ftruncate() to set the file size, then write a single byte to
+ ** the last byte in each block within the extended region. This
+ ** is the same technique used by glibc to implement posix_fallocate()
+ ** on systems that do not have a real fallocate() system call.
+ */
+ int64_t oldpos = PR_Seek64(aFD, 0, PR_SEEK_CUR);
+ if (oldpos == -1) {
+ return false;
+ }
+
+ struct stat buf;
+ int fd = PR_FileDesc2NativeHandle(aFD);
+ if (fstat(fd, &buf)) {
+ return false;
+ }
+
+ if (buf.st_size >= aLength) {
+ return false;
+ }
+
+ const int nBlk = buf.st_blksize;
+
+ if (!nBlk) {
+ return false;
+ }
+
+ if (ftruncate(fd, aLength)) {
+ return false;
+ }
+
+ int nWrite; // Return value from write()
+ int64_t iWrite = ((buf.st_size + 2 * nBlk - 1) / nBlk) * nBlk -
+ 1; // Next offset to write to
+ while (iWrite < aLength) {
+ nWrite = 0;
+ if (PR_Seek64(aFD, iWrite, PR_SEEK_SET) == iWrite) {
+ nWrite = PR_Write(aFD, "", 1);
+ }
+ if (nWrite != 1) {
+ break;
+ }
+ iWrite += nBlk;
+ }
+
+ PR_Seek64(aFD, oldpos, PR_SEEK_SET);
+ return nWrite == 1;
+# else
+ return false;
+# endif
+}
+
+void mozilla::ReadAheadLib(nsIFile* aFile) {
+# if defined(XP_WIN)
+ nsAutoString path;
+ if (!aFile || NS_FAILED(aFile->GetPath(path))) {
+ return;
+ }
+ ReadAheadLib(path.get());
+# elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
+ nsAutoCString nativePath;
+ if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) {
+ return;
+ }
+ ReadAheadLib(nativePath.get());
+# endif
+}
+
+void mozilla::ReadAheadFile(nsIFile* aFile, const size_t aOffset,
+ const size_t aCount, mozilla::filedesc_t* aOutFd) {
+# if defined(XP_WIN)
+ nsAutoString path;
+ if (!aFile || NS_FAILED(aFile->GetPath(path))) {
+ return;
+ }
+ ReadAheadFile(path.get(), aOffset, aCount, aOutFd);
+# elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
+ nsAutoCString nativePath;
+ if (!aFile || NS_FAILED(aFile->GetNativePath(nativePath))) {
+ return;
+ }
+ ReadAheadFile(nativePath.get(), aOffset, aCount, aOutFd);
+# endif
+}
+
+mozilla::PathString mozilla::GetLibraryName(mozilla::pathstr_t aDirectory,
+ const char* aLib) {
+# ifdef XP_WIN
+ nsAutoString fullName;
+ if (aDirectory) {
+ fullName.Assign(aDirectory);
+ fullName.Append('\\');
+ }
+ AppendUTF8toUTF16(MakeStringSpan(aLib), fullName);
+ if (!strstr(aLib, ".dll")) {
+ fullName.AppendLiteral(".dll");
+ }
+ return std::move(fullName);
+# else
+ char* temp = PR_GetLibraryName(aDirectory, aLib);
+ if (!temp) {
+ return ""_ns;
+ }
+ nsAutoCString libname(temp);
+ PR_FreeLibraryName(temp);
+ return std::move(libname);
+# endif
+}
+
+mozilla::PathString mozilla::GetLibraryFilePathname(mozilla::pathstr_t aName,
+ PRFuncPtr aAddr) {
+# ifdef XP_WIN
+ HMODULE handle = GetModuleHandleW(char16ptr_t(aName));
+ if (!handle) {
+ return u""_ns;
+ }
+
+ nsAutoString path;
+ path.SetLength(MAX_PATH);
+ DWORD len = GetModuleFileNameW(handle, char16ptr_t(path.BeginWriting()),
+ path.Length());
+ if (!len) {
+ return u""_ns;
+ }
+
+ path.SetLength(len);
+ return std::move(path);
+# else
+ char* temp = PR_GetLibraryFilePathname(aName, aAddr);
+ if (!temp) {
+ return ""_ns;
+ }
+ nsAutoCString path(temp);
+ PR_Free(temp); // PR_GetLibraryFilePathname() uses PR_Malloc().
+ return std::move(path);
+# endif
+}
+
+#endif // defined(MOZILLA_INTERNAL_API)
+
+#if defined(LINUX) && !defined(ANDROID)
+
+static const unsigned int bufsize = 4096;
+
+# ifdef __LP64__
+typedef Elf64_Ehdr Elf_Ehdr;
+typedef Elf64_Phdr Elf_Phdr;
+static const unsigned char ELFCLASS = ELFCLASS64;
+typedef Elf64_Off Elf_Off;
+# else
+typedef Elf32_Ehdr Elf_Ehdr;
+typedef Elf32_Phdr Elf_Phdr;
+static const unsigned char ELFCLASS = ELFCLASS32;
+typedef Elf32_Off Elf_Off;
+# endif
+
+#elif defined(XP_MACOSX)
+
+# if defined(__i386__)
+static const uint32_t CPU_TYPE = CPU_TYPE_X86;
+# elif defined(__x86_64__)
+static const uint32_t CPU_TYPE = CPU_TYPE_X86_64;
+# elif defined(__ppc__)
+static const uint32_t CPU_TYPE = CPU_TYPE_POWERPC;
+# elif defined(__ppc64__)
+static const uint32_t CPU_TYPE = CPU_TYPE_POWERPC64;
+# elif defined(__aarch64__)
+static const uint32_t CPU_TYPE = CPU_TYPE_ARM64;
+# else
+# error Unsupported CPU type
+# endif
+
+# ifdef __LP64__
+# undef LC_SEGMENT
+# define LC_SEGMENT LC_SEGMENT_64
+# undef MH_MAGIC
+# define MH_MAGIC MH_MAGIC_64
+# define cpu_mach_header mach_header_64
+# define segment_command segment_command_64
+# else
+# define cpu_mach_header mach_header
+# endif
+
+class ScopedMMap {
+ public:
+ explicit ScopedMMap(const char* aFilePath) : buf(nullptr) {
+ fd = open(aFilePath, O_RDONLY);
+ if (fd < 0) {
+ return;
+ }
+ struct stat st;
+ if (fstat(fd, &st) < 0) {
+ return;
+ }
+ size = st.st_size;
+ buf = (char*)mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (buf == MAP_FAILED) {
+ buf = nullptr;
+ }
+ }
+ ~ScopedMMap() {
+ if (buf) {
+ munmap(buf, size);
+ }
+ if (fd >= 0) {
+ close(fd);
+ }
+ }
+ operator char*() { return buf; }
+ int getFd() { return fd; }
+
+ private:
+ int fd;
+ char* buf;
+ size_t size;
+};
+#endif
+
+void mozilla::ReadAhead(mozilla::filedesc_t aFd, const size_t aOffset,
+ const size_t aCount) {
+#if defined(XP_WIN)
+
+ LARGE_INTEGER fpOriginal;
+ LARGE_INTEGER fpOffset;
+# if defined(HAVE_LONG_LONG)
+ fpOffset.QuadPart = 0;
+# else
+ fpOffset.u.LowPart = 0;
+ fpOffset.u.HighPart = 0;
+# endif
+
+ // Get the current file pointer so that we can restore it. This isn't
+ // really necessary other than to provide the same semantics regarding the
+ // file pointer that other platforms do
+ if (!SetFilePointerEx(aFd, fpOffset, &fpOriginal, FILE_CURRENT)) {
+ return;
+ }
+
+ if (aOffset) {
+# if defined(HAVE_LONG_LONG)
+ fpOffset.QuadPart = static_cast<LONGLONG>(aOffset);
+# else
+ fpOffset.u.LowPart = aOffset;
+ fpOffset.u.HighPart = 0;
+# endif
+
+ if (!SetFilePointerEx(aFd, fpOffset, nullptr, FILE_BEGIN)) {
+ return;
+ }
+ }
+
+ char buf[64 * 1024];
+ size_t totalBytesRead = 0;
+ DWORD dwBytesRead;
+ // Do dummy reads to trigger kernel-side readhead via
+ // FILE_FLAG_SEQUENTIAL_SCAN. Abort when underfilling because during testing
+ // the buffers are read fully A buffer that's not keeping up would imply that
+ // readahead isn't working right
+ while (totalBytesRead < aCount &&
+ ReadFile(aFd, buf, sizeof(buf), &dwBytesRead, nullptr) &&
+ dwBytesRead == sizeof(buf)) {
+ totalBytesRead += dwBytesRead;
+ }
+
+ // Restore the file pointer
+ SetFilePointerEx(aFd, fpOriginal, nullptr, FILE_BEGIN);
+
+#elif defined(LINUX) && !defined(ANDROID)
+
+ readahead(aFd, aOffset, aCount);
+
+#elif defined(XP_MACOSX)
+
+ struct radvisory ra;
+ ra.ra_offset = aOffset;
+ ra.ra_count = aCount;
+ // The F_RDADVISE fcntl is equivalent to Linux' readahead() system call.
+ fcntl(aFd, F_RDADVISE, &ra);
+
+#endif
+}
+
+void mozilla::ReadAheadLib(mozilla::pathstr_t aFilePath) {
+ if (!aFilePath) {
+ return;
+ }
+
+#ifdef XP_WIN
+ auto WideToUTF8 = [](const wchar_t* aStr) -> std::string {
+ std::string s;
+ // Determine the number of output bytes (including null terminator).
+ const int numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, -1, nullptr, 0,
+ nullptr, nullptr);
+ if (numConv == 0) {
+ return s;
+ }
+ s.resize(numConv);
+ const int numConvd = ::WideCharToMultiByte(CP_UTF8, 0, aStr, -1, s.data(),
+ numConv, nullptr, nullptr);
+ if (numConvd != numConv) {
+ // Error during conversion, remove any temporary data.
+ s.clear();
+ }
+ return s;
+ };
+#endif
+
+ AUTO_BASE_PROFILER_MARKER_TEXT("ReadAheadLib", OTHER, {},
+#ifdef XP_WIN
+ WideToUTF8(aFilePath)
+#else
+ aFilePath
+#endif
+ );
+
+#if defined(XP_WIN)
+ if (!CanPrefetchMemory()) {
+ ReadAheadFile(aFilePath);
+ return;
+ }
+ nsAutoHandle fd(CreateFileW(aFilePath, GENERIC_READ | GENERIC_EXECUTE,
+ FILE_SHARE_READ, nullptr, OPEN_EXISTING,
+ FILE_FLAG_SEQUENTIAL_SCAN, nullptr));
+ if (!fd) {
+ return;
+ }
+
+ nsAutoHandle mapping(CreateFileMapping(
+ fd, nullptr, SEC_IMAGE | PAGE_EXECUTE_READ, 0, 0, nullptr));
+ if (!mapping) {
+ return;
+ }
+
+ PVOID data = MapViewOfFile(
+ mapping, FILE_MAP_READ | FILE_MAP_EXECUTE | SEC_IMAGE, 0, 0, 0);
+ if (!data) {
+ return;
+ }
+ auto guard = MakeScopeExit([=]() { UnmapViewOfFile(data); });
+ mozilla::nt::PEHeaders headers(data);
+ Maybe<Span<const uint8_t>> bounds = headers.GetBounds();
+ if (!bounds) {
+ return;
+ }
+
+ PrefetchMemory((uint8_t*)data, bounds->Length());
+
+#elif defined(LINUX) && !defined(ANDROID)
+ int fd = open(aFilePath, O_RDONLY);
+ if (fd < 0) {
+ return;
+ }
+
+ union {
+ char buf[bufsize];
+ Elf_Ehdr ehdr;
+ } elf;
+ // Read ELF header (ehdr) and program header table (phdr).
+ // We check that the ELF magic is found, that the ELF class matches
+ // our own, and that the program header table as defined in the ELF
+ // headers fits in the buffer we read.
+ if ((read(fd, elf.buf, bufsize) <= 0) || (memcmp(elf.buf, ELFMAG, 4)) ||
+ (elf.ehdr.e_ident[EI_CLASS] != ELFCLASS) ||
+ // Upcast e_phentsize so the multiplication is done in the same precision
+ // as the subsequent addition, to satisfy static analyzers and avoid
+ // issues with abnormally large program header tables.
+ (elf.ehdr.e_phoff +
+ (static_cast<Elf_Off>(elf.ehdr.e_phentsize) * elf.ehdr.e_phnum) >=
+ bufsize)) {
+ close(fd);
+ return;
+ }
+ // The program header table contains segment definitions. One such
+ // segment type is PT_LOAD, which describes how the dynamic loader
+ // is going to map the file in memory. We use that information to
+ // find the biggest offset from the library that will be mapped in
+ // memory.
+ Elf_Phdr* phdr = (Elf_Phdr*)&elf.buf[elf.ehdr.e_phoff];
+ Elf_Off end = 0;
+ for (int phnum = elf.ehdr.e_phnum; phnum; phdr++, phnum--) {
+ if ((phdr->p_type == PT_LOAD) && (end < phdr->p_offset + phdr->p_filesz)) {
+ end = phdr->p_offset + phdr->p_filesz;
+ }
+ }
+ // Let the kernel read ahead what the dynamic loader is going to
+ // map in memory soon after.
+ if (end > 0) {
+ ReadAhead(fd, 0, end);
+ }
+ close(fd);
+#elif defined(XP_MACOSX)
+ ScopedMMap buf(aFilePath);
+ char* base = buf;
+ if (!base) {
+ return;
+ }
+
+ // An OSX binary might either be a fat (universal) binary or a
+ // Mach-O binary. A fat binary actually embeds several Mach-O
+ // binaries. If we have a fat binary, find the offset where the
+ // Mach-O binary for our CPU type can be found.
+ struct fat_header* fh = (struct fat_header*)base;
+
+ if (OSSwapBigToHostInt32(fh->magic) == FAT_MAGIC) {
+ uint32_t nfat_arch = OSSwapBigToHostInt32(fh->nfat_arch);
+ struct fat_arch* arch = (struct fat_arch*)&buf[sizeof(struct fat_header)];
+ for (; nfat_arch; arch++, nfat_arch--) {
+ if (OSSwapBigToHostInt32(arch->cputype) == CPU_TYPE) {
+ base += OSSwapBigToHostInt32(arch->offset);
+ break;
+ }
+ }
+ if (base == buf) {
+ return;
+ }
+ }
+
+ // Check Mach-O magic in the Mach header
+ struct cpu_mach_header* mh = (struct cpu_mach_header*)base;
+ if (mh->magic != MH_MAGIC) {
+ return;
+ }
+
+ // The Mach header is followed by a sequence of load commands.
+ // Each command has a header containing the command type and the
+ // command size. LD_SEGMENT commands describes how the dynamic
+ // loader is going to map the file in memory. We use that
+ // information to find the biggest offset from the library that
+ // will be mapped in memory.
+ char* cmd = &base[sizeof(struct cpu_mach_header)];
+ uint32_t end = 0;
+ for (uint32_t ncmds = mh->ncmds; ncmds; ncmds--) {
+ struct segment_command* sh = (struct segment_command*)cmd;
+ if (sh->cmd != LC_SEGMENT) {
+ continue;
+ }
+ if (end < sh->fileoff + sh->filesize) {
+ end = sh->fileoff + sh->filesize;
+ }
+ cmd += sh->cmdsize;
+ }
+ // Let the kernel read ahead what the dynamic loader is going to
+ // map in memory soon after.
+ if (end > 0) {
+ ReadAhead(buf.getFd(), base - buf, end);
+ }
+#endif
+}
+
+void mozilla::ReadAheadFile(mozilla::pathstr_t aFilePath, const size_t aOffset,
+ const size_t aCount, mozilla::filedesc_t* aOutFd) {
+#if defined(XP_WIN)
+ if (!aFilePath) {
+ if (aOutFd) {
+ *aOutFd = INVALID_HANDLE_VALUE;
+ }
+ return;
+ }
+ HANDLE fd = CreateFileW(aFilePath, GENERIC_READ, FILE_SHARE_READ, nullptr,
+ OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
+ if (aOutFd) {
+ *aOutFd = fd;
+ }
+ if (fd == INVALID_HANDLE_VALUE) {
+ return;
+ }
+ ReadAhead(fd, aOffset, aCount);
+ if (!aOutFd) {
+ CloseHandle(fd);
+ }
+#elif defined(LINUX) && !defined(ANDROID) || defined(XP_MACOSX)
+ if (!aFilePath) {
+ if (aOutFd) {
+ *aOutFd = -1;
+ }
+ return;
+ }
+ int fd = open(aFilePath, O_RDONLY);
+ if (aOutFd) {
+ *aOutFd = fd;
+ }
+ if (fd < 0) {
+ return;
+ }
+ size_t count;
+ if (aCount == SIZE_MAX) {
+ struct stat st;
+ if (fstat(fd, &st) < 0) {
+ if (!aOutFd) {
+ close(fd);
+ }
+ return;
+ }
+ count = st.st_size;
+ } else {
+ count = aCount;
+ }
+ ReadAhead(fd, aOffset, count);
+ if (!aOutFd) {
+ close(fd);
+ }
+#endif
+}
diff --git a/xpcom/glue/FileUtils.h b/xpcom/glue/FileUtils.h
new file mode 100644
index 0000000000..8e66ee5860
--- /dev/null
+++ b/xpcom/glue/FileUtils.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_FileUtils_h
+#define mozilla_FileUtils_h
+
+#include "nscore.h" // nullptr
+
+#if defined(XP_UNIX)
+# include <unistd.h>
+#elif defined(XP_WIN)
+# include <io.h>
+#endif
+#include "prio.h"
+#include "prlink.h"
+
+#include <memory> // unique_ptr
+#include "nsIFile.h"
+#include <errno.h>
+#include <limits.h>
+
+namespace mozilla {
+
+#if defined(XP_WIN)
+typedef void* filedesc_t;
+typedef const wchar_t* pathstr_t;
+#else
+typedef int filedesc_t;
+typedef const char* pathstr_t;
+#endif
+
+#if defined(MOZILLA_INTERNAL_API)
+
+struct PRCloseDeleter {
+ void operator()(PRFileDesc* aFd) {
+ if (aFd) {
+ PR_Close(aFd);
+ }
+ }
+};
+using AutoFDClose = UniquePtr<PRFileDesc, PRCloseDeleter>;
+
+/* RAII wrapper for FILE descriptors */
+struct FCloseDeleter {
+ void operator()(FILE* p) {
+ if (p) {
+ fclose(p);
+ }
+ }
+};
+using ScopedCloseFile = UniquePtr<FILE, FCloseDeleter>;
+
+/**
+ * Fallocate efficiently and continuously allocates files via fallocate-type
+ * APIs. This is useful for avoiding fragmentation. On sucess the file be padded
+ * with zeros to grow to aLength.
+ *
+ * @param aFD file descriptor.
+ * @param aLength length of file to grow to.
+ * @return true on success.
+ */
+bool fallocate(PRFileDesc* aFD, int64_t aLength);
+
+/**
+ * Use readahead to preload shared libraries into the file cache before loading.
+ * WARNING: This function should not be used without a telemetry field trial
+ * demonstrating a clear performance improvement!
+ *
+ * @param aFile nsIFile representing path to shared library
+ */
+void ReadAheadLib(nsIFile* aFile);
+
+/**
+ * Use readahead to preload a file into the file cache before reading.
+ * WARNING: This function should not be used without a telemetry field trial
+ * demonstrating a clear performance improvement!
+ *
+ * @param aFile nsIFile representing path to shared library
+ * @param aOffset Offset into the file to begin preloading
+ * @param aCount Number of bytes to preload (SIZE_MAX implies file size)
+ * @param aOutFd Pointer to file descriptor. If specified, ReadAheadFile will
+ * return its internal, opened file descriptor instead of closing it.
+ */
+void ReadAheadFile(nsIFile* aFile, const size_t aOffset = 0,
+ const size_t aCount = SIZE_MAX,
+ filedesc_t* aOutFd = nullptr);
+
+/*
+ * Wrappers for PR_GetLibraryName and PR_GetLibraryFilePathname.
+ */
+PathString GetLibraryName(pathstr_t aDirectory, const char* aLib);
+PathString GetLibraryFilePathname(pathstr_t aName, PRFuncPtr aAddr);
+
+#endif // MOZILLA_INTERNAL_API
+
+/**
+ * Use readahead to preload shared libraries into the file cache before loading.
+ * WARNING: This function should not be used without a telemetry field trial
+ * demonstrating a clear performance improvement!
+ *
+ * @param aFilePath path to shared library
+ */
+void ReadAheadLib(pathstr_t aFilePath);
+
+/**
+ * Use readahead to preload a file into the file cache before loading.
+ * WARNING: This function should not be used without a telemetry field trial
+ * demonstrating a clear performance improvement!
+ *
+ * @param aFilePath path to shared library
+ * @param aOffset Offset into the file to begin preloading
+ * @param aCount Number of bytes to preload (SIZE_MAX implies file size)
+ * @param aOutFd Pointer to file descriptor. If specified, ReadAheadFile will
+ * return its internal, opened file descriptor instead of closing it.
+ */
+void ReadAheadFile(pathstr_t aFilePath, const size_t aOffset = 0,
+ const size_t aCount = SIZE_MAX,
+ filedesc_t* aOutFd = nullptr);
+
+/**
+ * Use readahead to preload a file into the file cache before reading.
+ * When this function exits, the file pointer is guaranteed to be in the same
+ * position it was in before this function was called.
+ * WARNING: This function should not be used without a telemetry field trial
+ * demonstrating a clear performance improvement!
+ *
+ * @param aFd file descriptor opened for read access
+ * (on Windows, file must be opened with FILE_FLAG_SEQUENTIAL_SCAN)
+ * @param aOffset Offset into the file to begin preloading
+ * @param aCount Number of bytes to preload (SIZE_MAX implies file size)
+ */
+void ReadAhead(filedesc_t aFd, const size_t aOffset = 0,
+ const size_t aCount = SIZE_MAX);
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/glue/MemUtils.cpp b/xpcom/glue/MemUtils.cpp
new file mode 100644
index 0000000000..402568383c
--- /dev/null
+++ b/xpcom/glue/MemUtils.cpp
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/MemUtils.h"
+
+#if defined(XP_WIN)
+# include <windows.h>
+# include "mozilla/Maybe.h"
+#else
+# include <sys/mman.h>
+#endif
+
+#if defined(XP_WIN)
+typedef BOOL(WINAPI* PrefetchVirtualMemoryFn)(HANDLE, ULONG_PTR, PVOID, ULONG);
+
+static mozilla::Maybe<PrefetchVirtualMemoryFn> sPrefetchVirtualMemory;
+
+void MaybeInitPrefetchVirtualMemory() {
+ if (sPrefetchVirtualMemory.isNothing()) {
+ sPrefetchVirtualMemory.emplace(
+ reinterpret_cast<PrefetchVirtualMemoryFn>(GetProcAddress(
+ GetModuleHandleW(L"kernel32.dll"), "PrefetchVirtualMemory")));
+ }
+}
+#endif
+
+bool mozilla::CanPrefetchMemory() {
+#if defined(XP_SOLARIS) || defined(XP_UNIX)
+ return true;
+#elif defined(XP_WIN)
+ MaybeInitPrefetchVirtualMemory();
+ return *sPrefetchVirtualMemory;
+#else
+ return false;
+#endif
+}
+
+void mozilla::PrefetchMemory(uint8_t* aStart, size_t aNumBytes) {
+ if (aNumBytes == 0) {
+ return;
+ }
+
+#if defined(XP_SOLARIS)
+ posix_madvise(aStart, aNumBytes, POSIX_MADV_WILLNEED);
+#elif defined(XP_UNIX)
+ madvise(aStart, aNumBytes, MADV_WILLNEED);
+#elif defined(XP_WIN)
+ MaybeInitPrefetchVirtualMemory();
+ if (*sPrefetchVirtualMemory) {
+ WIN32_MEMORY_RANGE_ENTRY entry;
+ entry.VirtualAddress = aStart;
+ entry.NumberOfBytes = aNumBytes;
+ (*sPrefetchVirtualMemory)(GetCurrentProcess(), 1, &entry, 0);
+ return;
+ }
+#endif
+}
diff --git a/xpcom/glue/MemUtils.h b/xpcom/glue/MemUtils.h
new file mode 100644
index 0000000000..606df35d09
--- /dev/null
+++ b/xpcom/glue/MemUtils.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 mozilla_MemUtils_h
+#define mozilla_MemUtils_h
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace mozilla {
+
+bool CanPrefetchMemory();
+void PrefetchMemory(uint8_t* aStart, size_t aNumBytes);
+
+} // namespace mozilla
+
+#endif
diff --git a/xpcom/glue/XREAppData.cpp b/xpcom/glue/XREAppData.cpp
new file mode 100644
index 0000000000..82084d3b5f
--- /dev/null
+++ b/xpcom/glue/XREAppData.cpp
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/XREAppData.h"
+#include "nsCRTGlue.h"
+
+namespace mozilla {
+
+XREAppData& XREAppData::operator=(const StaticXREAppData& aOther) {
+ vendor = aOther.vendor;
+ name = aOther.name;
+ remotingName = aOther.remotingName;
+ version = aOther.version;
+ buildID = aOther.buildID;
+ ID = aOther.ID;
+ copyright = aOther.copyright;
+ flags = aOther.flags;
+ minVersion = aOther.minVersion;
+ maxVersion = aOther.maxVersion;
+ crashReporterURL = aOther.crashReporterURL;
+ profile = aOther.profile;
+ UAName = aOther.UAName;
+ sourceURL = aOther.sourceURL;
+ updateURL = aOther.updateURL;
+
+ return *this;
+}
+
+XREAppData& XREAppData::operator=(const XREAppData& aOther) = default;
+
+void XREAppData::SanitizeNameForDBus(nsACString& aName) {
+ auto IsValidDBusNameChar = [](char aChar) {
+ return IsAsciiAlpha(aChar) || IsAsciiDigit(aChar) || aChar == '_';
+ };
+
+ // D-Bus names can contain only [a-z][A-Z][0-9]_, so we replace all characters
+ // that aren't in that range with underscores.
+ char* cur = aName.BeginWriting();
+ char* end = aName.EndWriting();
+ for (; cur != end; cur++) {
+ if (!IsValidDBusNameChar(*cur)) {
+ *cur = '_';
+ }
+ }
+}
+
+void XREAppData::GetDBusAppName(nsACString& aName) const {
+ const char* env = getenv("MOZ_DBUS_APP_NAME");
+ if (env) {
+ aName.Assign(env);
+ } else {
+ aName.Assign(remotingName);
+ SanitizeNameForDBus(aName);
+ }
+}
+
+} // namespace mozilla
diff --git a/xpcom/glue/moz.build b/xpcom/glue/moz.build
new file mode 100644
index 0000000000..29c75e7241
--- /dev/null
+++ b/xpcom/glue/moz.build
@@ -0,0 +1,12 @@
+# -*- 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/.
+
+DIRS += ["standalone"]
+
+EXPORTS.mozilla += [
+ "FileUtils.h",
+ "MemUtils.h",
+]
diff --git a/xpcom/glue/objs.mozbuild b/xpcom/glue/objs.mozbuild
new file mode 100644
index 0000000000..709fd0c121
--- /dev/null
+++ b/xpcom/glue/objs.mozbuild
@@ -0,0 +1,17 @@
+# -*- 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/.
+
+xpcom_glue_src_lcppsrcs = [
+ "FileUtils.cpp",
+ "MemUtils.cpp",
+ "XREAppData.cpp",
+]
+
+xpcom_glue_src_cppsrcs = ["/xpcom/glue/%s" % s for s in xpcom_glue_src_lcppsrcs]
+
+xpcom_gluens_src_lcppsrcs = []
+
+xpcom_gluens_src_cppsrcs = ["/xpcom/glue/%s" % s for s in xpcom_gluens_src_lcppsrcs]
diff --git a/xpcom/glue/standalone/moz.build b/xpcom/glue/standalone/moz.build
new file mode 100644
index 0000000000..bf4cce5daf
--- /dev/null
+++ b/xpcom/glue/standalone/moz.build
@@ -0,0 +1,36 @@
+# -*- 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/.
+
+SOURCES += [
+ "../FileUtils.cpp",
+ "../MemUtils.cpp",
+ "nsXPCOMGlue.cpp",
+]
+
+Library("xpcomglue")
+
+FORCE_STATIC_LIB = True
+
+if CONFIG["CC_TYPE"] == "clang-cl":
+ DEFINES["_USE_ANSI_CPP"] = True
+ # Don't include directives about which CRT to use
+ CFLAGS += ["-Zl"]
+ CXXFLAGS += ["-Zl"]
+
+DEFINES["XPCOM_GLUE"] = True
+
+LOCAL_INCLUDES += [
+ "../../build",
+ "../../threads",
+]
+
+# Don't use STL wrappers here (i.e. wrapped <new>); they require mozalloc
+DisableStlWrapping()
+
+DIST_INSTALL = True
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
+ CXXFLAGS += CONFIG["GLIB_CFLAGS"]
diff --git a/xpcom/glue/standalone/nsXPCOMGlue.cpp b/xpcom/glue/standalone/nsXPCOMGlue.cpp
new file mode 100644
index 0000000000..e8d48193e2
--- /dev/null
+++ b/xpcom/glue/standalone/nsXPCOMGlue.cpp
@@ -0,0 +1,407 @@
+/* -*- 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/Bootstrap.h"
+
+#include "nsXPCOMPrivate.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "mozilla/FileUtils.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/Try.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/UniquePtrExtensions.h"
+
+using namespace mozilla;
+
+#define XPCOM_DEPENDENT_LIBS_LIST "dependentlibs.list"
+
+#if defined(XP_WIN)
+# define READ_TEXTMODE L"rt"
+#else
+# define READ_TEXTMODE "r"
+#endif
+
+typedef void (*NSFuncPtr)();
+
+#if defined(XP_WIN)
+# include <windows.h>
+using LibHandleType = HMODULE;
+#else
+using LibHandleType = void*;
+#endif
+
+using LibHandleResult = ::mozilla::Result<LibHandleType, DLErrorType>;
+
+#if defined(XP_WIN)
+# include <mbstring.h>
+# include "mozilla/PreXULSkeletonUI.h"
+
+static LibHandleResult GetLibHandle(pathstr_t aDependentLib) {
+ LibHandleType libHandle =
+ LoadLibraryExW(aDependentLib, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
+
+ if (!libHandle) {
+ DWORD err = GetLastError();
+# if defined(DEBUG)
+ LPWSTR lpMsgBuf;
+ FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPWSTR)&lpMsgBuf, 0, nullptr);
+ wprintf(L"Error loading %ls: %s\n", aDependentLib, lpMsgBuf);
+ LocalFree(lpMsgBuf);
+# endif // defined(DEBUG)
+ return Err(err);
+ }
+
+ return libHandle;
+}
+
+static NSFuncPtr GetSymbol(LibHandleType aLibHandle, const char* aSymbol) {
+ return (NSFuncPtr)GetProcAddress(aLibHandle, aSymbol);
+}
+
+static void CloseLibHandle(LibHandleType aLibHandle) {
+ FreeLibrary(aLibHandle);
+}
+
+#else
+# include <dlfcn.h>
+
+# if defined(MOZ_LINKER)
+extern "C" {
+NS_HIDDEN __typeof(dlopen) __wrap_dlopen;
+NS_HIDDEN __typeof(dlsym) __wrap_dlsym;
+NS_HIDDEN __typeof(dlclose) __wrap_dlclose;
+}
+
+# define dlopen __wrap_dlopen
+# define dlsym __wrap_dlsym
+# define dlclose __wrap_dlclose
+# endif
+
+static LibHandleResult GetLibHandle(pathstr_t aDependentLib) {
+ LibHandleType libHandle = dlopen(aDependentLib, RTLD_GLOBAL | RTLD_LAZY
+# ifdef XP_MACOSX
+ | RTLD_FIRST
+# endif
+ );
+ if (!libHandle) {
+ UniqueFreePtr<char> errMsg(strdup(dlerror()));
+ fprintf(stderr, "XPCOMGlueLoad error for file %s:\n%s\n", aDependentLib,
+ errMsg.get());
+ return Err(std::move(errMsg));
+ }
+ return libHandle;
+}
+
+static NSFuncPtr GetSymbol(LibHandleType aLibHandle, const char* aSymbol) {
+ return (NSFuncPtr)dlsym(aLibHandle, aSymbol);
+}
+
+# if !defined(MOZ_LINKER) && !defined(__ANDROID__)
+static void CloseLibHandle(LibHandleType aLibHandle) { dlclose(aLibHandle); }
+# endif
+#endif
+
+struct DependentLib {
+ LibHandleType libHandle;
+ DependentLib* next;
+};
+
+static DependentLib* sTop;
+
+static void AppendDependentLib(LibHandleType aLibHandle) {
+ auto* d = new DependentLib;
+ if (!d) {
+ return;
+ }
+
+ d->next = sTop;
+ d->libHandle = aLibHandle;
+
+ sTop = d;
+}
+
+using ReadDependentCBResult = ::mozilla::Result<::mozilla::Ok, DLErrorType>;
+
+static ReadDependentCBResult ReadDependentCB(
+ pathstr_t aDependentLib, LibLoadingStrategy aLibLoadingStrategy) {
+#if !defined(MOZ_LINKER) && !defined(__ANDROID__)
+ // Don't bother doing a ReadAhead if we're not in the parent process.
+ // What we need from the library should already be in the system file
+ // cache.
+ if (aLibLoadingStrategy == LibLoadingStrategy::ReadAhead) {
+ ReadAheadLib(aDependentLib);
+ }
+#endif
+ LibHandleType libHandle;
+ MOZ_TRY_VAR(libHandle, GetLibHandle(aDependentLib));
+
+ AppendDependentLib(libHandle);
+ return Ok();
+}
+
+#ifdef XP_WIN
+static ReadDependentCBResult ReadDependentCB(
+ const char* aDependentLib, LibLoadingStrategy aLibLoadingStrategy) {
+ wchar_t wideDependentLib[MAX_PATH];
+ MultiByteToWideChar(CP_UTF8, 0, aDependentLib, -1, wideDependentLib,
+ MAX_PATH);
+ return ReadDependentCB(wideDependentLib, aLibLoadingStrategy);
+}
+
+inline FILE* TS_tfopen(const char* path, const wchar_t* mode) {
+ wchar_t wPath[MAX_PATH];
+ MultiByteToWideChar(CP_UTF8, 0, path, -1, wPath, MAX_PATH);
+ return _wfopen(wPath, mode);
+}
+#else
+inline FILE* TS_tfopen(const char* aPath, const char* aMode) {
+ return fopen(aPath, aMode);
+}
+#endif
+
+#if !defined(MOZ_LINKER) && !defined(__ANDROID__)
+static void XPCOMGlueUnload() {
+ while (sTop) {
+ CloseLibHandle(sTop->libHandle);
+
+ DependentLib* temp = sTop;
+ sTop = sTop->next;
+
+ delete temp;
+ }
+}
+#endif
+
+#if defined(XP_WIN)
+// like strpbrk but finds the *last* char, not the first
+static const char* ns_strrpbrk(const char* string, const char* strCharSet) {
+ const char* found = nullptr;
+ for (; *string; ++string) {
+ for (const char* search = strCharSet; *search; ++search) {
+ if (*search == *string) {
+ found = string;
+ // Since we're looking for the last char, we save "found"
+ // until we're at the end of the string.
+ }
+ }
+ }
+
+ return found;
+}
+#endif
+
+using XPCOMGlueLoadError = BootstrapError;
+using XPCOMGlueLoadResult =
+ ::mozilla::Result<::mozilla::Ok, XPCOMGlueLoadError>;
+
+static XPCOMGlueLoadResult XPCOMGlueLoad(
+ const char* aXPCOMFile, LibLoadingStrategy aLibLoadingStrategy) {
+#if defined(MOZ_LINKER) || defined(__ANDROID__)
+ ReadDependentCBResult readDependentCBResult =
+ ReadDependentCB(aXPCOMFile, aLibLoadingStrategy);
+ if (readDependentCBResult.isErr()) {
+ return Err(AsVariant(readDependentCBResult.unwrapErr()));
+ }
+#else
+ char xpcomDir[MAXPATHLEN];
+# ifdef XP_WIN
+ const char* lastSlash = ns_strrpbrk(aXPCOMFile, "/\\");
+# elif XP_MACOSX
+ // On OSX, the dependentlibs.list file lives under Contents/Resources.
+ // However, the actual libraries listed in dependentlibs.list live under
+ // Contents/MacOS. We want to read the list from Contents/Resources, then
+ // load the libraries from Contents/MacOS.
+ const char* tempSlash = strrchr(aXPCOMFile, '/');
+ size_t tempLen = size_t(tempSlash - aXPCOMFile);
+ if (tempLen > MAXPATHLEN) {
+ return Err(AsVariant(NS_ERROR_FAILURE));
+ }
+ char tempBuffer[MAXPATHLEN];
+ memcpy(tempBuffer, aXPCOMFile, tempLen);
+ tempBuffer[tempLen] = '\0';
+ const char* slash = strrchr(tempBuffer, '/');
+ tempLen = size_t(slash - tempBuffer);
+ const char* lastSlash = aXPCOMFile + tempLen;
+# else
+ const char* lastSlash = strrchr(aXPCOMFile, '/');
+# endif
+ char* cursor;
+ if (lastSlash) {
+ size_t len = size_t(lastSlash - aXPCOMFile);
+
+ if (len > MAXPATHLEN - sizeof(XPCOM_FILE_PATH_SEPARATOR
+# ifdef XP_MACOSX
+ "Resources" XPCOM_FILE_PATH_SEPARATOR
+# endif
+ XPCOM_DEPENDENT_LIBS_LIST)) {
+ return Err(AsVariant(NS_ERROR_FAILURE));
+ }
+ memcpy(xpcomDir, aXPCOMFile, len);
+ strcpy(xpcomDir + len, XPCOM_FILE_PATH_SEPARATOR
+# ifdef XP_MACOSX
+ "Resources" XPCOM_FILE_PATH_SEPARATOR
+# endif
+ XPCOM_DEPENDENT_LIBS_LIST);
+ cursor = xpcomDir + len + 1;
+ } else {
+ strcpy(xpcomDir, XPCOM_DEPENDENT_LIBS_LIST);
+ cursor = xpcomDir;
+ }
+
+ if (getenv("MOZ_RUN_GTEST")
+# ifdef FUZZING
+ || getenv("FUZZER")
+# endif
+ ) {
+ strcat(xpcomDir, ".gtest");
+ }
+
+ const auto flist = TS_tfopen(xpcomDir, READ_TEXTMODE);
+ const auto cleanup = MakeScopeExit([&]() {
+ if (flist) {
+ fclose(flist);
+ }
+ });
+ if (!flist) {
+ return Err(AsVariant(NS_ERROR_FAILURE));
+ }
+
+# ifdef XP_MACOSX
+ tempLen = size_t(cursor - xpcomDir);
+ if (tempLen > MAXPATHLEN - sizeof("MacOS" XPCOM_FILE_PATH_SEPARATOR) - 1) {
+ return Err(AsVariant(NS_ERROR_FAILURE));
+ }
+ strcpy(cursor, "MacOS" XPCOM_FILE_PATH_SEPARATOR);
+ cursor += strlen(cursor);
+# endif
+ *cursor = '\0';
+
+ char buffer[MAXPATHLEN];
+
+ while (fgets(buffer, sizeof(buffer), flist)) {
+ int l = strlen(buffer);
+
+ // ignore empty lines and comments
+ if (l == 0 || *buffer == '#') {
+ continue;
+ }
+
+ // cut the trailing newline, if present
+ if (buffer[l - 1] == '\n') {
+ buffer[l - 1] = '\0';
+ }
+
+ if (l + size_t(cursor - xpcomDir) > MAXPATHLEN) {
+ return Err(AsVariant(NS_ERROR_FAILURE));
+ }
+
+ strcpy(cursor, buffer);
+ ReadDependentCBResult readDependentCBResult =
+ ReadDependentCB(xpcomDir, aLibLoadingStrategy);
+ if (readDependentCBResult.isErr()) {
+ XPCOMGlueUnload();
+ return Err(AsVariant(readDependentCBResult.unwrapErr()));
+ }
+
+# ifdef XP_WIN
+ // We call PollPreXULSkeletonUIEvents here in order to not get flagged by
+ // Windows as nonresponsive. In order to not be flagged as such, we seem to
+ // simply need to respond to *a* message every few seconds. The halfway
+ // point on slow systems between process start and nsWindow taking over the
+ // skeleton UI window seems to be XUL being loaded. Accordingly, placing
+ // this call here covers the most ground (as we will call this after
+ // prefetching and loading all of the dlls in dependentlibs.list, which
+ // includes xul.dll.)
+ PollPreXULSkeletonUIEvents();
+# endif
+ }
+#endif
+ return Ok();
+}
+
+#if defined(MOZ_WIDGET_GTK) && \
+ (defined(MOZ_MEMORY) || defined(__FreeBSD__) || defined(__NetBSD__))
+# define MOZ_GSLICE_INIT
+#endif
+
+#ifdef MOZ_GSLICE_INIT
+# include <glib.h>
+
+class GSliceInit {
+ public:
+ GSliceInit() {
+ mHadGSlice = bool(getenv("G_SLICE"));
+ if (!mHadGSlice) {
+ // Disable the slice allocator, since jemalloc already uses similar layout
+ // algorithms, and using a sub-allocator tends to increase fragmentation.
+ // This must be done before g_thread_init() is called.
+ // glib >= 2.36 initializes g_slice as a side effect of its various static
+ // initializers, so this needs to happen before glib is loaded, which is
+ // this is hooked in XPCOMGlueStartup before libxul is loaded. This
+ // relies on the main executable not depending on glib.
+ setenv("G_SLICE", "always-malloc", 1);
+ }
+ }
+
+ ~GSliceInit() {
+ if (!mHadGSlice) {
+ unsetenv("G_SLICE");
+ }
+ }
+
+ private:
+ bool mHadGSlice;
+};
+#endif
+
+namespace mozilla {
+
+BootstrapResult GetBootstrap(const char* aXPCOMFile,
+ LibLoadingStrategy aLibLoadingStrategy) {
+#ifdef MOZ_GSLICE_INIT
+ GSliceInit gSliceInit;
+#endif
+
+ if (!aXPCOMFile) {
+ return Err(AsVariant(NS_ERROR_INVALID_ARG));
+ }
+
+ char* lastSlash =
+ strrchr(const_cast<char*>(aXPCOMFile), XPCOM_FILE_PATH_SEPARATOR[0]);
+ if (!lastSlash) {
+ return Err(AsVariant(NS_ERROR_FILE_INVALID_PATH));
+ }
+
+ size_t base_len = size_t(lastSlash - aXPCOMFile) + 1;
+
+ UniqueFreePtr<char> file(
+ reinterpret_cast<char*>(malloc(base_len + sizeof(XPCOM_DLL))));
+ memcpy(file.get(), aXPCOMFile, base_len);
+ memcpy(file.get() + base_len, XPCOM_DLL, sizeof(XPCOM_DLL));
+
+ MOZ_TRY(XPCOMGlueLoad(file.get(), aLibLoadingStrategy));
+
+ if (!sTop) {
+ return Err(AsVariant(NS_ERROR_NOT_AVAILABLE));
+ }
+ GetBootstrapType func =
+ (GetBootstrapType)GetSymbol(sTop->libHandle, "XRE_GetBootstrap");
+ if (!func) {
+ return Err(AsVariant(NS_ERROR_NOT_AVAILABLE));
+ }
+
+ Bootstrap::UniquePtr b;
+ (*func)(b);
+
+ return b;
+}
+
+} // namespace mozilla