21800ee6311e370862a549cded7790af4d4f50bf
[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/page_range.h"
20 #include "printing/pdf_render_settings.h"
21
22 namespace printing {
23
24 namespace {
25
26 using content::BrowserThread;
27
28 class FileHandlers {
29  public:
30   FileHandlers() {}
31
32   ~FileHandlers() {
33     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
34   }
35
36   void Init(base::RefCountedMemory* data);
37   bool IsValid();
38
39   base::FilePath GetEmfPath() const {
40     return temp_dir_.path().AppendASCII("output.emf");
41   }
42
43   base::FilePath GetPdfPath() const {
44     return temp_dir_.path().AppendASCII("input.pdf");
45   }
46
47   IPC::PlatformFileForTransit GetPdfForProcess(base::ProcessHandle process) {
48     DCHECK(pdf_file_.IsValid());
49     IPC::PlatformFileForTransit transit =
50         IPC::TakeFileHandleForProcess(pdf_file_.Pass(), process);
51     return transit;
52   }
53
54   const base::FilePath& GetBasePath() const {
55     return temp_dir_.path();
56   }
57
58  private:
59   base::ScopedTempDir temp_dir_;
60   base::File pdf_file_;
61 };
62
63 void FileHandlers::Init(base::RefCountedMemory* data) {
64   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
65
66   if (!temp_dir_.CreateUniqueTempDir()) {
67     return;
68   }
69
70   if (static_cast<int>(data->size()) !=
71       base::WriteFile(GetPdfPath(), data->front_as<char>(), data->size())) {
72     return;
73   }
74
75   // Reopen in read only mode.
76   pdf_file_.Initialize(GetPdfPath(),
77                        base::File::FLAG_OPEN | base::File::FLAG_READ);
78 }
79
80 bool FileHandlers::IsValid() {
81   return pdf_file_.IsValid();
82 }
83
84 // Converts PDF into EMF.
85 // Class uses 3 threads: UI, IO and FILE.
86 // Internal workflow is following:
87 // 1. Create instance on the UI thread. (files_, settings_,)
88 // 2. Create file on the FILE thread.
89 // 3. Start utility process and start conversion on the IO thread.
90 // 4. Run result callback on the UI thread.
91 // 5. Instance is destroyed from any thread that has the last reference.
92 // 6. FileHandlers destroyed on the FILE thread.
93 //    This step posts |FileHandlers| to be destroyed on the FILE thread.
94 // All these steps work sequentially, so no data should be accessed
95 // simultaneously by several threads.
96 class PdfToEmfUtilityProcessHostClient
97     : public content::UtilityProcessHostClient {
98  public:
99   explicit PdfToEmfUtilityProcessHostClient(
100       const printing::PdfRenderSettings& settings);
101
102   void Convert(base::RefCountedMemory* data,
103                const PdfToEmfConverter::ResultCallback& callback);
104
105   // UtilityProcessHostClient implementation.
106   virtual void OnProcessCrashed(int exit_code) OVERRIDE;
107   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
108
109  private:
110   virtual ~PdfToEmfUtilityProcessHostClient();
111
112   // Message handlers.
113   void OnProcessStarted();
114   void OnSucceeded(const std::vector<printing::PageRange>& page_ranges,
115                    double scale_factor);
116   void OnFailed();
117
118   void RunCallback(const std::vector<printing::PageRange>& page_ranges,
119                    double scale_factor);
120
121   void StartProcessOnIOThread();
122
123   void RunCallbackOnUIThread(
124       const std::vector<printing::PageRange>& page_ranges,
125       double scale_factor);
126   void OnFilesReadyOnUIThread();
127
128   scoped_ptr<FileHandlers> files_;
129   printing::PdfRenderSettings settings_;
130   PdfToEmfConverter::ResultCallback callback_;
131   base::WeakPtr<content::UtilityProcessHost> utility_process_host_;
132
133   DISALLOW_COPY_AND_ASSIGN(PdfToEmfUtilityProcessHostClient);
134 };
135
136 PdfToEmfUtilityProcessHostClient::PdfToEmfUtilityProcessHostClient(
137     const printing::PdfRenderSettings& settings)
138     : settings_(settings) {}
139
140 PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() {
141   // Delete temp directory.
142   BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, files_.release());
143 }
144
145 void PdfToEmfUtilityProcessHostClient::Convert(
146     base::RefCountedMemory* data,
147     const PdfToEmfConverter::ResultCallback& callback) {
148   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
149   callback_ = callback;
150   CHECK(!files_);
151   files_.reset(new FileHandlers());
152   BrowserThread::PostTaskAndReply(
153       BrowserThread::FILE,
154       FROM_HERE,
155       base::Bind(&FileHandlers::Init,
156                  base::Unretained(files_.get()),
157                  make_scoped_refptr(data)),
158       base::Bind(&PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread,
159                  this));
160 }
161
162 void PdfToEmfUtilityProcessHostClient::OnProcessCrashed(int exit_code) {
163   OnFailed();
164 }
165
166 bool PdfToEmfUtilityProcessHostClient::OnMessageReceived(
167   const IPC::Message& message) {
168   bool handled = true;
169   IPC_BEGIN_MESSAGE_MAP(PdfToEmfUtilityProcessHostClient, message)
170     IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted)
171     IPC_MESSAGE_HANDLER(
172         ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_Succeeded, OnSucceeded)
173     IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed,
174                         OnFailed)
175     IPC_MESSAGE_UNHANDLED(handled = false)
176   IPC_END_MESSAGE_MAP()
177   return handled;
178 }
179
180 void PdfToEmfUtilityProcessHostClient::OnProcessStarted() {
181   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
182   if (!utility_process_host_) {
183     RunCallbackOnUIThread(std::vector<printing::PageRange>(), 0.0);
184     return;
185   }
186
187   base::ProcessHandle process = utility_process_host_->GetData().handle;
188   utility_process_host_->Send(
189       new ChromeUtilityMsg_RenderPDFPagesToMetafiles(
190           files_->GetPdfForProcess(process),
191           files_->GetEmfPath(),
192           settings_,
193           std::vector<printing::PageRange>()));
194   utility_process_host_.reset();
195 }
196
197 void PdfToEmfUtilityProcessHostClient::OnSucceeded(
198     const std::vector<printing::PageRange>& page_ranges,
199     double scale_factor) {
200   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
201   RunCallback(page_ranges, scale_factor);
202 }
203
204 void PdfToEmfUtilityProcessHostClient::OnFailed() {
205   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
206   RunCallback(std::vector<printing::PageRange>(), 0.0);
207 }
208
209 void PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread() {
210   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
211   if (!files_->IsValid()) {
212     RunCallbackOnUIThread(std::vector<printing::PageRange>(), 0.0);
213     return;
214   }
215   BrowserThread::PostTask(
216       BrowserThread::IO,
217       FROM_HERE,
218       base::Bind(&PdfToEmfUtilityProcessHostClient::StartProcessOnIOThread,
219                  this));
220 }
221
222 void PdfToEmfUtilityProcessHostClient::StartProcessOnIOThread() {
223   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
224   utility_process_host_ =
225       content::UtilityProcessHost::Create(
226           this,
227           base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr();
228   // NOTE: This process _must_ be sandboxed, otherwise the pdf dll will load
229   // gdiplus.dll, change how rendering happens, and not be able to correctly
230   // generate when sent to a metafile DC.
231   utility_process_host_->SetExposedDir(files_->GetBasePath());
232   utility_process_host_->Send(new ChromeUtilityMsg_StartupPing);
233 }
234
235 void PdfToEmfUtilityProcessHostClient::RunCallback(
236     const std::vector<printing::PageRange>& page_ranges,
237     double scale_factor) {
238   BrowserThread::PostTask(
239       BrowserThread::UI,
240       FROM_HERE,
241       base::Bind(&PdfToEmfUtilityProcessHostClient::RunCallbackOnUIThread,
242                  this,
243                  page_ranges,
244                  scale_factor));
245 }
246
247 void PdfToEmfUtilityProcessHostClient::RunCallbackOnUIThread(
248     const std::vector<printing::PageRange>& page_ranges,
249     double scale_factor) {
250   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
251   std::vector<base::FilePath> page_filenames;
252   std::vector<printing::PageRange>::const_iterator iter;
253   for (iter = page_ranges.begin(); iter != page_ranges.end(); ++iter) {
254     for (int page_number = iter->from; page_number <= iter->to; ++page_number) {
255       page_filenames.push_back(files_->GetEmfPath().InsertBeforeExtensionASCII(
256           base::StringPrintf(".%d", page_number)));
257     }
258   }
259   if (!callback_.is_null()) {
260     BrowserThread::PostTask(
261         BrowserThread::UI,
262         FROM_HERE,
263         base::Bind(callback_, scale_factor, page_filenames));
264     callback_.Reset();
265   }
266 }
267
268 class PdfToEmfConverterImpl : public PdfToEmfConverter {
269  public:
270   PdfToEmfConverterImpl();
271
272   virtual ~PdfToEmfConverterImpl();
273
274   virtual void Start(base::RefCountedMemory* data,
275                      const printing::PdfRenderSettings& conversion_settings,
276                      const ResultCallback& callback) OVERRIDE;
277
278  private:
279   scoped_refptr<PdfToEmfUtilityProcessHostClient> utility_client_;
280   base::CancelableCallback<ResultCallback::RunType> callback_;
281
282   DISALLOW_COPY_AND_ASSIGN(PdfToEmfConverterImpl);
283 };
284
285 PdfToEmfConverterImpl::PdfToEmfConverterImpl() {
286 }
287
288 PdfToEmfConverterImpl::~PdfToEmfConverterImpl() {
289 }
290
291 void PdfToEmfConverterImpl::Start(
292     base::RefCountedMemory* data,
293     const printing::PdfRenderSettings& conversion_settings,
294     const ResultCallback& callback) {
295   // Rebind cancelable callback to avoid calling callback if
296   // PdfToEmfConverterImpl is destroyed.
297   callback_.Reset(callback);
298   utility_client_ = new PdfToEmfUtilityProcessHostClient(conversion_settings);
299   utility_client_->Convert(data, callback_.callback());
300 }
301
302 }  // namespace
303
304 // static
305 scoped_ptr<PdfToEmfConverter> PdfToEmfConverter::CreateDefault() {
306   return scoped_ptr<PdfToEmfConverter>(new PdfToEmfConverterImpl());
307 }
308
309 }  // namespace printing