From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- toolkit/xre/nsEmbedFunctions.cpp | 833 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 833 insertions(+) create mode 100644 toolkit/xre/nsEmbedFunctions.cpp (limited to 'toolkit/xre/nsEmbedFunctions.cpp') diff --git a/toolkit/xre/nsEmbedFunctions.cpp b/toolkit/xre/nsEmbedFunctions.cpp new file mode 100644 index 0000000000..fa236e0f86 --- /dev/null +++ b/toolkit/xre/nsEmbedFunctions.cpp @@ -0,0 +1,833 @@ +/* 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 "nsXULAppAPI.h" + +#include +#if defined(MOZ_WIDGET_GTK) +# include +#endif + +#include "prenv.h" + +#include "nsIAppShell.h" +#include "nsIToolkitProfile.h" + +#ifdef XP_WIN +# include +# include +# include "mozilla/ipc/WindowsMessageLoop.h" +# ifdef MOZ_SANDBOX +# include "mozilla/RandomNum.h" +# endif +# include "mozilla/ScopeExit.h" +# include "mozilla/WinDllServices.h" +# include "mozilla/WindowsBCryptInitialization.h" +# include "WinUtils.h" +#endif + +#include "nsAppRunner.h" +#include "nsExceptionHandler.h" +#include "mozilla/RuntimeExceptionModule.h" +#include "nsThreadUtils.h" +#include "nsJSUtils.h" +#include "nsWidgetsCID.h" +#include "nsXREDirProvider.h" +#ifdef MOZ_ASAN_REPORTER +# include "CmdLineAndEnvUtils.h" +# include "nsIFile.h" +#endif + +#include "mozilla/Omnijar.h" +#if defined(XP_MACOSX) +# include +# include +# include "nsVersionComparator.h" +# include "chrome/common/mach_ipc_mac.h" +# include "gfxPlatformMac.h" +#endif +#include "nsX11ErrorHandler.h" +#include "nsGDKErrorHandler.h" +#include "base/at_exit.h" +#include "base/message_loop.h" +#include "base/process_util.h" +#if defined(MOZ_WIDGET_ANDROID) +# include "chrome/common/ipc_channel.h" +# include "mozilla/jni/Utils.h" +# include "mozilla/ipc/ProcessUtils.h" +#endif // defined(MOZ_WIDGET_ANDROID) + +#include "mozilla/AbstractThread.h" +#include "mozilla/FilePreferences.h" +#include "mozilla/IOInterposer.h" +#include "mozilla/ProcessType.h" +#include "mozilla/RDDProcessImpl.h" +#include "mozilla/ipc/UtilityProcessImpl.h" +#include "mozilla/UniquePtr.h" + +#include "mozilla/ipc/BrowserProcessSubThread.h" +#include "mozilla/ipc/IOThreadChild.h" +#include "mozilla/ipc/ProcessChild.h" + +#include "mozilla/dom/ContentProcess.h" +#include "mozilla/dom/ContentParent.h" + +#include "mozilla/ipc/TestShellParent.h" +#if defined(XP_WIN) +# include "mozilla/WindowsConsole.h" +# include "mozilla/WindowsDllBlocklist.h" +#endif + +#include "GMPProcessChild.h" +#include "mozilla/gfx/GPUProcessImpl.h" +#include "mozilla/net/SocketProcessImpl.h" + +#include "ProfilerControl.h" + +#if defined(MOZ_SANDBOX) && defined(XP_WIN) +# include "mozilla/sandboxTarget.h" +# include "mozilla/sandboxing/loggingCallbacks.h" +# include "mozilla/RemoteSandboxBrokerProcessChild.h" +#endif + +#if defined(MOZ_SANDBOX) +# include "XREChildData.h" +# include "mozilla/SandboxSettings.h" +#endif + +#if defined(XP_LINUX) && defined(MOZ_SANDBOX) +# include "mozilla/Sandbox.h" +#endif + +#if defined(XP_LINUX) +# include +# 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_JPROF +# include "jprof.h" +#endif + +#if defined(XP_WIN) && defined(MOZ_SANDBOX) +# include "mozilla/sandboxing/SandboxInitialization.h" +# include "mozilla/sandboxing/sandboxLogging.h" +#endif + +#if defined(MOZ_ENABLE_FORKSERVER) +# include "mozilla/ipc/ForkServer.h" +#endif + +#if defined(MOZ_X11) +# include +#endif + +#include "VRProcessChild.h" + +using namespace mozilla; + +using mozilla::ipc::BrowserProcessSubThread; +using mozilla::ipc::GeckoChildProcessHost; +using mozilla::ipc::IOThreadChild; +using mozilla::ipc::ProcessChild; + +using mozilla::dom::ContentParent; +using mozilla::dom::ContentProcess; + +using mozilla::gmp::GMPProcessChild; + +using mozilla::ipc::TestShellCommandParent; +using mozilla::ipc::TestShellParent; + +namespace mozilla::_ipdltest { +// Set in IPDLUnitTest.cpp when running gtests. +UniquePtr (*gMakeIPDLUnitTestProcessChild)( + base::ProcessId, const nsID&) = nullptr; +} // namespace mozilla::_ipdltest + +static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); + +const char* XRE_GeckoProcessTypeToString(GeckoProcessType aProcessType) { + switch (aProcessType) { +#define GECKO_PROCESS_TYPE(enum_value, enum_name, string_name, proc_typename, \ + process_bin_type, procinfo_typename, \ + webidl_typename, allcaps_name) \ + case GeckoProcessType::GeckoProcessType_##enum_name: \ + return string_name; +#include "mozilla/GeckoProcessTypes.h" +#undef GECKO_PROCESS_TYPE + default: + return "invalid"; + } +} + +const char* XRE_ChildProcessTypeToAnnotation(GeckoProcessType aProcessType) { + switch (aProcessType) { + case GeckoProcessType_GMPlugin: + return "plugin"; + case GeckoProcessType_Default: + return ""; + case GeckoProcessType_Content: + return "content"; + default: + return XRE_GeckoProcessTypeToString(aProcessType); + } +} + +#if defined(MOZ_WIDGET_ANDROID) +void XRE_SetAndroidChildFds(JNIEnv* env, const XRE_AndroidChildFds& fds) { + mozilla::jni::SetGeckoThreadEnv(env); + mozilla::ipc::SetPrefsFd(fds.mPrefsFd); + mozilla::ipc::SetPrefMapFd(fds.mPrefMapFd); + IPC::Channel::SetClientChannelFd(fds.mIpcFd); + CrashReporter::SetNotificationPipeForChild(fds.mCrashFd); + CrashReporter::SetCrashAnnotationPipeForChild(fds.mCrashAnnotationFd); +} +#endif // defined(MOZ_WIDGET_ANDROID) + +void XRE_SetProcessType(const char* aProcessTypeString) { + SetGeckoProcessType(aProcessTypeString); +} + +#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_SANDBOX) +void AddContentSandboxLevelAnnotation() { + if (XRE_GetProcessType() == GeckoProcessType_Content) { + int level = GetEffectiveContentSandboxLevel(); + CrashReporter::AnnotateCrashReport( + CrashReporter::Annotation::ContentSandboxLevel, level); + } else if (XRE_GetProcessType() == GeckoProcessType_GPU) { + int level = GetEffectiveGpuSandboxLevel(); + CrashReporter::AnnotateCrashReport( + CrashReporter::Annotation::GpuSandboxLevel, level); + } +} +#endif /* MOZ_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 +} + +static bool IsCrashReporterEnabled(const char* aArg) { + // on windows and mac, |aArg| is the named pipe on which the server is + // listening for requests, or "-" if crash reporting is disabled. +#if defined(XP_MACOSX) || defined(XP_WIN) + return 0 != strcmp("-", aArg); +#else + // on POSIX, |aArg| is "true" if crash reporting is enabled, false otherwise + return 0 != strcmp("false", aArg); +#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); + + NS_SetCurrentThreadName("MainThread"); + +#ifdef MOZ_ASAN_REPORTER + // In ASan reporter builds, we need to set ASan's log_path as early as + // possible, so it dumps its errors into files there instead of using + // the default stderr location. Since this is crucial for ASan reporter + // to work at all (and we don't want people to use a non-functional + // ASan reporter build), all failures while setting log_path are fatal. + // + // We receive this log_path via the ASAN_REPORTER_PATH environment variable + // because there is no other way to generically get the necessary profile + // directory in all child types without adding support for that in each + // child process type class (at the risk of missing this in a child). + // + // In certain cases (e.g. child startup through xpcshell or gtests), this + // code needs to remain disabled, as no ASAN_REPORTER_PATH would be available. + if (!PR_GetEnv("MOZ_DISABLE_ASAN_REPORTER") && !PR_GetEnv("MOZ_RUN_GTEST")) { + nsCOMPtr asanReporterPath = GetFileFromEnv("ASAN_REPORTER_PATH"); + if (!asanReporterPath) { + MOZ_CRASH("Child did not receive ASAN_REPORTER_PATH!"); + } + setASanReporterPath(asanReporterPath); + } +#endif + +#if defined(XP_LINUX) && defined(MOZ_SANDBOX) + // This has to happen before glib thread pools are started. + mozilla::SandboxEarlyInit(); + // This just needs to happen before sandboxing, to initialize the + // cached value, but libmozsandbox can't see this symbol. + mozilla::GetNumberOfProcessors(); +#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. + UseParentConsole(); + +# 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(aArgc, aArgv); + + AUTO_BASE_PROFILER_LABEL("XRE_InitChildProcess (around Gecko Profiler)", + OTHER); + AUTO_PROFILER_INIT; + AUTO_PROFILER_LABEL("XRE_InitChildProcess", OTHER); + +#ifdef XP_MACOSX + gfxPlatformMac::RegisterSupplementalFonts(); +#endif + + // 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; + +# if defined(MOZ_SANDBOX) + // Save the original number of arguments to pass to the sandbox + // setup routine which also uses the crash server argument. + int allArgc = aArgc; +# endif /* MOZ_SANDBOX */ + + // Acquire the mach bootstrap port name from our command line, and send our + // task_t to the parent process. + const char* const mach_port_name = aArgv[--aArgc]; + + const int kTimeoutMs = 1000; + + UniqueMachSendRight task_sender; + kern_return_t kr = bootstrap_look_up(bootstrap_port, mach_port_name, + getter_Transfers(task_sender)); + if (kr != KERN_SUCCESS) { + NS_WARNING(nsPrintfCString("child bootstrap_look_up failed: %s", + mach_error_string(kr)) + .get()); + return NS_ERROR_FAILURE; + } + + kr = MachSendPortSendRight(task_sender.get(), mach_task_self(), + Some(kTimeoutMs)); + if (kr != KERN_SUCCESS) { + NS_WARNING(nsPrintfCString("child MachSendPortSendRight failed: %s", + mach_error_string(kr)) + .get()); + return NS_ERROR_FAILURE; + } + +# if defined(MOZ_SANDBOX) + std::string sandboxError; + if (!GeckoChildProcessHost::StartMacSandbox(allArgc, aArgv, sandboxError)) { + printf_stderr("Sandbox error: %s\n", sandboxError.c_str()); + MOZ_CRASH("Sandbox initialization failed"); + } +# endif /* MOZ_SANDBOX */ + +#endif /* XP_MACOSX */ + + SetupErrorHandling(aArgv[0]); + + bool exceptionHandlerIsSet = false; + if (!CrashReporter::IsDummy()) { + CrashReporter::FileHandle crashTimeAnnotationFile = + CrashReporter::kInvalidFileHandle; +#if defined(XP_WIN) + if (aArgc < 1) { + return NS_ERROR_FAILURE; + } + // Pop the first argument, this is used by the WER runtime exception module + // which reads it from the command-line so we can just discard it here. + --aArgc; + + const char* const crashTimeAnnotationArg = aArgv[--aArgc]; + crashTimeAnnotationFile = reinterpret_cast( + std::stoul(std::string(crashTimeAnnotationArg))); +#endif + + if (aArgc < 1) return NS_ERROR_FAILURE; + const char* const crashReporterArg = aArgv[--aArgc]; + + if (IsCrashReporterEnabled(crashReporterArg)) { + exceptionHandlerIsSet = CrashReporter::SetRemoteExceptionHandler( + crashReporterArg, crashTimeAnnotationFile); + MOZ_ASSERT(exceptionHandlerIsSet, + "Should have been able to set remote exception handler"); + + if (!exceptionHandlerIsSet) { + // Bug 684322 will add better visibility into this condition + NS_WARNING("Could not setup crash reporting\n"); + } + } else { + // We might have registered a runtime exception module very early in + // process startup to catch early crashes. This is before we process the + // crash reporter arg, so unregister here if it turns out the crash + // reporter is disabled. + CrashReporter::UnregisterRuntimeExceptionModule(); + } + } + + 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_GetProcessTypeString(), 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 @ %lu\n\n", + XRE_GetProcessTypeString(), base::GetCurrentProcId()); + ::Sleep(GetDebugChildPauseTime()); + } +#endif + +#ifdef MOZ_WIDGET_ANDROID + // The parent process already did this, but Gecko child processes on + // Android aren't descendants of the parent process, so they don't + // inherit its rlimits. + mozilla::startup::IncreaseDescriptorLimits(); +#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"); + + // They also get the initial message channel ID passed in the same manner. + const char* const messageChannelIdString = aArgv[aArgc - 1]; + MOZ_ASSERT(messageChannelIdString, "NULL MessageChannel Id"); + --aArgc; + + nsID messageChannelId{}; + if (!messageChannelId.Parse(messageChannelIdString)) { + return NS_ERROR_FAILURE; + } + +#if defined(XP_WIN) + // On Win7+, when not running as an MSIX package, register the application + // user model id passed in by parent. This ensures windows created by the + // container properly group with the parent app on the Win7 taskbar. + // MSIX packages explicitly do not support setting the appid from within + // the app, as it is set in the package manifest instead. + const char* const appModelUserId = aArgv[--aArgc]; + if (appModelUserId && !mozilla::widget::WinUtils::HasPackageIdentity()) { + // '-' 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: + case GeckoProcessType_IPDLUnitTest: + case GeckoProcessType_VR: + case GeckoProcessType_RDD: + case GeckoProcessType_Socket: + case GeckoProcessType_Utility: + // Content processes need the XPCOM/chromium frankenventloop + uiLoopType = MessageLoop::TYPE_MOZILLA_CHILD; + break; + case GeckoProcessType_GMPlugin: + case GeckoProcessType_RemoteSandboxBroker: + uiLoopType = MessageLoop::TYPE_DEFAULT; + break; + default: + uiLoopType = MessageLoop::TYPE_UI; + break; + } + +#if defined(XP_WIN) +# if defined(MOZ_SANDBOX) + if (aChildData->sandboxBrokerServices) { + SandboxBroker::Initialize(aChildData->sandboxBrokerServices); + SandboxBroker::GeckoDependentInitialize(); + } +# endif // defined(MOZ_SANDBOX) + + { + DebugOnly result = mozilla::WindowsBCryptInitialization(); + MOZ_ASSERT(result); + } +#endif // defined(XP_WIN) + + { + // 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. + + Maybe ioInterposerGuard; + + // Associate this thread with a UI MessageLoop + MessageLoop uiMessageLoop(uiLoopType); + { + UniquePtr process; + switch (XRE_GetProcessType()) { + case GeckoProcessType_Default: + MOZ_CRASH("This makes no sense"); + break; + + case GeckoProcessType_Content: + ioInterposerGuard.emplace(); + process = MakeUnique(parentPID, messageChannelId); + break; + + case GeckoProcessType_IPDLUnitTest: + MOZ_RELEASE_ASSERT(mozilla::_ipdltest::gMakeIPDLUnitTestProcessChild, + "xul-gtest not loaded!"); + process = mozilla::_ipdltest::gMakeIPDLUnitTestProcessChild( + parentPID, messageChannelId); + break; + + case GeckoProcessType_GMPlugin: + process = + MakeUnique(parentPID, messageChannelId); + break; + + case GeckoProcessType_GPU: + process = + MakeUnique(parentPID, messageChannelId); + break; + + case GeckoProcessType_VR: + process = + MakeUnique(parentPID, messageChannelId); + break; + + case GeckoProcessType_RDD: + process = MakeUnique(parentPID, messageChannelId); + break; + + case GeckoProcessType_Socket: + ioInterposerGuard.emplace(); + process = + MakeUnique(parentPID, messageChannelId); + break; + + case GeckoProcessType_Utility: + process = + MakeUnique(parentPID, messageChannelId); + break; + +#if defined(MOZ_SANDBOX) && defined(XP_WIN) + case GeckoProcessType_RemoteSandboxBroker: + process = MakeUnique( + parentPID, messageChannelId); + break; +#endif + +#if defined(MOZ_ENABLE_FORKSERVER) + case GeckoProcessType_ForkServer: + MOZ_CRASH("Fork server should not go here"); + break; +#endif + 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); + + RefPtr dllSvc(DllServices::Get()); + auto dllSvcDisable = + MakeScopeExit([&dllSvc]() { dllSvc->DisableFull(); }); +#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 + if (XRE_GetProcessType() != GeckoProcessType_RemoteSandboxBroker) { + // Remote sandbox launcher process doesn't have prerequisites for + // these... + mozilla::FilePreferences::InitDirectoriesAllowlist(); + mozilla::FilePreferences::InitPrefs(); + OverrideDefaultLocaleIfNeeded(); + } + +#if defined(MOZ_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(); + } + } + + CrashReporter::UnsetRemoteExceptionHandler(exceptionHandlerIsSet); + + return XRE_DeinitCommandLine(); +} + +MessageLoop* XRE_GetIOMessageLoop() { + if (GetGeckoProcessType() == GeckoProcessType_Default) { + return BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO); + } + return IOThreadChild::message_loop(); +} + +nsresult XRE_RunAppShell() { + nsCOMPtr 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 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 ioLoop = XRE_GetIOMessageLoop(); + MOZ_ASSERT(!!ioLoop, "Bad shutdown order"); + + // 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 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 parent = + ContentParent::GetNewOrUsedBrowserProcess(DEFAULT_REMOTE_TYPE); + parent.forget(&gContentParent); + } else if (gContentParent->IsShuttingDown()) { + return nullptr; + } + TestShellParent* tsp = gContentParent->GetTestShellSingleton(); + if (!tsp) { + tsp = gContentParent->CreateTestShell(); + } + return tsp; +} + +} // namespace + +bool XRE_SendTestShellCommand(JSContext* aCx, JSString* aCommand, + JS::Value* aCallback) { + JS::Rooted 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( + tsp->SendPTestShellCommandConstructor(command)); + NS_ENSURE_TRUE(callback, false); + + NS_ENSURE_TRUE(callback->SetCallback(aCx, *aCallback), 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(); +# endif + + // Ensure our X11 error handler overrides the default GDK error handler such + // that errors are ignored by default. GDK will install its own error handler + // temporarily when pushing error traps internally as needed. This avoids us + // otherwise having to frequently override the error handler merely to trap + // errors in multiple places that would otherwise contend with GDK or other + // libraries that might also override the handler. + InstallX11ErrorHandler(); +} + +void XRE_CleanupX11ErrorHandler() { CleanupX11ErrorHandler(); } +#endif + +#ifdef MOZ_ENABLE_FORKSERVER +int XRE_ForkServer(int* aArgc, char*** aArgv) { + return mozilla::ipc::ForkServer::RunForkServer(aArgc, aArgv) ? 1 : 0; +} +#endif -- cgit v1.2.3