1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is Mozilla libXUL embedding.
16 * The Initial Developer of the Original Code is
17 * Benjamin Smedberg <benjamin@smedbergs.us>
19 * Portions created by the Initial Developer are Copyright (C) 2005
20 * the Mozilla Foundation. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #if defined(MOZ_WIDGET_QT)
39 #include "nsQAppInstance.h"
43 #include "base/basictypes.h"
46 #include "nsXULAppAPI.h"
49 #if defined(MOZ_WIDGET_GTK2)
55 #include "nsIAppShell.h"
56 #include "nsIAppStartupNotifier.h"
57 #include "nsIDirectoryService.h"
58 #include "nsILocalFile.h"
59 #include "nsIToolkitChromeRegistry.h"
60 #include "nsIToolkitProfile.h"
70 #include "nsAppDirectoryServiceDefs.h"
71 #include "nsAppRunner.h"
72 #include "nsAutoRef.h"
73 #include "nsDirectoryServiceDefs.h"
74 #include "nsExceptionHandler.h"
76 #include "nsThreadUtils.h"
77 #include "nsJSUtils.h"
78 #include "nsWidgetsCID.h"
79 #include "nsXREDirProvider.h"
81 #include "mozilla/Omnijar.h"
83 #if defined(XP_MACOSX)
84 #include "chrome/common/mach_ipc_mac.h"
86 #include "nsX11ErrorHandler.h"
87 #include "base/at_exit.h"
88 #include "base/command_line.h"
89 #include "base/message_loop.h"
90 #include "base/process_util.h"
91 #include "chrome/common/child_process.h"
92 #include "chrome/common/notification_service.h"
94 #include "mozilla/ipc/BrowserProcessSubThread.h"
95 #include "mozilla/ipc/GeckoChildProcessHost.h"
96 #include "mozilla/ipc/IOThreadChild.h"
97 #include "mozilla/ipc/ProcessChild.h"
98 #include "ScopedXREEmbed.h"
100 #include "mozilla/jetpack/JetpackProcessChild.h"
101 #include "mozilla/plugins/PluginProcessChild.h"
102 #include "mozilla/dom/ContentProcess.h"
103 #include "mozilla/dom/ContentParent.h"
104 #include "mozilla/dom/ContentChild.h"
106 #include "mozilla/jsipc/ContextWrapperParent.h"
108 #include "mozilla/ipc/TestShellParent.h"
109 #include "mozilla/ipc/XPCShellEnvironment.h"
111 #ifdef MOZ_IPDL_TESTS
112 #include "mozilla/_ipdltest/IPDLUnitTests.h"
113 #include "mozilla/_ipdltest/IPDLUnitTestProcessChild.h"
115 using mozilla::_ipdltest::IPDLUnitTestProcessChild
;
116 #endif // ifdef MOZ_IPDL_TESTS
118 using mozilla::ipc::BrowserProcessSubThread
;
119 using mozilla::ipc::GeckoChildProcessHost
;
120 using mozilla::ipc::IOThreadChild
;
121 using mozilla::ipc::ProcessChild
;
122 using mozilla::ipc::ScopedXREEmbed
;
124 using mozilla::jetpack::JetpackProcessChild
;
125 using mozilla::plugins::PluginProcessChild
;
126 using mozilla::dom::ContentProcess
;
127 using mozilla::dom::ContentParent
;
128 using mozilla::dom::ContentChild
;
130 using mozilla::jsipc::PContextWrapperParent
;
131 using mozilla::jsipc::ContextWrapperParent
;
133 using mozilla::ipc::TestShellParent
;
134 using mozilla::ipc::TestShellCommandParent
;
135 using mozilla::ipc::XPCShellEnvironment
;
137 using mozilla::startup::sChildProcessType
;
140 static NS_DEFINE_CID(kAppShellCID
, NS_APPSHELL_CID
);
143 static const PRUnichar kShellLibraryName
[] = L
"shell32.dll";
147 XRE_LockProfileDirectory(nsILocalFile
* aDirectory
,
148 nsISupports
* *aLockObject
)
150 nsCOMPtr
<nsIProfileLock
> lock
;
152 nsresult rv
= NS_LockProfilePath(aDirectory
, nsnull
, nsnull
,
153 getter_AddRefs(lock
));
154 if (NS_SUCCEEDED(rv
))
155 NS_ADDREF(*aLockObject
= lock
);
160 static PRInt32 sInitCounter
;
163 XRE_InitEmbedding2(nsILocalFile
*aLibXULDirectory
,
164 nsILocalFile
*aAppDirectory
,
165 nsIDirectoryServiceProvider
*aAppDirProvider
)
167 // Initialize some globals to make nsXREDirProvider happy
168 static char* kNullCommandLine
[] = { nsnull
};
169 gArgv
= kNullCommandLine
;
172 NS_ENSURE_ARG(aLibXULDirectory
);
174 if (++sInitCounter
> 1) // XXXbsmedberg is this really the right solution?
178 aAppDirectory
= aLibXULDirectory
;
182 new nsXREDirProvider
; // This sets gDirServiceProvider
183 if (!gDirServiceProvider
)
184 return NS_ERROR_OUT_OF_MEMORY
;
186 rv
= gDirServiceProvider
->Initialize(aAppDirectory
, aLibXULDirectory
,
191 rv
= NS_InitXPCOM2(nsnull
, aAppDirectory
, gDirServiceProvider
);
195 // We do not need to autoregister components here. The CheckCompatibility()
196 // bits in nsAppRunner.cpp check for an invalidation flag in
197 // compatibility.ini.
198 // If the app wants to autoregister every time (for instance, if it's debug),
199 // it can do so after we return from this function.
201 nsCOMPtr
<nsIObserver
> startupNotifier
202 (do_CreateInstance(NS_APPSTARTUPNOTIFIER_CONTRACTID
));
203 if (!startupNotifier
)
204 return NS_ERROR_FAILURE
;
206 startupNotifier
->Observe(nsnull
, APPSTARTUP_TOPIC
, nsnull
);
214 NS_ASSERTION(gDirServiceProvider
, "XRE_InitEmbedding was not called!");
215 gDirServiceProvider
->DoStartup();
221 if (--sInitCounter
!= 0)
224 NS_ASSERTION(gDirServiceProvider
,
225 "XRE_TermEmbedding without XRE_InitEmbedding");
227 gDirServiceProvider
->DoShutdown();
228 NS_ShutdownXPCOM(nsnull
);
229 delete gDirServiceProvider
;
233 XRE_ChildProcessTypeToString(GeckoProcessType aProcessType
)
235 return (aProcessType
< GeckoProcessType_End
) ?
236 kGeckoProcessTypeString
[aProcessType
] : nsnull
;
240 XRE_StringToChildProcessType(const char* aProcessTypeString
)
243 i
< (int) NS_ARRAY_LENGTH(kGeckoProcessTypeString
);
245 if (!strcmp(kGeckoProcessTypeString
[i
], aProcessTypeString
)) {
246 return static_cast<GeckoProcessType
>(i
);
249 return GeckoProcessType_Invalid
;
255 GeckoProcessType sChildProcessType
= GeckoProcessType_Default
;
259 #if defined(MOZ_CRASHREPORTER)
260 // FIXME/bug 539522: this out-of-place function is stuck here because
261 // IPDL wants access to this crashreporter interface, and
262 // crashreporter is built in such a way to make that awkward
264 XRE_TakeMinidumpForChild(PRUint32 aChildPid
, nsILocalFile
** aDump
)
266 return CrashReporter::TakeMinidumpForChild(aChildPid
, aDump
);
270 XRE_SetRemoteExceptionHandler(const char* aPipe
/*= 0*/)
272 #if defined(XP_WIN) || defined(XP_MACOSX)
273 return CrashReporter::SetRemoteExceptionHandler(nsDependentCString(aPipe
));
274 #elif defined(OS_LINUX)
275 return CrashReporter::SetRemoteExceptionHandler();
277 # error "OOP crash reporter unsupported on this platform"
280 #endif // if defined(MOZ_CRASHREPORTER)
284 SetTaskbarGroupId(const nsString
& aId
)
286 typedef HRESULT (WINAPI
* SetCurrentProcessExplicitAppUserModelIDPtr
)(PCWSTR AppID
);
288 SetCurrentProcessExplicitAppUserModelIDPtr funcAppUserModelID
= nsnull
;
290 HMODULE hDLL
= ::LoadLibraryW(kShellLibraryName
);
292 funcAppUserModelID
= (SetCurrentProcessExplicitAppUserModelIDPtr
)
293 GetProcAddress(hDLL
, "SetCurrentProcessExplicitAppUserModelID");
295 if (!funcAppUserModelID
) {
300 if (FAILED(funcAppUserModelID(aId
.get()))) {
301 NS_WARNING("SetCurrentProcessExplicitAppUserModelID failed for child process.");
310 XRE_InitChildProcess(int aArgc
,
312 GeckoProcessType aProcess
)
314 NS_ENSURE_ARG_MIN(aArgc
, 2);
315 NS_ENSURE_ARG_POINTER(aArgv
);
316 NS_ENSURE_ARG_POINTER(aArgv
[0]);
318 sChildProcessType
= aProcess
;
320 // Complete 'task_t' exchange for Mac OS X. This structure has the same size
321 // regardless of architecture so we don't have any cross-arch issues here.
325 const char* const mach_port_name
= aArgv
[--aArgc
];
327 const int kTimeoutMs
= 1000;
329 MachSendMessage
child_message(0);
330 if (!child_message
.AddDescriptor(mach_task_self())) {
331 NS_WARNING("child AddDescriptor(mach_task_self()) failed.");
335 ReceivePort child_recv_port
;
336 mach_port_t raw_child_recv_port
= child_recv_port
.GetPort();
337 if (!child_message
.AddDescriptor(raw_child_recv_port
)) {
338 NS_WARNING("Adding descriptor to message failed");
342 MachPortSender
child_sender(mach_port_name
);
343 kern_return_t err
= child_sender
.SendMessage(child_message
, kTimeoutMs
);
344 if (err
!= KERN_SUCCESS
) {
345 NS_WARNING("child SendMessage() failed");
349 MachReceiveMessage parent_message
;
350 err
= child_recv_port
.WaitForMessage(&parent_message
, kTimeoutMs
);
351 if (err
!= KERN_SUCCESS
) {
352 NS_WARNING("child WaitForMessage() failed");
356 if (parent_message
.GetTranslatedPort(0) == MACH_PORT_NULL
) {
357 NS_WARNING("child GetTranslatedPort(0) failed");
360 err
= task_set_bootstrap_port(mach_task_self(),
361 parent_message
.GetTranslatedPort(0));
362 if (err
!= KERN_SUCCESS
) {
363 NS_WARNING("child task_set_bootstrap_port() failed");
368 #if defined(MOZ_CRASHREPORTER)
371 const char* const crashReporterArg
= aArgv
[--aArgc
];
373 # if defined(XP_WIN) || defined(XP_MACOSX)
374 // on windows and mac, |crashReporterArg| is the named pipe on which the
375 // server is listening for requests, or "-" if crash reporting is
377 if (0 != strcmp("-", crashReporterArg
)
378 && !XRE_SetRemoteExceptionHandler(crashReporterArg
))
380 # elif defined(OS_LINUX)
381 // on POSIX, |crashReporterArg| is "true" if crash reporting is
382 // enabled, false otherwise
383 if (0 != strcmp("false", crashReporterArg
)
384 && !XRE_SetRemoteExceptionHandler(NULL
))
387 # error "OOP crash reporting unsupported on this platform"
389 #endif // if defined(MOZ_CRASHREPORTER)
394 SetupErrorHandling(aArgv
[0]);
396 #if defined(MOZ_WIDGET_GTK2)
400 #if defined(MOZ_WIDGET_QT)
401 nsQAppInstance::AddRef();
404 if (PR_GetEnv("MOZ_DEBUG_CHILD_PROCESS")) {
406 printf("\n\nCHILDCHILDCHILDCHILD\n debug me @%d\n\n", getpid());
408 #elif defined(OS_WIN)
409 printf("\n\nCHILDCHILDCHILDCHILD\n debug me @%d\n\n", _getpid());
414 // child processes launched by GeckoChildProcessHost get this magic
415 // argument appended to their command lines
416 const char* const parentPIDString
= aArgv
[aArgc
-1];
417 NS_ABORT_IF_FALSE(parentPIDString
, "NULL parent PID");
421 base::ProcessId parentPID
= strtol(parentPIDString
, &end
, 10);
422 NS_ABORT_IF_FALSE(!*end
, "invalid parent PID");
424 base::ProcessHandle parentHandle
;
425 bool ok
= base::OpenProcessHandle(parentPID
, &parentHandle
);
426 NS_ABORT_IF_FALSE(ok
, "can't open handle to parent");
429 // On Win7+, register the application user model id passed in by
430 // parent. This insures windows created by the container properly
431 // group with the parent app on the Win7 taskbar.
432 const char* const appModelUserId
= aArgv
[aArgc
-1];
434 if (appModelUserId
) {
435 // '-' implies no support
436 if (*appModelUserId
!= '-') {
438 appId
.AssignWithConversion(nsDependentCString(appModelUserId
));
439 // The version string is encased in quotes
440 appId
.Trim(NS_LITERAL_CSTRING("\"").get());
442 SetTaskbarGroupId(appId
);
447 base::AtExitManager exitManager
;
448 NotificationService notificationService
;
452 int rv
= XRE_InitCommandLine(aArgc
, aArgv
);
455 return NS_ERROR_FAILURE
;
458 MessageLoop::Type uiLoopType
;
460 case GeckoProcessType_Content
:
461 // Content processes need the XPCOM/chromium frankenventloop
462 uiLoopType
= MessageLoop::TYPE_MOZILLA_CHILD
;
465 uiLoopType
= MessageLoop::TYPE_UI
;
470 // This is a lexical scope for the MessageLoop below. We want it
471 // to go out of scope before NS_LogTerm() so that we don't get
472 // spurious warnings about XPCOM objects being destroyed from a
475 // Associate this thread with a UI MessageLoop
476 MessageLoop
uiMessageLoop(uiLoopType
);
478 nsAutoPtr
<ProcessChild
> process
;
481 case GeckoProcessType_Default
:
482 NS_RUNTIMEABORT("This makes no sense");
485 case GeckoProcessType_Plugin
:
486 process
= new PluginProcessChild(parentHandle
);
489 case GeckoProcessType_Content
:
490 process
= new ContentProcess(parentHandle
);
493 case GeckoProcessType_Jetpack
:
494 process
= new JetpackProcessChild(parentHandle
);
497 case GeckoProcessType_IPDLUnitTest
:
498 #ifdef MOZ_IPDL_TESTS
499 process
= new IPDLUnitTestProcessChild(parentHandle
);
501 NS_RUNTIMEABORT("rebuild with --enable-ipdl-tests");
506 NS_RUNTIMEABORT("Unknown main thread class");
509 if (!process
->Init()) {
511 return NS_ERROR_FAILURE
;
514 // Run the UI event loop on the main thread.
515 uiMessageLoop
.MessageLoop::Run();
517 // Allow ProcessChild to clean up after itself before going out of
518 // scope and being deleted
521 mozilla::SetOmnijar(nsnull
);
527 return XRE_DeinitCommandLine();
531 XRE_GetIOMessageLoop()
533 if (sChildProcessType
== GeckoProcessType_Default
) {
534 return BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO
);
536 return IOThreadChild::message_loop();
541 class MainFunctionRunnable
: public nsRunnable
546 MainFunctionRunnable(MainFunction aFunction
,
548 : mFunction(aFunction
),
551 NS_ASSERTION(aFunction
, "Don't give me a null pointer!");
555 MainFunction mFunction
;
559 } /* anonymous namespace */
562 MainFunctionRunnable::Run()
569 XRE_InitParentProcess(int aArgc
,
571 MainFunction aMainFunction
,
572 void* aMainFunctionData
)
574 NS_ENSURE_ARG_MIN(aArgc
, 1);
575 NS_ENSURE_ARG_POINTER(aArgv
);
576 NS_ENSURE_ARG_POINTER(aArgv
[0]);
580 int rv
= XRE_InitCommandLine(gArgc
, gArgv
);
582 return NS_ERROR_FAILURE
;
584 ScopedXREEmbed embed
;
589 nsCOMPtr
<nsIAppShell
> appShell(do_GetService(kAppShellCID
));
590 NS_ENSURE_TRUE(appShell
, NS_ERROR_FAILURE
);
593 nsCOMPtr
<nsIRunnable
> runnable
=
594 new MainFunctionRunnable(aMainFunction
, aMainFunctionData
);
595 NS_ENSURE_TRUE(runnable
, NS_ERROR_OUT_OF_MEMORY
);
597 nsresult rv
= NS_DispatchToCurrentThread(runnable
);
598 NS_ENSURE_SUCCESS(rv
, rv
);
602 if (NS_FAILED(appShell
->Run())) {
603 NS_WARNING("Failed to run appshell");
604 return NS_ERROR_FAILURE
;
608 return XRE_DeinitCommandLine();
611 #ifdef MOZ_IPDL_TESTS
612 //-----------------------------------------------------------------------------
616 XRE_RunIPDLTest(int aArgc
, char** aArgv
)
619 fprintf(stderr
, "TEST-UNEXPECTED-FAIL | <---> | insufficient #args, need at least 2\n");
623 void* data
= reinterpret_cast<void*>(aArgv
[aArgc
-1]);
626 XRE_InitParentProcess(
627 --aArgc
, aArgv
, mozilla::_ipdltest::IPDLUnitTestMain
, data
);
628 NS_ENSURE_SUCCESS(rv
, 1);
632 #endif // ifdef MOZ_IPDL_TESTS
637 nsCOMPtr
<nsIAppShell
> appShell(do_GetService(kAppShellCID
));
638 #if defined(XP_MACOSX)
640 // In content processes that want XPCOM (and hence want
641 // AppShell), we usually run our hybrid event loop through
642 // MessagePump::Run(), by way of nsBaseAppShell::Run(). The
643 // Cocoa nsAppShell impl, however, implements its own Run()
644 // that's unaware of MessagePump. That's all rather suboptimal,
645 // but oddly enough not a problem... usually.
647 // The problem with this setup comes during startup.
648 // XPCOM-in-subprocesses depends on IPC, e.g. to init the pref
649 // service, so we have to init IPC first. But, IPC also
650 // indirectly kinda-depends on XPCOM, because MessagePump
651 // schedules work from off-main threads (e.g. IO thread) by
652 // using NS_DispatchToMainThread(). If the IO thread receives a
653 // Message from the parent before nsThreadManager is
654 // initialized, then DispatchToMainThread() will fail, although
655 // MessagePump will remember the task. This race condition
656 // isn't a problem when appShell->Run() ends up in
657 // MessagePump::Run(), because MessagePump will immediate see it
658 // has work to do. It *is* a problem when we end up in [NSApp
659 // run], because it's not aware that MessagePump has work that
660 // needs to be processed; that was supposed to be signaled by
663 // So instead of hacking Cocoa nsAppShell or rewriting the
664 // event-loop system, we compromise here by processing any tasks
665 // that might have been enqueued on MessagePump, *before*
666 // MessagePump::ScheduleWork was able to successfully
667 // DispatchToMainThread().
668 MessageLoop
* loop
= MessageLoop::current();
669 bool couldNest
= loop
->NestableTasksAllowed();
671 loop
->SetNestableTasksAllowed(true);
672 loop
->PostTask(FROM_HERE
, new MessageLoop::QuitTask());
675 loop
->SetNestableTasksAllowed(couldNest
);
678 return appShell
->Run();
682 struct RunnableMethodTraits
<ContentChild
>
684 static void RetainCallee(ContentChild
* obj
) { }
685 static void ReleaseCallee(ContentChild
* obj
) { }
689 XRE_ShutdownChildProcess()
691 NS_ABORT_IF_FALSE(MessageLoopForUI::current(), "Wrong thread!");
693 MessageLoop
* ioLoop
= XRE_GetIOMessageLoop();
694 NS_ABORT_IF_FALSE(!!ioLoop
, "Bad shutdown order");
696 // Quit() sets off the following chain of events
697 // (1) UI loop starts quitting
698 // (2) UI loop returns from Run() in XRE_InitChildProcess()
699 // (3) ProcessChild goes out of scope and terminates the IO thread
700 // (4) ProcessChild joins the IO thread
702 MessageLoop::current()->Quit();
703 #if defined(XP_MACOSX)
704 nsCOMPtr
<nsIAppShell
> appShell(do_GetService(kAppShellCID
));
706 // On Mac, we might be only above nsAppShell::Run(), not
707 // MessagePump::Run(). See XRE_RunAppShell(). To account for
708 // that case, we fire off an Exit() here. If we were indeed
709 // above MessagePump::Run(), this Exit() is just superfluous.
716 TestShellParent
* gTestShellParent
= nsnull
;
717 TestShellParent
* GetOrCreateTestShellParent()
719 if (!gTestShellParent
) {
720 ContentParent
* parent
= ContentParent::GetSingleton();
721 NS_ENSURE_TRUE(parent
, nsnull
);
722 gTestShellParent
= parent
->CreateTestShell();
723 NS_ENSURE_TRUE(gTestShellParent
, nsnull
);
725 return gTestShellParent
;
730 XRE_SendTestShellCommand(JSContext
* aCx
,
734 TestShellParent
* tsp
= GetOrCreateTestShellParent();
735 NS_ENSURE_TRUE(tsp
, false);
737 nsDependentJSString command
;
738 NS_ENSURE_TRUE(command
.init(aCx
, aCommand
), NS_ERROR_FAILURE
);
741 return tsp
->SendExecuteCommand(command
);
744 TestShellCommandParent
* callback
= static_cast<TestShellCommandParent
*>(
745 tsp
->SendPTestShellCommandConstructor(command
));
746 NS_ENSURE_TRUE(callback
, false);
748 jsval callbackVal
= *reinterpret_cast<jsval
*>(aCallback
);
749 NS_ENSURE_TRUE(callback
->SetCallback(aCx
, callbackVal
), false);
755 XRE_GetChildGlobalObject(JSContext
* aCx
, JSObject
** aGlobalP
)
757 TestShellParent
* tsp
= GetOrCreateTestShellParent();
758 return tsp
&& tsp
->GetGlobalJSObject(aCx
, aGlobalP
);
762 XRE_ShutdownTestShell()
764 if (!gTestShellParent
)
766 return ContentParent::GetSingleton()->DestroyTestShell(gTestShellParent
);
771 XRE_InstallX11ErrorHandler()
773 InstallX11ErrorHandler();