Blob Blame History Raw
/* 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/DebugOnly.h"

#include "base/basictypes.h"

#include "nsXULAppAPI.h"

#include <stdlib.h>
#if defined(MOZ_WIDGET_GTK)
#include <glib.h>
#endif

#include "prenv.h"

#include "nsIAppShell.h"
#include "nsIAppStartupNotifier.h"
#include "nsIDirectoryService.h"
#include "nsIFile.h"
#include "nsIToolkitChromeRegistry.h"
#include "nsIToolkitProfile.h"

#ifdef XP_WIN
#include <process.h>
#include <shobjidl.h>
#include "mozilla/ipc/WindowsMessageLoop.h"
#endif

#include "nsAppDirectoryServiceDefs.h"
#include "nsAppRunner.h"
#include "nsAutoRef.h"
#include "nsDirectoryServiceDefs.h"
#include "nsExceptionHandler.h"
#include "nsString.h"
#include "nsThreadUtils.h"
#include "nsJSUtils.h"
#include "nsWidgetsCID.h"
#include "nsXREDirProvider.h"
#include "ThreadAnnotation.h"

#include "mozilla/Omnijar.h"
#if defined(XP_MACOSX)
#include "nsVersionComparator.h"
#include "chrome/common/mach_ipc_mac.h"
#endif
#include "nsX11ErrorHandler.h"
#include "nsGDKErrorHandler.h"
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/message_loop.h"
#include "base/process_util.h"
#include "chrome/common/child_process.h"
#if defined(MOZ_WIDGET_ANDROID)
#include "chrome/common/ipc_channel.h"
#include "mozilla/jni/Utils.h"
#endif  //  defined(MOZ_WIDGET_ANDROID)

#include "mozilla/AbstractThread.h"
#include "mozilla/FilePreferences.h"

#include "mozilla/ipc/BrowserProcessSubThread.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/ipc/IOThreadChild.h"
#include "mozilla/ipc/ProcessChild.h"
#include "ScopedXREEmbed.h"

#include "mozilla/plugins/PluginProcessChild.h"
#include "mozilla/dom/ContentProcess.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/ContentChild.h"

#include "mozilla/ipc/TestShellParent.h"
#include "mozilla/ipc/XPCShellEnvironment.h"
#include "mozilla/Scheduler.h"
#include "mozilla/WindowsDllBlocklist.h"

#include "GMPProcessChild.h"
#include "mozilla/gfx/GPUProcessImpl.h"

#include "GeckoProfiler.h"

#if defined(MOZ_SANDBOX) && defined(XP_WIN)
#include "mozilla/sandboxTarget.h"
#include "mozilla/sandboxing/loggingCallbacks.h"
#endif

#if defined(MOZ_CONTENT_SANDBOX)
#include "mozilla/SandboxSettings.h"
#include "mozilla/Preferences.h"
#endif

#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
#include "mozilla/Sandbox.h"
#endif

#if defined(XP_LINUX)
#include <sys/prctl.h>
#ifndef PR_SET_PTRACER
#define PR_SET_PTRACER 0x59616d61
#endif
#ifndef PR_SET_PTRACER_ANY
#define PR_SET_PTRACER_ANY ((unsigned long)-1)
#endif
#endif

#ifdef MOZ_IPDL_TESTS
#include "mozilla/_ipdltest/IPDLUnitTests.h"
#include "mozilla/_ipdltest/IPDLUnitTestProcessChild.h"

using mozilla::_ipdltest::IPDLUnitTestProcessChild;
#endif  // ifdef MOZ_IPDL_TESTS

#ifdef MOZ_JPROF
#include "jprof.h"
#endif

#if defined(XP_WIN) && defined(MOZ_ENABLE_SKIA_PDF)
#include "mozilla/widget/PDFiumProcessChild.h"
#endif

using namespace mozilla;

using mozilla::ipc::BrowserProcessSubThread;
using mozilla::ipc::GeckoChildProcessHost;
using mozilla::ipc::IOThreadChild;
using mozilla::ipc::ProcessChild;
using mozilla::ipc::ScopedXREEmbed;

using mozilla::dom::ContentChild;
using mozilla::dom::ContentParent;
using mozilla::dom::ContentProcess;
using mozilla::plugins::PluginProcessChild;

using mozilla::gmp::GMPProcessChild;

using mozilla::ipc::TestShellCommandParent;
using mozilla::ipc::TestShellParent;
using mozilla::ipc::XPCShellEnvironment;

using mozilla::startup::sChildProcessType;

static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);

nsresult XRE_LockProfileDirectory(nsIFile* aDirectory,
                                  nsISupports** aLockObject) {
  nsCOMPtr<nsIProfileLock> lock;

  nsresult rv =
      NS_LockProfilePath(aDirectory, nullptr, nullptr, getter_AddRefs(lock));
  if (NS_SUCCEEDED(rv)) NS_ADDREF(*aLockObject = lock);

  return rv;
}

static int32_t sInitCounter;

nsresult XRE_InitEmbedding2(nsIFile* aLibXULDirectory, nsIFile* aAppDirectory,
                            nsIDirectoryServiceProvider* aAppDirProvider) {
  // Initialize some globals to make nsXREDirProvider happy
  static char* kNullCommandLine[] = {nullptr};
  gArgv = kNullCommandLine;
  gArgc = 0;

  NS_ENSURE_ARG(aLibXULDirectory);

  if (++sInitCounter > 1)  // XXXbsmedberg is this really the right solution?
    return NS_OK;

  if (!aAppDirectory) aAppDirectory = aLibXULDirectory;

  nsresult rv;

  new nsXREDirProvider;  // This sets gDirServiceProvider
  if (!gDirServiceProvider) return NS_ERROR_OUT_OF_MEMORY;

  rv = gDirServiceProvider->Initialize(aAppDirectory, aLibXULDirectory,
                                       aAppDirProvider);
  if (NS_FAILED(rv)) return rv;

  rv = NS_InitXPCOM2(nullptr, aAppDirectory, gDirServiceProvider);
  if (NS_FAILED(rv)) return rv;

  // We do not need to autoregister components here. The CheckCompatibility()
  // bits in nsAppRunner.cpp check for an invalidation flag in
  // compatibility.ini.
  // If the app wants to autoregister every time (for instance, if it's debug),
  // it can do so after we return from this function.

  nsCOMPtr<nsIObserver> startupNotifier(
      do_CreateInstance(NS_APPSTARTUPNOTIFIER_CONTRACTID));
  if (!startupNotifier) return NS_ERROR_FAILURE;

  startupNotifier->Observe(nullptr, APPSTARTUP_TOPIC, nullptr);

  return NS_OK;
}

void XRE_NotifyProfile() {
  NS_ASSERTION(gDirServiceProvider, "XRE_InitEmbedding was not called!");
  gDirServiceProvider->DoStartup();
}

void XRE_TermEmbedding() {
  if (--sInitCounter != 0) return;

  NS_ASSERTION(gDirServiceProvider,
               "XRE_TermEmbedding without XRE_InitEmbedding");

  gDirServiceProvider->DoShutdown();
  NS_ShutdownXPCOM(nullptr);
  delete gDirServiceProvider;
}

const char* XRE_ChildProcessTypeToString(GeckoProcessType aProcessType) {
  return (aProcessType < GeckoProcessType_End)
             ? kGeckoProcessTypeString[aProcessType]
             : "invalid";
}

namespace mozilla {
namespace startup {
GeckoProcessType sChildProcessType = GeckoProcessType_Default;
}  // namespace startup
}  // namespace mozilla

#if defined(MOZ_WIDGET_ANDROID)
void XRE_SetAndroidChildFds(JNIEnv* env, int ipcFd, int crashFd,
                            int crashAnnotationFd) {
  mozilla::jni::SetGeckoThreadEnv(env);
  IPC::Channel::SetClientChannelFd(ipcFd);
  CrashReporter::SetNotificationPipeForChild(crashFd);
  CrashReporter::SetCrashAnnotationPipeForChild(crashAnnotationFd);
}
#endif  // defined(MOZ_WIDGET_ANDROID)

void XRE_SetProcessType(const char* aProcessTypeString) {
  static bool called = false;
  if (called) {
    MOZ_CRASH();
  }
  called = true;

  sChildProcessType = GeckoProcessType_Invalid;
  for (int i = 0; i < (int)ArrayLength(kGeckoProcessTypeString); ++i) {
    if (!strcmp(kGeckoProcessTypeString[i], aProcessTypeString)) {
      sChildProcessType = static_cast<GeckoProcessType>(i);
      return;
    }
  }
}

// FIXME/bug 539522: this out-of-place function is stuck here because
// IPDL wants access to this crashreporter interface, and
// crashreporter is built in such a way to make that awkward
bool XRE_TakeMinidumpForChild(uint32_t aChildPid, nsIFile** aDump,
                              uint32_t* aSequence) {
  return CrashReporter::TakeMinidumpForChild(aChildPid, aDump, aSequence);
}

bool
#if defined(XP_WIN)
XRE_SetRemoteExceptionHandler(const char* aPipe /*= 0*/,
                              uintptr_t aCrashTimeAnnotationFile)
