diff -r 000000000000 -r 6474c204b198 toolkit/xre/nsEmbedFunctions.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/toolkit/xre/nsEmbedFunctions.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,804 @@ +/* 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" + +#if defined(MOZ_WIDGET_QT) +#include "nsQAppInstance.h" +#endif + +#include "base/basictypes.h" + +#include "nsXULAppAPI.h" + +#include +#if defined(MOZ_WIDGET_GTK) +#include +#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 +#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 "mozilla/Omnijar.h" +#if defined(XP_MACOSX) +#include "nsVersionComparator.h" +#include "chrome/common/mach_ipc_mac.h" +#endif +#include "nsX11ErrorHandler.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" +#include "chrome/common/notification_service.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 "GeckoProfiler.h" + +#ifdef MOZ_IPDL_TESTS +#include "mozilla/_ipdltest/IPDLUnitTests.h" +#include "mozilla/_ipdltest/IPDLUnitTestProcessChild.h" + +using mozilla::_ipdltest::IPDLUnitTestProcessChild; +#endif // ifdef MOZ_IPDL_TESTS + +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::plugins::PluginProcessChild; +using mozilla::dom::ContentProcess; +using mozilla::dom::ContentParent; +using mozilla::dom::ContentChild; + +using mozilla::ipc::TestShellParent; +using mozilla::ipc::TestShellCommandParent; +using mozilla::ipc::XPCShellEnvironment; + +using mozilla::startup::sChildProcessType; + +static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); + +#ifdef XP_WIN +static const wchar_t kShellLibraryName[] = L"shell32.dll"; +#endif + +nsresult +XRE_LockProfileDirectory(nsIFile* aDirectory, + nsISupports* *aLockObject) +{ + nsCOMPtr 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 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] : nullptr; +} + +GeckoProcessType +XRE_StringToChildProcessType(const char* aProcessTypeString) +{ + for (int i = 0; + i < (int) ArrayLength(kGeckoProcessTypeString); + ++i) { + if (!strcmp(kGeckoProcessTypeString[i], aProcessTypeString)) { + return static_cast(i); + } + } + return GeckoProcessType_Invalid; +} + +namespace mozilla { +namespace startup { +GeckoProcessType sChildProcessType = GeckoProcessType_Default; +} +} + +#if defined(MOZ_CRASHREPORTER) +// 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 +XRE_SetRemoteExceptionHandler(const char* aPipe/*= 0*/) +{ +#if defined(XP_WIN) || defined(XP_MACOSX) + return CrashReporter::SetRemoteExceptionHandler(nsDependentCString(aPipe)); +#elif defined(OS_LINUX) + return CrashReporter::SetRemoteExceptionHandler(); +#else +# error "OOP crash reporter unsupported on this platform" +#endif +} +#endif // if defined(MOZ_CRASHREPORTER) + +#if defined(XP_WIN) +void +SetTaskbarGroupId(const nsString& aId) +{ + typedef HRESULT (WINAPI * SetCurrentProcessExplicitAppUserModelIDPtr)(PCWSTR AppID); + + SetCurrentProcessExplicitAppUserModelIDPtr funcAppUserModelID = nullptr; + + HMODULE hDLL = ::LoadLibraryW(kShellLibraryName); + + funcAppUserModelID = (SetCurrentProcessExplicitAppUserModelIDPtr) + GetProcAddress(hDLL, "SetCurrentProcessExplicitAppUserModelID"); + + if (!funcAppUserModelID) { + ::FreeLibrary(hDLL); + return; + } + + if (FAILED(funcAppUserModelID(aId.get()))) { + NS_WARNING("SetCurrentProcessExplicitAppUserModelID failed for child process."); + } + + if (hDLL) + ::FreeLibrary(hDLL); +} +#endif + +nsresult +XRE_InitChildProcess(int aArgc, + char* aArgv[], + GeckoProcessType aProcess) +{ + NS_ENSURE_ARG_MIN(aArgc, 2); + NS_ENSURE_ARG_POINTER(aArgv); + NS_ENSURE_ARG_POINTER(aArgv[0]); + +#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); + } +#endif + + char aLocal; + profiler_init(&aLocal); + PROFILER_LABEL("Startup", "XRE_InitChildProcess"); + + sChildProcessType = aProcess; + + // 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(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(raw_child_recv_port)) { + 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 (err != KERN_SUCCESS) { + NS_WARNING("child task_set_bootstrap_port() failed"); + return NS_ERROR_FAILURE; + } +#endif + + SetupErrorHandling(aArgv[0]); + +#if defined(MOZ_CRASHREPORTER) + if (aArgc < 1) + return NS_ERROR_FAILURE; + const char* const crashReporterArg = aArgv[--aArgc]; + +# if defined(XP_WIN) || 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(OS_LINUX) + // 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"); + } +# else +# error "OOP crash reporting unsupported on this platform" +# endif +#endif // if defined(MOZ_CRASHREPORTER) + + gArgv = aArgv; + gArgc = aArgc; + +#if defined(MOZ_WIDGET_GTK) + g_thread_init(nullptr); +#endif + +#if defined(MOZ_WIDGET_QT) + nsQAppInstance::AddRef(); +#endif + + if (PR_GetEnv("MOZ_DEBUG_CHILD_PROCESS")) { +#ifdef OS_POSIX + printf("\n\nCHILDCHILDCHILDCHILD\n debug me @ %d\n\n", getpid()); + sleep(30); +#elif defined(OS_WIN) + // Windows has a decent JIT debugging story, so NS_DebugBreak does the + // right thing. + NS_DebugBreak(NS_DEBUG_BREAK, + "Invoking NS_DebugBreak() to debug child process", + nullptr, __FILE__, __LINE__); +#endif + } + + // child processes launched by GeckoChildProcessHost get this magic + // argument appended to their command lines + const char* const parentPIDString = aArgv[aArgc-1]; + NS_ABORT_IF_FALSE(parentPIDString, "NULL parent PID"); + --aArgc; + + char* end = 0; + base::ProcessId parentPID = strtol(parentPIDString, &end, 10); + NS_ABORT_IF_FALSE(!*end, "invalid parent PID"); + + base::ProcessHandle parentHandle; + mozilla::DebugOnly ok = base::OpenProcessHandle(parentPID, &parentHandle); + NS_ABORT_IF_FALSE(ok, "can't open handle to parent"); + +#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; + appId.AssignWithConversion(nsDependentCString(appModelUserId)); + // The version string is encased in quotes + appId.Trim(NS_LITERAL_CSTRING("\"").get()); + // Set the id + SetTaskbarGroupId(appId); + } + } +#endif + + base::AtExitManager exitManager; + NotificationService notificationService; + + NS_LogInit(); + + nsresult rv = XRE_InitCommandLine(aArgc, aArgv); + if (NS_FAILED(rv)) { + profiler_shutdown(); + NS_LogTerm(); + return NS_ERROR_FAILURE; + } + + MessageLoop::Type uiLoopType; + switch (aProcess) { + case GeckoProcessType_Content: + // Content processes need the XPCOM/chromium frankenventloop + uiLoopType = MessageLoop::TYPE_MOZILLA_CHILD; + 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 process; + +#ifdef XP_WIN + mozilla::ipc::windows::InitUIThread(); +#endif + + switch (aProcess) { + case GeckoProcessType_Default: + NS_RUNTIMEABORT("This makes no sense"); + break; + + case GeckoProcessType_Plugin: + process = new PluginProcessChild(parentHandle); + break; + + case GeckoProcessType_Content: { + process = new ContentProcess(parentHandle); + // If passed in grab the application path for xpcom init + nsCString appDir; + for (int idx = aArgc; idx > 0; idx--) { + if (aArgv[idx] && !strcmp(aArgv[idx], "-appdir")) { + appDir.Assign(nsDependentCString(aArgv[idx+1])); + static_cast(process.get())->SetAppDir(appDir); + break; + } + } + } + break; + + case GeckoProcessType_IPDLUnitTest: +#ifdef MOZ_IPDL_TESTS + process = new IPDLUnitTestProcessChild(parentHandle); +#else + NS_RUNTIMEABORT("rebuild with --enable-ipdl-tests"); +#endif + break; + + default: + NS_RUNTIMEABORT("Unknown main thread class"); + } + + if (!process->Init()) { + profiler_shutdown(); + NS_LogTerm(); + return NS_ERROR_FAILURE; + } + + // 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(); + } + } + + profiler_shutdown(); + NS_LogTerm(); + return XRE_DeinitCommandLine(); +} + +MessageLoop* +XRE_GetIOMessageLoop() +{ + if (sChildProcessType == GeckoProcessType_Default) { + return BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO); + } + return IOThreadChild::message_loop(); +} + +namespace { + +class MainFunctionRunnable : public nsRunnable +{ +public: + NS_DECL_NSIRUNNABLE + + MainFunctionRunnable(MainFunction aFunction, + void* aData) + : 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]); + + ScopedXREEmbed embed; + + gArgc = aArgc; + gArgv = aArgv; + nsresult rv = XRE_InitCommandLine(gArgc, gArgv); + if (NS_FAILED(rv)) + return NS_ERROR_FAILURE; + + { + embed.Start(); + + nsCOMPtr appShell(do_GetService(kAppShellCID)); + NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE); + + if (aMainFunction) { + nsCOMPtr 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(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 appShell(do_GetService(kAppShellCID)); + NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE); +#if defined(XP_MACOSX) + { + // 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); + loop->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + loop->Run(); + + loop->SetNestableTasksAllowed(couldNest); + } +#endif // XP_MACOSX + return appShell->Run(); +} + +template<> +struct RunnableMethodTraits +{ + static void RetainCallee(ContentChild* obj) { } + static void ReleaseCallee(ContentChild* obj) { } +}; + +void +XRE_ShutdownChildProcess() +{ + NS_ABORT_IF_FALSE(MessageLoopForUI::current(), "Wrong thread!"); + + mozilla::DebugOnly ioLoop = XRE_GetIOMessageLoop(); + NS_ABORT_IF_FALSE(!!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) { + nsRefPtr parent = ContentParent::GetNewOrUsed(); + parent.forget(&gContentParent); + } else if (!gContentParent->IsAlive()) { + return nullptr; + } + TestShellParent* tsp = gContentParent->GetTestShellSingleton(); + if (!tsp) { + tsp = gContentParent->CreateTestShell(); + } + return tsp; +} +} + +bool +XRE_SendTestShellCommand(JSContext* aCx, + JSString* aCommand, + void* aCallback) +{ + JS::RootedString cmd(aCx, aCommand); + TestShellParent* tsp = GetOrCreateTestShellParent(); + NS_ENSURE_TRUE(tsp, false); + + nsDependentJSString 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); + + JS::Value callbackVal = *reinterpret_cast(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() +{ + InstallX11ErrorHandler(); +} +#endif + +#ifdef XP_WIN +static WindowsEnvironmentType +sWindowsEnvironmentType = WindowsEnvironmentType_Desktop; + +void +SetWindowsEnvironment(WindowsEnvironmentType aEnvID) +{ + sWindowsEnvironmentType = aEnvID; +} + +WindowsEnvironmentType +XRE_GetWindowsEnvironment() +{ + return sWindowsEnvironmentType; +} +#endif // XP_WIN +