Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / printing / pdf_to_emf_converter.cc
1 // Copyright 2014 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/browser/printing/pdf_to_emf_converter.h"
6
7 #include <queue>
8
9 #include "base/files/file.h"
10 #include "base/files/file_util.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/logging.h"
13 #include "chrome/common/chrome_utility_messages.h"
14 #include "chrome/common/chrome_utility_printing_messages.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/child_process_data.h"
17 #include "content/public/browser/utility_process_host.h"
18 #include "content/public/browser/utility_process_host_client.h"
19 #include "printing/emf_win.h"
20 #include "printing/pdf_render_settings.h"
21
22 namespace printing {
23
24 namespace {
25
26 using content::BrowserThread;
27
28 class PdfToEmfConverterImpl;
29
30 // Allows to delete temporary directory after all temporary files created inside
31 // are closed. Windows cannot delete directory with opened files. Directory is
32 // used to store PDF and metafiles. PDF should be gone by the time utility
33 // process exits. Metafiles should be gone when all LazyEmf destroyed.
34 class RefCountedTempDir
35     : public base::RefCountedThreadSafe<RefCountedTempDir,
36                                         BrowserThread::DeleteOnFileThread> {
37  public:
38   RefCountedTempDir() { ignore_result(temp_dir_.CreateUniqueTempDir()); }
39   bool IsValid() const { return temp_dir_.IsValid(); }
40   const base::FilePath& GetPath() const { return temp_dir_.path(); }
41
42  private:
43   friend struct BrowserThread::DeleteOnThread<BrowserThread::FILE>;
44   friend class base::DeleteHelper<RefCountedTempDir>;
45   ~RefCountedTempDir() {}
46
47   base::ScopedTempDir temp_dir_;
48   DISALLOW_COPY_AND_ASSIGN(RefCountedTempDir);
49 };
50
51 typedef scoped_ptr<base::File, BrowserThread::DeleteOnFileThread>
52     ScopedTempFile;
53
54 // Wrapper for Emf to keep only file handle in memory, and load actual data only
55 // on playback. Emf::InitFromFile() can play metafile directly from disk, but it
56 // can't open file handles. We need file handles to reliably delete temporary
57 // files, and to efficiently interact with utility process.
58 class LazyEmf : public MetafilePlayer {
59  public:
60   LazyEmf(const scoped_refptr<RefCountedTempDir>& temp_dir, ScopedTempFile file)
61       : temp_dir_(temp_dir), file_(file.Pass()) {}
62   virtual ~LazyEmf() { Close(); }
63
64   virtual bool SafePlayback(HDC hdc) const OVERRIDE;
65   virtual bool SaveTo(base::File* file) const OVERRIDE;
66
67  private:
68   void Close() const;
69   bool LoadEmf(Emf* emf) const;
70
71   mutable scoped_refptr<RefCountedTempDir> temp_dir_;
72   mutable ScopedTempFile file_;  // Mutable because of consts in base class.
73
74   DISALLOW_COPY_AND_ASSIGN(LazyEmf);
75 };
76
77 // Converts PDF into EMF.
78 // Class uses 3 threads: UI, IO and FILE.
79 // Internal workflow is following:
80 // 1. Create instance on the UI thread. (files_, settings_,)
81 // 2. Create pdf file on the FILE thread.
82 // 3. Start utility process and start conversion on the IO thread.
83 // 4. Utility process returns page count.
84 // 5. For each page:
85 //   1. Clients requests page with file handle to a temp file.
86 //   2. Utility converts the page, save it to the file and reply.
87 //
88 // All these steps work sequentially, so no data should be accessed
89 // simultaneously by several threads.
90 class PdfToEmfUtilityProcessHostClient
91     : public content::UtilityProcessHostClient {
92  public:
93   PdfToEmfUtilityProcessHostClient(
94       base::WeakPtr<PdfToEmfConverterImpl> converter,
95       const PdfRenderSettings& settings);
96
97   void Start(const scoped_refptr<base::RefCountedMemory>& data,
98              const PdfToEmfConverter::StartCallback& start_callback);
99
100   void GetPage(int page_number,
101                const PdfToEmfConverter::GetPageCallback& get_page_callback);
102
103   void Stop();
104
105   // UtilityProcessHostClient implementation.
106   virtual void OnProcessCrashed(int exit_code) OVERRIDE;
107   virtual void OnProcessLaunchFailed() OVERRIDE;
108   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
109
110  private:
111   class GetPageCallbackData {
112     MOVE_ONLY_TYPE_FOR_CPP_03(GetPageCallbackData, RValue);
113
114    public:
115     GetPageCallbackData(int page_number,
116                         PdfToEmfConverter::GetPageCallback callback)
117         : page_number_(page_number), callback_(callback) {}
118
119     // Move constructor for STL.
120     GetPageCallbackData(RValue other) { this->operator=(other); }
121
122     // Move assignment for STL.
123     GetPageCallbackData& operator=(RValue rhs) {
124       page_number_ = rhs.object->page_number_;
125       callback_ = rhs.object->callback_;
126       emf_ = rhs.object->emf_.Pass();
127       return *this;
128     }
129
130     int page_number() const { return page_number_; }
131     const PdfToEmfConverter::GetPageCallback& callback() const {
132       return callback_;
133     }
134     ScopedTempFile emf() { return emf_.Pass(); }
135     void set_emf(ScopedTempFile emf) { emf_ = emf.Pass(); }
136
137    private:
138     int page_number_;
139     PdfToEmfConverter::GetPageCallback callback_;
140     ScopedTempFile emf_;
141   };
142
143   virtual ~PdfToEmfUtilityProcessHostClient();
144
145   bool Send(IPC::Message* msg);
146
147   // Message handlers.
148   void OnProcessStarted();
149   void OnPageCount(int page_count);
150   void OnPageDone(bool success, double scale_factor);
151
152   void OnFailed();
153   void OnTempPdfReady(ScopedTempFile pdf);
154   void OnTempEmfReady(GetPageCallbackData* callback_data, ScopedTempFile emf);
155
156   scoped_refptr<RefCountedTempDir> temp_dir_;
157
158   // Used to suppress callbacks after PdfToEmfConverterImpl is deleted.
159   base::WeakPtr<PdfToEmfConverterImpl> converter_;
160   PdfRenderSettings settings_;
161   scoped_refptr<base::RefCountedMemory> data_;
162
163   // Document loaded callback.
164   PdfToEmfConverter::StartCallback start_callback_;
165
166   // Process host for IPC.
167   base::WeakPtr<content::UtilityProcessHost> utility_process_host_;
168
169   // Queue of callbacks for GetPage() requests. Utility process should reply
170   // with PageDone in the same order as requests were received.
171   // Use containers that keeps element pointers valid after push() and pop().
172   typedef std::queue<GetPageCallbackData> GetPageCallbacks;
173   GetPageCallbacks get_page_callbacks_;
174
175   DISALLOW_COPY_AND_ASSIGN(PdfToEmfUtilityProcessHostClient);
176 };
177
178 class PdfToEmfConverterImpl : public PdfToEmfConverter {
179  public:
180   PdfToEmfConverterImpl();
181
182   virtual ~PdfToEmfConverterImpl();
183
184   virtual void Start(const scoped_refptr<base::RefCountedMemory>& data,
185                      const PdfRenderSettings& conversion_settings,
186                      const StartCallback& start_callback) OVERRIDE;
187
188   virtual void GetPage(int page_number,
189                        const GetPageCallback& get_page_callback) OVERRIDE;
190
191   // Helps to cancel callbacks if this object is destroyed.
192   void RunCallback(const base::Closure& callback);
193
194  private:
195   scoped_refptr<PdfToEmfUtilityProcessHostClient> utility_client_;
196   base::WeakPtrFactory<PdfToEmfConverterImpl> weak_ptr_factory_;
197
198   DISALLOW_COPY_AND_ASSIGN(PdfToEmfConverterImpl);
199 };
200
201 ScopedTempFile CreateTempFile(scoped_refptr<RefCountedTempDir>* temp_dir) {
202   if (!temp_dir->get())
203     *temp_dir = new RefCountedTempDir();
204   ScopedTempFile file;
205   if (!(*temp_dir)->IsValid())
206     return file.Pass();
207   base::FilePath path;
208   if (!base::CreateTemporaryFileInDir((*temp_dir)->GetPath(), &path))
209     return file.Pass();
210   file.reset(new base::File(path,
211                             base::File::FLAG_CREATE_ALWAYS |
212                             base::File::FLAG_WRITE |
213                             base::File::FLAG_READ |
214                             base::File::FLAG_DELETE_ON_CLOSE |
215                             base::File::FLAG_TEMPORARY));
216   if (!file->IsValid())
217     file.reset();
218   return file.Pass();
219 }
220
221 ScopedTempFile CreateTempPdfFile(
222     const scoped_refptr<base::RefCountedMemory>& data,
223     scoped_refptr<RefCountedTempDir>* temp_dir) {
224   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
225
226   ScopedTempFile pdf_file = CreateTempFile(temp_dir);
227   if (!pdf_file ||
228       static_cast<int>(data->size()) !=
229           pdf_file->WriteAtCurrentPos(data->front_as<char>(), data->size())) {
230     pdf_file.reset();
231   }
232   pdf_file->Seek(base::File::FROM_BEGIN, 0);
233   return pdf_file.Pass();
234 }
235
236 bool LazyEmf::SafePlayback(HDC hdc) const {
237   Emf emf;
238   bool result = LoadEmf(&emf) && emf.SafePlayback(hdc);
239   // TODO(vitalybuka): Fix destruction of metafiles. For some reasons
240   // instances of Emf are not deleted. crbug.com/411683
241   // It's known that the Emf going to be played just once to a printer. So just
242   // release file here.
243   Close();
244   return result;
245 }
246
247 bool LazyEmf::SaveTo(base::File* file) const {
248   Emf emf;
249   return LoadEmf(&emf) && emf.SaveTo(file);
250 }
251
252 void LazyEmf::Close() const {
253   file_.reset();
254   temp_dir_ = NULL;
255 }
256
257 bool LazyEmf::LoadEmf(Emf* emf) const {
258   file_->Seek(base::File::FROM_BEGIN, 0);
259   int64 size = file_->GetLength();
260   if (size <= 0)
261     return false;
262   std::vector<char> data(size);
263   if (file_->ReadAtCurrentPos(data.data(), data.size()) != size)
264     return false;
265   return emf->InitFromData(data.data(), data.size());
266 }
267
268 PdfToEmfUtilityProcessHostClient::PdfToEmfUtilityProcessHostClient(
269     base::WeakPtr<PdfToEmfConverterImpl> converter,
270     const PdfRenderSettings& settings)
271     : converter_(converter), settings_(settings) {
272 }
273
274 PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() {
275 }
276
277 void PdfToEmfUtilityProcessHostClient::Start(
278     const scoped_refptr<base::RefCountedMemory>& data,
279     const PdfToEmfConverter::StartCallback& start_callback) {
280   if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
281     BrowserThread::PostTask(BrowserThread::IO,
282                             FROM_HERE,
283                             base::Bind(&PdfToEmfUtilityProcessHostClient::Start,
284                                        this,
285                                        data,
286                                        start_callback));
287     return;
288   }
289   data_ = data;
290
291   // Store callback before any OnFailed() call to make it called on failure.
292   start_callback_ = start_callback;
293
294   // NOTE: This process _must_ be sandboxed, otherwise the pdf dll will load
295   // gdiplus.dll, change how rendering happens, and not be able to correctly
296   // generate when sent to a metafile DC.
297   utility_process_host_ =
298       content::UtilityProcessHost::Create(
299           this, base::MessageLoop::current()->message_loop_proxy())
300           ->AsWeakPtr();
301   if (!utility_process_host_)
302     return OnFailed();
303   // Should reply with OnProcessStarted().
304   Send(new ChromeUtilityMsg_StartupPing);
305 }
306
307 void PdfToEmfUtilityProcessHostClient::OnProcessStarted() {
308   DCHECK_CURRENTLY_ON(BrowserThread::IO);
309   if (!utility_process_host_)
310     return OnFailed();
311
312   scoped_refptr<base::RefCountedMemory> data = data_;
313   data_ = NULL;
314   BrowserThread::PostTaskAndReplyWithResult(
315       BrowserThread::FILE,
316       FROM_HERE,
317       base::Bind(&CreateTempPdfFile, data, &temp_dir_),
318       base::Bind(&PdfToEmfUtilityProcessHostClient::OnTempPdfReady, this));
319 }
320
321 void PdfToEmfUtilityProcessHostClient::OnTempPdfReady(ScopedTempFile pdf) {
322   DCHECK_CURRENTLY_ON(BrowserThread::IO);
323   if (!utility_process_host_)
324     return OnFailed();
325   base::ProcessHandle process = utility_process_host_->GetData().handle;
326   // Should reply with OnPageCount().
327   Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles(
328       IPC::GetFileHandleForProcess(pdf->GetPlatformFile(), process, false),
329       settings_));
330 }
331
332 void PdfToEmfUtilityProcessHostClient::OnPageCount(int page_count) {
333   DCHECK_CURRENTLY_ON(BrowserThread::IO);
334   if (start_callback_.is_null())
335     return OnFailed();
336   BrowserThread::PostTask(BrowserThread::UI,
337                           FROM_HERE,
338                           base::Bind(&PdfToEmfConverterImpl::RunCallback,
339                                      converter_,
340                                      base::Bind(start_callback_, page_count)));
341   start_callback_.Reset();
342 }
343
344 void PdfToEmfUtilityProcessHostClient::GetPage(
345     int page_number,
346     const PdfToEmfConverter::GetPageCallback& get_page_callback) {
347   if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
348     BrowserThread::PostTask(
349         BrowserThread::IO,
350         FROM_HERE,
351         base::Bind(&PdfToEmfUtilityProcessHostClient::GetPage,
352                    this,
353                    page_number,
354                    get_page_callback));
355     return;
356   }
357
358   // Store callback before any OnFailed() call to make it called on failure.
359   get_page_callbacks_.push(GetPageCallbackData(page_number, get_page_callback));
360
361   if (!utility_process_host_)
362     return OnFailed();
363
364   BrowserThread::PostTaskAndReplyWithResult(
365       BrowserThread::FILE,
366       FROM_HERE,
367       base::Bind(&CreateTempFile, &temp_dir_),
368       base::Bind(&PdfToEmfUtilityProcessHostClient::OnTempEmfReady,
369                  this,
370                  &get_page_callbacks_.back()));
371 }
372
373 void PdfToEmfUtilityProcessHostClient::OnTempEmfReady(
374     GetPageCallbackData* callback_data,
375     ScopedTempFile emf) {
376   DCHECK_CURRENTLY_ON(BrowserThread::IO);
377   if (!utility_process_host_)
378     return OnFailed();
379   base::ProcessHandle process = utility_process_host_->GetData().handle;
380   IPC::PlatformFileForTransit transit =
381       IPC::GetFileHandleForProcess(emf->GetPlatformFile(), process, false);
382   callback_data->set_emf(emf.Pass());
383   // Should reply with OnPageDone().
384   Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_GetPage(
385       callback_data->page_number(), transit));
386 }
387
388 void PdfToEmfUtilityProcessHostClient::OnPageDone(bool success,
389                                                   double scale_factor) {
390   DCHECK_CURRENTLY_ON(BrowserThread::IO);
391   if (get_page_callbacks_.empty())
392     return OnFailed();
393   scoped_ptr<MetafilePlayer> emf;
394   GetPageCallbackData& data = get_page_callbacks_.front();
395   if (success)
396     emf.reset(new LazyEmf(temp_dir_, data.emf().Pass()));
397   BrowserThread::PostTask(BrowserThread::UI,
398                           FROM_HERE,
399                           base::Bind(&PdfToEmfConverterImpl::RunCallback,
400                                      converter_,
401                                      base::Bind(data.callback(),
402                                                 data.page_number(),
403                                                 scale_factor,
404                                                 base::Passed(&emf))));
405   get_page_callbacks_.pop();
406 }
407
408 void PdfToEmfUtilityProcessHostClient::Stop() {
409   if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
410     BrowserThread::PostTask(
411         BrowserThread::IO,
412         FROM_HERE,
413         base::Bind(&PdfToEmfUtilityProcessHostClient::Stop, this));
414     return;
415   }
416   Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop());
417 }
418
419 void PdfToEmfUtilityProcessHostClient::OnProcessCrashed(int exit_code) {
420   OnFailed();
421 }
422
423 void PdfToEmfUtilityProcessHostClient::OnProcessLaunchFailed() {
424   OnFailed();
425 }
426
427 bool PdfToEmfUtilityProcessHostClient::OnMessageReceived(
428     const IPC::Message& message) {
429   bool handled = true;
430   IPC_BEGIN_MESSAGE_MAP(PdfToEmfUtilityProcessHostClient, message)
431     IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted)
432     IPC_MESSAGE_HANDLER(
433         ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount, OnPageCount)
434     IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone,
435                         OnPageDone)
436     IPC_MESSAGE_UNHANDLED(handled = false)
437   IPC_END_MESSAGE_MAP()
438   return handled;
439 }
440
441 bool PdfToEmfUtilityProcessHostClient::Send(IPC::Message* msg) {
442   if (utility_process_host_)
443     return utility_process_host_->Send(msg);
444   delete msg;
445   return false;
446 }
447
448 void PdfToEmfUtilityProcessHostClient::OnFailed() {
449   DCHECK_CURRENTLY_ON(BrowserThread::IO);
450   if (!start_callback_.is_null())
451     OnPageCount(0);
452   while (!get_page_callbacks_.empty())
453     OnPageDone(false, 0.0);
454   utility_process_host_.reset();
455 }
456
457 PdfToEmfConverterImpl::PdfToEmfConverterImpl() : weak_ptr_factory_(this) {
458 }
459
460 PdfToEmfConverterImpl::~PdfToEmfConverterImpl() {
461   if (utility_client_.get())
462     utility_client_->Stop();
463 }
464
465 void PdfToEmfConverterImpl::Start(
466     const scoped_refptr<base::RefCountedMemory>& data,
467     const PdfRenderSettings& conversion_settings,
468     const StartCallback& start_callback) {
469   DCHECK(!utility_client_.get());
470   utility_client_ = new PdfToEmfUtilityProcessHostClient(
471       weak_ptr_factory_.GetWeakPtr(), conversion_settings);
472   utility_client_->Start(data, start_callback);
473 }
474
475 void PdfToEmfConverterImpl::GetPage(int page_number,
476                                     const GetPageCallback& get_page_callback) {
477   utility_client_->GetPage(page_number, get_page_callback);
478 }
479
480 void PdfToEmfConverterImpl::RunCallback(const base::Closure& callback) {
481   DCHECK_CURRENTLY_ON(BrowserThread::UI);
482   callback.Run();
483 }
484
485 }  // namespace
486
487 PdfToEmfConverter::~PdfToEmfConverter() {
488 }
489
490 // static
491 scoped_ptr<PdfToEmfConverter> PdfToEmfConverter::CreateDefault() {
492   return scoped_ptr<PdfToEmfConverter>(new PdfToEmfConverterImpl());
493 }
494
495 }  // namespace printing