Upstream version 5.34.104.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 "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_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"
26
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"
35
36 namespace {
37
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) {
45   }
46
47   virtual void PreSandbox(bool* disable_default_policy,
48                           base::FilePath* exposed_dir) OVERRIDE {
49     *exposed_dir = exposed_dir_;
50   }
51
52  private:
53   base::FilePath exposed_dir_;
54 };
55
56 }  // namespace
57
58 #endif  // OS_WIN
59
60 using content::ChildProcessHost;
61
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,
76 };
77 }  // namespace
78
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));
86 }
87
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);
91 }
92
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   if (!base::CreateTemporaryFileInDir(scratch_metafile_dir_->path(),
111                                       &metafile_path_)) {
112     return false;
113   }
114
115   if (!StartProcess(false, scratch_metafile_dir_->path()))
116     return false;
117
118   base::win::ScopedHandle pdf_file(
119       ::CreateFile(pdf_path.value().c_str(),
120                    GENERIC_READ,
121                    FILE_SHARE_READ | FILE_SHARE_WRITE,
122                    NULL,
123                    OPEN_EXISTING,
124                    FILE_ATTRIBUTE_NORMAL,
125                    NULL));
126   if (pdf_file == INVALID_HANDLE_VALUE)
127     return false;
128   HANDLE pdf_file_in_utility_process = NULL;
129   ::DuplicateHandle(::GetCurrentProcess(), pdf_file, handle(),
130                     &pdf_file_in_utility_process, 0, false,
131                     DUPLICATE_SAME_ACCESS);
132   if (!pdf_file_in_utility_process)
133     return false;
134   DCHECK(!waiting_for_reply_);
135   waiting_for_reply_ = true;
136   return child_process_host_->Send(
137       new ChromeUtilityMsg_RenderPDFPagesToMetafile(
138           pdf_file_in_utility_process,
139           metafile_path_,
140           render_settings,
141           page_ranges));
142 #endif  // !defined(OS_WIN)
143 }
144
145 bool ServiceUtilityProcessHost::StartGetPrinterCapsAndDefaults(
146     const std::string& printer_name) {
147   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
148                             SERVICE_UTILITY_CAPS_REQUEST,
149                             SERVICE_UTILITY_EVENT_MAX);
150   start_time_ = base::Time::Now();
151   base::FilePath exposed_path;
152   if (!StartProcess(true, exposed_path))
153     return false;
154   DCHECK(!waiting_for_reply_);
155   waiting_for_reply_ = true;
156   return child_process_host_->Send(
157       new ChromeUtilityMsg_GetPrinterCapsAndDefaults(printer_name));
158 }
159
160 bool ServiceUtilityProcessHost::StartGetPrinterSemanticCapsAndDefaults(
161     const std::string& printer_name) {
162   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
163                             SERVICE_UTILITY_SEMANTIC_CAPS_REQUEST,
164                             SERVICE_UTILITY_EVENT_MAX);
165   start_time_ = base::Time::Now();
166   base::FilePath exposed_path;
167   if (!StartProcess(true, exposed_path))
168     return false;
169   DCHECK(!waiting_for_reply_);
170   waiting_for_reply_ = true;
171   return child_process_host_->Send(
172       new ChromeUtilityMsg_GetPrinterSemanticCapsAndDefaults(printer_name));
173 }
174
175 bool ServiceUtilityProcessHost::StartProcess(
176     bool no_sandbox,
177     const base::FilePath& exposed_dir) {
178   std::string channel_id = child_process_host_->CreateChannel();
179   if (channel_id.empty())
180     return false;
181
182   base::FilePath exe_path = GetUtilityProcessCmd();
183   if (exe_path.empty()) {
184     NOTREACHED() << "Unable to get utility process binary name.";
185     return false;
186   }
187
188   CommandLine cmd_line(exe_path);
189   cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kUtilityProcess);
190   cmd_line.AppendSwitchASCII(switches::kProcessChannelID, channel_id);
191   cmd_line.AppendSwitch(switches::kLang);
192
193   if (Launch(&cmd_line, no_sandbox, exposed_dir)) {
194     UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
195                               SERVICE_UTILITY_STARTED,
196                               SERVICE_UTILITY_EVENT_MAX);
197     return true;
198   }
199   return false;
200 }
201
202 bool ServiceUtilityProcessHost::Launch(CommandLine* cmd_line,
203                                        bool no_sandbox,
204                                        const base::FilePath& exposed_dir) {
205 #if !defined(OS_WIN)
206   // TODO(sanjeevr): Implement for non-Windows OSes.
207   NOTIMPLEMENTED();
208   return false;
209 #else  // !defined(OS_WIN)
210
211   if (no_sandbox) {
212     base::ProcessHandle process = base::kNullProcessHandle;
213     cmd_line->AppendSwitch(switches::kNoSandbox);
214     base::LaunchProcess(*cmd_line, base::LaunchOptions(), &handle_);
215   } else {
216     ServiceSandboxedProcessLauncherDelegate delegate(exposed_dir);
217     handle_ = content::StartSandboxedProcess(&delegate, cmd_line);
218   }
219   return (handle_ != base::kNullProcessHandle);
220 #endif  // !defined(OS_WIN)
221 }
222
223 base::FilePath ServiceUtilityProcessHost::GetUtilityProcessCmd() {
224 #if defined(OS_LINUX)
225   int flags = ChildProcessHost::CHILD_ALLOW_SELF;
226 #else
227   int flags = ChildProcessHost::CHILD_NORMAL;
228 #endif
229   return ChildProcessHost::GetChildPath(flags);
230 }
231
232 void ServiceUtilityProcessHost::OnChildDisconnected() {
233   if (waiting_for_reply_) {
234     // If we are yet to receive a reply then notify the client that the
235     // child died.
236     client_message_loop_proxy_->PostTask(
237         FROM_HERE, base::Bind(&Client::OnChildDied, client_.get()));
238     UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
239                               SERVICE_UTILITY_DISCONNECTED,
240                               SERVICE_UTILITY_EVENT_MAX);
241     UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityDisconnectTime",
242                         base::Time::Now() - start_time_);
243   }
244   delete this;
245 }
246
247 bool ServiceUtilityProcessHost::OnMessageReceived(const IPC::Message& message) {
248   bool handled = true;
249   IPC_BEGIN_MESSAGE_MAP(ServiceUtilityProcessHost, message)
250     IPC_MESSAGE_HANDLER(
251         ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Succeeded,
252         OnRenderPDFPagesToMetafileSucceeded)
253     IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed,
254                         OnRenderPDFPagesToMetafileFailed)
255     IPC_MESSAGE_HANDLER(
256         ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Succeeded,
257         OnGetPrinterCapsAndDefaultsSucceeded)
258     IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Failed,
259                         OnGetPrinterCapsAndDefaultsFailed)
260     IPC_MESSAGE_HANDLER(
261         ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Succeeded,
262         OnGetPrinterSemanticCapsAndDefaultsSucceeded)
263     IPC_MESSAGE_HANDLER(
264         ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Failed,
265         OnGetPrinterSemanticCapsAndDefaultsFailed)
266     IPC_MESSAGE_UNHANDLED(handled = false)
267   IPC_END_MESSAGE_MAP()
268   return handled;
269 }
270
271 base::ProcessHandle ServiceUtilityProcessHost::GetHandle() const {
272   return handle_;
273 }
274
275 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafileSucceeded(
276     int highest_rendered_page_number,
277     double scale_factor) {
278   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
279                             SERVICE_UTILITY_METAFILE_SUCCEEDED,
280                             SERVICE_UTILITY_EVENT_MAX);
281   UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileTime",
282                       base::Time::Now() - start_time_);
283   DCHECK(waiting_for_reply_);
284   waiting_for_reply_ = false;
285   // If the metafile was successfully created, we need to take our hands off the
286   // scratch metafile directory. The client will delete it when it is done with
287   // metafile.
288   scratch_metafile_dir_->Take();
289   client_message_loop_proxy_->PostTask(
290       FROM_HERE,
291       base::Bind(&Client::MetafileAvailable, client_.get(), metafile_path_,
292                  highest_rendered_page_number, scale_factor));
293 }
294
295 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafileFailed() {
296   DCHECK(waiting_for_reply_);
297   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
298                             SERVICE_UTILITY_METAFILE_FAILED,
299                             SERVICE_UTILITY_EVENT_MAX);
300   UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileFailTime",
301                       base::Time::Now() - start_time_);
302   waiting_for_reply_ = false;
303   client_message_loop_proxy_->PostTask(
304       FROM_HERE,
305       base::Bind(&Client::OnRenderPDFPagesToMetafileFailed, client_.get()));
306 }
307
308 void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsSucceeded(
309     const std::string& printer_name,
310     const printing::PrinterCapsAndDefaults& caps_and_defaults) {
311   DCHECK(waiting_for_reply_);
312   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
313                             SERVICE_UTILITY_CAPS_SUCCEEDED,
314                             SERVICE_UTILITY_EVENT_MAX);
315   UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsTime",
316                       base::Time::Now() - start_time_);
317   waiting_for_reply_ = false;
318   client_message_loop_proxy_->PostTask(
319       FROM_HERE,
320       base::Bind(&Client::OnGetPrinterCapsAndDefaults, client_.get(), true,
321                  printer_name, caps_and_defaults));
322 }
323
324 void ServiceUtilityProcessHost::OnGetPrinterSemanticCapsAndDefaultsSucceeded(
325     const std::string& printer_name,
326     const printing::PrinterSemanticCapsAndDefaults& caps_and_defaults) {
327   DCHECK(waiting_for_reply_);
328   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
329                             SERVICE_UTILITY_SEMANTIC_CAPS_SUCCEEDED,
330                             SERVICE_UTILITY_EVENT_MAX);
331   UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilitySemanticCapsTime",
332                       base::Time::Now() - start_time_);
333   waiting_for_reply_ = false;
334   client_message_loop_proxy_->PostTask(
335       FROM_HERE,
336       base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults, client_.get(),
337                  true, printer_name, caps_and_defaults));
338 }
339
340 void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsFailed(
341     const std::string& printer_name) {
342   DCHECK(waiting_for_reply_);
343   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
344                             SERVICE_UTILITY_CAPS_FAILED,
345                             SERVICE_UTILITY_EVENT_MAX);
346   UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsFailTime",
347                       base::Time::Now() - start_time_);
348   waiting_for_reply_ = false;
349   client_message_loop_proxy_->PostTask(
350       FROM_HERE,
351       base::Bind(&Client::OnGetPrinterCapsAndDefaults, client_.get(), false,
352                  printer_name, printing::PrinterCapsAndDefaults()));
353 }
354
355 void ServiceUtilityProcessHost::OnGetPrinterSemanticCapsAndDefaultsFailed(
356     const std::string& printer_name) {
357   DCHECK(waiting_for_reply_);
358   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
359                             SERVICE_UTILITY_SEMANTIC_CAPS_FAILED,
360                             SERVICE_UTILITY_EVENT_MAX);
361   UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilitySemanticCapsFailTime",
362                       base::Time::Now() - start_time_);
363   waiting_for_reply_ = false;
364   client_message_loop_proxy_->PostTask(
365       FROM_HERE,
366       base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults,
367                  client_.get(), false, printer_name,
368                  printing::PrinterSemanticCapsAndDefaults()));
369 }
370
371 void ServiceUtilityProcessHost::Client::MetafileAvailable(
372     const base::FilePath& metafile_path,
373     int highest_rendered_page_number,
374     double scale_factor) {
375   // The metafile was created in a temp folder which needs to get deleted after
376   // we have processed it.
377   base::ScopedTempDir scratch_metafile_dir;
378   if (!scratch_metafile_dir.Set(metafile_path.DirName()))
379     LOG(WARNING) << "Unable to set scratch metafile directory";
380 #if defined(OS_WIN)
381   // It's important that metafile is declared after scratch_metafile_dir so
382   // that the metafile destructor closes the file before the base::ScopedTempDir
383   // destructor tries to remove the directory.
384   printing::Emf metafile;
385   if (!metafile.InitFromFile(metafile_path)) {
386     OnRenderPDFPagesToMetafileFailed();
387   } else {
388     OnRenderPDFPagesToMetafileSucceeded(metafile,
389                                         highest_rendered_page_number,
390                                         scale_factor);
391   }
392 #endif  // defined(OS_WIN)
393 }
394