#else
XRE_SetRemoteExceptionHandler(const char* aPipe /*= 0*/)
#endif
{
#if defined(XP_WIN)
  return CrashReporter::SetRemoteExceptionHandler(nsDependentCString(aPipe),
                                                  aCrashTimeAnnotationFile);
#elif defined(XP_MACOSX)
  return CrashReporter::SetRemoteExceptionHandler(nsDependentCString(aPipe));
#else
  return CrashReporter::SetRemoteExceptionHandler();
#endif
}

#if defined(XP_WIN)
void SetTaskbarGroupId(const nsString& aId) {
  if (FAILED(SetCurrentProcessExplicitAppUserModelID(aId.get()))) {
    NS_WARNING(
        "SetCurrentProcessExplicitAppUserModelID failed for child process.");
  }
}
#endif

#if defined(MOZ_CONTENT_SANDBOX)
void AddContentSandboxLevelAnnotation() {
  if (XRE_GetProcessType() == GeckoProcessType_Content) {
    int level = GetEffectiveContentSandboxLevel();
    nsAutoCString levelString;
    levelString.AppendInt(level);
    CrashReporter::AnnotateCrashReport(
        NS_LITERAL_CSTRING("ContentSandboxLevel"), levelString);
  }
}
#endif /* MOZ_CONTENT_SANDBOX */

namespace {

int GetDebugChildPauseTime() {
  auto pauseStr = PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE");
  if (pauseStr && *pauseStr) {
    int pause = atoi(pauseStr);
    if (pause != 1) {  // must be !=1 since =1 enables the default pause time
#if defined(OS_WIN)
      pause *= 1000;  // convert to ms
#endif
      return pause;
    }
  }
#ifdef OS_POSIX
  return 30;  // seconds
#elif defined(OS_WIN)
  return 10000;  // milliseconds
#else
  return 0;
#endif
}

}  // namespace

nsresult XRE_InitChildProcess(int aArgc, char* aArgv[],
                              const XREChildData* aChildData) {
  NS_ENSURE_ARG_MIN(aArgc, 2);
  NS_ENSURE_ARG_POINTER(aArgv);
  NS_ENSURE_ARG_POINTER(aArgv[0]);
  MOZ_ASSERT(aChildData);

#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
  // This has to happen before glib thread pools are started.
  mozilla::SandboxEarlyInit();
#endif

#ifdef MOZ_JPROF
  // Call the code to install our handler
  setupProfilingStuff();
#endif

#if defined(XP_WIN)
  // From the --attach-console support in nsNativeAppSupportWin.cpp, but
  // here we are a content child process, so we always attempt to attach
  // to the parent's (ie, the browser's) console.
  // Try to attach console to the parent process.
  // It will succeed when the parent process is a command line,
  // so that stdio will be displayed in it.
  if (AttachConsole(ATTACH_PARENT_PROCESS)) {
    // Change std handles to refer to new console handles.
    // Before doing so, ensure that stdout/stderr haven't been
    // redirected to a valid file
    if (_fileno(stdout) == -1 || _get_osfhandle(fileno(stdout)) == -1)
      freopen("CONOUT$", "w", stdout);
    // Merge stderr into CONOUT$ since there isn't any `CONERR$`.
    // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231%28v=vs.85%29.aspx
    if (_fileno(stderr) == -1 || _get_osfhandle(fileno(stderr)) == -1)
      freopen("CONOUT$", "w", stderr);
    if (_fileno(stdin) == -1 || _get_osfhandle(fileno(stdin)) == -1)
      freopen("CONIN$", "r", stdin);
  }

#if defined(MOZ_SANDBOX)
  if (aChildData->sandboxTargetServices) {
    SandboxTarget::Instance()->SetTargetServices(
        aChildData->sandboxTargetServices);
  }
#endif
#endif

  // NB: This must be called before profiler_init
  ScopedLogging logger;

  mozilla::LogModule::Init();

  AUTO_PROFILER_INIT;
  AUTO_PROFILER_LABEL("XRE_InitChildProcess", OTHER);

  // Ensure AbstractThread is minimally setup, so async IPC messages
  // work properly.
  AbstractThread::InitTLS();

  // Complete 'task_t' exchange for Mac OS X. This structure has the same size
  // regardless of architecture so we don't have any cross-arch issues here.
#ifdef XP_MACOSX
  if (aArgc < 1) return NS_ERROR_FAILURE;
  const char* const mach_port_name = aArgv[--aArgc];

  const int kTimeoutMs = 1000;

  MachSendMessage child_message(0);
  if (!child_message.AddDescriptor(MachMsgPortDescriptor(mach_task_self()))) {
    NS_WARNING("child AddDescriptor(mach_task_self()) failed.");
    return NS_ERROR_FAILURE;
  }

  ReceivePort child_recv_port;
  mach_port_t raw_child_recv_port = child_recv_port.GetPort();
  if (!child_message.AddDescriptor(
          MachMsgPortDescriptor(raw_child_recv_port))) {
    NS_WARNING("Adding descriptor to message failed");
    return NS_ERROR_FAILURE;
  }

  ReceivePort* ports_out_receiver = new ReceivePort();
  if (!child_message.AddDescriptor(
          MachMsgPortDescriptor(ports_out_receiver->GetPort()))) {
    NS_WARNING("Adding descriptor to message failed");
    return NS_ERROR_FAILURE;
  }

  ReceivePort* ports_in_receiver = new ReceivePort();
  if (!child_message.AddDescriptor(
          MachMsgPortDescriptor(ports_in_receiver->GetPort()))) {
    NS_WARNING("Adding descriptor to message failed");
    return NS_ERROR_FAILURE;
  }

  MachPortSender child_sender(mach_port_name);
  kern_return_t err = child_sender.SendMessage(child_message, kTimeoutMs);
  if (err != KERN_SUCCESS) {
    NS_WARNING("child SendMessage() failed");
    return NS_ERROR_FAILURE;
  }

  MachReceiveMessage parent_message;
  err = child_recv_port.WaitForMessage(&parent_message, kTimeoutMs);
  if (err != KERN_SUCCESS) {
    NS_WARNING("child WaitForMessage() failed");
    return NS_ERROR_FAILURE;
  }

  if (parent_message.GetTranslatedPort(0) == MACH_PORT_NULL) {
    NS_WARNING("child GetTranslatedPort(0) failed");
    return NS_ERROR_FAILURE;
  }

  err = task_set_bootstrap_port(mach_task_self(),
                                parent_message.GetTranslatedPort(0));

  if (parent_message.GetTranslatedPort(1) == MACH_PORT_NULL) {
    NS_WARNING("child GetTranslatedPort(1) failed");
    return NS_ERROR_FAILURE;
  }
  MachPortSender* ports_out_sender =
      new MachPortSender(parent_message.GetTranslatedPort(1));

  if (parent_message.GetTranslatedPort(2) == MACH_PORT_NULL) {
    NS_WARNING("child GetTranslatedPort(2) failed");
    return NS_ERROR_FAILURE;
  }
  MachPortSender* ports_in_sender =
      new MachPortSender(parent_message.GetTranslatedPort(2));

  if (err != KERN_SUCCESS) {
    NS_WARNING("child task_set_bootstrap_port() failed");
    return NS_ERROR_FAILURE;
  }

#endif

  SetupErrorHandling(aArgv[0]);

  if (!CrashReporter::IsDummy()) {
#if defined(XP_WIN)
    if (aArgc < 1) {
      return NS_ERROR_FAILURE;
    }
    const char* const crashTimeAnnotationArg = aArgv[--aArgc];
    uintptr_t crashTimeAnnotationFile =
        static_cast<uintptr_t>(std::stoul(std::string(crashTimeAnnotationArg)));
#endif

    if (aArgc < 1) return NS_ERROR_FAILURE;
    const char* const crashReporterArg = aArgv[--aArgc];

#if defined(XP_MACOSX)
    // on windows and mac, |crashReporterArg| is the named pipe on which the
    // server is listening for requests, or "-" if crash reporting is
    // disabled.
    if (0 != strcmp("-", crashReporterArg) &&
        !XRE_SetRemoteExceptionHandler(crashReporterArg)) {
      // Bug 684322 will add better visibility into this condition
      NS_WARNING("Could not setup crash reporting\n");
    }
#elif defined(XP_WIN)
    if (0 != strcmp("-", crashReporterArg) &&
        !XRE_SetRemoteExceptionHandler(crashReporterArg,
                                       crashTimeAnnotationFile)) {
      // Bug 684322 will add better visibility into this condition
      NS_WARNING("Could not setup crash reporting\n");
    }
#else
    // on POSIX, |crashReporterArg| is "true" if crash reporting is
    // enabled, false otherwise
    if (0 != strcmp("false", crashReporterArg) &&
        !XRE_SetRemoteExceptionHandler(nullptr)) {
      // Bug 684322 will add better visibility into this condition
      NS_WARNING("Could not setup crash reporting\n");
    }
#endif
  }

  // For Init/Shutdown thread name annotations in the crash reporter.
  CrashReporter::InitThreadAnnotationRAII annotation;

  gArgv = aArgv;
  gArgc = aArgc;

#ifdef MOZ_X11
  XInitThreads();
#endif
#ifdef MOZ_WIDGET_GTK
  // Setting the name here avoids the need to pass this through to gtk_init().
  g_set_prgname(aArgv[0]);
#endif

#ifdef OS_POSIX
  if (PR_GetEnv("MOZ_DEBUG_CHILD_PROCESS") ||
      PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE")) {
#if defined(XP_LINUX) && defined(DEBUG)
    if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0) != 0) {
      printf_stderr("Could not allow ptrace from any process.\n");
    }
