Make castv2 performance test work.
[chromium-blink-merge.git] / chrome / service / service_utility_process_host.cc
blob50da0b7386af8afe58d429ccb36e0b5b4914e439
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/message_loop/message_loop_proxy.h"
17 #include "base/metrics/histogram.h"
18 #include "base/process/launch.h"
19 #include "base/task_runner_util.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 virtual void PreSpawnTarget(sandbox::TargetPolicy* policy,
64 bool* success) override {
65 // Service process may run as windows service and it fails to create a
66 // window station.
67 policy->SetAlternateDesktop(false);
70 private:
71 DISALLOW_COPY_AND_ASSIGN(ServiceSandboxedProcessLauncherDelegate);
74 } // namespace
76 class ServiceUtilityProcessHost::PdfToEmfState {
77 public:
78 explicit PdfToEmfState(ServiceUtilityProcessHost* host)
79 : host_(host), page_count_(0), current_page_(0), pages_in_progress_(0) {}
80 ~PdfToEmfState() { Stop(); }
82 bool Start(base::File pdf_file,
83 const printing::PdfRenderSettings& conversion_settings) {
84 if (!temp_dir_.CreateUniqueTempDir())
85 return false;
86 return host_->Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles(
87 IPC::TakeFileHandleForProcess(pdf_file.Pass(), host_->handle()),
88 conversion_settings));
91 void GetMorePages() {
92 const int kMaxNumberOfTempFilesPerDocument = 3;
93 while (pages_in_progress_ < kMaxNumberOfTempFilesPerDocument &&
94 current_page_ < page_count_) {
95 ++pages_in_progress_;
96 emf_files_.push(CreateTempFile());
97 host_->Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_GetPage(
98 current_page_++,
99 IPC::GetFileHandleForProcess(
100 emf_files_.back().GetPlatformFile(), host_->handle(), false)));
104 // Returns true if all pages processed and client should not expect more
105 // results.
106 bool OnPageProcessed() {
107 --pages_in_progress_;
108 GetMorePages();
109 if (pages_in_progress_ || current_page_ < page_count_)
110 return false;
111 Stop();
112 return true;
115 base::File TakeNextFile() {
116 DCHECK(!emf_files_.empty());
117 base::File file;
118 if (!emf_files_.empty())
119 file = emf_files_.front().Pass();
120 emf_files_.pop();
121 return file.Pass();
124 void set_page_count(int page_count) { page_count_ = page_count; }
125 bool has_page_count() { return page_count_ > 0; }
127 private:
128 void Stop() {
129 host_->Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop());
132 base::File CreateTempFile() {
133 base::FilePath path;
134 if (!base::CreateTemporaryFileInDir(temp_dir_.path(), &path))
135 return base::File();
136 return base::File(path,
137 base::File::FLAG_CREATE_ALWAYS |
138 base::File::FLAG_WRITE |
139 base::File::FLAG_READ |
140 base::File::FLAG_DELETE_ON_CLOSE |
141 base::File::FLAG_TEMPORARY);
144 base::ScopedTempDir temp_dir_;
145 ServiceUtilityProcessHost* host_;
146 std::queue<base::File> emf_files_;
147 int page_count_;
148 int current_page_;
149 int pages_in_progress_;
152 ServiceUtilityProcessHost::ServiceUtilityProcessHost(
153 Client* client,
154 base::MessageLoopProxy* client_message_loop_proxy)
155 : client_(client),
156 client_message_loop_proxy_(client_message_loop_proxy),
157 waiting_for_reply_(false),
158 weak_ptr_factory_(this) {
159 child_process_host_.reset(ChildProcessHost::Create(this));
162 ServiceUtilityProcessHost::~ServiceUtilityProcessHost() {
163 // We need to kill the child process when the host dies.
164 process_.Terminate(content::RESULT_CODE_NORMAL_EXIT, false);
167 bool ServiceUtilityProcessHost::StartRenderPDFPagesToMetafile(
168 const base::FilePath& pdf_path,
169 const printing::PdfRenderSettings& render_settings) {
170 ReportUmaEvent(SERVICE_UTILITY_METAFILE_REQUEST);
171 start_time_ = base::Time::Now();
172 base::File pdf_file(pdf_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
173 if (!pdf_file.IsValid() || !StartProcess(false))
174 return false;
176 DCHECK(!waiting_for_reply_);
177 waiting_for_reply_ = true;
179 pdf_to_emf_state_.reset(new PdfToEmfState(this));
180 return pdf_to_emf_state_->Start(pdf_file.Pass(), render_settings);
183 bool ServiceUtilityProcessHost::StartGetPrinterCapsAndDefaults(
184 const std::string& printer_name) {
185 ReportUmaEvent(SERVICE_UTILITY_CAPS_REQUEST);
186 start_time_ = base::Time::Now();
187 if (!StartProcess(true))
188 return false;
189 DCHECK(!waiting_for_reply_);
190 waiting_for_reply_ = true;
191 return Send(new ChromeUtilityMsg_GetPrinterCapsAndDefaults(printer_name));
194 bool ServiceUtilityProcessHost::StartGetPrinterSemanticCapsAndDefaults(
195 const std::string& printer_name) {
196 ReportUmaEvent(SERVICE_UTILITY_SEMANTIC_CAPS_REQUEST);
197 start_time_ = base::Time::Now();
198 if (!StartProcess(true))
199 return false;
200 DCHECK(!waiting_for_reply_);
201 waiting_for_reply_ = true;
202 return Send(
203 new ChromeUtilityMsg_GetPrinterSemanticCapsAndDefaults(printer_name));
206 bool ServiceUtilityProcessHost::StartProcess(bool no_sandbox) {
207 std::string channel_id = child_process_host_->CreateChannel();
208 if (channel_id.empty())
209 return false;
211 base::FilePath exe_path = GetUtilityProcessCmd();
212 if (exe_path.empty()) {
213 NOTREACHED() << "Unable to get utility process binary name.";
214 return false;
217 base::CommandLine cmd_line(exe_path);
218 cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kUtilityProcess);
219 cmd_line.AppendSwitchASCII(switches::kProcessChannelID, channel_id);
220 cmd_line.AppendSwitch(switches::kLang);
222 if (Launch(&cmd_line, no_sandbox)) {
223 ReportUmaEvent(SERVICE_UTILITY_STARTED);
224 return true;
226 ReportUmaEvent(SERVICE_UTILITY_FAILED_TO_START);
227 return false;
230 bool ServiceUtilityProcessHost::Launch(base::CommandLine* cmd_line,
231 bool no_sandbox) {
232 if (no_sandbox) {
233 cmd_line->AppendSwitch(switches::kNoSandbox);
234 process_ = base::LaunchProcess(*cmd_line, base::LaunchOptions());
235 } else {
236 ServiceSandboxedProcessLauncherDelegate delegate;
237 process_ = content::StartSandboxedProcess(&delegate, cmd_line);
239 return process_.IsValid();
242 bool ServiceUtilityProcessHost::Send(IPC::Message* msg) {
243 if (child_process_host_)
244 return child_process_host_->Send(msg);
245 delete msg;
246 return false;
249 base::FilePath ServiceUtilityProcessHost::GetUtilityProcessCmd() {
250 #if defined(OS_LINUX)
251 int flags = ChildProcessHost::CHILD_ALLOW_SELF;
252 #else
253 int flags = ChildProcessHost::CHILD_NORMAL;
254 #endif
255 return ChildProcessHost::GetChildPath(flags);
258 void ServiceUtilityProcessHost::OnChildDisconnected() {
259 if (waiting_for_reply_) {
260 // If we are yet to receive a reply then notify the client that the
261 // child died.
262 client_message_loop_proxy_->PostTask(
263 FROM_HERE, base::Bind(&Client::OnChildDied, client_.get()));
264 ReportUmaEvent(SERVICE_UTILITY_DISCONNECTED);
265 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityDisconnectTime",
266 base::Time::Now() - start_time_);
268 delete this;
271 bool ServiceUtilityProcessHost::OnMessageReceived(const IPC::Message& message) {
272 bool handled = true;
273 IPC_BEGIN_MESSAGE_MAP(ServiceUtilityProcessHost, message)
274 IPC_MESSAGE_HANDLER(
275 ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount,
276 OnRenderPDFPagesToMetafilesPageCount)
277 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone,
278 OnRenderPDFPagesToMetafilesPageDone)
279 IPC_MESSAGE_HANDLER(
280 ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Succeeded,
281 OnGetPrinterCapsAndDefaultsSucceeded)
282 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Failed,
283 OnGetPrinterCapsAndDefaultsFailed)
284 IPC_MESSAGE_HANDLER(
285 ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Succeeded,
286 OnGetPrinterSemanticCapsAndDefaultsSucceeded)
287 IPC_MESSAGE_HANDLER(
288 ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Failed,
289 OnGetPrinterSemanticCapsAndDefaultsFailed)
290 IPC_MESSAGE_UNHANDLED(handled = false)
291 IPC_END_MESSAGE_MAP()
292 return handled;
295 const base::Process& ServiceUtilityProcessHost::GetProcess() const {
296 return process_;
299 void ServiceUtilityProcessHost::OnMetafileSpooled(bool success) {
300 if (!success || pdf_to_emf_state_->OnPageProcessed())
301 OnPDFToEmfFinished(success);
304 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafilesPageCount(
305 int page_count) {
306 DCHECK(waiting_for_reply_);
307 if (!pdf_to_emf_state_ || page_count <= 0 ||
308 pdf_to_emf_state_->has_page_count()) {
309 return OnPDFToEmfFinished(false);
311 pdf_to_emf_state_->set_page_count(page_count);
312 pdf_to_emf_state_->GetMorePages();
315 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafilesPageDone(
316 bool success,
317 float scale_factor) {
318 DCHECK(waiting_for_reply_);
319 if (!pdf_to_emf_state_ || !success)
320 return OnPDFToEmfFinished(false);
321 base::File emf_file = pdf_to_emf_state_->TakeNextFile();
322 base::PostTaskAndReplyWithResult(
323 client_message_loop_proxy_.get(), FROM_HERE,
324 base::Bind(&Client::MetafileAvailable, client_.get(), scale_factor,
325 base::Passed(&emf_file)),
326 base::Bind(&ServiceUtilityProcessHost::OnMetafileSpooled,
327 weak_ptr_factory_.GetWeakPtr()));
330 void ServiceUtilityProcessHost::OnPDFToEmfFinished(bool success) {
331 if (!waiting_for_reply_)
332 return;
333 waiting_for_reply_ = false;
334 if (success) {
335 ReportUmaEvent(SERVICE_UTILITY_METAFILE_SUCCEEDED);
336 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileTime",
337 base::Time::Now() - start_time_);
338 } else {
339 ReportUmaEvent(SERVICE_UTILITY_METAFILE_FAILED);
340 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileFailTime",
341 base::Time::Now() - start_time_);
343 client_message_loop_proxy_->PostTask(
344 FROM_HERE,
345 base::Bind(
346 &Client::OnRenderPDFPagesToMetafileDone, client_.get(), success));
347 pdf_to_emf_state_.reset();
350 void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsSucceeded(
351 const std::string& printer_name,
352 const printing::PrinterCapsAndDefaults& caps_and_defaults) {
353 DCHECK(waiting_for_reply_);
354 ReportUmaEvent(SERVICE_UTILITY_CAPS_SUCCEEDED);
355 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsTime",
356 base::Time::Now() - start_time_);
357 waiting_for_reply_ = false;
358 client_message_loop_proxy_->PostTask(
359 FROM_HERE,
360 base::Bind(&Client::OnGetPrinterCapsAndDefaults, client_.get(), true,
361 printer_name, caps_and_defaults));
364 void ServiceUtilityProcessHost::OnGetPrinterSemanticCapsAndDefaultsSucceeded(
365 const std::string& printer_name,
366 const printing::PrinterSemanticCapsAndDefaults& caps_and_defaults) {
367 DCHECK(waiting_for_reply_);
368 ReportUmaEvent(SERVICE_UTILITY_SEMANTIC_CAPS_SUCCEEDED);
369 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilitySemanticCapsTime",
370 base::Time::Now() - start_time_);
371 waiting_for_reply_ = false;
372 client_message_loop_proxy_->PostTask(
373 FROM_HERE,
374 base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults, client_.get(),
375 true, printer_name, caps_and_defaults));
378 void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsFailed(
379 const std::string& printer_name) {
380 DCHECK(waiting_for_reply_);
381 ReportUmaEvent(SERVICE_UTILITY_CAPS_FAILED);
382 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsFailTime",
383 base::Time::Now() - start_time_);
384 waiting_for_reply_ = false;
385 client_message_loop_proxy_->PostTask(
386 FROM_HERE,
387 base::Bind(&Client::OnGetPrinterCapsAndDefaults, client_.get(), false,
388 printer_name, printing::PrinterCapsAndDefaults()));
391 void ServiceUtilityProcessHost::OnGetPrinterSemanticCapsAndDefaultsFailed(
392 const std::string& printer_name) {
393 DCHECK(waiting_for_reply_);
394 ReportUmaEvent(SERVICE_UTILITY_SEMANTIC_CAPS_FAILED);
395 UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilitySemanticCapsFailTime",
396 base::Time::Now() - start_time_);
397 waiting_for_reply_ = false;
398 client_message_loop_proxy_->PostTask(
399 FROM_HERE,
400 base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults,
401 client_.get(), false, printer_name,
402 printing::PrinterSemanticCapsAndDefaults()));
405 bool ServiceUtilityProcessHost::Client::MetafileAvailable(float scale_factor,
406 base::File file) {
407 file.Seek(base::File::FROM_BEGIN, 0);
408 int64 size = file.GetLength();
409 if (size <= 0) {
410 OnRenderPDFPagesToMetafileDone(false);
411 return false;
413 std::vector<char> data(size);
414 if (file.ReadAtCurrentPos(data.data(), data.size()) != size) {
415 OnRenderPDFPagesToMetafileDone(false);
416 return false;
418 printing::Emf emf;
419 if (!emf.InitFromData(data.data(), data.size())) {
420 OnRenderPDFPagesToMetafileDone(false);
421 return false;
423 OnRenderPDFPagesToMetafilePageDone(scale_factor, emf);
424 return true;