- add sources.
[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_EVENT_MAX,
73 };
74 }  // namespace
75
76 ServiceUtilityProcessHost::ServiceUtilityProcessHost(
77     Client* client, base::MessageLoopProxy* client_message_loop_proxy)
78         : handle_(base::kNullProcessHandle),
79           client_(client),
80           client_message_loop_proxy_(client_message_loop_proxy),
81           waiting_for_reply_(false) {
82   child_process_host_.reset(ChildProcessHost::Create(this));
83 }
84
85 ServiceUtilityProcessHost::~ServiceUtilityProcessHost() {
86   // We need to kill the child process when the host dies.
87   base::KillProcess(handle_, content::RESULT_CODE_NORMAL_EXIT, false);
88 }
89
90 bool ServiceUtilityProcessHost::StartRenderPDFPagesToMetafile(
91     const base::FilePath& pdf_path,
92     const printing::PdfRenderSettings& render_settings,
93     const std::vector<printing::PageRange>& page_ranges) {
94   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
95                             SERVICE_UTILITY_METAFILE_REQUEST,
96                             SERVICE_UTILITY_EVENT_MAX);
97   start_time_ = base::Time::Now();
98 #if !defined(OS_WIN)
99   // This is only implemented on Windows (because currently it is only needed
100   // on Windows). Will add implementations on other platforms when needed.
101   NOTIMPLEMENTED();
102   return false;
103 #else  // !defined(OS_WIN)
104   scratch_metafile_dir_.reset(new base::ScopedTempDir);
105   if (!scratch_metafile_dir_->CreateUniqueTempDir())
106     return false;
107   if (!file_util::CreateTemporaryFileInDir(scratch_metafile_dir_->path(),
108                                            &metafile_path_)) {
109     return false;
110   }
111
112   if (!StartProcess(false, scratch_metafile_dir_->path()))
113     return false;
114
115   base::win::ScopedHandle pdf_file(
116       ::CreateFile(pdf_path.value().c_str(),
117                    GENERIC_READ,
118                    FILE_SHARE_READ | FILE_SHARE_WRITE,
119                    NULL,
120                    OPEN_EXISTING,
121                    FILE_ATTRIBUTE_NORMAL,
122                    NULL));
123   if (pdf_file == INVALID_HANDLE_VALUE)
124     return false;
125   HANDLE pdf_file_in_utility_process = NULL;
126   ::DuplicateHandle(::GetCurrentProcess(), pdf_file, handle(),
127                     &pdf_file_in_utility_process, 0, false,
128                     DUPLICATE_SAME_ACCESS);
129   if (!pdf_file_in_utility_process)
130     return false;
131   waiting_for_reply_ = true;
132   return child_process_host_->Send(
133       new ChromeUtilityMsg_RenderPDFPagesToMetafile(
134           pdf_file_in_utility_process,
135           metafile_path_,
136           render_settings,
137           page_ranges));
138 #endif  // !defined(OS_WIN)
139 }
140
141 bool ServiceUtilityProcessHost::StartGetPrinterCapsAndDefaults(
142     const std::string& printer_name) {
143   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
144                             SERVICE_UTILITY_CAPS_REQUEST,
145                             SERVICE_UTILITY_EVENT_MAX);
146   start_time_ = base::Time::Now();
147   base::FilePath exposed_path;
148   if (!StartProcess(true, exposed_path))
149     return false;
150   waiting_for_reply_ = true;
151   return child_process_host_->Send(
152       new ChromeUtilityMsg_GetPrinterCapsAndDefaults(printer_name));
153 }
154
155 bool ServiceUtilityProcessHost::StartProcess(
156     bool no_sandbox,
157     const base::FilePath& exposed_dir) {
158   std::string channel_id = child_process_host_->CreateChannel();
159   if (channel_id.empty())
160     return false;
161
162   base::FilePath exe_path = GetUtilityProcessCmd();
163   if (exe_path.empty()) {
164     NOTREACHED() << "Unable to get utility process binary name.";
165     return false;
166   }
167
168   CommandLine cmd_line(exe_path);
169   cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kUtilityProcess);
170   cmd_line.AppendSwitchASCII(switches::kProcessChannelID, channel_id);
171   cmd_line.AppendSwitch(switches::kLang);
172
173   if (Launch(&cmd_line, no_sandbox, exposed_dir)) {
174     UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
175                               SERVICE_UTILITY_STARTED,
176                               SERVICE_UTILITY_EVENT_MAX);
177     return true;
178   }
179   return false;
180 }
181
182 bool ServiceUtilityProcessHost::Launch(CommandLine* cmd_line,
183                                        bool no_sandbox,
184                                        const base::FilePath& exposed_dir) {
185 #if !defined(OS_WIN)
186   // TODO(sanjeevr): Implement for non-Windows OSes.
187   NOTIMPLEMENTED();
188   return false;
189 #else  // !defined(OS_WIN)
190
191   if (no_sandbox) {
192     base::ProcessHandle process = base::kNullProcessHandle;
193     cmd_line->AppendSwitch(switches::kNoSandbox);
194     base::LaunchProcess(*cmd_line, base::LaunchOptions(), &handle_);
195   } else {
196     ServiceSandboxedProcessLauncherDelegate delegate(exposed_dir);
197     handle_ = content::StartSandboxedProcess(&delegate, cmd_line);
198   }
199   return (handle_ != base::kNullProcessHandle);
200 #endif  // !defined(OS_WIN)
201 }
202
203 base::FilePath ServiceUtilityProcessHost::GetUtilityProcessCmd() {
204 #if defined(OS_LINUX)
205   int flags = ChildProcessHost::CHILD_ALLOW_SELF;
206 #else
207   int flags = ChildProcessHost::CHILD_NORMAL;
208 #endif
209   return ChildProcessHost::GetChildPath(flags);
210 }
211
212 void ServiceUtilityProcessHost::OnChildDisconnected() {
213   if (waiting_for_reply_) {
214     // If we are yet to receive a reply then notify the client that the
215     // child died.
216     client_message_loop_proxy_->PostTask(
217         FROM_HERE, base::Bind(&Client::OnChildDied, client_.get()));
218     UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
219                               SERVICE_UTILITY_DISCONNECTED,
220                               SERVICE_UTILITY_EVENT_MAX);
221     UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityDisconnectTime",
222                         base::Time::Now() - start_time_);
223   }
224   delete this;
225 }
226
227 bool ServiceUtilityProcessHost::OnMessageReceived(const IPC::Message& message) {
228   bool handled = true;
229   IPC_BEGIN_MESSAGE_MAP(ServiceUtilityProcessHost, message)
230     IPC_MESSAGE_HANDLER(
231         ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Succeeded,
232         OnRenderPDFPagesToMetafileSucceeded)
233     IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed,
234                         OnRenderPDFPagesToMetafileFailed)
235     IPC_MESSAGE_HANDLER(
236         ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Succeeded,
237         OnGetPrinterCapsAndDefaultsSucceeded)
238     IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_GetPrinterCapsAndDefaults_Failed,
239                         OnGetPrinterCapsAndDefaultsFailed)
240     IPC_MESSAGE_UNHANDLED(handled = false)
241   IPC_END_MESSAGE_MAP()
242   return handled;
243 }
244
245 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafileSucceeded(
246     int highest_rendered_page_number,
247     double scale_factor) {
248   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
249                             SERVICE_UTILITY_METAFILE_SUCCEEDED,
250                             SERVICE_UTILITY_EVENT_MAX);
251   UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileTime",
252                       base::Time::Now() - start_time_);
253   DCHECK(waiting_for_reply_);
254   waiting_for_reply_ = false;
255   // If the metafile was successfully created, we need to take our hands off the
256   // scratch metafile directory. The client will delete it when it is done with
257   // metafile.
258   scratch_metafile_dir_->Take();
259   client_message_loop_proxy_->PostTask(
260       FROM_HERE,
261       base::Bind(&Client::MetafileAvailable, client_.get(), metafile_path_,
262                  highest_rendered_page_number, scale_factor));
263 }
264
265 void ServiceUtilityProcessHost::OnRenderPDFPagesToMetafileFailed() {
266   DCHECK(waiting_for_reply_);
267   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
268                             SERVICE_UTILITY_METAFILE_FAILED,
269                             SERVICE_UTILITY_EVENT_MAX);
270   UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityMetafileFailTime",
271                       base::Time::Now() - start_time_);
272   waiting_for_reply_ = false;
273   client_message_loop_proxy_->PostTask(
274       FROM_HERE,
275       base::Bind(&Client::OnRenderPDFPagesToMetafileFailed, client_.get()));
276 }
277
278 void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsSucceeded(
279     const std::string& printer_name,
280     const printing::PrinterCapsAndDefaults& caps_and_defaults) {
281   DCHECK(waiting_for_reply_);
282   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
283                             SERVICE_UTILITY_CAPS_SUCCEEDED,
284                             SERVICE_UTILITY_EVENT_MAX);
285   UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsTime",
286                       base::Time::Now() - start_time_);
287   waiting_for_reply_ = false;
288   client_message_loop_proxy_->PostTask(
289       FROM_HERE,
290       base::Bind(&Client::OnGetPrinterCapsAndDefaultsSucceeded, client_.get(),
291                  printer_name, caps_and_defaults));
292 }
293
294 void ServiceUtilityProcessHost::OnGetPrinterCapsAndDefaultsFailed(
295     const std::string& printer_name) {
296   DCHECK(waiting_for_reply_);
297   UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent",
298                             SERVICE_UTILITY_CAPS_FAILED,
299                             SERVICE_UTILITY_EVENT_MAX);
300   UMA_HISTOGRAM_TIMES("CloudPrint.ServiceUtilityCapsFailTime",
301                       base::Time::Now() - start_time_);
302   waiting_for_reply_ = false;
303   client_message_loop_proxy_->PostTask(
304       FROM_HERE,
305       base::Bind(&Client::OnGetPrinterCapsAndDefaultsFailed, client_.get(),
306                  printer_name));
307 }
308
309 void ServiceUtilityProcessHost::Client::MetafileAvailable(
310     const base::FilePath& metafile_path,
311     int highest_rendered_page_number,
312     double scale_factor) {
313   // The metafile was created in a temp folder which needs to get deleted after
314   // we have processed it.
315   base::ScopedTempDir scratch_metafile_dir;
316   if (!scratch_metafile_dir.Set(metafile_path.DirName()))
317     LOG(WARNING) << "Unable to set scratch metafile directory";
318 #if defined(OS_WIN)
319   // It's important that metafile is declared after scratch_metafile_dir so
320   // that the metafile destructor closes the file before the base::ScopedTempDir
321   // destructor tries to remove the directory.
322   printing::Emf metafile;
323   if (!metafile.InitFromFile(metafile_path)) {
324     OnRenderPDFPagesToMetafileFailed();
325   } else {
326     OnRenderPDFPagesToMetafileSucceeded(metafile,
327                                         highest_rendered_page_number,
328                                         scale_factor);
329   }
330 #endif  // defined(OS_WIN)
331 }
332