#endif
    printf_stderr(
        "\n\nCHILDCHILDCHILDCHILD (process type %s)\n  debug me @ %d\n\n",
        XRE_ChildProcessTypeToString(XRE_GetProcessType()),
        base::GetCurrentProcId());
    sleep(GetDebugChildPauseTime());
  }
#elif defined(OS_WIN)
  if (PR_GetEnv("MOZ_DEBUG_CHILD_PROCESS")) {
    NS_DebugBreak(NS_DEBUG_BREAK,
                  "Invoking NS_DebugBreak() to debug child process", nullptr,
                  __FILE__, __LINE__);
  } else if (PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE")) {
    printf_stderr(
        "\n\nCHILDCHILDCHILDCHILD (process type %s)\n  debug me @ %d\n\n",
        XRE_ChildProcessTypeToString(XRE_GetProcessType()),
        base::GetCurrentProcId());
    ::Sleep(GetDebugChildPauseTime());
  }
#endif

  // child processes launched by GeckoChildProcessHost get this magic
  // argument appended to their command lines
  const char* const parentPIDString = aArgv[aArgc - 1];
  MOZ_ASSERT(parentPIDString, "NULL parent PID");
  --aArgc;

  char* end = 0;
  base::ProcessId parentPID = strtol(parentPIDString, &end, 10);
  MOZ_ASSERT(!*end, "invalid parent PID");

  nsCOMPtr<nsIFile> crashReportTmpDir;
  if (XRE_GetProcessType() == GeckoProcessType_GPU) {
    aArgc--;
    if (strlen(aArgv[aArgc])) {  // if it's empty, ignore it
      nsresult rv =
          XRE_GetFileFromPath(aArgv[aArgc], getter_AddRefs(crashReportTmpDir));
      if (NS_FAILED(rv)) {
        // If we don't have a valid tmp dir we can probably still run ok, but
        // crash report .extra files might not get picked up by the parent
        // process. Debug-assert because this shouldn't happen in practice.
        MOZ_ASSERT(false, "GPU process started without valid tmp dir!");
      }
    }
  }

