michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/DebugOnly.h" michael@0: michael@0: #if defined(MOZ_WIDGET_QT) michael@0: #include "nsQAppInstance.h" michael@0: #endif michael@0: michael@0: #include "base/basictypes.h" michael@0: michael@0: #include "nsXULAppAPI.h" michael@0: michael@0: #include michael@0: #if defined(MOZ_WIDGET_GTK) michael@0: #include michael@0: #endif michael@0: michael@0: #include "prenv.h" michael@0: michael@0: #include "nsIAppShell.h" michael@0: #include "nsIAppStartupNotifier.h" michael@0: #include "nsIDirectoryService.h" michael@0: #include "nsIFile.h" michael@0: #include "nsIToolkitChromeRegistry.h" michael@0: #include "nsIToolkitProfile.h" michael@0: michael@0: #ifdef XP_WIN michael@0: #include michael@0: #include "mozilla/ipc/WindowsMessageLoop.h" michael@0: #endif michael@0: michael@0: #include "nsAppDirectoryServiceDefs.h" michael@0: #include "nsAppRunner.h" michael@0: #include "nsAutoRef.h" michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsExceptionHandler.h" michael@0: #include "nsString.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsJSUtils.h" michael@0: #include "nsWidgetsCID.h" michael@0: #include "nsXREDirProvider.h" michael@0: michael@0: #include "mozilla/Omnijar.h" michael@0: #if defined(XP_MACOSX) michael@0: #include "nsVersionComparator.h" michael@0: #include "chrome/common/mach_ipc_mac.h" michael@0: #endif michael@0: #include "nsX11ErrorHandler.h" michael@0: #include "base/at_exit.h" michael@0: #include "base/command_line.h" michael@0: #include "base/message_loop.h" michael@0: #include "base/process_util.h" michael@0: #include "chrome/common/child_process.h" michael@0: #include "chrome/common/notification_service.h" michael@0: michael@0: #include "mozilla/ipc/BrowserProcessSubThread.h" michael@0: #include "mozilla/ipc/GeckoChildProcessHost.h" michael@0: #include "mozilla/ipc/IOThreadChild.h" michael@0: #include "mozilla/ipc/ProcessChild.h" michael@0: #include "ScopedXREEmbed.h" michael@0: michael@0: #include "mozilla/plugins/PluginProcessChild.h" michael@0: #include "mozilla/dom/ContentProcess.h" michael@0: #include "mozilla/dom/ContentParent.h" michael@0: #include "mozilla/dom/ContentChild.h" michael@0: michael@0: #include "mozilla/ipc/TestShellParent.h" michael@0: #include "mozilla/ipc/XPCShellEnvironment.h" michael@0: michael@0: #include "GeckoProfiler.h" michael@0: michael@0: #ifdef MOZ_IPDL_TESTS michael@0: #include "mozilla/_ipdltest/IPDLUnitTests.h" michael@0: #include "mozilla/_ipdltest/IPDLUnitTestProcessChild.h" michael@0: michael@0: using mozilla::_ipdltest::IPDLUnitTestProcessChild; michael@0: #endif // ifdef MOZ_IPDL_TESTS michael@0: michael@0: using namespace mozilla; michael@0: michael@0: using mozilla::ipc::BrowserProcessSubThread; michael@0: using mozilla::ipc::GeckoChildProcessHost; michael@0: using mozilla::ipc::IOThreadChild; michael@0: using mozilla::ipc::ProcessChild; michael@0: using mozilla::ipc::ScopedXREEmbed; michael@0: michael@0: using mozilla::plugins::PluginProcessChild; michael@0: using mozilla::dom::ContentProcess; michael@0: using mozilla::dom::ContentParent; michael@0: using mozilla::dom::ContentChild; michael@0: michael@0: using mozilla::ipc::TestShellParent; michael@0: using mozilla::ipc::TestShellCommandParent; michael@0: using mozilla::ipc::XPCShellEnvironment; michael@0: michael@0: using mozilla::startup::sChildProcessType; michael@0: michael@0: static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); michael@0: michael@0: #ifdef XP_WIN michael@0: static const wchar_t kShellLibraryName[] = L"shell32.dll"; michael@0: #endif michael@0: michael@0: nsresult michael@0: XRE_LockProfileDirectory(nsIFile* aDirectory, michael@0: nsISupports* *aLockObject) michael@0: { michael@0: nsCOMPtr lock; michael@0: michael@0: nsresult rv = NS_LockProfilePath(aDirectory, nullptr, nullptr, michael@0: getter_AddRefs(lock)); michael@0: if (NS_SUCCEEDED(rv)) michael@0: NS_ADDREF(*aLockObject = lock); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: static int32_t sInitCounter; michael@0: michael@0: nsresult michael@0: XRE_InitEmbedding2(nsIFile *aLibXULDirectory, michael@0: nsIFile *aAppDirectory, michael@0: nsIDirectoryServiceProvider *aAppDirProvider) michael@0: { michael@0: // Initialize some globals to make nsXREDirProvider happy michael@0: static char* kNullCommandLine[] = { nullptr }; michael@0: gArgv = kNullCommandLine; michael@0: gArgc = 0; michael@0: michael@0: NS_ENSURE_ARG(aLibXULDirectory); michael@0: michael@0: if (++sInitCounter > 1) // XXXbsmedberg is this really the right solution? michael@0: return NS_OK; michael@0: michael@0: if (!aAppDirectory) michael@0: aAppDirectory = aLibXULDirectory; michael@0: michael@0: nsresult rv; michael@0: michael@0: new nsXREDirProvider; // This sets gDirServiceProvider michael@0: if (!gDirServiceProvider) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: rv = gDirServiceProvider->Initialize(aAppDirectory, aLibXULDirectory, michael@0: aAppDirProvider); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: rv = NS_InitXPCOM2(nullptr, aAppDirectory, gDirServiceProvider); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // We do not need to autoregister components here. The CheckCompatibility() michael@0: // bits in nsAppRunner.cpp check for an invalidation flag in michael@0: // compatibility.ini. michael@0: // If the app wants to autoregister every time (for instance, if it's debug), michael@0: // it can do so after we return from this function. michael@0: michael@0: nsCOMPtr startupNotifier michael@0: (do_CreateInstance(NS_APPSTARTUPNOTIFIER_CONTRACTID)); michael@0: if (!startupNotifier) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: startupNotifier->Observe(nullptr, APPSTARTUP_TOPIC, nullptr); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: XRE_NotifyProfile() michael@0: { michael@0: NS_ASSERTION(gDirServiceProvider, "XRE_InitEmbedding was not called!"); michael@0: gDirServiceProvider->DoStartup(); michael@0: } michael@0: michael@0: void michael@0: XRE_TermEmbedding() michael@0: { michael@0: if (--sInitCounter != 0) michael@0: return; michael@0: michael@0: NS_ASSERTION(gDirServiceProvider, michael@0: "XRE_TermEmbedding without XRE_InitEmbedding"); michael@0: michael@0: gDirServiceProvider->DoShutdown(); michael@0: NS_ShutdownXPCOM(nullptr); michael@0: delete gDirServiceProvider; michael@0: } michael@0: michael@0: const char* michael@0: XRE_ChildProcessTypeToString(GeckoProcessType aProcessType) michael@0: { michael@0: return (aProcessType < GeckoProcessType_End) ? michael@0: kGeckoProcessTypeString[aProcessType] : nullptr; michael@0: } michael@0: michael@0: GeckoProcessType michael@0: XRE_StringToChildProcessType(const char* aProcessTypeString) michael@0: { michael@0: for (int i = 0; michael@0: i < (int) ArrayLength(kGeckoProcessTypeString); michael@0: ++i) { michael@0: if (!strcmp(kGeckoProcessTypeString[i], aProcessTypeString)) { michael@0: return static_cast(i); michael@0: } michael@0: } michael@0: return GeckoProcessType_Invalid; michael@0: } michael@0: michael@0: namespace mozilla { michael@0: namespace startup { michael@0: GeckoProcessType sChildProcessType = GeckoProcessType_Default; michael@0: } michael@0: } michael@0: michael@0: #if defined(MOZ_CRASHREPORTER) michael@0: // FIXME/bug 539522: this out-of-place function is stuck here because michael@0: // IPDL wants access to this crashreporter interface, and michael@0: // crashreporter is built in such a way to make that awkward michael@0: bool michael@0: XRE_TakeMinidumpForChild(uint32_t aChildPid, nsIFile** aDump, michael@0: uint32_t* aSequence) michael@0: { michael@0: return CrashReporter::TakeMinidumpForChild(aChildPid, aDump, aSequence); michael@0: } michael@0: michael@0: bool michael@0: XRE_SetRemoteExceptionHandler(const char* aPipe/*= 0*/) michael@0: { michael@0: #if defined(XP_WIN) || defined(XP_MACOSX) michael@0: return CrashReporter::SetRemoteExceptionHandler(nsDependentCString(aPipe)); michael@0: #elif defined(OS_LINUX) michael@0: return CrashReporter::SetRemoteExceptionHandler(); michael@0: #else michael@0: # error "OOP crash reporter unsupported on this platform" michael@0: #endif michael@0: } michael@0: #endif // if defined(MOZ_CRASHREPORTER) michael@0: michael@0: #if defined(XP_WIN) michael@0: void michael@0: SetTaskbarGroupId(const nsString& aId) michael@0: { michael@0: typedef HRESULT (WINAPI * SetCurrentProcessExplicitAppUserModelIDPtr)(PCWSTR AppID); michael@0: michael@0: SetCurrentProcessExplicitAppUserModelIDPtr funcAppUserModelID = nullptr; michael@0: michael@0: HMODULE hDLL = ::LoadLibraryW(kShellLibraryName); michael@0: michael@0: funcAppUserModelID = (SetCurrentProcessExplicitAppUserModelIDPtr) michael@0: GetProcAddress(hDLL, "SetCurrentProcessExplicitAppUserModelID"); michael@0: michael@0: if (!funcAppUserModelID) { michael@0: ::FreeLibrary(hDLL); michael@0: return; michael@0: } michael@0: michael@0: if (FAILED(funcAppUserModelID(aId.get()))) { michael@0: NS_WARNING("SetCurrentProcessExplicitAppUserModelID failed for child process."); michael@0: } michael@0: michael@0: if (hDLL) michael@0: ::FreeLibrary(hDLL); michael@0: } michael@0: #endif michael@0: michael@0: nsresult michael@0: XRE_InitChildProcess(int aArgc, michael@0: char* aArgv[], michael@0: GeckoProcessType aProcess) michael@0: { michael@0: NS_ENSURE_ARG_MIN(aArgc, 2); michael@0: NS_ENSURE_ARG_POINTER(aArgv); michael@0: NS_ENSURE_ARG_POINTER(aArgv[0]); michael@0: michael@0: #if defined(XP_WIN) michael@0: // From the --attach-console support in nsNativeAppSupportWin.cpp, but michael@0: // here we are a content child process, so we always attempt to attach michael@0: // to the parent's (ie, the browser's) console. michael@0: // Try to attach console to the parent process. michael@0: // It will succeed when the parent process is a command line, michael@0: // so that stdio will be displayed in it. michael@0: if (AttachConsole(ATTACH_PARENT_PROCESS)) { michael@0: // Change std handles to refer to new console handles. michael@0: // Before doing so, ensure that stdout/stderr haven't been michael@0: // redirected to a valid file michael@0: if (_fileno(stdout) == -1 || michael@0: _get_osfhandle(fileno(stdout)) == -1) michael@0: freopen("CONOUT$", "w", stdout); michael@0: // Merge stderr into CONOUT$ since there isn't any `CONERR$`. michael@0: // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231%28v=vs.85%29.aspx michael@0: if (_fileno(stderr) == -1 || michael@0: _get_osfhandle(fileno(stderr)) == -1) michael@0: freopen("CONOUT$", "w", stderr); michael@0: if (_fileno(stdin) == -1 || _get_osfhandle(fileno(stdin)) == -1) michael@0: freopen("CONIN$", "r", stdin); michael@0: } michael@0: #endif michael@0: michael@0: char aLocal; michael@0: profiler_init(&aLocal); michael@0: PROFILER_LABEL("Startup", "XRE_InitChildProcess"); michael@0: michael@0: sChildProcessType = aProcess; michael@0: michael@0: // Complete 'task_t' exchange for Mac OS X. This structure has the same size michael@0: // regardless of architecture so we don't have any cross-arch issues here. michael@0: #ifdef XP_MACOSX michael@0: if (aArgc < 1) michael@0: return NS_ERROR_FAILURE; michael@0: const char* const mach_port_name = aArgv[--aArgc]; michael@0: michael@0: const int kTimeoutMs = 1000; michael@0: michael@0: MachSendMessage child_message(0); michael@0: if (!child_message.AddDescriptor(mach_task_self())) { michael@0: NS_WARNING("child AddDescriptor(mach_task_self()) failed."); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: ReceivePort child_recv_port; michael@0: mach_port_t raw_child_recv_port = child_recv_port.GetPort(); michael@0: if (!child_message.AddDescriptor(raw_child_recv_port)) { michael@0: NS_WARNING("Adding descriptor to message failed"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: MachPortSender child_sender(mach_port_name); michael@0: kern_return_t err = child_sender.SendMessage(child_message, kTimeoutMs); michael@0: if (err != KERN_SUCCESS) { michael@0: NS_WARNING("child SendMessage() failed"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: MachReceiveMessage parent_message; michael@0: err = child_recv_port.WaitForMessage(&parent_message, kTimeoutMs); michael@0: if (err != KERN_SUCCESS) { michael@0: NS_WARNING("child WaitForMessage() failed"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (parent_message.GetTranslatedPort(0) == MACH_PORT_NULL) { michael@0: NS_WARNING("child GetTranslatedPort(0) failed"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: err = task_set_bootstrap_port(mach_task_self(), michael@0: parent_message.GetTranslatedPort(0)); michael@0: if (err != KERN_SUCCESS) { michael@0: NS_WARNING("child task_set_bootstrap_port() failed"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: #endif michael@0: michael@0: SetupErrorHandling(aArgv[0]); michael@0: michael@0: #if defined(MOZ_CRASHREPORTER) michael@0: if (aArgc < 1) michael@0: return NS_ERROR_FAILURE; michael@0: const char* const crashReporterArg = aArgv[--aArgc]; michael@0: michael@0: # if defined(XP_WIN) || defined(XP_MACOSX) michael@0: // on windows and mac, |crashReporterArg| is the named pipe on which the michael@0: // server is listening for requests, or "-" if crash reporting is michael@0: // disabled. michael@0: if (0 != strcmp("-", crashReporterArg) && michael@0: !XRE_SetRemoteExceptionHandler(crashReporterArg)) { michael@0: // Bug 684322 will add better visibility into this condition michael@0: NS_WARNING("Could not setup crash reporting\n"); michael@0: } michael@0: # elif defined(OS_LINUX) michael@0: // on POSIX, |crashReporterArg| is "true" if crash reporting is michael@0: // enabled, false otherwise michael@0: if (0 != strcmp("false", crashReporterArg) && michael@0: !XRE_SetRemoteExceptionHandler(nullptr)) { michael@0: // Bug 684322 will add better visibility into this condition michael@0: NS_WARNING("Could not setup crash reporting\n"); michael@0: } michael@0: # else michael@0: # error "OOP crash reporting unsupported on this platform" michael@0: # endif michael@0: #endif // if defined(MOZ_CRASHREPORTER) michael@0: michael@0: gArgv = aArgv; michael@0: gArgc = aArgc; michael@0: michael@0: #if defined(MOZ_WIDGET_GTK) michael@0: g_thread_init(nullptr); michael@0: #endif michael@0: michael@0: #if defined(MOZ_WIDGET_QT) michael@0: nsQAppInstance::AddRef(); michael@0: #endif michael@0: michael@0: if (PR_GetEnv("MOZ_DEBUG_CHILD_PROCESS")) { michael@0: #ifdef OS_POSIX michael@0: printf("\n\nCHILDCHILDCHILDCHILD\n debug me @ %d\n\n", getpid()); michael@0: sleep(30); michael@0: #elif defined(OS_WIN) michael@0: // Windows has a decent JIT debugging story, so NS_DebugBreak does the michael@0: // right thing. michael@0: NS_DebugBreak(NS_DEBUG_BREAK, michael@0: "Invoking NS_DebugBreak() to debug child process", michael@0: nullptr, __FILE__, __LINE__); michael@0: #endif michael@0: } michael@0: michael@0: // child processes launched by GeckoChildProcessHost get this magic michael@0: // argument appended to their command lines michael@0: const char* const parentPIDString = aArgv[aArgc-1]; michael@0: NS_ABORT_IF_FALSE(parentPIDString, "NULL parent PID"); michael@0: --aArgc; michael@0: michael@0: char* end = 0; michael@0: base::ProcessId parentPID = strtol(parentPIDString, &end, 10); michael@0: NS_ABORT_IF_FALSE(!*end, "invalid parent PID"); michael@0: michael@0: base::ProcessHandle parentHandle; michael@0: mozilla::DebugOnly ok = base::OpenProcessHandle(parentPID, &parentHandle); michael@0: NS_ABORT_IF_FALSE(ok, "can't open handle to parent"); michael@0: michael@0: #if defined(XP_WIN) michael@0: // On Win7+, register the application user model id passed in by michael@0: // parent. This insures windows created by the container properly michael@0: // group with the parent app on the Win7 taskbar. michael@0: const char* const appModelUserId = aArgv[--aArgc]; michael@0: if (appModelUserId) { michael@0: // '-' implies no support michael@0: if (*appModelUserId != '-') { michael@0: nsString appId; michael@0: appId.AssignWithConversion(nsDependentCString(appModelUserId)); michael@0: // The version string is encased in quotes michael@0: appId.Trim(NS_LITERAL_CSTRING("\"").get()); michael@0: // Set the id michael@0: SetTaskbarGroupId(appId); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: base::AtExitManager exitManager; michael@0: NotificationService notificationService; michael@0: michael@0: NS_LogInit(); michael@0: michael@0: nsresult rv = XRE_InitCommandLine(aArgc, aArgv); michael@0: if (NS_FAILED(rv)) { michael@0: profiler_shutdown(); michael@0: NS_LogTerm(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: MessageLoop::Type uiLoopType; michael@0: switch (aProcess) { michael@0: case GeckoProcessType_Content: michael@0: // Content processes need the XPCOM/chromium frankenventloop michael@0: uiLoopType = MessageLoop::TYPE_MOZILLA_CHILD; michael@0: break; michael@0: default: michael@0: uiLoopType = MessageLoop::TYPE_UI; michael@0: break; michael@0: } michael@0: michael@0: { michael@0: // This is a lexical scope for the MessageLoop below. We want it michael@0: // to go out of scope before NS_LogTerm() so that we don't get michael@0: // spurious warnings about XPCOM objects being destroyed from a michael@0: // static context. michael@0: michael@0: // Associate this thread with a UI MessageLoop michael@0: MessageLoop uiMessageLoop(uiLoopType); michael@0: { michael@0: nsAutoPtr process; michael@0: michael@0: #ifdef XP_WIN michael@0: mozilla::ipc::windows::InitUIThread(); michael@0: #endif michael@0: michael@0: switch (aProcess) { michael@0: case GeckoProcessType_Default: michael@0: NS_RUNTIMEABORT("This makes no sense"); michael@0: break; michael@0: michael@0: case GeckoProcessType_Plugin: michael@0: process = new PluginProcessChild(parentHandle); michael@0: break; michael@0: michael@0: case GeckoProcessType_Content: { michael@0: process = new ContentProcess(parentHandle); michael@0: // If passed in grab the application path for xpcom init michael@0: nsCString appDir; michael@0: for (int idx = aArgc; idx > 0; idx--) { michael@0: if (aArgv[idx] && !strcmp(aArgv[idx], "-appdir")) { michael@0: appDir.Assign(nsDependentCString(aArgv[idx+1])); michael@0: static_cast(process.get())->SetAppDir(appDir); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: break; michael@0: michael@0: case GeckoProcessType_IPDLUnitTest: michael@0: #ifdef MOZ_IPDL_TESTS michael@0: process = new IPDLUnitTestProcessChild(parentHandle); michael@0: #else michael@0: NS_RUNTIMEABORT("rebuild with --enable-ipdl-tests"); michael@0: #endif michael@0: break; michael@0: michael@0: default: michael@0: NS_RUNTIMEABORT("Unknown main thread class"); michael@0: } michael@0: michael@0: if (!process->Init()) { michael@0: profiler_shutdown(); michael@0: NS_LogTerm(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Run the UI event loop on the main thread. michael@0: uiMessageLoop.MessageLoop::Run(); michael@0: michael@0: // Allow ProcessChild to clean up after itself before going out of michael@0: // scope and being deleted michael@0: process->CleanUp(); michael@0: mozilla::Omnijar::CleanUp(); michael@0: } michael@0: } michael@0: michael@0: profiler_shutdown(); michael@0: NS_LogTerm(); michael@0: return XRE_DeinitCommandLine(); michael@0: } michael@0: michael@0: MessageLoop* michael@0: XRE_GetIOMessageLoop() michael@0: { michael@0: if (sChildProcessType == GeckoProcessType_Default) { michael@0: return BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO); michael@0: } michael@0: return IOThreadChild::message_loop(); michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: class MainFunctionRunnable : public nsRunnable michael@0: { michael@0: public: michael@0: NS_DECL_NSIRUNNABLE michael@0: michael@0: MainFunctionRunnable(MainFunction aFunction, michael@0: void* aData) michael@0: : mFunction(aFunction), michael@0: mData(aData) michael@0: { michael@0: NS_ASSERTION(aFunction, "Don't give me a null pointer!"); michael@0: } michael@0: michael@0: private: michael@0: MainFunction mFunction; michael@0: void* mData; michael@0: }; michael@0: michael@0: } /* anonymous namespace */ michael@0: michael@0: NS_IMETHODIMP michael@0: MainFunctionRunnable::Run() michael@0: { michael@0: mFunction(mData); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: XRE_InitParentProcess(int aArgc, michael@0: char* aArgv[], michael@0: MainFunction aMainFunction, michael@0: void* aMainFunctionData) michael@0: { michael@0: NS_ENSURE_ARG_MIN(aArgc, 1); michael@0: NS_ENSURE_ARG_POINTER(aArgv); michael@0: NS_ENSURE_ARG_POINTER(aArgv[0]); michael@0: michael@0: ScopedXREEmbed embed; michael@0: michael@0: gArgc = aArgc; michael@0: gArgv = aArgv; michael@0: nsresult rv = XRE_InitCommandLine(gArgc, gArgv); michael@0: if (NS_FAILED(rv)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: { michael@0: embed.Start(); michael@0: michael@0: nsCOMPtr appShell(do_GetService(kAppShellCID)); michael@0: NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE); michael@0: michael@0: if (aMainFunction) { michael@0: nsCOMPtr runnable = michael@0: new MainFunctionRunnable(aMainFunction, aMainFunctionData); michael@0: NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: nsresult rv = NS_DispatchToCurrentThread(runnable); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // Do event loop michael@0: if (NS_FAILED(appShell->Run())) { michael@0: NS_WARNING("Failed to run appshell"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: return XRE_DeinitCommandLine(); michael@0: } michael@0: michael@0: #ifdef MOZ_IPDL_TESTS michael@0: //----------------------------------------------------------------------------- michael@0: // IPDL unit test michael@0: michael@0: int michael@0: XRE_RunIPDLTest(int aArgc, char** aArgv) michael@0: { michael@0: if (aArgc < 2) { michael@0: fprintf(stderr, "TEST-UNEXPECTED-FAIL | <---> | insufficient #args, need at least 2\n"); michael@0: return 1; michael@0: } michael@0: michael@0: void* data = reinterpret_cast(aArgv[aArgc-1]); michael@0: michael@0: nsresult rv = michael@0: XRE_InitParentProcess( michael@0: --aArgc, aArgv, mozilla::_ipdltest::IPDLUnitTestMain, data); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: michael@0: return 0; michael@0: } michael@0: #endif // ifdef MOZ_IPDL_TESTS michael@0: michael@0: nsresult michael@0: XRE_RunAppShell() michael@0: { michael@0: nsCOMPtr appShell(do_GetService(kAppShellCID)); michael@0: NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE); michael@0: #if defined(XP_MACOSX) michael@0: { michael@0: // In content processes that want XPCOM (and hence want michael@0: // AppShell), we usually run our hybrid event loop through michael@0: // MessagePump::Run(), by way of nsBaseAppShell::Run(). The michael@0: // Cocoa nsAppShell impl, however, implements its own Run() michael@0: // that's unaware of MessagePump. That's all rather suboptimal, michael@0: // but oddly enough not a problem... usually. michael@0: // michael@0: // The problem with this setup comes during startup. michael@0: // XPCOM-in-subprocesses depends on IPC, e.g. to init the pref michael@0: // service, so we have to init IPC first. But, IPC also michael@0: // indirectly kinda-depends on XPCOM, because MessagePump michael@0: // schedules work from off-main threads (e.g. IO thread) by michael@0: // using NS_DispatchToMainThread(). If the IO thread receives a michael@0: // Message from the parent before nsThreadManager is michael@0: // initialized, then DispatchToMainThread() will fail, although michael@0: // MessagePump will remember the task. This race condition michael@0: // isn't a problem when appShell->Run() ends up in michael@0: // MessagePump::Run(), because MessagePump will immediate see it michael@0: // has work to do. It *is* a problem when we end up in [NSApp michael@0: // run], because it's not aware that MessagePump has work that michael@0: // needs to be processed; that was supposed to be signaled by michael@0: // nsIRunnable(s). michael@0: // michael@0: // So instead of hacking Cocoa nsAppShell or rewriting the michael@0: // event-loop system, we compromise here by processing any tasks michael@0: // that might have been enqueued on MessagePump, *before* michael@0: // MessagePump::ScheduleWork was able to successfully michael@0: // DispatchToMainThread(). michael@0: MessageLoop* loop = MessageLoop::current(); michael@0: bool couldNest = loop->NestableTasksAllowed(); michael@0: michael@0: loop->SetNestableTasksAllowed(true); michael@0: loop->PostTask(FROM_HERE, new MessageLoop::QuitTask()); michael@0: loop->Run(); michael@0: michael@0: loop->SetNestableTasksAllowed(couldNest); michael@0: } michael@0: #endif // XP_MACOSX michael@0: return appShell->Run(); michael@0: } michael@0: michael@0: template<> michael@0: struct RunnableMethodTraits michael@0: { michael@0: static void RetainCallee(ContentChild* obj) { } michael@0: static void ReleaseCallee(ContentChild* obj) { } michael@0: }; michael@0: michael@0: void michael@0: XRE_ShutdownChildProcess() michael@0: { michael@0: NS_ABORT_IF_FALSE(MessageLoopForUI::current(), "Wrong thread!"); michael@0: michael@0: mozilla::DebugOnly ioLoop = XRE_GetIOMessageLoop(); michael@0: NS_ABORT_IF_FALSE(!!ioLoop, "Bad shutdown order"); michael@0: michael@0: // Quit() sets off the following chain of events michael@0: // (1) UI loop starts quitting michael@0: // (2) UI loop returns from Run() in XRE_InitChildProcess() michael@0: // (3) ProcessChild goes out of scope and terminates the IO thread michael@0: // (4) ProcessChild joins the IO thread michael@0: // (5) exit() michael@0: MessageLoop::current()->Quit(); michael@0: #if defined(XP_MACOSX) michael@0: nsCOMPtr appShell(do_GetService(kAppShellCID)); michael@0: if (appShell) { michael@0: // On Mac, we might be only above nsAppShell::Run(), not michael@0: // MessagePump::Run(). See XRE_RunAppShell(). To account for michael@0: // that case, we fire off an Exit() here. If we were indeed michael@0: // above MessagePump::Run(), this Exit() is just superfluous. michael@0: appShell->Exit(); michael@0: } michael@0: #endif // XP_MACOSX michael@0: } michael@0: michael@0: namespace { michael@0: ContentParent* gContentParent; //long-lived, manually refcounted michael@0: TestShellParent* GetOrCreateTestShellParent() michael@0: { michael@0: if (!gContentParent) { michael@0: nsRefPtr parent = ContentParent::GetNewOrUsed(); michael@0: parent.forget(&gContentParent); michael@0: } else if (!gContentParent->IsAlive()) { michael@0: return nullptr; michael@0: } michael@0: TestShellParent* tsp = gContentParent->GetTestShellSingleton(); michael@0: if (!tsp) { michael@0: tsp = gContentParent->CreateTestShell(); michael@0: } michael@0: return tsp; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: XRE_SendTestShellCommand(JSContext* aCx, michael@0: JSString* aCommand, michael@0: void* aCallback) michael@0: { michael@0: JS::RootedString cmd(aCx, aCommand); michael@0: TestShellParent* tsp = GetOrCreateTestShellParent(); michael@0: NS_ENSURE_TRUE(tsp, false); michael@0: michael@0: nsDependentJSString command; michael@0: NS_ENSURE_TRUE(command.init(aCx, cmd), false); michael@0: michael@0: if (!aCallback) { michael@0: return tsp->SendExecuteCommand(command); michael@0: } michael@0: michael@0: TestShellCommandParent* callback = static_cast( michael@0: tsp->SendPTestShellCommandConstructor(command)); michael@0: NS_ENSURE_TRUE(callback, false); michael@0: michael@0: JS::Value callbackVal = *reinterpret_cast(aCallback); michael@0: NS_ENSURE_TRUE(callback->SetCallback(aCx, callbackVal), false); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: XRE_ShutdownTestShell() michael@0: { michael@0: if (!gContentParent) { michael@0: return true; michael@0: } michael@0: bool ret = true; michael@0: if (gContentParent->IsAlive()) { michael@0: ret = gContentParent->DestroyTestShell( michael@0: gContentParent->GetTestShellSingleton()); michael@0: } michael@0: NS_RELEASE(gContentParent); michael@0: return ret; michael@0: } michael@0: michael@0: #ifdef MOZ_X11 michael@0: void michael@0: XRE_InstallX11ErrorHandler() michael@0: { michael@0: InstallX11ErrorHandler(); michael@0: } michael@0: #endif michael@0: michael@0: #ifdef XP_WIN michael@0: static WindowsEnvironmentType michael@0: sWindowsEnvironmentType = WindowsEnvironmentType_Desktop; michael@0: michael@0: void michael@0: SetWindowsEnvironment(WindowsEnvironmentType aEnvID) michael@0: { michael@0: sWindowsEnvironmentType = aEnvID; michael@0: } michael@0: michael@0: WindowsEnvironmentType michael@0: XRE_GetWindowsEnvironment() michael@0: { michael@0: return sWindowsEnvironmentType; michael@0: } michael@0: #endif // XP_WIN michael@0: