Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(64)

Side by Side Diff: content/app/content_main_runner.cc

Issue 9190018: Support sharing of ContentMain and BrowserMain code with embedded use cases. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
jam 2012/01/12 18:07:18 can you "svn copy" content_main.cc to this file so
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/app/content_main_runner.h"
6
7 #include "base/at_exit.h"
8 #include "base/command_line.h"
9 #include "base/debug/debugger.h"
10 #include "base/debug/trace_event.h"
11 #include "base/i18n/icu_util.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/metrics/stats_table.h"
15 #include "base/process_util.h"
16 #include "base/stringprintf.h"
17 #include "base/string_number_conversions.h"
18 #include "content/browser/browser_main.h"
19 #include "content/common/set_process_title.h"
20 #include "content/public/app/content_main_delegate.h"
21 #include "content/public/app/startup_helper_win.h"
22 #include "content/public/common/content_client.h"
23 #include "content/public/common/content_constants.h"
24 #include "content/public/common/content_paths.h"
25 #include "content/public/common/content_switches.h"
26 #include "content/public/common/main_function_params.h"
27 #include "content/public/common/sandbox_init.h"
28 #include "crypto/nss_util.h"
29 #include "ipc/ipc_switches.h"
30 #include "sandbox/src/sandbox_types.h"
31 #include "ui/base/ui_base_switches.h"
32 #include "ui/base/ui_base_paths.h"
33 #include "webkit/glue/webkit_glue.h"
34
35 #if defined(OS_WIN)
36 #include <atlbase.h>
37 #include <atlapp.h>
38 #include <malloc.h>
39 #elif defined(OS_MACOSX)
40 #include "base/mac/scoped_nsautorelease_pool.h"
41 #include "base/mach_ipc_mac.h"
42 #include "base/system_monitor/system_monitor.h"
43 #include "content/browser/mach_broker_mac.h"
44 #include "content/common/sandbox_init_mac.h"
45 #endif // OS_WIN
46
47 #if defined(OS_POSIX)
48 #include <signal.h>
49
50 #include "base/global_descriptors_posix.h"
51 #include "content/common/chrome_descriptors.h"
52
53 #if !defined(OS_MACOSX)
54 #include "content/public/common/zygote_fork_delegate_linux.h"
55 #endif
56
57 #endif // OS_POSIX
58
59 #if !defined(OS_MACOSX) && defined(USE_TCMALLOC)
60 extern "C" {
61 int tc_set_new_mode(int mode);
62 }
63 #endif
64
65 extern int GpuMain(const content::MainFunctionParams&);
66 extern int PluginMain(const content::MainFunctionParams&);
67 extern int PpapiPluginMain(const content::MainFunctionParams&);
68 extern int PpapiBrokerMain(const content::MainFunctionParams&);
69 extern int RendererMain(const content::MainFunctionParams&);
70 extern int WorkerMain(const content::MainFunctionParams&);
71 extern int UtilityMain(const content::MainFunctionParams&);
72 #if defined(OS_POSIX) && !defined(OS_MACOSX)
73 extern int ZygoteMain(const content::MainFunctionParams&,
74 content::ZygoteForkDelegate* forkdelegate);
75 #endif
76
77 namespace {
78
79 #if defined(OS_MACOSX)
80
81 // Completes the Mach IPC handshake by sending this process' task port to the
82 // parent process. The parent is listening on the Mach port given by
83 // |GetMachPortName()|. The task port is used by the parent to get CPU/memory
84 // stats to display in the task manager.
85 void SendTaskPortToParentProcess() {
86 const mach_msg_timeout_t kTimeoutMs = 100;
87 const int32_t kMessageId = 0;
88 std::string mach_port_name = MachBroker::GetMachPortName();
89
90 base::MachSendMessage child_message(kMessageId);
91 if (!child_message.AddDescriptor(mach_task_self())) {
92 LOG(ERROR) << "child AddDescriptor(mach_task_self()) failed.";
93 return;
94 }
95
96 base::MachPortSender child_sender(mach_port_name.c_str());
97 kern_return_t err = child_sender.SendMessage(child_message, kTimeoutMs);
98 if (err != KERN_SUCCESS) {
99 LOG(ERROR) << StringPrintf("child SendMessage() failed: 0x%x %s", err,
100 mach_error_string(err));
101 }
102 }
103
104 #endif // defined(OS_WIN)
105
106 #if defined(OS_POSIX)
107
108 // Setup signal-handling state: resanitize most signals, ignore SIGPIPE.
109 void SetupSignalHandlers() {
110 // Sanitise our signal handling state. Signals that were ignored by our
111 // parent will also be ignored by us. We also inherit our parent's sigmask.
112 sigset_t empty_signal_set;
113 CHECK(0 == sigemptyset(&empty_signal_set));
114 CHECK(0 == sigprocmask(SIG_SETMASK, &empty_signal_set, NULL));
115
116 struct sigaction sigact;
117 memset(&sigact, 0, sizeof(sigact));
118 sigact.sa_handler = SIG_DFL;
119 static const int signals_to_reset[] =
120 {SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV,
121 SIGALRM, SIGTERM, SIGCHLD, SIGBUS, SIGTRAP}; // SIGPIPE is set below.
122 for (unsigned i = 0; i < arraysize(signals_to_reset); i++) {
123 CHECK(0 == sigaction(signals_to_reset[i], &sigact, NULL));
124 }
125
126 // Always ignore SIGPIPE. We check the return value of write().
127 CHECK(signal(SIGPIPE, SIG_IGN) != SIG_ERR);
128 }
129
130 #endif // OS_POSIX
131
132 void CommonSubprocessInit(const std::string& process_type) {
133 #if defined(OS_WIN)
134 // HACK: Let Windows know that we have started. This is needed to suppress
135 // the IDC_APPSTARTING cursor from being displayed for a prolonged period
136 // while a subprocess is starting.
137 PostThreadMessage(GetCurrentThreadId(), WM_NULL, 0, 0);
138 MSG msg;
139 PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
140 #endif
141 #if defined(OS_POSIX) && !defined(OS_MACOSX)
142 // Various things break when you're using a locale where the decimal
143 // separator isn't a period. See e.g. bugs 22782 and 39964. For
144 // all processes except the browser process (where we call system
145 // APIs that may rely on the correct locale for formatting numbers
146 // when presenting them to the user), reset the locale for numeric
147 // formatting.
148 // Note that this is not correct for plugin processes -- they can
149 // surface UI -- but it's likely they get this wrong too so why not.
150 setlocale(LC_NUMERIC, "C");
151 #endif
152 }
153
154 void InitializeStatsTable(base::ProcessId browser_pid,
155 const CommandLine& command_line) {
156 // Initialize the Stats Counters table. With this initialized,
157 // the StatsViewer can be utilized to read counters outside of
158 // Chrome. These lines can be commented out to effectively turn
159 // counters 'off'. The table is created and exists for the life
160 // of the process. It is not cleaned up.
161 if (command_line.HasSwitch(switches::kEnableStatsTable)) {
162 // NOTIMPLEMENTED: we probably need to shut this down correctly to avoid
163 // leaking shared memory regions on posix platforms.
164 std::string statsfile =
165 base::StringPrintf("%s-%u",
166 content::kStatsFilename,
167 static_cast<unsigned int>(browser_pid));
168 base::StatsTable* stats_table = new base::StatsTable(statsfile,
169 content::kStatsMaxThreads, content::kStatsMaxCounters);
170 base::StatsTable::set_current(stats_table);
171 }
172 }
173
174 // We dispatch to a process-type-specific FooMain() based on a command-line
175 // flag. This struct is used to build a table of (flag, main function) pairs.
176 struct MainFunction {
177 const char* name;
178 int (*function)(const content::MainFunctionParams&);
179 };
180
181 #if defined(OS_POSIX) && !defined(OS_MACOSX)
182 // On platforms that use the zygote, we have a special subset of
183 // subprocesses that are launched via the zygote. This function
184 // fills in some process-launching bits around ZygoteMain().
185 // Returns the exit code of the subprocess.
186 int RunZygote(const content::MainFunctionParams& main_function_params,
187 content::ContentMainDelegate* delegate) {
188 static const MainFunction kMainFunctions[] = {
189 { switches::kRendererProcess, RendererMain },
190 { switches::kWorkerProcess, WorkerMain },
191 { switches::kPpapiPluginProcess, PpapiPluginMain },
192 { switches::kUtilityProcess, UtilityMain },
193 };
194
195 scoped_ptr<content::ZygoteForkDelegate> zygote_fork_delegate;
196 if (delegate) zygote_fork_delegate.reset(delegate->ZygoteStarting());
197
198 // This function call can return multiple times, once per fork().
199 if (!ZygoteMain(main_function_params, zygote_fork_delegate.get()))
200 return 1;
201
202 if (delegate) delegate->ZygoteForked();
203
204 // Zygote::HandleForkRequest may have reallocated the command
205 // line so update it here with the new version.
206 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
207
208 // If a custom user agent was passed on the command line, we need
209 // to (re)set it now, rather than using the default one the zygote
210 // initialized.
211 bool custom = false;
212 std::string ua = content::GetContentClient()->GetUserAgent(&custom);
213 if (custom) webkit_glue::SetUserAgent(ua, custom);
214
215 // The StatsTable must be initialized in each process; we already
216 // initialized for the browser process, now we need to initialize
217 // within the new processes as well.
218 pid_t browser_pid = base::GetParentProcessId(
219 base::GetParentProcessId(base::GetCurrentProcId()));
220 InitializeStatsTable(browser_pid, command_line);
221
222 content::MainFunctionParams main_params(command_line);
223
224 // Get the new process type from the new command line.
225 std::string process_type =
226 command_line.GetSwitchValueASCII(switches::kProcessType);
227
228 for (size_t i = 0; i < arraysize(kMainFunctions); ++i) {
229 if (process_type == kMainFunctions[i].name)
230 return kMainFunctions[i].function(main_params);
231 }
232
233 if (delegate)
234 return delegate->RunProcess(process_type, main_params);
235
236 NOTREACHED() << "Unknown zygote process type: " << process_type;
237 return 1;
238 }
239 #endif // defined(OS_POSIX) && !defined(OS_MACOSX)
240
241 // Run the FooMain() for a given process type.
242 // If |process_type| is empty, runs BrowserMain().
243 // Returns the exit code for this process.
244 int RunNamedProcessTypeMain(
245 const std::string& process_type,
246 const content::MainFunctionParams& main_function_params,
247 content::ContentMainDelegate* delegate) {
248 static const MainFunction kMainFunctions[] = {
249 { "", BrowserMain },
250 { switches::kRendererProcess, RendererMain },
251 { switches::kPluginProcess, PluginMain },
252 { switches::kWorkerProcess, WorkerMain },
253 { switches::kPpapiPluginProcess, PpapiPluginMain },
254 { switches::kPpapiBrokerProcess, PpapiBrokerMain },
255 { switches::kUtilityProcess, UtilityMain },
256 { switches::kGpuProcess, GpuMain },
257 };
258
259 for (size_t i = 0; i < arraysize(kMainFunctions); ++i) {
260 if (process_type == kMainFunctions[i].name) {
261 if (delegate) {
262 int exit_code = delegate->RunKnownProcess(process_type,
263 main_function_params);
264 if (exit_code >= 0)
265 return exit_code;
266 }
267 return kMainFunctions[i].function(main_function_params);
268 }
269 }
270
271 #if defined(OS_POSIX) && !defined(OS_MACOSX)
272 // Zygote startup is special -- see RunZygote comments above
273 // for why we don't use ZygoteMain directly.
274 if (process_type == switches::kZygoteProcess)
275 return RunZygote(main_function_params, delegate);
276 #endif
277
278 // If it's a process we don't know about, the embedder should know.
279 if (delegate)
280 return delegate->RunUnknownProcess(process_type, main_function_params);
281
282 NOTREACHED() << "Unknown process type: " << process_type;
283 return 1;
284 }
285
286 class ContentMainRunnerImpl : public content::ContentMainRunner {
287 public:
288 ContentMainRunnerImpl();
289 ~ContentMainRunnerImpl();
290
291 #if defined(OS_WIN)
292 virtual int Initialize(HINSTANCE instance,
293 sandbox::SandboxInterfaceInfo* sandbox_info,
294 content::ContentMainDelegate* delegate) OVERRIDE;
295 #else
296 virtual int Initialize(int argc,
297 const char** argv,
298 content::ContentMainDelegate* delegate) OVERRIDE;
299 #endif
300 virtual int Run() OVERRIDE;
301 virtual void Shutdown() OVERRIDE;
302
303 protected:
304 // True if the runner has been initialized.
305 bool is_initialized_;
306
307 // True if the runner has been shut down.
308 bool is_shutdown_;
309
310 // The delegate will outlive this object.
311 content::ContentMainDelegate* delegate_;
312
313 scoped_ptr<base::AtExitManager> exit_manager_;
314 #if defined(OS_WIN)
315 sandbox::SandboxInterfaceInfo sandbox_info_;
316 scoped_ptr<CAppModule> app_module_;
317 #elif defined(OS_MACOSX)
318 scoped_ptr<base::mac::ScopedNSAutoreleasePool> autorelease_pool_;
319 #endif
320
321 DISALLOW_COPY_AND_ASSIGN(ContentMainRunnerImpl);
322 };
323
324 ContentMainRunnerImpl::ContentMainRunnerImpl()
325 : is_initialized_(false),
326 is_shutdown_(false) {
327 }
328
329 ContentMainRunnerImpl::~ContentMainRunnerImpl() {
330 if (is_initialized_ && !is_shutdown_)
331 Shutdown();
332 }
333
334 #if defined(OS_WIN)
335 int ContentMainRunnerImpl::Initialize(
336 HINSTANCE instance,
337 sandbox::SandboxInterfaceInfo* sandbox_info,
338 content::ContentMainDelegate* delegate) {
339 is_initialized_ = true;
340
341 // argc/argv are ignored on Windows; see command_line.h for details.
342 int argc = 0;
343 char** argv = NULL;
344
345 content::RegisterInvalidParamHandler();
346 app_module_.reset(new CAppModule);
347 app_module_->Init(NULL, static_cast<HINSTANCE>(instance));
348
349 if (sandbox_info)
350 sandbox_info_ = *sandbox_info;
351 else
352 memset(&sandbox_info_, 0, sizeof(sandbox_info_));
353
354 #else
355 int ContentMainRunnerImpl::Initialize(
356 int argc,
357 const char** argv,
358 content::ContentMainDelegate* delegate) {
359 is_initialized_ = true;
360
361 // NOTE(willchan): One might ask why this call is done here rather than in
362 // process_util_linux.cc with the definition of
363 // EnableTerminationOnOutOfMemory(). That's because base shouldn't have a
364 // dependency on TCMalloc. Really, we ought to have our allocator shim code
365 // implement this EnableTerminationOnOutOfMemory() function. Whateverz. This
366 // works for now.
367 #if !defined(OS_MACOSX) && defined(USE_TCMALLOC)
368 // For tcmalloc, we need to tell it to behave like new.
369 tc_set_new_mode(1);
370 #endif
371
372 // Set C library locale to make sure CommandLine can parse argument values
373 // in correct encoding.
374 setlocale(LC_ALL, "");
375
376 SetupSignalHandlers();
377
378 base::GlobalDescriptors* g_fds = base::GlobalDescriptors::GetInstance();
379 g_fds->Set(kPrimaryIPCChannel,
380 kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor);
381 #if defined(OS_LINUX) || defined(OS_OPENBSD)
382 g_fds->Set(kCrashDumpSignal,
383 kCrashDumpSignal + base::GlobalDescriptors::kBaseDescriptor);
384 #endif
385
386 #endif // OS_WIN
387
388 delegate_ = delegate;
389
390 base::EnableTerminationOnHeapCorruption();
391 base::EnableTerminationOnOutOfMemory();
392
393 // The exit manager is in charge of calling the dtors of singleton objects.
394 exit_manager_.reset(new base::AtExitManager);
395
396 #if defined(OS_MACOSX)
397 // We need this pool for all the objects created before we get to the
398 // event loop, but we don't want to leave them hanging around until the
399 // app quits. Each "main" needs to flush this pool right before it goes into
400 // its main event loop to get rid of the cruft.
401 autorelease_pool_.reset(new base::mac::ScopedNSAutoreleasePool());
402 #endif
403
404 CommandLine::Init(argc, argv);
405
406 int exit_code;
407 if (delegate && delegate->BasicStartupComplete(&exit_code))
408 return exit_code;
409
410 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
411 std::string process_type =
412 command_line.GetSwitchValueASCII(switches::kProcessType);
413
414 // Enable startup tracing asap to avoid early TRACE_EVENT calls being ignored.
415 if (command_line.HasSwitch(switches::kTraceStartup)) {
416 base::debug::TraceLog::GetInstance()->SetEnabled(
417 command_line.GetSwitchValueASCII(switches::kTraceStartup));
418 }
419
420 #if defined(OS_MACOSX)
421 // We need to allocate the IO Ports before the Sandbox is initialized or
422 // the first instance of SystemMonitor is created.
423 // It's important not to allocate the ports for processes which don't register
424 // with the system monitor - see crbug.com/88867.
425 if (process_type.empty() ||
426 process_type == switches::kPluginProcess ||
427 process_type == switches::kRendererProcess ||
428 process_type == switches::kUtilityProcess ||
429 process_type == switches::kWorkerProcess ||
430 (delegate && delegate->ProcessRegistersWithSystemProcess(process_type))) {
431 base::SystemMonitor::AllocateSystemIOPorts();
432 }
433
434 if (!process_type.empty() &&
435 (!delegate || delegate->ShouldSendMachPort(process_type))) {
436 SendTaskPortToParentProcess();
437 }
438 #elif defined(OS_WIN)
439 content::SetupCRT(command_line);
440 #endif
441
442 #if defined(OS_POSIX)
443 if (!process_type.empty()) {
444 // When you hit Ctrl-C in a terminal running the browser
445 // process, a SIGINT is delivered to the entire process group.
446 // When debugging the browser process via gdb, gdb catches the
447 // SIGINT for the browser process (and dumps you back to the gdb
448 // console) but doesn't for the child processes, killing them.
449 // The fix is to have child processes ignore SIGINT; they'll die
450 // on their own when the browser process goes away.
451 //
452 // Note that we *can't* rely on BeingDebugged to catch this case because we
453 // are the child process, which is not being debugged.
454 // TODO(evanm): move this to some shared subprocess-init function.
455 if (!base::debug::BeingDebugged())
456 signal(SIGINT, SIG_IGN);
457 }
458 #endif
459
460 #if defined(USE_NSS)
461 crypto::EarlySetupForNSSInit();
462 #endif
463
464 ui::RegisterPathProvider();
465 content::RegisterPathProvider();
466
467 CHECK(icu_util::Initialize());
468
469 base::ProcessId browser_pid = base::GetCurrentProcId();
470 if (command_line.HasSwitch(switches::kProcessChannelID)) {
471 #if defined(OS_WIN) || defined(OS_MACOSX)
472 std::string channel_name =
473 command_line.GetSwitchValueASCII(switches::kProcessChannelID);
474
475 int browser_pid_int;
476 base::StringToInt(channel_name, &browser_pid_int);
477 browser_pid = static_cast<base::ProcessId>(browser_pid_int);
478 DCHECK_NE(browser_pid_int, 0);
479 #elif defined(OS_POSIX)
480 // On linux, we're in the zygote here; so we need the parent process' id.
481 browser_pid = base::GetParentProcessId(base::GetCurrentProcId());
482 #endif
483 }
484
485 InitializeStatsTable(browser_pid, command_line);
486
487 if (delegate) delegate->PreSandboxStartup();
488
489 if (!process_type.empty())
490 CommonSubprocessInit(process_type);
491
492 #if defined(OS_WIN)
493 CHECK(content::InitializeSandbox(sandbox_info));
494 #elif defined(OS_MACOSX)
495 if (process_type == switches::kRendererProcess ||
496 process_type == switches::kPpapiPluginProcess ||
497 (delegate && delegate->DelaySandboxInitialization(process_type))) {
498 // On OS X the renderer sandbox needs to be initialized later in the startup
499 // sequence in RendererMainPlatformDelegate::EnableSandbox().
500 } else {
501 CHECK(content::InitializeSandbox());
502 }
503 #endif
504
505 if (delegate) delegate->SandboxInitialized(process_type);
506
507 #if defined(OS_POSIX)
508 SetProcessTitleFromCommandLine(argv);
509 #endif
510
511 // Return -1 to indicate no early termination.
512 return -1;
513 }
514
515 int ContentMainRunnerImpl::Run() {
516 DCHECK(is_initialized_);
517 DCHECK(!is_shutdown_);
518 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
519 std::string process_type =
520 command_line.GetSwitchValueASCII(switches::kProcessType);
521
522 content::MainFunctionParams main_params(command_line);
523 #if defined(OS_WIN)
524 main_params.sandbox_info = &sandbox_info_;
525 #elif defined(OS_MACOSX)
526 main_params.autorelease_pool = autorelease_pool_.get();
527 #endif
528
529 return RunNamedProcessTypeMain(process_type, main_params, delegate_);
530 }
531
532 void ContentMainRunnerImpl::Shutdown() {
533 DCHECK(is_initialized_);
534 DCHECK(!is_shutdown_);
535 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
536 std::string process_type =
537 command_line.GetSwitchValueASCII(switches::kProcessType);
538
539 if (delegate_) delegate_->ProcessExiting(process_type);
540
541 #if defined(OS_WIN)
542 #ifdef _CRTDBG_MAP_ALLOC
543 _CrtDumpMemoryLeaks();
544 #endif // _CRTDBG_MAP_ALLOC
545
546 app_module_->Term();
547 #endif // OS_WIN
548
549 #if defined(OS_MACOSX)
550 autorelease_pool_.reset(NULL);
551 #endif
552
553 exit_manager_.reset(NULL);
554
555 #if defined(OS_WIN)
556 app_module_.reset(NULL);
557 #endif
558
559 delegate_ = NULL;
560 is_shutdown_ = true;
561 }
562
563 } // namespace
564
565 namespace content {
566
567 // static
568 ContentMainRunner* ContentMainRunner::CreateMainRunner() {
569 return new ContentMainRunnerImpl();
570 }
571
572 std::string GetProcessType(int argc, const char** argv) {
573 // Create a temporary CommandLine object because the global object may not
574 // have been initialized yet.
575
576 CommandLine command_line(CommandLine::NO_PROGRAM);
577 #if defined(OS_WIN)
578 command_line.ParseFromString(::GetCommandLineW());
579 #else
580 command_line.InitFromArgv(argc, argv);
581 #endif
582
583 return command_line.GetSwitchValueASCII(switches::kProcessType);
584 }
585
586 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698