#ifdef XP_MACOSX
  mozilla::ipc::SharedMemoryBasic::SetupMachMemory(
      parentPID, ports_in_receiver, ports_in_sender, ports_out_sender,
      ports_out_receiver, true);
#endif

#if defined(XP_WIN)
  // On Win7+, register the application user model id passed in by
  // parent. This insures windows created by the container properly
  // group with the parent app on the Win7 taskbar.
  const char* const appModelUserId = aArgv[--aArgc];
  if (appModelUserId) {
    // '-' implies no support
    if (*appModelUserId != '-') {
      nsString appId;
      CopyASCIItoUTF16(nsDependentCString(appModelUserId), appId);
      // The version string is encased in quotes
      appId.Trim("\"");
      // Set the id
      SetTaskbarGroupId(appId);
    }
  }
#endif

  base::AtExitManager exitManager;

  nsresult rv = XRE_InitCommandLine(aArgc, aArgv);
  if (NS_FAILED(rv)) {
    return NS_ERROR_FAILURE;
  }

  MessageLoop::Type uiLoopType;
  switch (XRE_GetProcessType()) {
    case GeckoProcessType_Content:
    case GeckoProcessType_GPU:
      // Content processes need the XPCOM/chromium frankenventloop
      uiLoopType = MessageLoop::TYPE_MOZILLA_CHILD;
      break;
    case GeckoProcessType_GMPlugin:
    case GeckoProcessType_PDFium:
      uiLoopType = MessageLoop::TYPE_DEFAULT;
      break;
    default:
      uiLoopType = MessageLoop::TYPE_UI;
      break;
  }

  {
    // This is a lexical scope for the MessageLoop below.  We want it
    // to go out of scope before NS_LogTerm() so that we don't get
    // spurious warnings about XPCOM objects being destroyed from a
    // static context.

    // Associate this thread with a UI MessageLoop
    MessageLoop uiMessageLoop(uiLoopType);
    {
      nsAutoPtr<ProcessChild> process;

#ifdef XP_WIN
      mozilla::ipc::windows::InitUIThread();
#endif

      switch (XRE_GetProcessType()) {
        case GeckoProcessType_Default:
          MOZ_CRASH("This makes no sense");
          break;

        case GeckoProcessType_Plugin:
          process = new PluginProcessChild(parentPID);
          break;

        case GeckoProcessType_Content:
          process = new ContentProcess(parentPID);
          break;

        case GeckoProcessType_IPDLUnitTest:
#ifdef MOZ_IPDL_TESTS
          process = new IPDLUnitTestProcessChild(parentPID);
#else
          MOZ_CRASH("rebuild with --enable-ipdl-tests");
#endif
          break;

        case GeckoProcessType_GMPlugin:
          process = new gmp::GMPProcessChild(parentPID);
          break;

#if defined(XP_WIN) && defined(MOZ_ENABLE_SKIA_PDF)
        case GeckoProcessType_PDFium:
          process = new widget::PDFiumProcessChild(parentPID);
          break;
#endif
        case GeckoProcessType_GPU:
          process = new gfx::GPUProcessImpl(parentPID);
          break;

        default:
          MOZ_CRASH("Unknown main thread class");
      }

      if (!process->Init(aArgc, aArgv)) {
        return NS_ERROR_FAILURE;
      }

#if defined(XP_WIN)
      // Set child processes up such that they will get killed after the
      // chrome process is killed in cases where the user shuts the system
      // down or logs off.
      ::SetProcessShutdownParameters(0x280 - 1, SHUTDOWN_NORETRY);
#endif

#if defined(MOZ_SANDBOX) && defined(XP_WIN)
      // We need to do this after the process has been initialised, as
      // InitLoggingIfRequired may need access to prefs.
      mozilla::sandboxing::InitLoggingIfRequired(
          aChildData->ProvideLogFunction);
#endif
      mozilla::FilePreferences::InitDirectoriesWhitelist();
      mozilla::FilePreferences::InitPrefs();

      OverrideDefaultLocaleIfNeeded();

#if defined(MOZ_CONTENT_SANDBOX)
      AddContentSandboxLevelAnnotation();
#endif

      // Run the UI event loop on the main thread.
      uiMessageLoop.MessageLoop::Run();

      // Allow ProcessChild to clean up after itself before going out of
      // scope and being deleted
      process->CleanUp();
      mozilla::Omnijar::CleanUp();

#if defined(XP_MACOSX)
      // Everybody should be done using shared memory by now.
      mozilla::ipc::SharedMemoryBasic::Shutdown();
#endif
    }
  }

  return XRE_DeinitCommandLine();
}

