summaryrefslogtreecommitdiffstats
path: root/ipc/glue/GeckoChildProcessHost.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/glue/GeckoChildProcessHost.cpp')
-rw-r--r--ipc/glue/GeckoChildProcessHost.cpp1268
1 files changed, 1268 insertions, 0 deletions
diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp
new file mode 100644
index 000000000..48051472a
--- /dev/null
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -0,0 +1,1268 @@
+/* -*- 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 "GeckoChildProcessHost.h"
+
+#include "base/command_line.h"
+#include "base/string_util.h"
+#include "base/task.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/process_watcher.h"
+#ifdef MOZ_WIDGET_COCOA
+#include "chrome/common/mach_ipc_mac.h"
+#include "base/rand_util.h"
+#include "nsILocalFileMac.h"
+#include "SharedMemoryBasic.h"
+#endif
+
+#include "MainThreadUtils.h"
+#include "mozilla/Sprintf.h"
+#include "prenv.h"
+#include "nsXPCOMPrivate.h"
+
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+#include "nsAppDirectoryServiceDefs.h"
+#endif
+
+#include "nsExceptionHandler.h"
+
+#include "nsDirectoryServiceDefs.h"
+#include "nsIFile.h"
+#include "nsPrintfCString.h"
+
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/ipc/BrowserProcessSubThread.h"
+#include "mozilla/Omnijar.h"
+#include "mozilla/Telemetry.h"
+#include "ProtocolUtils.h"
+#include <sys/stat.h>
+
+#ifdef XP_WIN
+#include "nsIWinTaskbar.h"
+#define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
+
+#if defined(MOZ_SANDBOX)
+#include "mozilla/Preferences.h"
+#include "mozilla/sandboxing/sandboxLogging.h"
+#include "nsDirectoryServiceUtils.h"
+#endif
+#endif
+
+#include "nsTArray.h"
+#include "nsClassHashtable.h"
+#include "nsHashKeys.h"
+#include "nsNativeCharsetUtils.h"
+#include "nscore.h" // for NS_FREE_PERMANENT_DATA
+
+using mozilla::MonitorAutoLock;
+using mozilla::ipc::GeckoChildProcessHost;
+
+#ifdef ANDROID
+// Like its predecessor in nsExceptionHandler.cpp, this is
+// the magic number of a file descriptor remapping we must
+// preserve for the child process.
+static const int kMagicAndroidSystemPropFd = 5;
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#endif
+
+static const bool kLowRightsSubprocesses =
+ // We currently only attempt to drop privileges on gonk, because we
+ // have no plugins or extensions to worry about breaking.
+#ifdef MOZ_WIDGET_GONK
+ true
+#else
+ false
+#endif
+ ;
+
+static bool
+ShouldHaveDirectoryService()
+{
+ return GeckoProcessType_Default == XRE_GetProcessType();
+}
+
+/*static*/
+base::ChildPrivileges
+GeckoChildProcessHost::DefaultChildPrivileges()
+{
+ return (kLowRightsSubprocesses ?
+ base::PRIVILEGES_UNPRIVILEGED : base::PRIVILEGES_INHERIT);
+}
+
+GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType,
+ ChildPrivileges aPrivileges)
+ : mProcessType(aProcessType),
+ mPrivileges(aPrivileges),
+ mMonitor("mozilla.ipc.GeckChildProcessHost.mMonitor"),
+ mProcessState(CREATING_CHANNEL),
+#if defined(MOZ_SANDBOX) && defined(XP_WIN)
+ mEnableSandboxLogging(false),
+ mSandboxLevel(0),
+#endif
+ mChildProcessHandle(0)
+#if defined(MOZ_WIDGET_COCOA)
+ , mChildTask(MACH_PORT_NULL)
+#endif
+{
+ MOZ_COUNT_CTOR(GeckoChildProcessHost);
+}
+
+GeckoChildProcessHost::~GeckoChildProcessHost()
+
+{
+ AssertIOThread();
+
+ MOZ_COUNT_DTOR(GeckoChildProcessHost);
+
+ if (mChildProcessHandle != 0) {
+#if defined(MOZ_WIDGET_COCOA)
+ SharedMemoryBasic::CleanupForPid(mChildProcessHandle);
+#endif
+ ProcessWatcher::EnsureProcessTerminated(mChildProcessHandle
+#ifdef NS_FREE_PERMANENT_DATA
+ // If we're doing leak logging, shutdown can be slow.
+ , false // don't "force"
+#endif
+ );
+ }
+
+#if defined(MOZ_WIDGET_COCOA)
+ if (mChildTask != MACH_PORT_NULL)
+ mach_port_deallocate(mach_task_self(), mChildTask);
+#endif
+}
+
+//static
+auto
+GeckoChildProcessHost::GetPathToBinary(FilePath& exePath, GeckoProcessType processType) -> BinaryPathType
+{
+ if (sRunSelfAsContentProc &&
+ (processType == GeckoProcessType_Content || processType == GeckoProcessType_GPU)) {
+#if defined(OS_WIN)
+ wchar_t exePathBuf[MAXPATHLEN];
+ if (!::GetModuleFileNameW(nullptr, exePathBuf, MAXPATHLEN)) {
+ MOZ_CRASH("GetModuleFileNameW failed (FIXME)");
+ }
+ exePath = FilePath::FromWStringHack(exePathBuf);
+#elif defined(OS_POSIX)
+ exePath = FilePath(CommandLine::ForCurrentProcess()->argv()[0]);
+#else
+# error Sorry; target OS not supported yet.
+#endif
+ return BinaryPathType::Self;
+ }
+
+ if (ShouldHaveDirectoryService()) {
+ MOZ_ASSERT(gGREBinPath);
+#ifdef OS_WIN
+ exePath = FilePath(char16ptr_t(gGREBinPath));
+#elif MOZ_WIDGET_COCOA
+ nsCOMPtr<nsIFile> childProcPath;
+ NS_NewLocalFile(nsDependentString(gGREBinPath), false,
+ getter_AddRefs(childProcPath));
+
+ // We need to use an App Bundle on OS X so that we can hide
+ // the dock icon. See Bug 557225.
+ childProcPath->AppendNative(NS_LITERAL_CSTRING("plugin-container.app"));
+ childProcPath->AppendNative(NS_LITERAL_CSTRING("Contents"));
+ childProcPath->AppendNative(NS_LITERAL_CSTRING("MacOS"));
+ nsCString tempCPath;
+ childProcPath->GetNativePath(tempCPath);
+ exePath = FilePath(tempCPath.get());
+#else
+ nsCString path;
+ NS_CopyUnicodeToNative(nsDependentString(gGREBinPath), path);
+ exePath = FilePath(path.get());
+#endif
+ }
+
+ if (exePath.empty()) {
+#ifdef OS_WIN
+ exePath = FilePath::FromWStringHack(CommandLine::ForCurrentProcess()->program());
+#else
+ exePath = FilePath(CommandLine::ForCurrentProcess()->argv()[0]);
+#endif
+ exePath = exePath.DirName();
+ }
+
+#ifdef MOZ_WIDGET_ANDROID
+ exePath = exePath.AppendASCII("lib");
+
+ // We must use the PIE binary on 5.0 and higher
+ const char* processName = mozilla::AndroidBridge::Bridge()->GetAPIVersion() >= 21 ?
+ MOZ_CHILD_PROCESS_NAME_PIE : MOZ_CHILD_PROCESS_NAME;
+
+ exePath = exePath.AppendASCII(processName);
+#else
+ exePath = exePath.AppendASCII(MOZ_CHILD_PROCESS_NAME);
+#endif
+
+ return BinaryPathType::PluginContainer;
+}
+
+#ifdef MOZ_WIDGET_COCOA
+class AutoCFTypeObject {
+public:
+ explicit AutoCFTypeObject(CFTypeRef object)
+ {
+ mObject = object;
+ }
+ ~AutoCFTypeObject()
+ {
+ ::CFRelease(mObject);
+ }
+private:
+ CFTypeRef mObject;
+};
+#endif
+
+nsresult GeckoChildProcessHost::GetArchitecturesForBinary(const char *path, uint32_t *result)
+{
+ *result = 0;
+
+#ifdef MOZ_WIDGET_COCOA
+ CFURLRef url = ::CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
+ (const UInt8*)path,
+ strlen(path),
+ false);
+ if (!url) {
+ return NS_ERROR_FAILURE;
+ }
+ AutoCFTypeObject autoPluginContainerURL(url);
+
+ CFArrayRef pluginContainerArchs = ::CFBundleCopyExecutableArchitecturesForURL(url);
+ if (!pluginContainerArchs) {
+ return NS_ERROR_FAILURE;
+ }
+ AutoCFTypeObject autoPluginContainerArchs(pluginContainerArchs);
+
+ CFIndex pluginArchCount = ::CFArrayGetCount(pluginContainerArchs);
+ for (CFIndex i = 0; i < pluginArchCount; i++) {
+ CFNumberRef currentArch = static_cast<CFNumberRef>(::CFArrayGetValueAtIndex(pluginContainerArchs, i));
+ int currentArchInt = 0;
+ if (!::CFNumberGetValue(currentArch, kCFNumberIntType, &currentArchInt)) {
+ continue;
+ }
+ switch (currentArchInt) {
+ case kCFBundleExecutableArchitectureI386:
+ *result |= base::PROCESS_ARCH_I386;
+ break;
+ case kCFBundleExecutableArchitectureX86_64:
+ *result |= base::PROCESS_ARCH_X86_64;
+ break;
+ case kCFBundleExecutableArchitecturePPC:
+ *result |= base::PROCESS_ARCH_PPC;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return (*result ? NS_OK : NS_ERROR_FAILURE);
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+uint32_t GeckoChildProcessHost::GetSupportedArchitecturesForProcessType(GeckoProcessType type)
+{
+#ifdef MOZ_WIDGET_COCOA
+ if (type == GeckoProcessType_Plugin) {
+
+ // Cache this, it shouldn't ever change.
+ static uint32_t pluginContainerArchs = 0;
+ if (pluginContainerArchs == 0) {
+ FilePath exePath;
+ GetPathToBinary(exePath, type);
+ nsresult rv = GetArchitecturesForBinary(exePath.value().c_str(), &pluginContainerArchs);
+ NS_ASSERTION(NS_SUCCEEDED(rv) && pluginContainerArchs != 0, "Getting architecture of plugin container failed!");
+ if (NS_FAILED(rv) || pluginContainerArchs == 0) {
+ pluginContainerArchs = base::GetCurrentProcessArchitecture();
+ }
+ }
+ return pluginContainerArchs;
+ }
+#endif
+
+ return base::GetCurrentProcessArchitecture();
+}
+
+// We start the unique IDs at 1 so that 0 can be used to mean that
+// a component has no unique ID assigned to it.
+uint32_t GeckoChildProcessHost::sNextUniqueID = 1;
+
+/* static */
+uint32_t
+GeckoChildProcessHost::GetUniqueID()
+{
+ return sNextUniqueID++;
+}
+
+void
+GeckoChildProcessHost::PrepareLaunch()
+{
+#ifdef MOZ_CRASHREPORTER
+ if (CrashReporter::GetEnabled()) {
+ CrashReporter::OOPInit();
+ }
+#endif
+
+#ifdef XP_WIN
+ if (mProcessType == GeckoProcessType_Plugin) {
+ InitWindowsGroupID();
+ }
+
+#if defined(MOZ_CONTENT_SANDBOX)
+ // We need to get the pref here as the process is launched off main thread.
+ if (mProcessType == GeckoProcessType_Content) {
+ mSandboxLevel = Preferences::GetInt("security.sandbox.content.level");
+ mEnableSandboxLogging =
+ Preferences::GetBool("security.sandbox.windows.log");
+ }
+#endif
+
+#if defined(MOZ_SANDBOX)
+ // For other process types we can't rely on them being launched on main
+ // thread and they may not have access to prefs in the child process, so allow
+ // them to turn on logging via an environment variable.
+ mEnableSandboxLogging = mEnableSandboxLogging
+ || !!PR_GetEnv("MOZ_WIN_SANDBOX_LOGGING");
+#endif
+#endif
+}
+
+#ifdef XP_WIN
+void GeckoChildProcessHost::InitWindowsGroupID()
+{
+ // On Win7+, pass the application user model to the child, so it can
+ // register with it. This insures windows created by the container
+ // properly group with the parent app on the Win7 taskbar.
+ nsCOMPtr<nsIWinTaskbar> taskbarInfo =
+ do_GetService(NS_TASKBAR_CONTRACTID);
+ if (taskbarInfo) {
+ bool isSupported = false;
+ taskbarInfo->GetAvailable(&isSupported);
+ nsAutoString appId;
+ if (isSupported && NS_SUCCEEDED(taskbarInfo->GetDefaultGroupId(appId))) {
+ mGroupId.Append(appId);
+ } else {
+ mGroupId.Assign('-');
+ }
+ }
+}
+#endif
+
+bool
+GeckoChildProcessHost::SyncLaunch(std::vector<std::string> aExtraOpts, int aTimeoutMs, base::ProcessArchitecture arch)
+{
+ PrepareLaunch();
+
+ MessageLoop* ioLoop = XRE_GetIOMessageLoop();
+ NS_ASSERTION(MessageLoop::current() != ioLoop, "sync launch from the IO thread NYI");
+
+ ioLoop->PostTask(NewNonOwningRunnableMethod
+ <std::vector<std::string>, base::ProcessArchitecture>
+ (this, &GeckoChildProcessHost::RunPerformAsyncLaunch,
+ aExtraOpts, arch));
+
+ return WaitUntilConnected(aTimeoutMs);
+}
+
+bool
+GeckoChildProcessHost::AsyncLaunch(std::vector<std::string> aExtraOpts,
+ base::ProcessArchitecture arch)
+{
+ PrepareLaunch();
+
+ MessageLoop* ioLoop = XRE_GetIOMessageLoop();
+
+ ioLoop->PostTask(NewNonOwningRunnableMethod
+ <std::vector<std::string>, base::ProcessArchitecture>
+ (this, &GeckoChildProcessHost::RunPerformAsyncLaunch,
+ aExtraOpts, arch));
+
+ // This may look like the sync launch wait, but we only delay as
+ // long as it takes to create the channel.
+ MonitorAutoLock lock(mMonitor);
+ while (mProcessState < CHANNEL_INITIALIZED) {
+ lock.Wait();
+ }
+
+ return true;
+}
+
+bool
+GeckoChildProcessHost::WaitUntilConnected(int32_t aTimeoutMs)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
+
+ // NB: this uses a different mechanism than the chromium parent
+ // class.
+ PRIntervalTime timeoutTicks = (aTimeoutMs > 0) ?
+ PR_MillisecondsToInterval(aTimeoutMs) : PR_INTERVAL_NO_TIMEOUT;
+
+ MonitorAutoLock lock(mMonitor);
+ PRIntervalTime waitStart = PR_IntervalNow();
+ PRIntervalTime current;
+
+ // We'll receive several notifications, we need to exit when we
+ // have either successfully launched or have timed out.
+ while (mProcessState != PROCESS_CONNECTED) {
+ // If there was an error then return it, don't wait out the timeout.
+ if (mProcessState == PROCESS_ERROR) {
+ break;
+ }
+
+ lock.Wait(timeoutTicks);
+
+ if (timeoutTicks != PR_INTERVAL_NO_TIMEOUT) {
+ current = PR_IntervalNow();
+ PRIntervalTime elapsed = current - waitStart;
+ if (elapsed > timeoutTicks) {
+ break;
+ }
+ timeoutTicks = timeoutTicks - elapsed;
+ waitStart = current;
+ }
+ }
+
+ return mProcessState == PROCESS_CONNECTED;
+}
+
+bool
+GeckoChildProcessHost::LaunchAndWaitForProcessHandle(StringVector aExtraOpts)
+{
+ PrepareLaunch();
+
+ MessageLoop* ioLoop = XRE_GetIOMessageLoop();
+ ioLoop->PostTask(NewNonOwningRunnableMethod
+ <std::vector<std::string>, base::ProcessArchitecture>
+ (this, &GeckoChildProcessHost::RunPerformAsyncLaunch,
+ aExtraOpts, base::GetCurrentProcessArchitecture()));
+
+ MonitorAutoLock lock(mMonitor);
+ while (mProcessState < PROCESS_CREATED) {
+ lock.Wait();
+ }
+ MOZ_ASSERT(mProcessState == PROCESS_ERROR || mChildProcessHandle);
+
+ return mProcessState < PROCESS_ERROR;
+}
+
+void
+GeckoChildProcessHost::InitializeChannel()
+{
+ CreateChannel();
+
+ MonitorAutoLock lock(mMonitor);
+ mProcessState = CHANNEL_INITIALIZED;
+ lock.Notify();
+}
+
+void
+GeckoChildProcessHost::Join()
+{
+ AssertIOThread();
+
+ if (!mChildProcessHandle) {
+ return;
+ }
+
+ // If this fails, there's nothing we can do.
+ base::KillProcess(mChildProcessHandle, 0, /*wait*/true);
+ SetAlreadyDead();
+}
+
+void
+GeckoChildProcessHost::SetAlreadyDead()
+{
+ if (mChildProcessHandle &&
+ mChildProcessHandle != kInvalidProcessHandle) {
+ base::CloseProcessHandle(mChildProcessHandle);
+ }
+
+ mChildProcessHandle = 0;
+}
+
+int32_t GeckoChildProcessHost::mChildCounter = 0;
+
+void
+GeckoChildProcessHost::SetChildLogName(const char* varName, const char* origLogName,
+ nsACString &buffer)
+{
+ // We currently have no portable way to launch child with environment
+ // different than parent. So temporarily change NSPR_LOG_FILE so child
+ // inherits value we want it to have. (NSPR only looks at NSPR_LOG_FILE at
+ // startup, so it's 'safe' to play with the parent's environment this way.)
+ buffer.Assign(varName);
+ buffer.Append(origLogName);
+
+ // Append child-specific postfix to name
+ buffer.AppendLiteral(".child-");
+ buffer.AppendInt(mChildCounter);
+
+ // Passing temporary to PR_SetEnv is ok here if we keep the temporary
+ // for the time we launch the sub-process. It's copied to the new
+ // environment.
+ PR_SetEnv(buffer.BeginReading());
+}
+
+bool
+GeckoChildProcessHost::PerformAsyncLaunch(std::vector<std::string> aExtraOpts, base::ProcessArchitecture arch)
+{
+ // If NSPR log files are not requested, we're done.
+ const char* origNSPRLogName = PR_GetEnv("NSPR_LOG_FILE");
+ const char* origMozLogName = PR_GetEnv("MOZ_LOG_FILE");
+ if (!origNSPRLogName && !origMozLogName) {
+ return PerformAsyncLaunchInternal(aExtraOpts, arch);
+ }
+
+ // - Note: this code is not called re-entrantly, nor are restoreOrig*LogName
+ // or mChildCounter touched by any other thread, so this is safe.
+ ++mChildCounter;
+
+ // Must keep these on the same stack where from we call PerformAsyncLaunchInternal
+ // so that PR_DuplicateEnvironment() still sees a valid memory.
+ nsAutoCString nsprLogName;
+ nsAutoCString mozLogName;
+
+ if (origNSPRLogName) {
+ if (mRestoreOrigNSPRLogName.IsEmpty()) {
+ mRestoreOrigNSPRLogName.AssignLiteral("NSPR_LOG_FILE=");
+ mRestoreOrigNSPRLogName.Append(origNSPRLogName);
+ }
+ SetChildLogName("NSPR_LOG_FILE=", origNSPRLogName, nsprLogName);
+ }
+ if (origMozLogName) {
+ if (mRestoreOrigMozLogName.IsEmpty()) {
+ mRestoreOrigMozLogName.AssignLiteral("MOZ_LOG_FILE=");
+ mRestoreOrigMozLogName.Append(origMozLogName);
+ }
+ SetChildLogName("MOZ_LOG_FILE=", origMozLogName, mozLogName);
+ }
+
+ bool retval = PerformAsyncLaunchInternal(aExtraOpts, arch);
+
+ // Revert to original value
+ if (origNSPRLogName) {
+ PR_SetEnv(mRestoreOrigNSPRLogName.get());
+ }
+ if (origMozLogName) {
+ PR_SetEnv(mRestoreOrigMozLogName.get());
+ }
+
+ return retval;
+}
+
+bool
+GeckoChildProcessHost::RunPerformAsyncLaunch(std::vector<std::string> aExtraOpts,
+ base::ProcessArchitecture aArch)
+{
+ InitializeChannel();
+
+ bool ok = PerformAsyncLaunch(aExtraOpts, aArch);
+ if (!ok) {
+ // WaitUntilConnected might be waiting for us to signal.
+ // If something failed let's set the error state and notify.
+ MonitorAutoLock lock(mMonitor);
+ mProcessState = PROCESS_ERROR;
+ lock.Notify();
+ CHROMIUM_LOG(ERROR) << "Failed to launch " <<
+ XRE_ChildProcessTypeToString(mProcessType) << " subprocess";
+ Telemetry::Accumulate(Telemetry::SUBPROCESS_LAUNCH_FAILURE,
+ nsDependentCString(XRE_ChildProcessTypeToString(mProcessType)));
+ }
+ return ok;
+}
+
+void
+#if defined(XP_WIN)
+AddAppDirToCommandLine(CommandLine& aCmdLine)
+#else
+AddAppDirToCommandLine(std::vector<std::string>& aCmdLine)
+#endif
+{
+ // Content processes need access to application resources, so pass
+ // the full application directory path to the child process.
+ if (ShouldHaveDirectoryService()) {
+ nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
+ NS_ASSERTION(directoryService, "Expected XPCOM to be available");
+ if (directoryService) {
+ nsCOMPtr<nsIFile> appDir;
+ // NS_XPCOM_CURRENT_PROCESS_DIR really means the app dir, not the
+ // current process dir.
+ nsresult rv = directoryService->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
+ NS_GET_IID(nsIFile),
+ getter_AddRefs(appDir));
+ if (NS_SUCCEEDED(rv)) {
+#if defined(XP_WIN)
+ nsString path;
+ MOZ_ALWAYS_SUCCEEDS(appDir->GetPath(path));
+ aCmdLine.AppendLooseValue(UTF8ToWide("-appdir"));
+ std::wstring wpath(path.get());
+ aCmdLine.AppendLooseValue(wpath);
+#else
+ nsAutoCString path;
+ MOZ_ALWAYS_SUCCEEDS(appDir->GetNativePath(path));
+ aCmdLine.push_back("-appdir");
+ aCmdLine.push_back(path.get());
+#endif
+ }
+
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+ // Full path to the profile dir
+ nsCOMPtr<nsIFile> profileDir;
+ rv = directoryService->Get(NS_APP_USER_PROFILE_50_DIR,
+ NS_GET_IID(nsIFile),
+ getter_AddRefs(profileDir));
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString path;
+ MOZ_ALWAYS_SUCCEEDS(profileDir->GetNativePath(path));
+ aCmdLine.push_back("-profile");
+ aCmdLine.push_back(path.get());
+ }
+#endif
+ }
+ }
+}
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+static void
+MaybeAddNsprLogFileAccess(std::vector<std::wstring>& aAllowedFilesReadWrite)
+{
+ const char* nsprLogFileEnv = PR_GetEnv("NSPR_LOG_FILE");
+ if (!nsprLogFileEnv) {
+ return;
+ }
+
+ nsDependentCString nsprLogFilePath(nsprLogFileEnv);
+ nsCOMPtr<nsIFile> nsprLogFile;
+ nsresult rv = NS_NewNativeLocalFile(nsprLogFilePath, true,
+ getter_AddRefs(nsprLogFile));
+ if (NS_FAILED(rv)) {
+ // Not an absolute path, try it as a relative one.
+ nsresult rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR,
+ getter_AddRefs(nsprLogFile));
+ if (NS_FAILED(rv) || !nsprLogFile) {
+ NS_WARNING("Failed to get current working directory");
+ return;
+ }
+
+ rv = nsprLogFile->AppendRelativeNativePath(nsprLogFilePath);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+ }
+
+ nsAutoString resolvedFilePath;
+ rv = nsprLogFile->GetPath(resolvedFilePath);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ // Update the environment variable as well as adding the rule, because the
+ // Chromium sandbox can only allow access to fully qualified file paths. This
+ // only affects the environment for the child process we're about to create,
+ // because this will get reset to the original value in PerformAsyncLaunch.
+ aAllowedFilesReadWrite.push_back(std::wstring(resolvedFilePath.get()));
+ nsAutoCString resolvedEnvVar("NSPR_LOG_FILE=");
+ AppendUTF16toUTF8(resolvedFilePath, resolvedEnvVar);
+ PR_SetEnv(resolvedEnvVar.get());
+}
+
+static void
+AddContentSandboxAllowedFiles(int32_t aSandboxLevel,
+ std::vector<std::wstring>& aAllowedFilesRead)
+{
+ if (aSandboxLevel < 1) {
+ return;
+ }
+
+ nsCOMPtr<nsIFile> binDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(binDir));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ nsAutoString binDirPath;
+ rv = binDir->GetPath(binDirPath);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ // If bin directory is on a remote drive add read access.
+ wchar_t volPath[MAX_PATH];
+ if (!::GetVolumePathNameW(binDirPath.get(), volPath, MAX_PATH)) {
+ return;
+ }
+
+ if (::GetDriveTypeW(volPath) != DRIVE_REMOTE) {
+ return;
+ }
+
+ // Convert network share path to format for sandbox policy.
+ if (Substring(binDirPath, 0, 2).Equals(L"\\\\")) {
+ binDirPath.InsertLiteral(u"??\\UNC", 1);
+ }
+
+ binDirPath.AppendLiteral(u"\\*");
+
+ aAllowedFilesRead.push_back(std::wstring(binDirPath.get()));
+}
+#endif
+
+bool
+GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExtraOpts, base::ProcessArchitecture arch)
+{
+ // We rely on the fact that InitializeChannel() has already been processed
+ // on the IO thread before this point is reached.
+ if (!GetChannel()) {
+ return false;
+ }
+
+ base::ProcessHandle process = 0;
+
+ // send the child the PID so that it can open a ProcessHandle back to us.
+ // probably don't want to do this in the long run
+ char pidstring[32];
+ SprintfLiteral(pidstring,"%d", base::Process::Current().pid());
+
+ const char* const childProcessType =
+ XRE_ChildProcessTypeToString(mProcessType);
+
+//--------------------------------------------------
+#if defined(OS_POSIX)
+ // For POSIX, we have to be extremely anal about *not* using
+ // std::wstring in code compiled with Mozilla's -fshort-wchar
+ // configuration, because chromium is compiled with -fno-short-wchar
+ // and passing wstrings from one config to the other is unsafe. So
+ // we split the logic here.
+
+#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD)
+ base::environment_map newEnvVars;
+ ChildPrivileges privs = mPrivileges;
+ if (privs == base::PRIVILEGES_DEFAULT) {
+ privs = DefaultChildPrivileges();
+ }
+
+#if defined(MOZ_WIDGET_GTK)
+ if (mProcessType == GeckoProcessType_Content) {
+ // disable IM module to avoid sandbox violation
+ newEnvVars["GTK_IM_MODULE"] = "gtk-im-context-simple";
+ }
+#endif
+
+ // XPCOM may not be initialized in some subprocesses. We don't want
+ // to initialize XPCOM just for the directory service, especially
+ // since LD_LIBRARY_PATH is already set correctly in subprocesses
+ // (meaning that we don't need to set that up in the environment).
+ if (ShouldHaveDirectoryService()) {
+ MOZ_ASSERT(gGREBinPath);
+ nsCString path;
+ NS_CopyUnicodeToNative(nsDependentString(gGREBinPath), path);
+# if defined(OS_LINUX) || defined(OS_BSD)
+# if defined(MOZ_WIDGET_ANDROID)
+ path += "/lib";
+# endif // MOZ_WIDGET_ANDROID
+ const char *ld_library_path = PR_GetEnv("LD_LIBRARY_PATH");
+ nsCString new_ld_lib_path(path.get());
+
+# if (MOZ_WIDGET_GTK == 3)
+ if (mProcessType == GeckoProcessType_Plugin) {
+ new_ld_lib_path.Append("/gtk2:");
+ new_ld_lib_path.Append(path.get());
+ }
+#endif
+ if (ld_library_path && *ld_library_path) {
+ new_ld_lib_path.Append(':');
+ new_ld_lib_path.Append(ld_library_path);
+ }
+ newEnvVars["LD_LIBRARY_PATH"] = new_ld_lib_path.get();
+
+# elif OS_MACOSX
+ newEnvVars["DYLD_LIBRARY_PATH"] = path.get();
+ // XXX DYLD_INSERT_LIBRARIES should only be set when launching a plugin
+ // process, and has no effect on other subprocesses (the hooks in
+ // libplugin_child_interpose.dylib become noops). But currently it
+ // gets set when launching any kind of subprocess.
+ //
+ // Trigger "dyld interposing" for the dylib that contains
+ // plugin_child_interpose.mm. This allows us to hook OS calls in the
+ // plugin process (ones that don't work correctly in a background
+ // process). Don't break any other "dyld interposing" that has already
+ // been set up by whatever may have launched the browser.
+ const char* prevInterpose = PR_GetEnv("DYLD_INSERT_LIBRARIES");
+ nsCString interpose;
+ if (prevInterpose && strlen(prevInterpose) > 0) {
+ interpose.Assign(prevInterpose);
+ interpose.Append(':');
+ }
+ interpose.Append(path.get());
+ interpose.AppendLiteral("/libplugin_child_interpose.dylib");
+ newEnvVars["DYLD_INSERT_LIBRARIES"] = interpose.get();
+# endif // OS_LINUX
+ }
+#endif // OS_LINUX || OS_MACOSX
+
+ FilePath exePath;
+ BinaryPathType pathType = GetPathToBinary(exePath, mProcessType);
+
+#ifdef MOZ_WIDGET_ANDROID
+ // The java wrapper unpacks this for us but can't make it executable
+ chmod(exePath.value().c_str(), 0700);
+#endif // MOZ_WIDGET_ANDROID
+
+#ifdef ANDROID
+ // Remap the Android property workspace to a well-known int,
+ // and update the environment to reflect the new value for the
+ // child process.
+ const char *apws = getenv("ANDROID_PROPERTY_WORKSPACE");
+ if (apws) {
+ int fd = atoi(apws);
+ mFileMap.push_back(std::pair<int, int>(fd, kMagicAndroidSystemPropFd));
+
+ char buf[32];
+ char *szptr = strchr(apws, ',');
+
+ snprintf(buf, sizeof(buf), "%d%s", kMagicAndroidSystemPropFd, szptr);
+ newEnvVars["ANDROID_PROPERTY_WORKSPACE"] = buf;
+ }
+#endif // ANDROID
+
+#ifdef MOZ_WIDGET_GONK
+ if (const char *ldPreloadPath = getenv("LD_PRELOAD")) {
+ newEnvVars["LD_PRELOAD"] = ldPreloadPath;
+ }
+#endif // MOZ_WIDGET_GONK
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+ // Preload libmozsandbox.so so that sandbox-related interpositions
+ // can be defined there instead of in the executable.
+ // (This could be made conditional on intent to use sandboxing, but
+ // it's harmless for non-sandboxed processes.)
+ {
+ nsAutoCString preload;
+ // Prepend this, because people can and do preload libpthread.
+ // (See bug 1222500.)
+ preload.AssignLiteral("libmozsandbox.so");
+ if (const char* oldPreload = PR_GetEnv("LD_PRELOAD")) {
+ // Doesn't matter if oldPreload is ""; extra separators are ignored.
+ preload.Append(' ');
+ preload.Append(oldPreload);
+ }
+ // Explicitly construct the std::string to make it clear that this
+ // isn't retaining a pointer to the nsCString's buffer.
+ newEnvVars["LD_PRELOAD"] = std::string(preload.get());
+ }
+#endif
+
+ // remap the IPC socket fd to a well-known int, as the OS does for
+ // STDOUT_FILENO, for example
+ int srcChannelFd, dstChannelFd;
+ channel().GetClientFileDescriptorMapping(&srcChannelFd, &dstChannelFd);
+ mFileMap.push_back(std::pair<int,int>(srcChannelFd, dstChannelFd));
+
+ // no need for kProcessChannelID, the child process inherits the
+ // other end of the socketpair() from us
+
+ std::vector<std::string> childArgv;
+
+ childArgv.push_back(exePath.value());
+
+ if (pathType == BinaryPathType::Self) {
+ childArgv.push_back("-contentproc");
+ }
+
+ childArgv.insert(childArgv.end(), aExtraOpts.begin(), aExtraOpts.end());
+
+ if (Omnijar::IsInitialized()) {
+ // Make sure that child processes can find the omnijar
+ // See XRE_InitCommandLine in nsAppRunner.cpp
+ nsAutoCString path;
+ nsCOMPtr<nsIFile> file = Omnijar::GetPath(Omnijar::GRE);
+ if (file && NS_SUCCEEDED(file->GetNativePath(path))) {
+ childArgv.push_back("-greomni");
+ childArgv.push_back(path.get());
+ }
+ file = Omnijar::GetPath(Omnijar::APP);
+ if (file && NS_SUCCEEDED(file->GetNativePath(path))) {
+ childArgv.push_back("-appomni");
+ childArgv.push_back(path.get());
+ }
+ }
+
+ // Add the application directory path (-appdir path)
+ AddAppDirToCommandLine(childArgv);
+
+ childArgv.push_back(pidstring);
+
+#if defined(MOZ_CRASHREPORTER)
+# if defined(OS_LINUX) || defined(OS_BSD)
+ int childCrashFd, childCrashRemapFd;
+ if (!CrashReporter::CreateNotificationPipeForChild(
+ &childCrashFd, &childCrashRemapFd))
+ return false;
+ if (0 <= childCrashFd) {
+ mFileMap.push_back(std::pair<int,int>(childCrashFd, childCrashRemapFd));
+ // "true" == crash reporting enabled
+ childArgv.push_back("true");
+ }
+ else {
+ // "false" == crash reporting disabled
+ childArgv.push_back("false");
+ }
+# elif defined(MOZ_WIDGET_COCOA)
+ childArgv.push_back(CrashReporter::GetChildNotificationPipe());
+# endif // OS_LINUX
+#endif
+
+#ifdef MOZ_WIDGET_COCOA
+ // Add a mach port to the command line so the child can communicate its
+ // 'task_t' back to the parent.
+ //
+ // Put a random number into the channel name, so that a compromised renderer
+ // can't pretend being the child that's forked off.
+ std::string mach_connection_name = StringPrintf("org.mozilla.machname.%d",
+ base::RandInt(0, std::numeric_limits<int>::max()));
+ childArgv.push_back(mach_connection_name.c_str());
+#endif
+
+ childArgv.push_back(childProcessType);
+
+ base::LaunchApp(childArgv, mFileMap,
+#if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_BSD)
+ newEnvVars, privs,
+#endif
+ false, &process, arch);
+
+ // We're in the parent and the child was launched. Close the child FD in the
+ // parent as soon as possible, which will allow the parent to detect when the
+ // child closes its FD (either due to normal exit or due to crash).
+ GetChannel()->CloseClientFileDescriptor();
+
+#ifdef MOZ_WIDGET_COCOA
+ // Wait for the child process to send us its 'task_t' data.
+ const int kTimeoutMs = 10000;
+
+ MachReceiveMessage child_message;
+ ReceivePort parent_recv_port(mach_connection_name.c_str());
+ kern_return_t err = parent_recv_port.WaitForMessage(&child_message, kTimeoutMs);
+ if (err != KERN_SUCCESS) {
+ std::string errString = StringPrintf("0x%x %s", err, mach_error_string(err));
+ CHROMIUM_LOG(ERROR) << "parent WaitForMessage() failed: " << errString;
+ return false;
+ }
+
+ task_t child_task = child_message.GetTranslatedPort(0);
+ if (child_task == MACH_PORT_NULL) {
+ CHROMIUM_LOG(ERROR) << "parent GetTranslatedPort(0) failed.";
+ return false;
+ }
+
+ if (child_message.GetTranslatedPort(1) == MACH_PORT_NULL) {
+ CHROMIUM_LOG(ERROR) << "parent GetTranslatedPort(1) failed.";
+ return false;
+ }
+ MachPortSender parent_sender(child_message.GetTranslatedPort(1));
+
+ if (child_message.GetTranslatedPort(2) == MACH_PORT_NULL) {
+ CHROMIUM_LOG(ERROR) << "parent GetTranslatedPort(2) failed.";
+ }
+ MachPortSender* parent_recv_port_memory_ack = new MachPortSender(child_message.GetTranslatedPort(2));
+
+ if (child_message.GetTranslatedPort(3) == MACH_PORT_NULL) {
+ CHROMIUM_LOG(ERROR) << "parent GetTranslatedPort(3) failed.";
+ }
+ MachPortSender* parent_send_port_memory = new MachPortSender(child_message.GetTranslatedPort(3));
+
+ MachSendMessage parent_message(/* id= */0);
+ if (!parent_message.AddDescriptor(MachMsgPortDescriptor(bootstrap_port))) {
+ CHROMIUM_LOG(ERROR) << "parent AddDescriptor(" << bootstrap_port << ") failed.";
+ return false;
+ }
+
+ ReceivePort* parent_recv_port_memory = new ReceivePort();
+ if (!parent_message.AddDescriptor(MachMsgPortDescriptor(parent_recv_port_memory->GetPort()))) {
+ CHROMIUM_LOG(ERROR) << "parent AddDescriptor(" << parent_recv_port_memory->GetPort() << ") failed.";
+ return false;
+ }
+
+ ReceivePort* parent_send_port_memory_ack = new ReceivePort();
+ if (!parent_message.AddDescriptor(MachMsgPortDescriptor(parent_send_port_memory_ack->GetPort()))) {
+ CHROMIUM_LOG(ERROR) << "parent AddDescriptor(" << parent_send_port_memory_ack->GetPort() << ") failed.";
+ return false;
+ }
+
+ err = parent_sender.SendMessage(parent_message, kTimeoutMs);
+ if (err != KERN_SUCCESS) {
+ std::string errString = StringPrintf("0x%x %s", err, mach_error_string(err));
+ CHROMIUM_LOG(ERROR) << "parent SendMessage() failed: " << errString;
+ return false;
+ }
+
+ SharedMemoryBasic::SetupMachMemory(process, parent_recv_port_memory, parent_recv_port_memory_ack,
+ parent_send_port_memory, parent_send_port_memory_ack, false);
+
+#endif
+
+//--------------------------------------------------
+#elif defined(OS_WIN)
+
+ FilePath exePath;
+ BinaryPathType pathType = GetPathToBinary(exePath, mProcessType);
+
+ CommandLine cmdLine(exePath.ToWStringHack());
+
+ if (pathType == BinaryPathType::Self) {
+ cmdLine.AppendLooseValue(UTF8ToWide("-contentproc"));
+ }
+
+ cmdLine.AppendSwitchWithValue(switches::kProcessChannelID, channel_id());
+
+ for (std::vector<std::string>::iterator it = aExtraOpts.begin();
+ it != aExtraOpts.end();
+ ++it) {
+ cmdLine.AppendLooseValue(UTF8ToWide(*it));
+ }
+
+ if (Omnijar::IsInitialized()) {
+ // Make sure the child process can find the omnijar
+ // See XRE_InitCommandLine in nsAppRunner.cpp
+ nsAutoString path;
+ nsCOMPtr<nsIFile> file = Omnijar::GetPath(Omnijar::GRE);
+ if (file && NS_SUCCEEDED(file->GetPath(path))) {
+ cmdLine.AppendLooseValue(UTF8ToWide("-greomni"));
+ cmdLine.AppendLooseValue(path.get());
+ }
+ file = Omnijar::GetPath(Omnijar::APP);
+ if (file && NS_SUCCEEDED(file->GetPath(path))) {
+ cmdLine.AppendLooseValue(UTF8ToWide("-appomni"));
+ cmdLine.AppendLooseValue(path.get());
+ }
+ }
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ bool shouldSandboxCurrentProcess = false;
+
+ // XXX: Bug 1124167: We should get rid of the process specific logic for
+ // sandboxing in this class at some point. Unfortunately it will take a bit
+ // of reorganizing so I don't think this patch is the right time.
+ switch (mProcessType) {
+ case GeckoProcessType_Content:
+#if defined(MOZ_CONTENT_SANDBOX)
+ if (mSandboxLevel > 0 &&
+ !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
+ // For now we treat every failure as fatal in SetSecurityLevelForContentProcess
+ // and just crash there right away. Should this change in the future then we
+ // should also handle the error here.
+ mSandboxBroker.SetSecurityLevelForContentProcess(mSandboxLevel);
+ shouldSandboxCurrentProcess = true;
+ AddContentSandboxAllowedFiles(mSandboxLevel, mAllowedFilesRead);
+ }
+#endif // MOZ_CONTENT_SANDBOX
+ break;
+ case GeckoProcessType_Plugin:
+ if (mSandboxLevel > 0 &&
+ !PR_GetEnv("MOZ_DISABLE_NPAPI_SANDBOX")) {
+ bool ok = mSandboxBroker.SetSecurityLevelForPluginProcess(mSandboxLevel);
+ if (!ok) {
+ return false;
+ }
+ shouldSandboxCurrentProcess = true;
+ }
+ break;
+ case GeckoProcessType_IPDLUnitTest:
+ // XXX: We don't sandbox this process type yet
+ break;
+ case GeckoProcessType_GMPlugin:
+ if (!PR_GetEnv("MOZ_DISABLE_GMP_SANDBOX")) {
+ // The Widevine CDM on Windows can only load at USER_RESTRICTED,
+ // not at USER_LOCKDOWN. So look in the command line arguments
+ // to see if we're loading the path to the Widevine CDM, and if
+ // so use sandbox level USER_RESTRICTED instead of USER_LOCKDOWN.
+ bool isWidevine = std::any_of(aExtraOpts.begin(), aExtraOpts.end(),
+ [](const std::string arg) { return arg.find("gmp-widevinecdm") != std::string::npos; });
+ auto level = isWidevine ? SandboxBroker::Restricted : SandboxBroker::LockDown;
+ bool ok = mSandboxBroker.SetSecurityLevelForGMPlugin(level);
+ if (!ok) {
+ return false;
+ }
+ shouldSandboxCurrentProcess = true;
+ }
+ break;
+ case GeckoProcessType_GPU:
+ break;
+ case GeckoProcessType_Default:
+ default:
+ MOZ_CRASH("Bad process type in GeckoChildProcessHost");
+ break;
+ };
+
+ if (shouldSandboxCurrentProcess) {
+ MaybeAddNsprLogFileAccess(mAllowedFilesReadWrite);
+ for (auto it = mAllowedFilesRead.begin();
+ it != mAllowedFilesRead.end();
+ ++it) {
+ mSandboxBroker.AllowReadFile(it->c_str());
+ }
+
+ for (auto it = mAllowedFilesReadWrite.begin();
+ it != mAllowedFilesReadWrite.end();
+ ++it) {
+ mSandboxBroker.AllowReadWriteFile(it->c_str());
+ }
+
+ for (auto it = mAllowedDirectories.begin();
+ it != mAllowedDirectories.end();
+ ++it) {
+ mSandboxBroker.AllowDirectory(it->c_str());
+ }
+ }
+#endif // XP_WIN && MOZ_SANDBOX
+
+ // Add the application directory path (-appdir path)
+ AddAppDirToCommandLine(cmdLine);
+
+ // XXX Command line params past this point are expected to be at
+ // the end of the command line string, and in a specific order.
+ // See XRE_InitChildProcess in nsEmbedFunction.
+
+ // Win app model id
+ cmdLine.AppendLooseValue(mGroupId.get());
+
+ // Process id
+ cmdLine.AppendLooseValue(UTF8ToWide(pidstring));
+
+#if defined(MOZ_CRASHREPORTER)
+ cmdLine.AppendLooseValue(
+ UTF8ToWide(CrashReporter::GetChildNotificationPipe()));
+#endif
+
+ // Process type
+ cmdLine.AppendLooseValue(UTF8ToWide(childProcessType));
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ if (shouldSandboxCurrentProcess) {
+ if (mSandboxBroker.LaunchApp(cmdLine.program().c_str(),
+ cmdLine.command_line_string().c_str(),
+ mEnableSandboxLogging,
+ &process)) {
+ EnvironmentLog("MOZ_PROCESS_LOG").print(
+ "==> process %d launched child process %d (%S)\n",
+ base::GetCurrentProcId(), base::GetProcId(process),
+ cmdLine.command_line_string().c_str());
+ }
+ } else
+#endif
+ {
+ base::LaunchApp(cmdLine, false, false, &process);
+
+#ifdef MOZ_SANDBOX
+ // We need to be able to duplicate handles to some types of non-sandboxed
+ // child processes.
+ if (mProcessType == GeckoProcessType_Content ||
+ mProcessType == GeckoProcessType_GPU ||
+ mProcessType == GeckoProcessType_GMPlugin) {
+ if (!mSandboxBroker.AddTargetPeer(process)) {
+ NS_WARNING("Failed to add content process as target peer.");
+ }
+ }
+#endif
+ }
+
+#else
+# error Sorry
+#endif
+
+ if (!process) {
+ return false;
+ }
+ // NB: on OS X, we block much longer than we need to in order to
+ // reach this call, waiting for the child process's task_t. The
+ // best way to fix that is to refactor this file, hard.
+#if defined(MOZ_WIDGET_COCOA)
+ mChildTask = child_task;
+#endif
+
+ if (!OpenPrivilegedHandle(base::GetProcId(process))
+#ifdef XP_WIN
+ // If we failed in opening the process handle, try harder by duplicating
+ // one.
+ && !::DuplicateHandle(::GetCurrentProcess(), process,
+ ::GetCurrentProcess(), &mChildProcessHandle,
+ PROCESS_DUP_HANDLE | PROCESS_TERMINATE |
+ PROCESS_QUERY_INFORMATION | PROCESS_VM_READ |
+ SYNCHRONIZE,
+ FALSE, 0)
+#endif
+ ) {
+ NS_RUNTIMEABORT("cannot open handle to child process");
+ }
+ MonitorAutoLock lock(mMonitor);
+ mProcessState = PROCESS_CREATED;
+ lock.Notify();
+
+ return true;
+}
+
+bool
+GeckoChildProcessHost::OpenPrivilegedHandle(base::ProcessId aPid)
+{
+ if (mChildProcessHandle) {
+ MOZ_ASSERT(aPid == base::GetProcId(mChildProcessHandle));
+ return true;
+ }
+
+ return base::OpenPrivilegedProcessHandle(aPid, &mChildProcessHandle);
+}
+
+void
+GeckoChildProcessHost::OnChannelConnected(int32_t peer_pid)
+{
+ if (!OpenPrivilegedHandle(peer_pid)) {
+ NS_RUNTIMEABORT("can't open handle to child process");
+ }
+ MonitorAutoLock lock(mMonitor);
+ mProcessState = PROCESS_CONNECTED;
+ lock.Notify();
+}
+
+void
+GeckoChildProcessHost::OnMessageReceived(IPC::Message&& aMsg)
+{
+ // We never process messages ourself, just save them up for the next
+ // listener.
+ mQueue.push(Move(aMsg));
+}
+
+void
+GeckoChildProcessHost::OnChannelError()
+{
+ // Update the process state to an error state if we have a channel
+ // error before we're connected. This fixes certain failures,
+ // but does not address the full range of possible issues described
+ // in the FIXME comment below.
+ MonitorAutoLock lock(mMonitor);
+ if (mProcessState < PROCESS_CONNECTED) {
+ mProcessState = PROCESS_ERROR;
+ lock.Notify();
+ }
+ // FIXME/bug 773925: save up this error for the next listener.
+}
+
+void
+GeckoChildProcessHost::GetQueuedMessages(std::queue<IPC::Message>& queue)
+{
+ // If this is called off the IO thread, bad things will happen.
+ DCHECK(MessageLoopForIO::current());
+ swap(queue, mQueue);
+ // We expect the next listener to take over processing of our queue.
+}
+
+bool GeckoChildProcessHost::sRunSelfAsContentProc(false);