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