MessageLoop* XRE_GetIOMessageLoop() {
  if (sChildProcessType == GeckoProcessType_Default) {
    return BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO);
  }
  return IOThreadChild::message_loop();
}

namespace {

class MainFunctionRunnable : public Runnable {
 public:
  NS_DECL_NSIRUNNABLE

  MainFunctionRunnable(MainFunction aFunction, void* aData)
      : mozilla::Runnable("MainFunctionRunnable"),
        mFunction(aFunction),
        mData(aData) {
    NS_ASSERTION(aFunction, "Don't give me a null pointer!");
  }

 private:
  MainFunction mFunction;
  void* mData;
};

} /* anonymous namespace */

NS_IMETHODIMP
MainFunctionRunnable::Run() {
  mFunction(mData);
  return NS_OK;
}

nsresult XRE_InitParentProcess(int aArgc, char* aArgv[],
                               MainFunction aMainFunction,
                               void* aMainFunctionData) {
  NS_ENSURE_ARG_MIN(aArgc, 1);
  NS_ENSURE_ARG_POINTER(aArgv);
  NS_ENSURE_ARG_POINTER(aArgv[0]);

  // Set main thread before we initialize the profiler
  NS_SetMainThread();

  mozilla::LogModule::Init();

  AUTO_PROFILER_INIT;

  ScopedXREEmbed embed;

  gArgc = aArgc;
  gArgv = aArgv;
  nsresult rv = XRE_InitCommandLine(gArgc, gArgv);
  if (NS_FAILED(rv)) return NS_ERROR_FAILURE;

  {
    embed.Start();

    nsCOMPtr<nsIAppShell> appShell(do_GetService(kAppShellCID));
    NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);

    if (aMainFunction) {
      nsCOMPtr<nsIRunnable> runnable =
          new MainFunctionRunnable(aMainFunction, aMainFunctionData);
      NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);

      nsresult rv = NS_DispatchToCurrentThread(runnable);
      NS_ENSURE_SUCCESS(rv, rv);
    }

    // Do event loop
    if (NS_FAILED(appShell->Run())) {
      NS_WARNING("Failed to run appshell");
      return NS_ERROR_FAILURE;
    }
  }

  return XRE_DeinitCommandLine();
}

