Fix a null pointer dereference in CreateProfileHandler::ShowProfileCreationError.
[chromium-blink-merge.git] / chrome / service / service_utility_process_host.cc
blob96cd0d1465ccf0ae91f915d78e096dec7ccc4b44
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 "chrome/service/service_utility_process_host.h"
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/file_util.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/message_loop/message_loop_proxy.h"
14 #include "base/metrics/histogram.h"
15 #include "base/process/kill.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "chrome/common/chrome_switches.h"
18 #include "chrome/common/chrome_utility_printing_messages.h"
19 #include "content/public/common/child_process_host.h"
20 #include "content/public/common/result_codes.h"
21 #include "content/public/common/sandbox_init.h"
22 #include "ipc/ipc_switches.h"
23 #include "printing/page_range.h"
24 #include "ui/base/ui_base_switches.h"
25 #include "ui/gfx/rect.h"
27 #if defined(OS_WIN)
28 #include "base/files/file_path.h"
29 #include "base/memory/scoped_ptr.h"
30 #include "base/process/launch.h"
31 #include "base/win/scoped_handle.h"
32 #include "content/public/common/sandbox_init.h"
33 #include "content/public/common/sandboxed_process_launcher_delegate.h"
34 #include "printing/emf_win.h"
36 namespace {
38 // NOTE: changes to this class need to be reviewed by the security team.
39 class ServiceSandboxedProcessLauncherDelegate
40 : public content::SandboxedProcessLauncherDelegate {
41 public:
42 explicit ServiceSandboxedProcessLauncherDelegate(
43 const base::FilePath& exposed_dir)
44 : exposed_dir_(exposed_dir) {
47 virtual void PreSandbox(bool* disable_default_policy,
48 base::FilePath* exposed_dir) OVERRIDE {
49 *exposed_dir = exposed_dir_;
52 private:
53 base::FilePath exposed_dir_;
56 } // namespace
58 #endif // OS_WIN
60 using content::ChildProcessHost;
62 namespace {
63 enum ServiceUtilityProcessHostEvent {
64 SERVICE_UTILITY_STARTED,
65 SERVICE_UTILITY_DISCONNECTED,
66 SERVICE_UTILITY_METAFILE_REQUEST,
67 SERVICE_UTILITY_METAFILE_SUCCEEDED,
68 SERVICE_UTILITY_METAFILE_FAILED,
69 SERVICE_UTILITY_CAPS_REQUEST,
70 SERVICE_UTILITY_CAPS_SUCCEEDED,
71 SERVICE_UTILITY_CAPS_FAILED,
72 SERVICE_UTILITY_SEMANTIC_CAPS_REQUEST,
73 SERVICE_UTILITY_SEMANTIC_CAPS_SUCCEEDED,
74 SERVICE_UTILITY_SEMANTIC_CAPS_FAILED,
75 SERVICE_UTILITY_EVENT_MAX,
77 } // namespace
79 ServiceUtilityProcessHost::ServiceUtilityProcessHost(
80 Client* client, base::MessageLoopProxy* client_message_loop_proxy)
81 : handle_(base::kNullProcessHandle),
82 client_(client),
83 client_message_loop_proxy_(client_message_loop_proxy),
84 waiting_for_reply_(false) {
85 child_process_host_.reset(ChildProcessHost::Create(this));
88 ServiceUtilityProcessHost::~ServiceUtilityProcessHost() {
89 // We need to kill the child process when the host dies.
90 base::KillProcess(handle_, content::RESULT_CODE_NORMAL_EXIT, false);
93 bool ServiceUtilityProcessHost::StartRenderPDFPagesToMetafile(
94 const base::FilePath& pdf_path,
95 const printing::PdfRenderSettings& render_settings,
96 const std::vector<printing::PageRange>& page_ranges) {
97 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
98 SERVICE_UTILITY_METAFILE_REQUEST,
99 SERVICE_UTILITY_EVENT_MAX);
100 start_time_ = base::Time::Now();
101 #if !defined(OS_WIN)
102 // This is only implemented on Windows (because currently it is only needed
103 // on Windows). Will add implementations on other platforms when needed.
104 NOTIMPLEMENTED();
105 return false;
106 #else // !defined(OS_WIN)
107 scratch_metafile_dir_.reset(new base::ScopedTempDir);
108 if (!scratch_metafile_dir_->CreateUniqueTempDir())
109 return false;
110 metafile_path_ = scratch_metafile_dir_->path().AppendASCII("output.emf");
111 if (!StartProcess(false, scratch_metafile_dir_->path()))
112 return false;
114 base::File pdf_file(
115 pdf_path,
116 base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE);
117 DCHECK(!waiting_for_reply_);
118 waiting_for_reply_ = true;
119 return child_process_host_->Send(
120 new ChromeUtilityMsg_RenderPDFPagesToMetafiles(
121 IPC::TakeFileHandleForProcess(pdf_file.Pass(), handle()),
122 metafile_path_,
123 render_settings,
124 page_ranges));
125 #endif // !defined(OS_WIN)
128 bool ServiceUtilityProcessHost::StartGetPrinterCapsAndDefaults(
129 const std::string& printer_name) {
130 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
131 SERVICE_UTILITY_CAPS_REQUEST,
132 SERVICE_UTILITY_EVENT_MAX);
133 start_time_ = base::Time::Now();
134 base::FilePath exposed_path;
135 if (!StartProcess(true, exposed_path))
136 return false;
137 DCHECK(!waiting_for_reply_);
138 waiting_for_reply_ = true;
139 return child_process_host_->Send(
140 new ChromeUtilityMsg_GetPrinterCapsAndDefaults(printer_name));
143 bool ServiceUtilityProcessHost::StartGetPrinterSemanticCapsAndDefaults(
144 const std::string& printer_name) {
145 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
146 SERVICE_UTILITY_SEMANTIC_CAPS_REQUEST,
147 SERVICE_UTILITY_EVENT_MAX);
148 start_time_ = base::Time::Now();
149 base::FilePath exposed_path;
150 if (!StartProcess(true, exposed_path))
151 return false;
152 DCHECK(!waiting_for_reply_);
153 waiting_for_reply_ = true;
154 return child_process_host_->Send(
155 new ChromeUtilityMsg_GetPrinterSemanticCapsAndDefaults(printer_name));
158 bool ServiceUtilityProcessHost::StartProcess(
159 bool no_sandbox,
160 const base::FilePath& exposed_dir) {
161 std::string channel_id = child_process_host_->CreateChannel();
162 if (channel_id.empty())
163 return false;
165 base::FilePath exe_path = GetUtilityProcessCmd();
166 if (exe_path.empty()) {
167 NOTREACHED() << "Unable to get utility process binary name.";
168 return false;
171 CommandLine cmd_line(exe_path);
172 cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kUtilityProcess);
173 cmd_line.AppendSwitchASCII(switches::kProcessChannelID, channel_id);
174 cmd_line.AppendSwitch(switches::kLang);
176 if (Launch(&cmd_line, no_sandbox, exposed_dir)) {
177 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
178 SERVICE_UTILITY_STARTED,
179 SERVICE_UTILITY_EVENT_MAX);
180 return true;
182 return false;
185 bool ServiceUtilityProcessHost::Launch(CommandLine* cmd_line,
186 bool no_sandbox,
187 const base::FilePath& exposed_dir) {
188 #if !defined(OS_WIN)
189 // TODO(sanjeevr): Implement for non-Windows OSes.
190 NOTIMPLEMENTED();
191 return false;
192 #else // !defined(OS_WIN)
194 if (no_sandbox) {
195 base::ProcessHandle process = base::kNullProcessHandle;
196 cmd_line->AppendSwitch(switches::kNoSandbox);
197 base::LaunchProcess(*cmd_line, base::LaunchOptions(), &handle_);
198 } else {
199 ServiceSandboxedProcessLauncherDelegate delegate(exposed_dir);
200 handle_ = content::StartSandboxedProcess(&delegate, cmd_line);
202 return (handle_ != base::kNullProcessHandle);
203 #endif // !defined(OS_WIN)
206 base::FilePath ServiceUtilityProcessHost::GetUtilityProcessCmd() {
207 #if defined(OS_LINUX)
208 int flags = ChildProcessHost::CHILD_ALLOW_SELF;
209 #else
210 int flags = ChildProcessHost::CHILD_NORMAL;
211 #endif
212 return ChildProcessHost::GetChildPath(flags);
215 void ServiceUtilityProcessHost::OnChildDisconnected() {
216 if (waiting_for_reply_) {
217 // If we are yet to receive a reply then notify the client that the
218 // child died.
219 client_message_loop_proxy_->PostTask(
220 FROM_HERE, base::Bind(&Client::OnChildDied, client_.get()));
221 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
222 SERVICE_UTILITY_DISCONNECTED,
223 SERVICE_UTILITY_EVENT_MAX);
224 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityDisconnectTime",
225 base::Time::Now() - start_time_);
227 delete this;
230 bool ServiceUtilityProcessHost::OnMessageReceived(const IPC::Message& message) {
231 bool handled = true;
232 IPC_BEGIN_MESSAGE_MAP(ServiceUtilityProcessHost, message)
233 #if defined(WIN_PDF_METAFILE_FOR_PRINTING)
234 IPC_MESSAGE_HANDLER(
235 ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_Succeeded,
236 OnRenderPDFPagesToMetafilesSucceeded)
237 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed,
238 OnRenderPDFPagesToMetafileFailed)
239 #endif
240 IPC_MESSAGE_HANDLER(
241 ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Succeeded,
242 OnGetPrinterCapsAndDefaultsSucceeded)
243 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Failed,
244 OnGetPrinterCapsAndDefaultsFailed)
245 IPC_MESSAGE_HANDLER(
246 ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Succeeded,
247 OnGetPrinterSemanticCapsAndDefaultsSucceeded)
248 IPC_MESSAGE_HANDLER(
249 ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Failed,
250 OnGetPrinterSemanticCapsAndDefaultsFailed)
251 IPC_MESSAGE_UNHANDLED(handled = false)
252 IPC_END_MESSAGE_MAP()
253 return handled;
256 base::ProcessHandle ServiceUtilityProcessHost::GetHandle() const {
257 return handle_;
260 #if defined(WIN_PDF_METAFILE_FOR_PRINTING)
261 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafilesSucceeded(
262 const std::vector<printing::PageRange>& page_ranges,
263 double scale_factor) {
264 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
265 SERVICE_UTILITY_METAFILE_SUCCEEDED,
266 SERVICE_UTILITY_EVENT_MAX);
267 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileTime",
268 base::Time::Now() - start_time_);
269 DCHECK(waiting_for_reply_);
270 waiting_for_reply_ = false;
271 // If the metafile was successfully created, we need to take our hands off the
272 // scratch metafile directory. The client will delete it when it is done with
273 // metafile.
274 scratch_metafile_dir_->Take();
276 // TODO(vitalybuka|scottmg): http://crbug.com/170859: Currently, only one
277 // page is printed at a time. This would need to be refactored to change
278 // this.
279 CHECK_EQ(1u, page_ranges.size());
280 CHECK_EQ(page_ranges[0].from, page_ranges[0].to);
281 int page_number = page_ranges[0].from;
282 client_message_loop_proxy_->PostTask(
283 FROM_HERE,
284 base::Bind(&Client::MetafileAvailable,
285 client_.get(),
286 metafile_path_.InsertBeforeExtensionASCII(
287 base::StringPrintf(".%d", page_number)),
288 page_number,
289 scale_factor));
292 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafileFailed() {
293 DCHECK(waiting_for_reply_);
294 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
295 SERVICE_UTILITY_METAFILE_FAILED,
296 SERVICE_UTILITY_EVENT_MAX);
297 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileFailTime",
298 base::Time::Now() - start_time_);
299 waiting_for_reply_ = false;
300 client_message_loop_proxy_->PostTask(
301 FROM_HERE,
302 base::Bind(&Client::OnRenderPDFPagesToMetafileFailed, client_.get()));
304 #endif // defined(WIN_PDF_METAFILE_FOR_PRINTING)
306 void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsSucceeded(
307 const std::string& printer_name,
308 const printing::PrinterCapsAndDefaults& caps_and_defaults) {
309 DCHECK(waiting_for_reply_);
310 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
311 SERVICE_UTILITY_CAPS_SUCCEEDED,
312 SERVICE_UTILITY_EVENT_MAX);
313 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsTime",
314 base::Time::Now() - start_time_);
315 waiting_for_reply_ = false;
316 client_message_loop_proxy_->PostTask(
317 FROM_HERE,
318 base::Bind(&Client::OnGetPrinterCapsAndDefaults, client_.get(), true,
319 printer_name, caps_and_defaults));
322 void ServiceUtilityProcessHost::OnGetPrinterSemanticCapsAndDefaultsSucceeded(
323 const std::string& printer_name,
324 const printing::PrinterSemanticCapsAndDefaults& caps_and_defaults) {
325 DCHECK(waiting_for_reply_);
326 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
327 SERVICE_UTILITY_SEMANTIC_CAPS_SUCCEEDED,
328 SERVICE_UTILITY_EVENT_MAX);
329 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilitySemanticCapsTime",
330 base::Time::Now() - start_time_);
331 waiting_for_reply_ = false;
332 client_message_loop_proxy_->PostTask(
333 FROM_HERE,
334 base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults, client_.get(),
335 true, printer_name, caps_and_defaults));
338 void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsFailed(
339 const std::string& printer_name) {
340 DCHECK(waiting_for_reply_);
341 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
342 SERVICE_UTILITY_CAPS_FAILED,
343 SERVICE_UTILITY_EVENT_MAX);
344 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsFailTime",
345 base::Time::Now() - start_time_);
346 waiting_for_reply_ = false;
347 client_message_loop_proxy_->PostTask(
348 FROM_HERE,
349 base::Bind(&Client::OnGetPrinterCapsAndDefaults, client_.get(), false,
350 printer_name, printing::PrinterCapsAndDefaults()));
353 void ServiceUtilityProcessHost::OnGetPrinterSemanticCapsAndDefaultsFailed(
354 const std::string& printer_name) {
355 DCHECK(waiting_for_reply_);
356 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
357 SERVICE_UTILITY_SEMANTIC_CAPS_FAILED,
358 SERVICE_UTILITY_EVENT_MAX);
359 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilitySemanticCapsFailTime",
360 base::Time::Now() - start_time_);
361 waiting_for_reply_ = false;
362 client_message_loop_proxy_->PostTask(
363 FROM_HERE,
364 base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults,
365 client_.get(), false, printer_name,
366 printing::PrinterSemanticCapsAndDefaults()));
369 void ServiceUtilityProcessHost::Client::MetafileAvailable(
370 const base::FilePath& metafile_path,
371 int highest_rendered_page_number,
372 double scale_factor) {
373 // The metafile was created in a temp folder which needs to get deleted after
374 // we have processed it.
375 base::ScopedTempDir scratch_metafile_dir;
376 if (!scratch_metafile_dir.Set(metafile_path.DirName()))
377 LOG(WARNING) << "Unable to set scratch metafile directory";
378 #if defined(OS_WIN)
379 // It's important that metafile is declared after scratch_metafile_dir so
380 // that the metafile destructor closes the file before the base::ScopedTempDir
381 // destructor tries to remove the directory.
382 printing::Emf metafile;
383 if (!metafile.InitFromFile(metafile_path)) {
384 OnRenderPDFPagesToMetafileFailed();
385 } else {
386 OnRenderPDFPagesToMetafileSucceeded(metafile,
387 highest_rendered_page_number,
388 scale_factor);
390 #endif // defined(OS_WIN)