Add ICU message format support
[chromium-blink-merge.git] / content / browser / utility_process_host_impl.cc
blob67f08a23210b3851a1ab993eb77b23864507a049
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/utility_process_host_impl.h"
7 #include "base/base_switches.h"
8 #include "base/bind.h"
9 #include "base/bind_helpers.h"
10 #include "base/command_line.h"
11 #include "base/lazy_instance.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/process/process_handle.h"
14 #include "base/run_loop.h"
15 #include "base/sequenced_task_runner.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/synchronization/lock.h"
18 #include "base/synchronization/waitable_event.h"
19 #include "content/browser/browser_child_process_host_impl.h"
20 #include "content/browser/mojo/mojo_application_host.h"
21 #include "content/browser/renderer_host/render_process_host_impl.h"
22 #include "content/common/child_process_host_impl.h"
23 #include "content/common/in_process_child_thread_params.h"
24 #include "content/common/utility_messages.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/content_browser_client.h"
27 #include "content/public/browser/utility_process_host_client.h"
28 #include "content/public/common/content_switches.h"
29 #include "content/public/common/process_type.h"
30 #include "content/public/common/sandbox_type.h"
31 #include "content/public/common/sandboxed_process_launcher_delegate.h"
32 #include "ipc/ipc_switches.h"
33 #include "ui/base/ui_base_switches.h"
35 namespace content {
37 // NOTE: changes to this class need to be reviewed by the security team.
38 class UtilitySandboxedProcessLauncherDelegate
39 : public SandboxedProcessLauncherDelegate {
40 public:
41 UtilitySandboxedProcessLauncherDelegate(const base::FilePath& exposed_dir,
42 bool launch_elevated,
43 bool no_sandbox,
44 const base::EnvironmentMap& env,
45 ChildProcessHost* host)
46 : exposed_dir_(exposed_dir),
47 #if defined(OS_WIN)
48 launch_elevated_(launch_elevated)
49 #elif defined(OS_POSIX)
50 env_(env),
51 no_sandbox_(no_sandbox),
52 ipc_fd_(host->TakeClientFileDescriptor())
53 #endif // OS_WIN
56 ~UtilitySandboxedProcessLauncherDelegate() override {}
58 #if defined(OS_WIN)
59 bool ShouldLaunchElevated() override { return launch_elevated_; }
60 void PreSandbox(bool* disable_default_policy,
61 base::FilePath* exposed_dir) override {
62 *exposed_dir = exposed_dir_;
64 #elif defined(OS_POSIX)
66 bool ShouldUseZygote() override {
67 return !no_sandbox_ && exposed_dir_.empty();
69 base::EnvironmentMap GetEnvironment() override { return env_; }
70 base::ScopedFD TakeIpcFd() override { return ipc_fd_.Pass(); }
71 #endif // OS_WIN
73 SandboxType GetSandboxType() override {
74 return SANDBOX_TYPE_UTILITY;
77 private:
78 base::FilePath exposed_dir_;
80 #if defined(OS_WIN)
81 bool launch_elevated_;
82 #elif defined(OS_POSIX)
83 base::EnvironmentMap env_;
84 bool no_sandbox_;
85 base::ScopedFD ipc_fd_;
86 #endif // OS_WIN
89 UtilityMainThreadFactoryFunction g_utility_main_thread_factory = NULL;
91 UtilityProcessHost* UtilityProcessHost::Create(
92 const scoped_refptr<UtilityProcessHostClient>& client,
93 const scoped_refptr<base::SequencedTaskRunner>& client_task_runner) {
94 return new UtilityProcessHostImpl(client, client_task_runner);
97 void UtilityProcessHostImpl::RegisterUtilityMainThreadFactory(
98 UtilityMainThreadFactoryFunction create) {
99 g_utility_main_thread_factory = create;
102 UtilityProcessHostImpl::UtilityProcessHostImpl(
103 const scoped_refptr<UtilityProcessHostClient>& client,
104 const scoped_refptr<base::SequencedTaskRunner>& client_task_runner)
105 : client_(client),
106 client_task_runner_(client_task_runner),
107 is_batch_mode_(false),
108 is_mdns_enabled_(false),
109 no_sandbox_(false),
110 run_elevated_(false),
111 #if defined(OS_LINUX)
112 child_flags_(ChildProcessHost::CHILD_ALLOW_SELF),
113 #else
114 child_flags_(ChildProcessHost::CHILD_NORMAL),
115 #endif
116 started_(false),
117 name_(base::ASCIIToUTF16("utility process")) {
120 UtilityProcessHostImpl::~UtilityProcessHostImpl() {
121 DCHECK_CURRENTLY_ON(BrowserThread::IO);
122 if (is_batch_mode_)
123 EndBatchMode();
125 // We could be destroyed as a result of Chrome shutdown. When that happens,
126 // the Mojo channel doesn't get the opportunity to shut down cleanly because
127 // it posts to the IO thread (the current thread) which is being destroyed.
128 // To guarantee proper shutdown of the Mojo channel, do it explicitly here.
129 if (mojo_application_host_)
130 mojo_application_host_->ShutdownOnIOThread();
133 bool UtilityProcessHostImpl::Send(IPC::Message* message) {
134 if (!StartProcess())
135 return false;
137 return process_->Send(message);
140 bool UtilityProcessHostImpl::StartBatchMode() {
141 CHECK(!is_batch_mode_);
142 is_batch_mode_ = StartProcess();
143 Send(new UtilityMsg_BatchMode_Started());
144 return is_batch_mode_;
147 void UtilityProcessHostImpl::EndBatchMode() {
148 CHECK(is_batch_mode_);
149 is_batch_mode_ = false;
150 Send(new UtilityMsg_BatchMode_Finished());
153 void UtilityProcessHostImpl::SetExposedDir(const base::FilePath& dir) {
154 exposed_dir_ = dir;
157 void UtilityProcessHostImpl::EnableMDns() {
158 is_mdns_enabled_ = true;
161 void UtilityProcessHostImpl::DisableSandbox() {
162 no_sandbox_ = true;
165 #if defined(OS_WIN)
166 void UtilityProcessHostImpl::ElevatePrivileges() {
167 no_sandbox_ = true;
168 run_elevated_ = true;
170 #endif
172 const ChildProcessData& UtilityProcessHostImpl::GetData() {
173 return process_->GetData();
176 #if defined(OS_POSIX)
178 void UtilityProcessHostImpl::SetEnv(const base::EnvironmentMap& env) {
179 env_ = env;
182 #endif // OS_POSIX
184 bool UtilityProcessHostImpl::StartMojoMode() {
185 CHECK(!mojo_application_host_);
186 mojo_application_host_.reset(new MojoApplicationHost);
188 bool mojo_result = mojo_application_host_->Init();
189 if (!mojo_result)
190 return false;
192 return StartProcess();
195 ServiceRegistry* UtilityProcessHostImpl::GetServiceRegistry() {
196 if (mojo_application_host_)
197 return mojo_application_host_->service_registry();
198 return nullptr;
201 void UtilityProcessHostImpl::SetName(const base::string16& name) {
202 name_ = name;
205 bool UtilityProcessHostImpl::StartProcess() {
206 if (started_)
207 return true;
208 started_ = true;
210 if (is_batch_mode_)
211 return true;
213 // Name must be set or metrics_service will crash in any test which
214 // launches a UtilityProcessHost.
215 process_.reset(new BrowserChildProcessHostImpl(PROCESS_TYPE_UTILITY, this));
216 process_->SetName(name_);
218 std::string channel_id = process_->GetHost()->CreateChannel();
219 if (channel_id.empty())
220 return false;
222 if (RenderProcessHost::run_renderer_in_process()) {
223 DCHECK(g_utility_main_thread_factory);
224 // See comment in RenderProcessHostImpl::Init() for the background on why we
225 // support single process mode this way.
226 in_process_thread_.reset(
227 g_utility_main_thread_factory(InProcessChildThreadParams(
228 channel_id, BrowserThread::UnsafeGetMessageLoopForThread(
229 BrowserThread::IO)->task_runner())));
230 in_process_thread_->Start();
231 OnProcessLaunched();
232 } else {
233 const base::CommandLine& browser_command_line =
234 *base::CommandLine::ForCurrentProcess();
235 int child_flags = child_flags_;
237 bool has_cmd_prefix = browser_command_line.HasSwitch(
238 switches::kUtilityCmdPrefix);
240 // When running under gdb, forking /proc/self/exe ends up forking the gdb
241 // executable instead of Chromium. It is almost safe to assume that no
242 // updates will happen while a developer is running with
243 // |switches::kUtilityCmdPrefix|. See ChildProcessHost::GetChildPath() for
244 // a similar case with Valgrind.
245 if (has_cmd_prefix)
246 child_flags = ChildProcessHost::CHILD_NORMAL;
248 base::FilePath exe_path = ChildProcessHost::GetChildPath(child_flags);
249 if (exe_path.empty()) {
250 NOTREACHED() << "Unable to get utility process binary name.";
251 return false;
254 base::CommandLine* cmd_line = new base::CommandLine(exe_path);
255 cmd_line->AppendSwitchASCII(switches::kProcessType,
256 switches::kUtilityProcess);
257 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id);
258 std::string locale = GetContentClient()->browser()->GetApplicationLocale();
259 cmd_line->AppendSwitchASCII(switches::kLang, locale);
261 if (no_sandbox_)
262 cmd_line->AppendSwitch(switches::kNoSandbox);
264 // Browser command-line switches to propagate to the utility process.
265 static const char* const kSwitchNames[] = {
266 switches::kDebugPluginLoading,
267 switches::kNoSandbox,
268 switches::kProfilerTiming,
269 #if defined(OS_MACOSX)
270 switches::kEnableSandboxLogging,
271 #endif
273 cmd_line->CopySwitchesFrom(browser_command_line, kSwitchNames,
274 arraysize(kSwitchNames));
276 if (has_cmd_prefix) {
277 // Launch the utility child process with some prefix
278 // (usually "xterm -e gdb --args").
279 cmd_line->PrependWrapper(browser_command_line.GetSwitchValueNative(
280 switches::kUtilityCmdPrefix));
283 if (!exposed_dir_.empty()) {
284 cmd_line->AppendSwitchPath(switches::kUtilityProcessAllowedDir,
285 exposed_dir_);
288 if (is_mdns_enabled_)
289 cmd_line->AppendSwitch(switches::kUtilityProcessEnableMDns);
291 #if defined(OS_WIN)
292 // Let the utility process know if it is intended to be elevated.
293 if (run_elevated_)
294 cmd_line->AppendSwitch(switches::kUtilityProcessRunningElevated);
295 #endif
297 process_->Launch(
298 new UtilitySandboxedProcessLauncherDelegate(exposed_dir_,
299 run_elevated_,
300 no_sandbox_, env_,
301 process_->GetHost()),
302 cmd_line,
303 true);
306 return true;
309 bool UtilityProcessHostImpl::OnMessageReceived(const IPC::Message& message) {
310 if (!client_.get())
311 return true;
313 client_task_runner_->PostTask(
314 FROM_HERE,
315 base::Bind(
316 base::IgnoreResult(&UtilityProcessHostClient::OnMessageReceived),
317 client_.get(),
318 message));
320 return true;
323 void UtilityProcessHostImpl::OnProcessLaunchFailed() {
324 if (!client_.get())
325 return;
327 client_task_runner_->PostTask(
328 FROM_HERE,
329 base::Bind(&UtilityProcessHostClient::OnProcessLaunchFailed,
330 client_.get()));
333 void UtilityProcessHostImpl::OnProcessCrashed(int exit_code) {
334 if (!client_.get())
335 return;
337 client_task_runner_->PostTask(
338 FROM_HERE,
339 base::Bind(&UtilityProcessHostClient::OnProcessCrashed, client_.get(),
340 exit_code));
343 void UtilityProcessHostImpl::OnProcessLaunched() {
344 if (mojo_application_host_) {
345 base::ProcessHandle handle;
346 if (RenderProcessHost::run_renderer_in_process())
347 handle = base::GetCurrentProcessHandle();
348 else
349 handle = process_->GetData().handle;
351 mojo_application_host_->Activate(this, handle);
355 } // namespace content