#ifdef MOZ_IPDL_TESTS
//-----------------------------------------------------------------------------
// IPDL unit test

int XRE_RunIPDLTest(int aArgc, char** aArgv) {
  if (aArgc < 2) {
    fprintf(
        stderr,
        "TEST-UNEXPECTED-FAIL | <---> | insufficient #args, need at least 2\n");
    return 1;
  }

  void* data = reinterpret_cast<void*>(aArgv[aArgc - 1]);

  nsresult rv = XRE_InitParentProcess(
      --aArgc, aArgv, mozilla::_ipdltest::IPDLUnitTestMain, data);
  NS_ENSURE_SUCCESS(rv, 1);

  return 0;
}
#endif  // ifdef MOZ_IPDL_TESTS

nsresult XRE_RunAppShell() {
  nsCOMPtr<nsIAppShell> appShell(do_GetService(kAppShellCID));
  NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);
#if defined(XP_MACOSX)
  if (XRE_UseNativeEventProcessing()) {
    // In content processes that want XPCOM (and hence want
    // AppShell), we usually run our hybrid event loop through
    // MessagePump::Run(), by way of nsBaseAppShell::Run().  The
    // Cocoa nsAppShell impl, however, implements its own Run()
    // that's unaware of MessagePump.  That's all rather suboptimal,
    // but oddly enough not a problem... usually.
    //
    // The problem with this setup comes during startup.
    // XPCOM-in-subprocesses depends on IPC, e.g. to init the pref
    // service, so we have to init IPC first.  But, IPC also
    // indirectly kinda-depends on XPCOM, because MessagePump
    // schedules work from off-main threads (e.g. IO thread) by
    // using NS_DispatchToMainThread().  If the IO thread receives a
    // Message from the parent before nsThreadManager is
    // initialized, then DispatchToMainThread() will fail, although
    // MessagePump will remember the task.  This race condition
    // isn't a problem when appShell->Run() ends up in
    // MessagePump::Run(), because MessagePump will immediate see it
    // has work to do.  It *is* a problem when we end up in [NSApp
    // run], because it's not aware that MessagePump has work that
    // needs to be processed; that was supposed to be signaled by
    // nsIRunnable(s).
    //
    // So instead of hacking Cocoa nsAppShell or rewriting the
    // event-loop system, we compromise here by processing any tasks
    // that might have been enqueued on MessagePump, *before*
    // MessagePump::ScheduleWork was able to successfully
    // DispatchToMainThread().
    MessageLoop* loop = MessageLoop::current();
    bool couldNest = loop->NestableTasksAllowed();

    loop->SetNestableTasksAllowed(true);
    RefPtr<Runnable> task = new MessageLoop::QuitTask();
    loop->PostTask(task.forget());
    loop->Run();

    loop->SetNestableTasksAllowed(couldNest);
  }
