Avoid crashing when going back/forward to debug URLs on a sad WebUI tab.
[chromium-blink-merge.git] / chrome / service / service_utility_process_host.cc
blob42a5a532958c7b326bf352e9286ae3c56edc576a
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 <queue>
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/files/file.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/files/scoped_temp_dir.h"
15 #include "base/logging.h"
16 #include "base/metrics/histogram.h"
17 #include "base/process/launch.h"
18 #include "base/task_runner_util.h"
19 #include "base/thread_task_runner_handle.h"
20 #include "chrome/common/chrome_switches.h"
21 #include "chrome/common/chrome_utility_printing_messages.h"
22 #include "content/public/common/child_process_host.h"
23 #include "content/public/common/result_codes.h"
24 #include "content/public/common/sandbox_init.h"
25 #include "content/public/common/sandboxed_process_launcher_delegate.h"
26 #include "ipc/ipc_switches.h"
27 #include "printing/emf_win.h"
28 #include "sandbox/win/src/sandbox_policy_base.h"
29 #include "ui/base/ui_base_switches.h"
31 namespace {
33 using content::ChildProcessHost;
35 enum ServiceUtilityProcessHostEvent {
36 SERVICE_UTILITY_STARTED,
37 SERVICE_UTILITY_DISCONNECTED,
38 SERVICE_UTILITY_METAFILE_REQUEST,
39 SERVICE_UTILITY_METAFILE_SUCCEEDED,
40 SERVICE_UTILITY_METAFILE_FAILED,
41 SERVICE_UTILITY_CAPS_REQUEST,
42 SERVICE_UTILITY_CAPS_SUCCEEDED,
43 SERVICE_UTILITY_CAPS_FAILED,
44 SERVICE_UTILITY_SEMANTIC_CAPS_REQUEST,
45 SERVICE_UTILITY_SEMANTIC_CAPS_SUCCEEDED,
46 SERVICE_UTILITY_SEMANTIC_CAPS_FAILED,
47 SERVICE_UTILITY_FAILED_TO_START,
48 SERVICE_UTILITY_EVENT_MAX,
51 void ReportUmaEvent(ServiceUtilityProcessHostEvent id) {
52 UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
53 id,
54 SERVICE_UTILITY_EVENT_MAX);
57 // NOTE: changes to this class need to be reviewed by the security team.
58 class ServiceSandboxedProcessLauncherDelegate
59 : public content::SandboxedProcessLauncherDelegate {
60 public:
61 ServiceSandboxedProcessLauncherDelegate() {}
63 void PreSpawnTarget(sandbox::TargetPolicy* policy, bool* success) override {
64 // Service process may run as windows service and it fails to create a
65 // window station.
66 policy->SetAlternateDesktop(false);
69 private:
70 DISALLOW_COPY_AND_ASSIGN(ServiceSandboxedProcessLauncherDelegate);
73 } // namespace
75 class ServiceUtilityProcessHost::PdfToEmfState {
76 public:
77 explicit PdfToEmfState(ServiceUtilityProcessHost* host)
78 : host_(host), page_count_(0), current_page_(0), pages_in_progress_(0) {}
79 ~PdfToEmfState() { Stop(); }
81 bool Start(base::File pdf_file,
82 const printing::PdfRenderSettings& conversion_settings) {
83 if (!temp_dir_.CreateUniqueTempDir())
84 return false;
85 return host_->Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles(
86 IPC::TakeFileHandleForProcess(pdf_file.Pass(), host_->handle()),
87 conversion_settings));
90 void GetMorePages() {
91 const int kMaxNumberOfTempFilesPerDocument = 3;
92 while (pages_in_progress_ < kMaxNumberOfTempFilesPerDocument &&
93 current_page_ < page_count_) {
94 ++pages_in_progress_;
95 emf_files_.push(CreateTempFile());
96 host_->Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_GetPage(
97 current_page_++,
98 IPC::GetFileHandleForProcess(
99 emf_files_.back().GetPlatformFile(), host_->handle(), false)));
103 // Returns true if all pages processed and client should not expect more
104 // results.
105 bool OnPageProcessed() {
106 --pages_in_progress_;
107 GetMorePages();
108 if (pages_in_progress_ || current_page_ < page_count_)
109 return false;
110 Stop();
111 return true;
114 base::File TakeNextFile() {
115 DCHECK(!emf_files_.empty());
116 base::File file;
117 if (!emf_files_.empty())
118 file = emf_files_.front().Pass();
119 emf_files_.pop();
120 return file.Pass();
123 void set_page_count(int page_count) { page_count_ = page_count; }
124 bool has_page_count() { return page_count_ > 0; }
126 private:
127 void Stop() {
128 host_->Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop());
131 base::File CreateTempFile() {
132 base::FilePath path;
133 if (!base::CreateTemporaryFileInDir(temp_dir_.path(), &path))
134 return base::File();
135 return base::File(path,
136 base::File::FLAG_CREATE_ALWAYS |
137 base::File::FLAG_WRITE |
138 base::File::FLAG_READ |
139 base::File::FLAG_DELETE_ON_CLOSE |
140 base::File::FLAG_TEMPORARY);
143 base::ScopedTempDir temp_dir_;
144 ServiceUtilityProcessHost* host_;
145 std::queue<base::File> emf_files_;
146 int page_count_;
147 int current_page_;
148 int pages_in_progress_;
151 ServiceUtilityProcessHost::ServiceUtilityProcessHost(
152 Client* client,
153 base::SingleThreadTaskRunner* client_task_runner)
154 : client_(client),
155 client_task_runner_(client_task_runner),
156 waiting_for_reply_(false),
157 weak_ptr_factory_(this) {
158 child_process_host_.reset(ChildProcessHost::Create(this));
161 ServiceUtilityProcessHost::~ServiceUtilityProcessHost() {
162 // We need to kill the child process when the host dies.
163 process_.Terminate(content::RESULT_CODE_NORMAL_EXIT, false);
166 bool ServiceUtilityProcessHost::StartRenderPDFPagesToMetafile(
167 const base::FilePath& pdf_path,
168 const printing::PdfRenderSettings& render_settings) {
169 ReportUmaEvent(SERVICE_UTILITY_METAFILE_REQUEST);
170 start_time_ = base::Time::Now();
171 base::File pdf_file(pdf_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
172 if (!pdf_file.IsValid() || !StartProcess(false))
173 return false;
175 DCHECK(!waiting_for_reply_);
176 waiting_for_reply_ = true;
178 pdf_to_emf_state_.reset(new PdfToEmfState(this));
179 return pdf_to_emf_state_->Start(pdf_file.Pass(), render_settings);
182 bool ServiceUtilityProcessHost::StartGetPrinterCapsAndDefaults(
183 const std::string& printer_name) {
184 ReportUmaEvent(SERVICE_UTILITY_CAPS_REQUEST);
185 start_time_ = base::Time::Now();
186 if (!StartProcess(true))
187 return false;
188 DCHECK(!waiting_for_reply_);
189 waiting_for_reply_ = true;
190 return Send(new ChromeUtilityMsg_GetPrinterCapsAndDefaults(printer_name));
193 bool ServiceUtilityProcessHost::StartGetPrinterSemanticCapsAndDefaults(
194 const std::string& printer_name) {
195 ReportUmaEvent(SERVICE_UTILITY_SEMANTIC_CAPS_REQUEST);
196 start_time_ = base::Time::Now();
197 if (!StartProcess(true))
198 return false;
199 DCHECK(!waiting_for_reply_);
200 waiting_for_reply_ = true;
201 return Send(
202 new ChromeUtilityMsg_GetPrinterSemanticCapsAndDefaults(printer_name));
205 bool ServiceUtilityProcessHost::StartProcess(bool no_sandbox) {
206 std::string channel_id = child_process_host_->CreateChannel();
207 if (channel_id.empty())
208 return false;
210 base::FilePath exe_path = GetUtilityProcessCmd();
211 if (exe_path.empty()) {
212 NOTREACHED() << "Unable to get utility process binary name.";
213 return false;
216 base::CommandLine cmd_line(exe_path);
217 cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kUtilityProcess);
218 cmd_line.AppendSwitchASCII(switches::kProcessChannelID, channel_id);
219 cmd_line.AppendSwitch(switches::kLang);
221 if (Launch(&cmd_line, no_sandbox)) {
222 ReportUmaEvent(SERVICE_UTILITY_STARTED);
223 return true;
225 ReportUmaEvent(SERVICE_UTILITY_FAILED_TO_START);
226 return false;
229 bool ServiceUtilityProcessHost::Launch(base::CommandLine* cmd_line,
230 bool no_sandbox) {
231 if (no_sandbox) {
232 cmd_line->AppendSwitch(switches::kNoSandbox);
233 process_ = base::LaunchProcess(*cmd_line, base::LaunchOptions());
234 } else {
235 ServiceSandboxedProcessLauncherDelegate delegate;
236 process_ = content::StartSandboxedProcess(&delegate, cmd_line);
238 return process_.IsValid();
241 bool ServiceUtilityProcessHost::Send(IPC::Message* msg) {
242 if (child_process_host_)
243 return child_process_host_->Send(msg);
244 delete msg;
245 return false;
248 base::FilePath ServiceUtilityProcessHost::GetUtilityProcessCmd() {
249 #if defined(OS_LINUX)
250 int flags = ChildProcessHost::CHILD_ALLOW_SELF;
251 #else
252 int flags = ChildProcessHost::CHILD_NORMAL;
253 #endif
254 return ChildProcessHost::GetChildPath(flags);
257 void ServiceUtilityProcessHost::OnChildDisconnected() {
258 if (waiting_for_reply_) {
259 // If we are yet to receive a reply then notify the client that the
260 // child died.
261 client_task_runner_->PostTask(
262 FROM_HERE, base::Bind(&Client::OnChildDied, client_.get()));
263 ReportUmaEvent(SERVICE_UTILITY_DISCONNECTED);
264 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityDisconnectTime",
265 base::Time::Now() - start_time_);
267 delete this;
270 bool ServiceUtilityProcessHost::OnMessageReceived(const IPC::Message& message) {
271 bool handled = true;
272 IPC_BEGIN_MESSAGE_MAP(ServiceUtilityProcessHost, message)
273 IPC_MESSAGE_HANDLER(
274 ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount,
275 OnRenderPDFPagesToMetafilesPageCount)
276 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone,
277 OnRenderPDFPagesToMetafilesPageDone)
278 IPC_MESSAGE_HANDLER(
279 ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Succeeded,
280 OnGetPrinterCapsAndDefaultsSucceeded)
281 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Failed,
282 OnGetPrinterCapsAndDefaultsFailed)
283 IPC_MESSAGE_HANDLER(
284 ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Succeeded,
285 OnGetPrinterSemanticCapsAndDefaultsSucceeded)
286 IPC_MESSAGE_HANDLER(
287 ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Failed,
288 OnGetPrinterSemanticCapsAndDefaultsFailed)
289 IPC_MESSAGE_UNHANDLED(handled = false)
290 IPC_END_MESSAGE_MAP()
291 return handled;
294 const base::Process& ServiceUtilityProcessHost::GetProcess() const {
295 return process_;
298 void ServiceUtilityProcessHost::OnMetafileSpooled(bool success) {
299 if (!success || pdf_to_emf_state_->OnPageProcessed())
300 OnPDFToEmfFinished(success);
303 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafilesPageCount(
304 int page_count) {
305 DCHECK(waiting_for_reply_);
306 if (!pdf_to_emf_state_ || page_count <= 0 ||
307 pdf_to_emf_state_->has_page_count()) {
308 return OnPDFToEmfFinished(false);
310 pdf_to_emf_state_->set_page_count(page_count);
311 pdf_to_emf_state_->GetMorePages();
314 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafilesPageDone(
315 bool success,
316 float scale_factor) {
317 DCHECK(waiting_for_reply_);
318 if (!pdf_to_emf_state_ || !success)
319 return OnPDFToEmfFinished(false);
320 base::File emf_file = pdf_to_emf_state_->TakeNextFile();
321 base::PostTaskAndReplyWithResult(
322 client_task_runner_.get(), FROM_HERE,
323 base::Bind(&Client::MetafileAvailable, client_.get(), scale_factor,
324 base::Passed(&emf_file)),
325 base::Bind(&ServiceUtilityProcessHost::OnMetafileSpooled,
326 weak_ptr_factory_.GetWeakPtr()));
329 void ServiceUtilityProcessHost::OnPDFToEmfFinished(bool success) {
330 if (!waiting_for_reply_)
331 return;
332 waiting_for_reply_ = false;
333 if (success) {
334 ReportUmaEvent(SERVICE_UTILITY_METAFILE_SUCCEEDED);
335 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileTime",
336 base::Time::Now() - start_time_);
337 } else {
338 ReportUmaEvent(SERVICE_UTILITY_METAFILE_FAILED);
339 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileFailTime",
340 base::Time::Now() - start_time_);
342 client_task_runner_->PostTask(
343 FROM_HERE, base::Bind(&Client::OnRenderPDFPagesToMetafileDone,
344 client_.get(), success));
345 pdf_to_emf_state_.reset();
348 void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsSucceeded(
349 const std::string& printer_name,
350 const printing::PrinterCapsAndDefaults& caps_and_defaults) {
351 DCHECK(waiting_for_reply_);
352 ReportUmaEvent(SERVICE_UTILITY_CAPS_SUCCEEDED);
353 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsTime",
354 base::Time::Now() - start_time_);
355 waiting_for_reply_ = false;
356 client_task_runner_->PostTask(
357 FROM_HERE, base::Bind(&Client::OnGetPrinterCapsAndDefaults, client_.get(),
358 true, printer_name, caps_and_defaults));
361 void ServiceUtilityProcessHost::OnGetPrinterSemanticCapsAndDefaultsSucceeded(
362 const std::string& printer_name,
363 const printing::PrinterSemanticCapsAndDefaults& caps_and_defaults) {
364 DCHECK(waiting_for_reply_);
365 ReportUmaEvent(SERVICE_UTILITY_SEMANTIC_CAPS_SUCCEEDED);
366 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilitySemanticCapsTime",
367 base::Time::Now() - start_time_);
368 waiting_for_reply_ = false;
369 client_task_runner_->PostTask(
370 FROM_HERE,
371 base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults, client_.get(),
372 true, printer_name, caps_and_defaults));
375 void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsFailed(
376 const std::string& printer_name) {
377 DCHECK(waiting_for_reply_);
378 ReportUmaEvent(SERVICE_UTILITY_CAPS_FAILED);
379 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsFailTime",
380 base::Time::Now() - start_time_);
381 waiting_for_reply_ = false;
382 client_task_runner_->PostTask(
383 FROM_HERE,
384 base::Bind(&Client::OnGetPrinterCapsAndDefaults, client_.get(), false,
385 printer_name, printing::PrinterCapsAndDefaults()));
388 void ServiceUtilityProcessHost::OnGetPrinterSemanticCapsAndDefaultsFailed(
389 const std::string& printer_name) {
390 DCHECK(waiting_for_reply_);
391 ReportUmaEvent(SERVICE_UTILITY_SEMANTIC_CAPS_FAILED);
392 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilitySemanticCapsFailTime",
393 base::Time::Now() - start_time_);
394 waiting_for_reply_ = false;
395 client_task_runner_->PostTask(
396 FROM_HERE, base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults,
397 client_.get(), false, printer_name,
398 printing::PrinterSemanticCapsAndDefaults()));
401 bool ServiceUtilityProcessHost::Client::MetafileAvailable(float scale_factor,
402 base::File file) {
403 file.Seek(base::File::FROM_BEGIN, 0);
404 int64 size = file.GetLength();
405 if (size <= 0) {
406 OnRenderPDFPagesToMetafileDone(false);
407 return false;
409 std::vector<char> data(size);
410 if (file.ReadAtCurrentPos(data.data(), data.size()) != size) {
411 OnRenderPDFPagesToMetafileDone(false);
412 return false;
414 printing::Emf emf;
415 if (!emf.InitFromData(data.data(), data.size())) {
416 OnRenderPDFPagesToMetafileDone(false);
417 return false;
419 OnRenderPDFPagesToMetafilePageDone(scale_factor, emf);
420 return true;