Upstream version 10.38.208.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_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"
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 #include "sandbox/win/src/sandbox_policy_base.h"
36
37 namespace {
38
39 // NOTE: changes to this class need to be reviewed by the security team.
40 class ServiceSandboxedProcessLauncherDelegate
41     : public content::SandboxedProcessLauncherDelegate {
42  public:
43   explicit ServiceSandboxedProcessLauncherDelegate(
44       const base::FilePath& exposed_dir)
45     : exposed_dir_(exposed_dir) {
46   }
47
48   virtual void PreSandbox(bool* disable_default_policy,
49                           base::FilePath* exposed_dir) OVERRIDE {
50     *exposed_dir = exposed_dir_;
51   }
52
53   virtual void PreSpawnTarget(sandbox::TargetPolicy* policy,
54                               bool* success) OVERRIDE {
55     // Service process may run as windows service and it fails to create a
56     // window station.
57     policy->SetAlternateDesktop(false);
58   }
59
60  private:
61   base::FilePath exposed_dir_;
62 };
63
64 }  // namespace
65
66 #endif  // OS_WIN
67
68 using content::ChildProcessHost;
69
70 namespace {
71 enum ServiceUtilityProcessHostEvent {
72   SERVICE_UTILITY_STARTED,
73   SERVICE_UTILITY_DISCONNECTED,
74   SERVICE_UTILITY_METAFILE_REQUEST,
75   SERVICE_UTILITY_METAFILE_SUCCEEDED,
76   SERVICE_UTILITY_METAFILE_FAILED,
77   SERVICE_UTILITY_CAPS_REQUEST,
78   SERVICE_UTILITY_CAPS_SUCCEEDED,
79   SERVICE_UTILITY_CAPS_FAILED,
80   SERVICE_UTILITY_SEMANTIC_CAPS_REQUEST,
81   SERVICE_UTILITY_SEMANTIC_CAPS_SUCCEEDED,
82   SERVICE_UTILITY_SEMANTIC_CAPS_FAILED,
83   SERVICE_UTILITY_FAILED_TO_START,
84   SERVICE_UTILITY_EVENT_MAX,
85 };
86 }  // namespace
87
88 ServiceUtilityProcessHost::ServiceUtilityProcessHost(
89     Client* client, base::MessageLoopProxy* client_message_loop_proxy)
90         : handle_(base::kNullProcessHandle),
91           client_(client),
92           client_message_loop_proxy_(client_message_loop_proxy),
93           waiting_for_reply_(false) {
94   child_process_host_.reset(ChildProcessHost::Create(this));
95 }
96
97 ServiceUtilityProcessHost::~ServiceUtilityProcessHost() {
98   // We need to kill the child process when the host dies.
99   base::KillProcess(handle_, content::RESULT_CODE_NORMAL_EXIT, false);
100 }
101
102 bool ServiceUtilityProcessHost::StartRenderPDFPagesToMetafile(
103     const base::FilePath& pdf_path,
104     const printing::PdfRenderSettings& render_settings,
105     const std::vector<printing::PageRange>& page_ranges) {
106   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
107                             SERVICE_UTILITY_METAFILE_REQUEST,
108                             SERVICE_UTILITY_EVENT_MAX);
109   start_time_ = base::Time::Now();
110 #if !defined(OS_WIN)
111   // This is only implemented on Windows (because currently it is only needed
112   // on Windows). Will add implementations on other platforms when needed.
113   NOTIMPLEMENTED();
114   return false;
115 #else  // !defined(OS_WIN)
116   scratch_metafile_dir_.reset(new base::ScopedTempDir);
117   if (!scratch_metafile_dir_->CreateUniqueTempDir())
118     return false;
119   metafile_path_ = scratch_metafile_dir_->path().AppendASCII("output.emf");
120   if (!StartProcess(false, scratch_metafile_dir_->path()))
121     return false;
122
123   base::File pdf_file(
124       pdf_path,
125       base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE);
126   DCHECK(!waiting_for_reply_);
127   waiting_for_reply_ = true;
128   return child_process_host_->Send(
129       new ChromeUtilityMsg_RenderPDFPagesToMetafiles(
130           IPC::TakeFileHandleForProcess(pdf_file.Pass(), handle()),
131           metafile_path_,
132           render_settings,
133           page_ranges));
134 #endif  // !defined(OS_WIN)
135 }
136
137 bool ServiceUtilityProcessHost::StartGetPrinterCapsAndDefaults(
138     const std::string& printer_name) {
139   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
140                             SERVICE_UTILITY_CAPS_REQUEST,
141                             SERVICE_UTILITY_EVENT_MAX);
142   start_time_ = base::Time::Now();
143   base::FilePath exposed_path;
144   if (!StartProcess(true, exposed_path))
145     return false;
146   DCHECK(!waiting_for_reply_);
147   waiting_for_reply_ = true;
148   return child_process_host_->Send(
149       new ChromeUtilityMsg_GetPrinterCapsAndDefaults(printer_name));
150 }
151
152 bool ServiceUtilityProcessHost::StartGetPrinterSemanticCapsAndDefaults(
153     const std::string& printer_name) {
154   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
155                             SERVICE_UTILITY_SEMANTIC_CAPS_REQUEST,
156                             SERVICE_UTILITY_EVENT_MAX);
157   start_time_ = base::Time::Now();
158   base::FilePath exposed_path;
159   if (!StartProcess(true, exposed_path))
160     return false;
161   DCHECK(!waiting_for_reply_);
162   waiting_for_reply_ = true;
163   return child_process_host_->Send(
164       new ChromeUtilityMsg_GetPrinterSemanticCapsAndDefaults(printer_name));
165 }
166
167 bool ServiceUtilityProcessHost::StartProcess(
168     bool no_sandbox,
169     const base::FilePath& exposed_dir) {
170   std::string channel_id = child_process_host_->CreateChannel();
171   if (channel_id.empty())
172     return false;
173
174   base::FilePath exe_path = GetUtilityProcessCmd();
175   if (exe_path.empty()) {
176     NOTREACHED() << "Unable to get utility process binary name.";
177     return false;
178   }
179
180   CommandLine cmd_line(exe_path);
181   cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kUtilityProcess);
182   cmd_line.AppendSwitchASCII(switches::kProcessChannelID, channel_id);
183   cmd_line.AppendSwitch(switches::kLang);
184
185   if (Launch(&cmd_line, no_sandbox, exposed_dir)) {
186     UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
187                               SERVICE_UTILITY_STARTED,
188                               SERVICE_UTILITY_EVENT_MAX);
189     return true;
190   }
191   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
192                             SERVICE_UTILITY_FAILED_TO_START,
193                             SERVICE_UTILITY_EVENT_MAX);
194   return false;
195 }
196
197 bool ServiceUtilityProcessHost::Launch(CommandLine* cmd_line,
198                                        bool no_sandbox,
199                                        const base::FilePath& exposed_dir) {
200 #if !defined(OS_WIN)
201   // TODO(sanjeevr): Implement for non-Windows OSes.
202   NOTIMPLEMENTED();
203   return false;
204 #else  // !defined(OS_WIN)
205
206   if (no_sandbox) {
207     base::ProcessHandle process = base::kNullProcessHandle;
208     cmd_line->AppendSwitch(switches::kNoSandbox);
209     base::LaunchProcess(*cmd_line, base::LaunchOptions(), &handle_);
210   } else {
211     ServiceSandboxedProcessLauncherDelegate delegate(exposed_dir);
212     handle_ = content::StartSandboxedProcess(&delegate, cmd_line);
213   }
214   return (handle_ != base::kNullProcessHandle);
215 #endif  // !defined(OS_WIN)
216 }
217
218 base::FilePath ServiceUtilityProcessHost::GetUtilityProcessCmd() {
219 #if defined(OS_LINUX)
220   int flags = ChildProcessHost::CHILD_ALLOW_SELF;
221 #else
222   int flags = ChildProcessHost::CHILD_NORMAL;
223 #endif
224   return ChildProcessHost::GetChildPath(flags);
225 }
226
227 void ServiceUtilityProcessHost::OnChildDisconnected() {
228   if (waiting_for_reply_) {
229     // If we are yet to receive a reply then notify the client that the
230     // child died.
231     client_message_loop_proxy_->PostTask(
232         FROM_HERE, base::Bind(&Client::OnChildDied, client_.get()));
233     UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
234                               SERVICE_UTILITY_DISCONNECTED,
235                               SERVICE_UTILITY_EVENT_MAX);
236     UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityDisconnectTime",
237                         base::Time::Now() - start_time_);
238   }
239   delete this;
240 }
241
242 bool ServiceUtilityProcessHost::OnMessageReceived(const IPC::Message& message) {
243   bool handled = true;
244   IPC_BEGIN_MESSAGE_MAP(ServiceUtilityProcessHost, message)
245 #if defined(WIN_PDF_METAFILE_FOR_PRINTING)
246     IPC_MESSAGE_HANDLER(
247         ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_Succeeded,
248         OnRenderPDFPagesToMetafilesSucceeded)
249     IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed,
250                         OnRenderPDFPagesToMetafileFailed)
251 #endif
252     IPC_MESSAGE_HANDLER(
253         ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Succeeded,
254         OnGetPrinterCapsAndDefaultsSucceeded)
255     IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Failed,
256                         OnGetPrinterCapsAndDefaultsFailed)
257     IPC_MESSAGE_HANDLER(
258         ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Succeeded,
259         OnGetPrinterSemanticCapsAndDefaultsSucceeded)
260     IPC_MESSAGE_HANDLER(
261         ChromeUtilityHostMsg_GetPrinterSemanticCapsAndDefaults_Failed,
262         OnGetPrinterSemanticCapsAndDefaultsFailed)
263     IPC_MESSAGE_UNHANDLED(handled = false)
264   IPC_END_MESSAGE_MAP()
265   return handled;
266 }
267
268 base::ProcessHandle ServiceUtilityProcessHost::GetHandle() const {
269   return handle_;
270 }
271
272 #if defined(WIN_PDF_METAFILE_FOR_PRINTING)
273 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafilesSucceeded(
274     const std::vector<printing::PageRange>& page_ranges,
275     double scale_factor) {
276   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
277                             SERVICE_UTILITY_METAFILE_SUCCEEDED,
278                             SERVICE_UTILITY_EVENT_MAX);
279   UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileTime",
280                       base::Time::Now() - start_time_);
281   DCHECK(waiting_for_reply_);
282   waiting_for_reply_ = false;
283   // If the metafile was successfully created, we need to take our hands off the
284   // scratch metafile directory. The client will delete it when it is done with
285   // metafile.
286   scratch_metafile_dir_->Take();
287
288   // TODO(vitalybuka|scottmg): http://crbug.com/170859: Currently, only one
289   // page is printed at a time. This would need to be refactored to change
290   // this.
291   CHECK_EQ(1u, page_ranges.size());
292   CHECK_EQ(page_ranges[0].from, page_ranges[0].to);
293   int page_number = page_ranges[0].from;
294   client_message_loop_proxy_->PostTask(
295       FROM_HERE,
296       base::Bind(&Client::MetafileAvailable,
297                  client_.get(),
298                  metafile_path_.InsertBeforeExtensionASCII(
299                      base::StringPrintf(".%d", page_number)),
300                  page_number,
301                  scale_factor));
302 }
303
304 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafileFailed() {
305   DCHECK(waiting_for_reply_);
306   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
307                             SERVICE_UTILITY_METAFILE_FAILED,
308                             SERVICE_UTILITY_EVENT_MAX);
309   UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileFailTime",
310                       base::Time::Now() - start_time_);
311   waiting_for_reply_ = false;
312   client_message_loop_proxy_->PostTask(
313       FROM_HERE,
314       base::Bind(&Client::OnRenderPDFPagesToMetafileFailed, client_.get()));
315 }
316 #endif  // defined(WIN_PDF_METAFILE_FOR_PRINTING)
317
318 void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsSucceeded(
319     const std::string& printer_name,
320     const printing::PrinterCapsAndDefaults& caps_and_defaults) {
321   DCHECK(waiting_for_reply_);
322   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
323                             SERVICE_UTILITY_CAPS_SUCCEEDED,
324                             SERVICE_UTILITY_EVENT_MAX);
325   UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsTime",
326                       base::Time::Now() - start_time_);
327   waiting_for_reply_ = false;
328   client_message_loop_proxy_->PostTask(
329       FROM_HERE,
330       base::Bind(&Client::OnGetPrinterCapsAndDefaults, client_.get(), true,
331                  printer_name, caps_and_defaults));
332 }
333
334 void ServiceUtilityProcessHost::OnGetPrinterSemanticCapsAndDefaultsSucceeded(
335     const std::string& printer_name,
336     const printing::PrinterSemanticCapsAndDefaults& caps_and_defaults) {
337   DCHECK(waiting_for_reply_);
338   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
339                             SERVICE_UTILITY_SEMANTIC_CAPS_SUCCEEDED,
340                             SERVICE_UTILITY_EVENT_MAX);
341   UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilitySemanticCapsTime",
342                       base::Time::Now() - start_time_);
343   waiting_for_reply_ = false;
344   client_message_loop_proxy_->PostTask(
345       FROM_HERE,
346       base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults, client_.get(),
347                  true, printer_name, caps_and_defaults));
348 }
349
350 void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsFailed(
351     const std::string& printer_name) {
352   DCHECK(waiting_for_reply_);
353   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
354                             SERVICE_UTILITY_CAPS_FAILED,
355                             SERVICE_UTILITY_EVENT_MAX);
356   UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsFailTime",
357                       base::Time::Now() - start_time_);
358   waiting_for_reply_ = false;
359   client_message_loop_proxy_->PostTask(
360       FROM_HERE,
361       base::Bind(&Client::OnGetPrinterCapsAndDefaults, client_.get(), false,
362                  printer_name, printing::PrinterCapsAndDefaults()));
363 }
364
365 void ServiceUtilityProcessHost::OnGetPrinterSemanticCapsAndDefaultsFailed(
366     const std::string& printer_name) {
367   DCHECK(waiting_for_reply_);
368   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
369                             SERVICE_UTILITY_SEMANTIC_CAPS_FAILED,
370                             SERVICE_UTILITY_EVENT_MAX);
371   UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilitySemanticCapsFailTime",
372                       base::Time::Now() - start_time_);
373   waiting_for_reply_ = false;
374   client_message_loop_proxy_->PostTask(
375       FROM_HERE,
376       base::Bind(&Client::OnGetPrinterSemanticCapsAndDefaults,
377                  client_.get(), false, printer_name,
378                  printing::PrinterSemanticCapsAndDefaults()));
379 }
380
381 void ServiceUtilityProcessHost::Client::MetafileAvailable(
382     const base::FilePath& metafile_path,
383     int highest_rendered_page_number,
384     double scale_factor) {
385   // The metafile was created in a temp folder which needs to get deleted after
386   // we have processed it.
387   base::ScopedTempDir scratch_metafile_dir;
388   if (!scratch_metafile_dir.Set(metafile_path.DirName()))
389     LOG(WARNING) << "Unable to set scratch metafile directory";
390 #if defined(OS_WIN)
391   // It's important that metafile is declared after scratch_metafile_dir so
392   // that the metafile destructor closes the file before the base::ScopedTempDir
393   // destructor tries to remove the directory.
394   printing::Emf metafile;
395   if (!metafile.InitFromFile(metafile_path)) {
396     OnRenderPDFPagesToMetafileFailed();
397   } else {
398     OnRenderPDFPagesToMetafileSucceeded(metafile,
399                                         highest_rendered_page_number,
400                                         scale_factor);
401   }
402 #endif  // defined(OS_WIN)
403 }
404