#endif  // XP_MACOSX
  return appShell->Run();
}

void XRE_ShutdownChildProcess() {
  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");

  mozilla::DebugOnly<MessageLoop*> ioLoop = XRE_GetIOMessageLoop();
  MOZ_ASSERT(!!ioLoop, "Bad shutdown order");

  Scheduler::Shutdown();

  // Quit() sets off the following chain of events
  //  (1) UI loop starts quitting
  //  (2) UI loop returns from Run() in XRE_InitChildProcess()
  //  (3) ProcessChild goes out of scope and terminates the IO thread
  //  (4) ProcessChild joins the IO thread
  //  (5) exit()
  MessageLoop::current()->Quit();

#if defined(XP_MACOSX)
  nsCOMPtr<nsIAppShell> appShell(do_GetService(kAppShellCID));
  if (appShell) {
    // On Mac, we might be only above nsAppShell::Run(), not
    // MessagePump::Run().  See XRE_RunAppShell(). To account for
    // that case, we fire off an Exit() here.  If we were indeed
    // above MessagePump::Run(), this Exit() is just superfluous.
    appShell->Exit();
  }
#endif  // XP_MACOSX
}

namespace {
ContentParent* gContentParent;  // long-lived, manually refcounted
TestShellParent* GetOrCreateTestShellParent() {
  if (!gContentParent) {
    // Use a "web" child process by default.  File a bug if you don't like
    // this and you're sure you wouldn't be better off writing a "browser"
    // chrome mochitest where you can have multiple types of content
    // processes.
    RefPtr<ContentParent> parent = ContentParent::GetNewOrUsedBrowserProcess(
        NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE));
    parent.forget(&gContentParent);
  } else if (!gContentParent->IsAlive()) {
    return nullptr;
  }
  TestShellParent* tsp = gContentParent->GetTestShellSingleton();
  if (!tsp) {
    tsp = gContentParent->CreateTestShell();
  }
  return tsp;
}

}  // namespace

bool XRE_SendTestShellCommand(JSContext* aCx, JSString* aCommand,
                              void* aCallback) {
  JS::RootedString cmd(aCx, aCommand);
  TestShellParent* tsp = GetOrCreateTestShellParent();
  NS_ENSURE_TRUE(tsp, false);

  nsAutoJSString command;
  NS_ENSURE_TRUE(command.init(aCx, cmd), false);

  if (!aCallback) {
    return tsp->SendExecuteCommand(command);
  }

  TestShellCommandParent* callback = static_cast<TestShellCommandParent*>(
      tsp->SendPTestShellCommandConstructor(command));
  NS_ENSURE_TRUE(callback, false);

  JS::Value callbackVal = *reinterpret_cast<JS::Value*>(aCallback);
  NS_ENSURE_TRUE(callback->SetCallback(aCx, callbackVal), false);

  return true;
}

bool XRE_ShutdownTestShell() {
  if (!gContentParent) {
    return true;
  }
  bool ret = true;
  if (gContentParent->IsAlive()) {
    ret = gContentParent->DestroyTestShell(
        gContentParent->GetTestShellSingleton());
  }
  NS_RELEASE(gContentParent);
  return ret;
}

#ifdef MOZ_X11
void XRE_InstallX11ErrorHandler() {
#ifdef MOZ_WIDGET_GTK
  InstallGdkErrorHandler();
#else
  InstallX11ErrorHandler();
#endif
}
#endif