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