Upstream version 10.38.208.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 "base/bind_helpers.h"
8 #include "base/cancelable_callback.h"
9 #include "base/file_util.h"
10 #include "base/files/file.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/page_range.h"
21 #include "printing/pdf_render_settings.h"
22
23 namespace printing {
24
25 namespace {
26
27 using content::BrowserThread;
28
29 class FileHandlers
30     : public base::RefCountedThreadSafe<FileHandlers,
31                                         BrowserThread::DeleteOnFileThread> {
32  public:
33   FileHandlers() {}
34
35   void Init(base::RefCountedMemory* data);
36   bool IsValid();
37
38   base::FilePath GetEmfPath() const {
39     return temp_dir_.path().AppendASCII("output.emf");
40   }
41
42   base::FilePath GetEmfPagePath(int page_number) const {
43     return GetEmfPath().InsertBeforeExtensionASCII(
44         base::StringPrintf(".%d", page_number));
45   }
46
47   base::FilePath GetPdfPath() const {
48     return temp_dir_.path().AppendASCII("input.pdf");
49   }
50
51   IPC::PlatformFileForTransit GetPdfForProcess(base::ProcessHandle process) {
52     DCHECK(pdf_file_.IsValid());
53     IPC::PlatformFileForTransit transit =
54         IPC::TakeFileHandleForProcess(pdf_file_.Pass(), process);
55     return transit;
56   }
57
58   const base::FilePath& GetBasePath() const {
59     return temp_dir_.path();
60   }
61
62  private:
63   friend struct BrowserThread::DeleteOnThread<BrowserThread::FILE>;
64   friend class base::DeleteHelper<FileHandlers>;
65
66   ~FileHandlers() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); }
67
68   base::ScopedTempDir temp_dir_;
69   base::File pdf_file_;
70 };
71
72 void FileHandlers::Init(base::RefCountedMemory* data) {
73   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
74
75   if (!temp_dir_.CreateUniqueTempDir()) {
76     return;
77   }
78
79   pdf_file_.Initialize(GetPdfPath(),
80                        base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
81                            base::File::FLAG_READ |
82                            base::File::FLAG_DELETE_ON_CLOSE);
83   if (static_cast<int>(data->size()) !=
84       pdf_file_.WriteAtCurrentPos(data->front_as<char>(), data->size())) {
85     pdf_file_.Close();
86     return;
87   }
88   pdf_file_.Seek(base::File::FROM_BEGIN, 0);
89   pdf_file_.Flush();
90 }
91
92 bool FileHandlers::IsValid() {
93   return pdf_file_.IsValid();
94 }
95
96 // Modification of Emf to keep references to |FileHandlers|.
97 // |FileHandlers| must be deleted after the last metafile is closed because
98 // Emf holds files locked.
99 // Ideally we want to use FLAG_DELETE_ON_CLOSE, but it requires large changes.
100 // It's going to be done for crbug.com/408184
101 class TempEmf : public Emf {
102  public:
103   explicit TempEmf(const scoped_refptr<FileHandlers>& files) : files_(files) {}
104   virtual ~TempEmf() {}
105
106   virtual bool SafePlayback(HDC hdc) const OVERRIDE {
107     bool result = Emf::SafePlayback(hdc);
108     TempEmf* this_mutable = const_cast<TempEmf*>(this);
109     // TODO(vitalybuka): Fix destruction of metafiles. For some reasons
110     // instances of Emf are not deleted. crbug.com/411683
111     // |files_| must be released as soon as possible to guarantee deletion.
112     // It's know that this Emf file is going to be played just once to
113     // a printer. So just release files here.
114     this_mutable->Close();
115     this_mutable->files_ = NULL;
116     return result;
117   }
118
119  private:
120   scoped_refptr<FileHandlers> files_;
121   DISALLOW_COPY_AND_ASSIGN(TempEmf);
122 };
123
124 // Converts PDF into EMF.
125 // Class uses 3 threads: UI, IO and FILE.
126 // Internal workflow is following:
127 // 1. Create instance on the UI thread. (files_, settings_,)
128 // 2. Create file on the FILE thread.
129 // 3. Start utility process and start conversion on the IO thread.
130 // 4. Run result callback on the UI thread.
131 // 5. Instance is destroyed from any thread that has the last reference.
132 // 6. FileHandlers destroyed on the FILE thread.
133 //    This step posts |FileHandlers| to be destroyed on the FILE thread.
134 // All these steps work sequentially, so no data should be accessed
135 // simultaneously by several threads.
136 class PdfToEmfUtilityProcessHostClient
137     : public content::UtilityProcessHostClient {
138  public:
139   explicit PdfToEmfUtilityProcessHostClient(
140       const printing::PdfRenderSettings& settings);
141
142   void Convert(base::RefCountedMemory* data,
143                const PdfToEmfConverter::ResultCallback& callback);
144
145   // UtilityProcessHostClient implementation.
146   virtual void OnProcessCrashed(int exit_code) OVERRIDE;
147   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
148
149  private:
150   virtual ~PdfToEmfUtilityProcessHostClient();
151
152   // Message handlers.
153   void OnProcessStarted();
154   void OnSucceeded(const std::vector<printing::PageRange>& page_ranges,
155                    double scale_factor);
156   void OnFailed();
157
158   void RunCallback(const std::vector<printing::PageRange>& page_ranges,
159                    double scale_factor);
160
161   void StartProcessOnIOThread();
162
163   void RunCallbackOnUIThread(
164       const std::vector<printing::PageRange>& page_ranges,
165       double scale_factor);
166   void OnFilesReadyOnUIThread();
167
168   scoped_refptr<FileHandlers> files_;
169   printing::PdfRenderSettings settings_;
170   PdfToEmfConverter::ResultCallback callback_;
171   base::WeakPtr<content::UtilityProcessHost> utility_process_host_;
172
173   DISALLOW_COPY_AND_ASSIGN(PdfToEmfUtilityProcessHostClient);
174 };
175
176 PdfToEmfUtilityProcessHostClient::PdfToEmfUtilityProcessHostClient(
177     const printing::PdfRenderSettings& settings)
178     : settings_(settings) {}
179
180 PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() {
181 }
182
183 void PdfToEmfUtilityProcessHostClient::Convert(
184     base::RefCountedMemory* data,
185     const PdfToEmfConverter::ResultCallback& callback) {
186   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
187   callback_ = callback;
188   CHECK(!files_.get());
189   files_ = new FileHandlers();
190   BrowserThread::PostTaskAndReply(
191       BrowserThread::FILE,
192       FROM_HERE,
193       base::Bind(&FileHandlers::Init, files_, make_scoped_refptr(data)),
194       base::Bind(&PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread,
195                  this));
196 }
197
198 void PdfToEmfUtilityProcessHostClient::OnProcessCrashed(int exit_code) {
199   OnFailed();
200 }
201
202 bool PdfToEmfUtilityProcessHostClient::OnMessageReceived(
203   const IPC::Message& message) {
204   bool handled = true;
205   IPC_BEGIN_MESSAGE_MAP(PdfToEmfUtilityProcessHostClient, message)
206     IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted)
207     IPC_MESSAGE_HANDLER(
208         ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_Succeeded, OnSucceeded)
209     IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed,
210                         OnFailed)
211     IPC_MESSAGE_UNHANDLED(handled = false)
212   IPC_END_MESSAGE_MAP()
213   return handled;
214 }
215
216 void PdfToEmfUtilityProcessHostClient::OnProcessStarted() {
217   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
218   if (!utility_process_host_) {
219     RunCallbackOnUIThread(std::vector<printing::PageRange>(), 0.0);
220     return;
221   }
222
223   base::ProcessHandle process = utility_process_host_->GetData().handle;
224   utility_process_host_->Send(
225       new ChromeUtilityMsg_RenderPDFPagesToMetafiles(
226           files_->GetPdfForProcess(process),
227           files_->GetEmfPath(),
228           settings_,
229           std::vector<printing::PageRange>()));
230   utility_process_host_.reset();
231 }
232
233 void PdfToEmfUtilityProcessHostClient::OnSucceeded(
234     const std::vector<printing::PageRange>& page_ranges,
235     double scale_factor) {
236   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
237   RunCallback(page_ranges, scale_factor);
238 }
239
240 void PdfToEmfUtilityProcessHostClient::OnFailed() {
241   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
242   RunCallback(std::vector<printing::PageRange>(), 0.0);
243 }
244
245 void PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread() {
246   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
247   if (!files_->IsValid()) {
248     RunCallbackOnUIThread(std::vector<printing::PageRange>(), 0.0);
249     return;
250   }
251   BrowserThread::PostTask(
252       BrowserThread::IO,
253       FROM_HERE,
254       base::Bind(&PdfToEmfUtilityProcessHostClient::StartProcessOnIOThread,
255                  this));
256 }
257
258 void PdfToEmfUtilityProcessHostClient::StartProcessOnIOThread() {
259   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
260   utility_process_host_ =
261       content::UtilityProcessHost::Create(
262           this,
263           base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr();
264   // NOTE: This process _must_ be sandboxed, otherwise the pdf dll will load
265   // gdiplus.dll, change how rendering happens, and not be able to correctly
266   // generate when sent to a metafile DC.
267   utility_process_host_->SetExposedDir(files_->GetBasePath());
268   utility_process_host_->Send(new ChromeUtilityMsg_StartupPing);
269 }
270
271 void PdfToEmfUtilityProcessHostClient::RunCallback(
272     const std::vector<printing::PageRange>& page_ranges,
273     double scale_factor) {
274   BrowserThread::PostTask(
275       BrowserThread::UI,
276       FROM_HERE,
277       base::Bind(&PdfToEmfUtilityProcessHostClient::RunCallbackOnUIThread,
278                  this,
279                  page_ranges,
280                  scale_factor));
281 }
282
283 void PdfToEmfUtilityProcessHostClient::RunCallbackOnUIThread(
284     const std::vector<printing::PageRange>& page_ranges,
285     double scale_factor) {
286   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
287   ScopedVector<Metafile> pages;
288   std::vector<printing::PageRange>::const_iterator iter;
289   for (iter = page_ranges.begin(); iter != page_ranges.end(); ++iter) {
290     for (int page_number = iter->from; page_number <= iter->to; ++page_number) {
291       scoped_ptr<TempEmf> metafile(new TempEmf(files_));
292       if (!metafile->InitFromFile(files_->GetEmfPagePath(page_number))) {
293         NOTREACHED() << "Invalid metafile";
294         metafile.reset();
295       }
296       pages.push_back(metafile.release());
297     }
298   }
299   files_ = NULL;
300   if (!callback_.is_null()) {
301     callback_.Run(scale_factor, &pages);
302     callback_.Reset();
303   }
304 }
305
306 class PdfToEmfConverterImpl : public PdfToEmfConverter {
307  public:
308   PdfToEmfConverterImpl();
309
310   virtual ~PdfToEmfConverterImpl();
311
312   virtual void Start(base::RefCountedMemory* data,
313                      const printing::PdfRenderSettings& conversion_settings,
314                      const ResultCallback& callback) OVERRIDE;
315
316  private:
317   scoped_refptr<PdfToEmfUtilityProcessHostClient> utility_client_;
318   base::CancelableCallback<ResultCallback::RunType> callback_;
319
320   DISALLOW_COPY_AND_ASSIGN(PdfToEmfConverterImpl);
321 };
322
323 PdfToEmfConverterImpl::PdfToEmfConverterImpl() {
324 }
325
326 PdfToEmfConverterImpl::~PdfToEmfConverterImpl() {
327 }
328
329 void PdfToEmfConverterImpl::Start(
330     base::RefCountedMemory* data,
331     const printing::PdfRenderSettings& conversion_settings,
332     const ResultCallback& callback) {
333   // Rebind cancelable callback to avoid calling callback if
334   // PdfToEmfConverterImpl is destroyed.
335   callback_.Reset(callback);
336   utility_client_ = new PdfToEmfUtilityProcessHostClient(conversion_settings);
337   utility_client_->Convert(data, callback_.callback());
338 }
339
340 }  // namespace
341
342 // static
343 scoped_ptr<PdfToEmfConverter> PdfToEmfConverter::CreateDefault() {
344   return scoped_ptr<PdfToEmfConverter>(new PdfToEmfConverterImpl());
345 }
346
347 }  // namespace printing