OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/printing/pdf_to_emf_converter.h" | 5 #include "chrome/browser/printing/pdf_to_emf_converter.h" |
6 | 6 |
7 #include <stdint.h> | 7 #include <stdint.h> |
8 #include <windows.h> | 8 #include <windows.h> |
9 | 9 |
10 #include <memory> | 10 #include <memory> |
(...skipping 12 matching lines...) Expand all Loading... | |
23 #include "chrome/common/chrome_utility_printing_messages.h" | 23 #include "chrome/common/chrome_utility_printing_messages.h" |
24 #include "chrome/grit/generated_resources.h" | 24 #include "chrome/grit/generated_resources.h" |
25 #include "content/public/browser/browser_thread.h" | 25 #include "content/public/browser/browser_thread.h" |
26 #include "content/public/browser/child_process_data.h" | 26 #include "content/public/browser/child_process_data.h" |
27 #include "content/public/browser/utility_process_host.h" | 27 #include "content/public/browser/utility_process_host.h" |
28 #include "content/public/browser/utility_process_host_client.h" | 28 #include "content/public/browser/utility_process_host_client.h" |
29 #include "printing/emf_win.h" | 29 #include "printing/emf_win.h" |
30 #include "printing/pdf_render_settings.h" | 30 #include "printing/pdf_render_settings.h" |
31 #include "ui/base/l10n/l10n_util.h" | 31 #include "ui/base/l10n/l10n_util.h" |
32 | 32 |
33 using content::BrowserThread; | |
34 | |
33 namespace printing { | 35 namespace printing { |
34 | 36 |
35 namespace { | 37 namespace { |
36 | 38 |
37 using content::BrowserThread; | |
38 | |
39 class PdfToEmfConverterImpl; | 39 class PdfToEmfConverterImpl; |
40 | 40 |
41 // Allows to delete temporary directory after all temporary files created inside | 41 // Allows to delete temporary directory after all temporary files created inside |
42 // are closed. Windows cannot delete directory with opened files. Directory is | 42 // are closed. Windows cannot delete directory with opened files. Directory is |
43 // used to store PDF and metafiles. PDF should be gone by the time utility | 43 // used to store PDF and metafiles. PDF should be gone by the time utility |
44 // process exits. Metafiles should be gone when all LazyEmf destroyed. | 44 // process exits. Metafiles should be gone when all LazyEmf destroyed. |
45 class RefCountedTempDir | 45 class RefCountedTempDir |
46 : public base::RefCountedThreadSafe<RefCountedTempDir, | 46 : public base::RefCountedThreadSafe<RefCountedTempDir, |
47 BrowserThread::DeleteOnFileThread> { | 47 BrowserThread::DeleteOnFileThread> { |
48 public: | 48 public: |
49 RefCountedTempDir() { ignore_result(temp_dir_.CreateUniqueTempDir()); } | 49 RefCountedTempDir() { ignore_result(temp_dir_.CreateUniqueTempDir()); } |
50 bool IsValid() const { return temp_dir_.IsValid(); } | 50 bool IsValid() const { return temp_dir_.IsValid(); } |
51 const base::FilePath& GetPath() const { return temp_dir_.GetPath(); } | 51 const base::FilePath& GetPath() const { return temp_dir_.GetPath(); } |
52 | 52 |
53 private: | 53 private: |
54 friend struct BrowserThread::DeleteOnThread<BrowserThread::FILE>; | 54 friend struct BrowserThread::DeleteOnThread<BrowserThread::FILE>; |
55 friend class base::DeleteHelper<RefCountedTempDir>; | 55 friend class base::DeleteHelper<RefCountedTempDir>; |
56 ~RefCountedTempDir() {} | 56 ~RefCountedTempDir() {} |
57 | 57 |
58 base::ScopedTempDir temp_dir_; | 58 base::ScopedTempDir temp_dir_; |
59 DISALLOW_COPY_AND_ASSIGN(RefCountedTempDir); | 59 DISALLOW_COPY_AND_ASSIGN(RefCountedTempDir); |
60 }; | 60 }; |
61 | 61 |
62 typedef std::unique_ptr<base::File, BrowserThread::DeleteOnFileThread> | 62 using ScopedTempFile = |
63 ScopedTempFile; | 63 std::unique_ptr<base::File, BrowserThread::DeleteOnFileThread>; |
64 | 64 |
65 // Wrapper for Emf to keep only file handle in memory, and load actual data only | 65 // Wrapper for Emf to keep only file handle in memory, and load actual data only |
66 // on playback. Emf::InitFromFile() can play metafile directly from disk, but it | 66 // on playback. Emf::InitFromFile() can play metafile directly from disk, but it |
67 // can't open file handles. We need file handles to reliably delete temporary | 67 // can't open file handles. We need file handles to reliably delete temporary |
68 // files, and to efficiently interact with utility process. | 68 // files, and to efficiently interact with utility process. |
69 class LazyEmf : public MetafilePlayer { | 69 class LazyEmf : public MetafilePlayer { |
70 public: | 70 public: |
71 LazyEmf(const scoped_refptr<RefCountedTempDir>& temp_dir, ScopedTempFile file) | 71 LazyEmf(const scoped_refptr<RefCountedTempDir>& temp_dir, ScopedTempFile file) |
72 : temp_dir_(temp_dir), file_(std::move(file)) { | 72 : temp_dir_(temp_dir), file_(std::move(file)) { |
73 CHECK(file_); | 73 CHECK(file_); |
74 } | 74 } |
75 ~LazyEmf() override { Close(); } | 75 ~LazyEmf() override { Close(); } |
76 | 76 |
77 private: | |
78 // MetafilePlayer: | |
77 bool SafePlayback(HDC hdc) const override; | 79 bool SafePlayback(HDC hdc) const override; |
78 bool GetDataAsVector(std::vector<char>* buffer) const override; | 80 bool GetDataAsVector(std::vector<char>* buffer) const override; |
79 bool SaveTo(base::File* file) const override; | 81 bool SaveTo(base::File* file) const override; |
80 | 82 |
81 private: | |
82 void Close() const; | 83 void Close() const; |
83 bool LoadEmf(Emf* emf) const; | 84 bool LoadEmf(Emf* emf) const; |
84 | 85 |
85 mutable scoped_refptr<RefCountedTempDir> temp_dir_; | 86 mutable scoped_refptr<RefCountedTempDir> temp_dir_; |
86 mutable ScopedTempFile file_; // Mutable because of consts in base class. | 87 mutable ScopedTempFile file_; // Mutable because of consts in base class. |
87 | 88 |
88 DISALLOW_COPY_AND_ASSIGN(LazyEmf); | 89 DISALLOW_COPY_AND_ASSIGN(LazyEmf); |
89 }; | 90 }; |
90 | 91 |
91 // Converts PDF into EMF. | 92 // Converts PDF into EMF. |
(...skipping 10 matching lines...) Expand all Loading... | |
102 // All these steps work sequentially, so no data should be accessed | 103 // All these steps work sequentially, so no data should be accessed |
103 // simultaneously by several threads. | 104 // simultaneously by several threads. |
104 class PdfToEmfUtilityProcessHostClient | 105 class PdfToEmfUtilityProcessHostClient |
105 : public content::UtilityProcessHostClient { | 106 : public content::UtilityProcessHostClient { |
106 public: | 107 public: |
107 PdfToEmfUtilityProcessHostClient( | 108 PdfToEmfUtilityProcessHostClient( |
108 base::WeakPtr<PdfToEmfConverterImpl> converter, | 109 base::WeakPtr<PdfToEmfConverterImpl> converter, |
109 const PdfRenderSettings& settings); | 110 const PdfRenderSettings& settings); |
110 | 111 |
111 void Start(const scoped_refptr<base::RefCountedMemory>& data, | 112 void Start(const scoped_refptr<base::RefCountedMemory>& data, |
112 bool print_text_with_gdi, | |
113 const PdfToEmfConverter::StartCallback& start_callback); | 113 const PdfToEmfConverter::StartCallback& start_callback); |
114 | 114 |
115 void GetPage(int page_number, | 115 void GetPage(int page_number, |
116 const PdfToEmfConverter::GetPageCallback& get_page_callback); | 116 const PdfToEmfConverter::GetPageCallback& get_page_callback); |
117 | 117 |
118 void Stop(); | 118 void Stop(); |
119 | 119 |
120 // Needs to be public to handle ChromeUtilityHostMsg_PreCacheFontCharacters | 120 // Needs to be public to handle ChromeUtilityHostMsg_PreCacheFontCharacters |
121 // sync message replies. | 121 // sync message replies. |
122 bool Send(IPC::Message* msg); | 122 bool Send(IPC::Message* msg); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
161 | 161 |
162 ~PdfToEmfUtilityProcessHostClient() override; | 162 ~PdfToEmfUtilityProcessHostClient() override; |
163 | 163 |
164 // Message handlers. | 164 // Message handlers. |
165 void OnPageCount(int page_count); | 165 void OnPageCount(int page_count); |
166 void OnPageDone(bool success, float scale_factor); | 166 void OnPageDone(bool success, float scale_factor); |
167 void OnPreCacheFontCharacters(const LOGFONT& log_font, | 167 void OnPreCacheFontCharacters(const LOGFONT& log_font, |
168 const base::string16& characters); | 168 const base::string16& characters); |
169 | 169 |
170 void OnFailed(); | 170 void OnFailed(); |
171 void OnTempPdfReady(bool print_text_with_gdi, ScopedTempFile pdf); | 171 void OnTempPdfReady(ScopedTempFile pdf); |
172 void OnTempEmfReady(GetPageCallbackData* callback_data, ScopedTempFile emf); | 172 void OnTempEmfReady(GetPageCallbackData* callback_data, ScopedTempFile emf); |
173 | 173 |
174 scoped_refptr<RefCountedTempDir> temp_dir_; | 174 scoped_refptr<RefCountedTempDir> temp_dir_; |
175 | 175 |
176 // Used to suppress callbacks after PdfToEmfConverterImpl is deleted. | 176 // Used to suppress callbacks after PdfToEmfConverterImpl is deleted. |
177 base::WeakPtr<PdfToEmfConverterImpl> converter_; | 177 base::WeakPtr<PdfToEmfConverterImpl> converter_; |
178 PdfRenderSettings settings_; | 178 PdfRenderSettings settings_; |
179 | 179 |
180 // Document loaded callback. | 180 // Document loaded callback. |
181 PdfToEmfConverter::StartCallback start_callback_; | 181 PdfToEmfConverter::StartCallback start_callback_; |
182 | 182 |
183 // Process host for IPC. | 183 // Process host for IPC. |
184 base::WeakPtr<content::UtilityProcessHost> utility_process_host_; | 184 base::WeakPtr<content::UtilityProcessHost> utility_process_host_; |
185 | 185 |
186 // Queue of callbacks for GetPage() requests. Utility process should reply | 186 // Queue of callbacks for GetPage() requests. Utility process should reply |
187 // with PageDone in the same order as requests were received. | 187 // with PageDone in the same order as requests were received. |
188 // Use containers that keeps element pointers valid after push() and pop(). | 188 // Use containers that keeps element pointers valid after push() and pop(). |
189 typedef std::queue<GetPageCallbackData> GetPageCallbacks; | 189 using GetPageCallbacks = std::queue<GetPageCallbackData>; |
190 GetPageCallbacks get_page_callbacks_; | 190 GetPageCallbacks get_page_callbacks_; |
191 | 191 |
192 DISALLOW_COPY_AND_ASSIGN(PdfToEmfUtilityProcessHostClient); | 192 DISALLOW_COPY_AND_ASSIGN(PdfToEmfUtilityProcessHostClient); |
193 }; | 193 }; |
194 | 194 |
195 class PdfToEmfConverterImpl : public PdfToEmfConverter { | 195 class PdfToEmfConverterImpl : public PdfToEmfConverter { |
196 public: | 196 public: |
197 PdfToEmfConverterImpl(); | 197 PdfToEmfConverterImpl(); |
198 | 198 |
199 ~PdfToEmfConverterImpl() override; | 199 ~PdfToEmfConverterImpl() override; |
200 | 200 |
201 void Start(const scoped_refptr<base::RefCountedMemory>& data, | 201 void Start(const scoped_refptr<base::RefCountedMemory>& data, |
202 const PdfRenderSettings& conversion_settings, | 202 const PdfRenderSettings& conversion_settings, |
203 bool print_text_with_gdi, | |
204 const StartCallback& start_callback) override; | 203 const StartCallback& start_callback) override; |
205 | 204 |
206 void GetPage(int page_number, | 205 void GetPage(int page_number, |
207 const GetPageCallback& get_page_callback) override; | 206 const GetPageCallback& get_page_callback) override; |
208 | 207 |
209 // Helps to cancel callbacks if this object is destroyed. | 208 // Helps to cancel callbacks if this object is destroyed. |
210 void RunCallback(const base::Closure& callback); | 209 void RunCallback(const base::Closure& callback); |
211 | 210 |
212 private: | 211 private: |
213 scoped_refptr<PdfToEmfUtilityProcessHostClient> utility_client_; | 212 scoped_refptr<PdfToEmfUtilityProcessHostClient> utility_client_; |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
253 pdf_file.reset(); | 252 pdf_file.reset(); |
254 return pdf_file; | 253 return pdf_file; |
255 } | 254 } |
256 pdf_file->Seek(base::File::FROM_BEGIN, 0); | 255 pdf_file->Seek(base::File::FROM_BEGIN, 0); |
257 return pdf_file; | 256 return pdf_file; |
258 } | 257 } |
259 | 258 |
260 bool LazyEmf::SafePlayback(HDC hdc) const { | 259 bool LazyEmf::SafePlayback(HDC hdc) const { |
261 Emf emf; | 260 Emf emf; |
262 bool result = LoadEmf(&emf) && emf.SafePlayback(hdc); | 261 bool result = LoadEmf(&emf) && emf.SafePlayback(hdc); |
263 // TODO(vitalybuka): Fix destruction of metafiles. For some reasons | 262 // TODO(thestig): Fix destruction of metafiles. For some reasons |
264 // instances of Emf are not deleted. crbug.com/411683 | 263 // instances of Emf are not deleted. https://crbug.com/260806 |
265 // It's known that the Emf going to be played just once to a printer. So just | 264 // It's known that the Emf going to be played just once to a printer. So just |
266 // release file here. | 265 // release |file_| here. |
267 Close(); | 266 Close(); |
268 return result; | 267 return result; |
269 } | 268 } |
270 | 269 |
271 bool LazyEmf::GetDataAsVector(std::vector<char>* buffer) const { | 270 bool LazyEmf::GetDataAsVector(std::vector<char>* buffer) const { |
272 NOTREACHED(); | 271 NOTREACHED(); |
273 return false; | 272 return false; |
274 } | 273 } |
275 | 274 |
276 bool LazyEmf::SaveTo(base::File* file) const { | 275 bool LazyEmf::SaveTo(base::File* file) const { |
277 Emf emf; | 276 Emf emf; |
278 return LoadEmf(&emf) && emf.SaveTo(file); | 277 return LoadEmf(&emf) && emf.SaveTo(file); |
279 } | 278 } |
280 | 279 |
281 void LazyEmf::Close() const { | 280 void LazyEmf::Close() const { |
282 file_.reset(); | 281 file_.reset(); |
283 temp_dir_ = NULL; | 282 temp_dir_ = nullptr; |
284 } | 283 } |
285 | 284 |
286 bool LazyEmf::LoadEmf(Emf* emf) const { | 285 bool LazyEmf::LoadEmf(Emf* emf) const { |
287 file_->Seek(base::File::FROM_BEGIN, 0); | 286 file_->Seek(base::File::FROM_BEGIN, 0); |
288 int64_t size = file_->GetLength(); | 287 int64_t size = file_->GetLength(); |
289 if (size <= 0) | 288 if (size <= 0) |
290 return false; | 289 return false; |
291 std::vector<char> data(size); | 290 std::vector<char> data(size); |
292 if (file_->ReadAtCurrentPos(data.data(), data.size()) != size) | 291 if (file_->ReadAtCurrentPos(data.data(), data.size()) != size) |
293 return false; | 292 return false; |
294 return emf->InitFromData(data.data(), data.size()); | 293 return emf->InitFromData(data.data(), data.size()); |
295 } | 294 } |
296 | 295 |
297 PdfToEmfUtilityProcessHostClient::PdfToEmfUtilityProcessHostClient( | 296 PdfToEmfUtilityProcessHostClient::PdfToEmfUtilityProcessHostClient( |
298 base::WeakPtr<PdfToEmfConverterImpl> converter, | 297 base::WeakPtr<PdfToEmfConverterImpl> converter, |
299 const PdfRenderSettings& settings) | 298 const PdfRenderSettings& settings) |
300 : converter_(converter), settings_(settings) { | 299 : converter_(converter), settings_(settings) { |
301 } | 300 } |
302 | 301 |
303 PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() { | 302 PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() { |
304 } | 303 } |
305 | 304 |
306 void PdfToEmfUtilityProcessHostClient::Start( | 305 void PdfToEmfUtilityProcessHostClient::Start( |
307 const scoped_refptr<base::RefCountedMemory>& data, | 306 const scoped_refptr<base::RefCountedMemory>& data, |
308 bool print_text_with_gdi, | |
309 const PdfToEmfConverter::StartCallback& start_callback) { | 307 const PdfToEmfConverter::StartCallback& start_callback) { |
310 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { | 308 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { |
311 BrowserThread::PostTask( | 309 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, |
312 BrowserThread::IO, FROM_HERE, | 310 base::Bind(&PdfToEmfUtilityProcessHostClient::Start, |
313 base::Bind(&PdfToEmfUtilityProcessHostClient::Start, this, data, | 311 this, data, start_callback)); |
314 print_text_with_gdi, start_callback)); | |
315 return; | 312 return; |
316 } | 313 } |
317 | 314 |
318 // Store callback before any OnFailed() call to make it called on failure. | 315 // Store callback before any OnFailed() call to make it called on failure. |
319 start_callback_ = start_callback; | 316 start_callback_ = start_callback; |
320 | 317 |
321 // NOTE: This process _must_ be sandboxed, otherwise the pdf dll will load | 318 // NOTE: This process _must_ be sandboxed, otherwise the pdf dll will load |
322 // gdiplus.dll, change how rendering happens, and not be able to correctly | 319 // gdiplus.dll, change how rendering happens, and not be able to correctly |
323 // generate when sent to a metafile DC. | 320 // generate when sent to a metafile DC. |
324 utility_process_host_ = content::UtilityProcessHost::Create( | 321 utility_process_host_ = content::UtilityProcessHost::Create( |
325 this, base::ThreadTaskRunnerHandle::Get()) | 322 this, base::ThreadTaskRunnerHandle::Get()) |
326 ->AsWeakPtr(); | 323 ->AsWeakPtr(); |
327 utility_process_host_->SetName(l10n_util::GetStringUTF16( | 324 utility_process_host_->SetName(l10n_util::GetStringUTF16( |
328 IDS_UTILITY_PROCESS_EMF_CONVERTOR_NAME)); | 325 IDS_UTILITY_PROCESS_EMF_CONVERTOR_NAME)); |
329 | 326 |
330 BrowserThread::PostTaskAndReplyWithResult( | 327 BrowserThread::PostTaskAndReplyWithResult( |
331 BrowserThread::FILE, FROM_HERE, | 328 BrowserThread::FILE, FROM_HERE, |
332 base::Bind(&CreateTempPdfFile, data, &temp_dir_), | 329 base::Bind(&CreateTempPdfFile, data, &temp_dir_), |
333 base::Bind(&PdfToEmfUtilityProcessHostClient::OnTempPdfReady, this, | 330 base::Bind(&PdfToEmfUtilityProcessHostClient::OnTempPdfReady, this)); |
334 print_text_with_gdi)); | |
335 } | 331 } |
336 | 332 |
337 void PdfToEmfUtilityProcessHostClient::OnTempPdfReady(bool print_text_with_gdi, | 333 void PdfToEmfUtilityProcessHostClient::OnTempPdfReady(ScopedTempFile pdf) { |
338 ScopedTempFile pdf) { | |
339 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 334 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
340 if (!utility_process_host_ || !pdf) | 335 if (!utility_process_host_ || !pdf) |
341 return OnFailed(); | 336 return OnFailed(); |
342 // Should reply with OnPageCount(). | 337 // Should reply with OnPageCount(). |
343 Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles( | 338 Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles( |
344 IPC::GetPlatformFileForTransit(pdf->GetPlatformFile(), false), settings_, | 339 IPC::GetPlatformFileForTransit(pdf->GetPlatformFile(), false), |
345 print_text_with_gdi)); | 340 settings_)); |
346 } | 341 } |
347 | 342 |
348 void PdfToEmfUtilityProcessHostClient::OnPageCount(int page_count) { | 343 void PdfToEmfUtilityProcessHostClient::OnPageCount(int page_count) { |
349 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 344 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
350 if (start_callback_.is_null()) | 345 if (start_callback_.is_null()) |
351 return OnFailed(); | 346 return OnFailed(); |
352 BrowserThread::PostTask(BrowserThread::UI, | 347 BrowserThread::PostTask(BrowserThread::UI, |
353 FROM_HERE, | 348 FROM_HERE, |
354 base::Bind(&PdfToEmfConverterImpl::RunCallback, | 349 base::Bind(&PdfToEmfConverterImpl::RunCallback, |
355 converter_, | 350 converter_, |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
512 } | 507 } |
513 | 508 |
514 PdfToEmfConverterImpl::~PdfToEmfConverterImpl() { | 509 PdfToEmfConverterImpl::~PdfToEmfConverterImpl() { |
515 if (utility_client_.get()) | 510 if (utility_client_.get()) |
516 utility_client_->Stop(); | 511 utility_client_->Stop(); |
517 } | 512 } |
518 | 513 |
519 void PdfToEmfConverterImpl::Start( | 514 void PdfToEmfConverterImpl::Start( |
520 const scoped_refptr<base::RefCountedMemory>& data, | 515 const scoped_refptr<base::RefCountedMemory>& data, |
521 const PdfRenderSettings& conversion_settings, | 516 const PdfRenderSettings& conversion_settings, |
522 bool print_text_with_gdi, | |
523 const StartCallback& start_callback) { | 517 const StartCallback& start_callback) { |
524 DCHECK(!utility_client_.get()); | 518 DCHECK(!utility_client_.get()); |
525 utility_client_ = new PdfToEmfUtilityProcessHostClient( | 519 utility_client_ = new PdfToEmfUtilityProcessHostClient( |
526 weak_ptr_factory_.GetWeakPtr(), conversion_settings); | 520 weak_ptr_factory_.GetWeakPtr(), conversion_settings); |
527 utility_client_->Start(data, print_text_with_gdi, start_callback); | 521 utility_client_->Start(data, start_callback); |
528 } | 522 } |
529 | 523 |
530 void PdfToEmfConverterImpl::GetPage(int page_number, | 524 void PdfToEmfConverterImpl::GetPage(int page_number, |
531 const GetPageCallback& get_page_callback) { | 525 const GetPageCallback& get_page_callback) { |
532 utility_client_->GetPage(page_number, get_page_callback); | 526 utility_client_->GetPage(page_number, get_page_callback); |
533 } | 527 } |
534 | 528 |
535 void PdfToEmfConverterImpl::RunCallback(const base::Closure& callback) { | 529 void PdfToEmfConverterImpl::RunCallback(const base::Closure& callback) { |
536 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 530 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
537 callback.Run(); | 531 callback.Run(); |
538 } | 532 } |
539 | 533 |
534 // See LazyEmf/RefCountedTempDir comments for why it is also needed here. | |
535 // Maybe combine the two classes since they are so similar. | |
536 class PostScriptMetaFile : public MetafilePlayer { | |
537 public: | |
Vitaly Buka (NO REVIEWS)
2017/01/17 19:29:16
To much of duplicated code.
Could you just do publ
| |
538 PostScriptMetaFile(const scoped_refptr<RefCountedTempDir>& temp_dir, | |
539 ScopedTempFile file) | |
540 : temp_dir_(temp_dir), file_(std::move(file)) { | |
541 CHECK(file_); | |
542 } | |
543 ~PostScriptMetaFile() override { Close(); } | |
544 | |
545 private: | |
546 // MetafilePlayer: | |
547 bool SafePlayback(HDC hdc) const override; | |
548 bool GetDataAsVector(std::vector<char>* buffer) const override; | |
549 bool SaveTo(base::File* file) const override; | |
550 | |
551 void Close() const; | |
552 bool LoadEmf(Emf* emf) const; | |
553 | |
554 mutable scoped_refptr<RefCountedTempDir> temp_dir_; | |
555 mutable ScopedTempFile file_; // Mutable because of consts in base class. | |
556 | |
557 DISALLOW_COPY_AND_ASSIGN(PostScriptMetaFile); | |
558 }; | |
559 | |
560 bool PostScriptMetaFile::SafePlayback(HDC hdc) const { | |
561 // TODO(thestig): Fix destruction of metafiles. For some reasons | |
562 // instances of Emf are not deleted. https://crbug.com/260806 | |
563 // It's known that the Emf going to be played just once to a printer. So just | |
564 // release |file_| before returning. | |
565 Emf emf; | |
566 if (!LoadEmf(&emf)) { | |
567 Close(); | |
568 return false; | |
569 } | |
570 | |
571 { | |
572 // Ensure enumerator destruction before calling Close() below. | |
573 Emf::Enumerator emf_enum(emf, nullptr, nullptr); | |
574 for (const Emf::Record& record : emf_enum) { | |
575 auto* emf_record = record.record(); | |
576 if (emf_record->iType != EMR_GDICOMMENT) | |
577 continue; | |
578 | |
579 const EMRGDICOMMENT* comment = | |
580 reinterpret_cast<const EMRGDICOMMENT*>(emf_record); | |
581 const char* data = reinterpret_cast<const char*>(comment->Data); | |
582 const uint16_t* ptr = reinterpret_cast<const uint16_t*>(data); | |
583 int ret = ExtEscape(hdc, PASSTHROUGH, 2 + *ptr, data, 0, nullptr); | |
584 DCHECK_EQ(*ptr, ret); | |
585 } | |
586 } | |
587 Close(); | |
588 return true; | |
589 } | |
590 | |
591 bool PostScriptMetaFile::GetDataAsVector(std::vector<char>* buffer) const { | |
592 NOTREACHED(); | |
593 return false; | |
594 } | |
595 | |
596 bool PostScriptMetaFile::SaveTo(base::File* file) const { | |
597 Emf emf; | |
598 return LoadEmf(&emf) && emf.SaveTo(file); | |
599 } | |
600 | |
601 void PostScriptMetaFile::Close() const { | |
602 file_.reset(); | |
603 temp_dir_ = nullptr; | |
604 } | |
605 | |
606 bool PostScriptMetaFile::LoadEmf(Emf* emf) const { | |
607 file_->Seek(base::File::FROM_BEGIN, 0); | |
608 int64_t size = file_->GetLength(); | |
609 if (size <= 0) | |
610 return false; | |
611 std::vector<char> data(size); | |
612 if (file_->ReadAtCurrentPos(data.data(), data.size()) != size) | |
613 return false; | |
614 return emf->InitFromData(data.data(), data.size()); | |
615 } | |
616 | |
617 class PdfToPostScriptConverterImpl; | |
618 | |
619 // Converts PDF into PostScript. | |
620 // Class uses 3 threads: UI, IO and FILE. | |
621 // Internal workflow is following: | |
622 // 1. Create instance on the UI thread. (files_, settings_,) | |
623 // 2. Create pdf file on the FILE thread. | |
624 // 3. Start utility process and start conversion on the IO thread. | |
625 // 4. Utility process returns page count. | |
626 // 5. For each page: | |
627 // 1. Clients requests page with file handle to a temp file. | |
628 // 2. Utility converts the page, save it to the file and reply. | |
629 // | |
630 // All these steps work sequentially, so no data should be accessed | |
631 // simultaneously by several threads. | |
632 class PdfToPostScriptUtilityProcessHostClient | |
633 : public content::UtilityProcessHostClient { | |
634 public: | |
635 PdfToPostScriptUtilityProcessHostClient( | |
636 base::WeakPtr<PdfToPostScriptConverterImpl> converter, | |
637 const PdfRenderSettings& settings); | |
638 | |
639 void Start(const scoped_refptr<base::RefCountedMemory>& data, | |
640 const PdfToPostScriptConverter::StartCallback& start_callback); | |
641 | |
642 void GetPage( | |
643 int page_number, | |
644 const PdfToPostScriptConverter::GetPageCallback& get_page_callback); | |
645 | |
646 void Stop(); | |
647 | |
648 // UtilityProcessHostClient implementation. | |
649 void OnProcessCrashed(int exit_code) override; | |
650 void OnProcessLaunchFailed(int exit_code) override; | |
651 bool OnMessageReceived(const IPC::Message& message) override; | |
652 | |
653 private: | |
654 class GetPageCallbackData { | |
655 public: | |
656 GetPageCallbackData( | |
657 int page_number, | |
658 const PdfToPostScriptConverter::GetPageCallback& callback) | |
659 : page_number_(page_number), callback_(callback) {} | |
660 | |
661 GetPageCallbackData(GetPageCallbackData&& other) { | |
662 *this = std::move(other); | |
663 } | |
664 | |
665 GetPageCallbackData& operator=(GetPageCallbackData&& rhs) { | |
666 page_number_ = rhs.page_number_; | |
667 callback_ = rhs.callback_; | |
668 ps_ = std::move(rhs.ps_); | |
669 return *this; | |
670 } | |
671 | |
672 int page_number() const { return page_number_; } | |
673 const PdfToPostScriptConverter::GetPageCallback& callback() const { | |
674 return callback_; | |
675 } | |
676 ScopedTempFile TakePostScript() { return std::move(ps_); } | |
677 void set_ps(ScopedTempFile ps) { ps_ = std::move(ps); } | |
678 | |
679 private: | |
680 int page_number_; | |
681 PdfToPostScriptConverter::GetPageCallback callback_; | |
682 ScopedTempFile ps_; | |
683 | |
684 DISALLOW_COPY_AND_ASSIGN(GetPageCallbackData); | |
685 }; | |
686 | |
687 ~PdfToPostScriptUtilityProcessHostClient() override; | |
688 | |
689 bool Send(IPC::Message* msg); | |
690 | |
691 // Message handlers. | |
692 void OnPageCount(int page_count); | |
693 void OnPageDone(bool success); | |
694 | |
695 void OnFailed(); | |
696 void OnTempPdfReady(ScopedTempFile pdf); | |
697 void OnTempPostScriptReady(GetPageCallbackData* callback_data, | |
698 ScopedTempFile ps); | |
699 | |
700 scoped_refptr<RefCountedTempDir> temp_dir_; | |
701 | |
702 // Used to suppress callbacks after PdfToPostScriptConverterImpl is deleted. | |
703 base::WeakPtr<PdfToPostScriptConverterImpl> converter_; | |
704 PdfRenderSettings settings_; | |
705 | |
706 // Document loaded callback. | |
707 PdfToPostScriptConverter::StartCallback start_callback_; | |
708 | |
709 // Process host for IPC. | |
710 base::WeakPtr<content::UtilityProcessHost> utility_process_host_; | |
711 | |
712 // Queue of callbacks for GetPage() requests. Utility process should reply | |
713 // with PageDone in the same order as requests were received. | |
714 // Use containers that keeps element pointers valid after push() and pop(). | |
715 using GetPageCallbacks = std::queue<GetPageCallbackData>; | |
716 GetPageCallbacks get_page_callbacks_; | |
717 | |
718 DISALLOW_COPY_AND_ASSIGN(PdfToPostScriptUtilityProcessHostClient); | |
719 }; | |
720 | |
721 class PdfToPostScriptConverterImpl : public PdfToPostScriptConverter { | |
722 public: | |
723 PdfToPostScriptConverterImpl(); | |
724 ~PdfToPostScriptConverterImpl() override; | |
725 | |
726 void Start(const scoped_refptr<base::RefCountedMemory>& data, | |
727 const PdfRenderSettings& conversion_settings, | |
728 const StartCallback& start_callback) override; | |
729 | |
730 void GetPage(int page_number, | |
731 const GetPageCallback& get_page_callback) override; | |
732 | |
733 // Helps to cancel callbacks if this object is destroyed. | |
734 void RunCallback(const base::Closure& callback); | |
735 | |
736 private: | |
737 scoped_refptr<PdfToPostScriptUtilityProcessHostClient> utility_client_; | |
738 base::WeakPtrFactory<PdfToPostScriptConverterImpl> weak_ptr_factory_; | |
739 | |
740 DISALLOW_COPY_AND_ASSIGN(PdfToPostScriptConverterImpl); | |
741 }; | |
742 | |
743 PdfToPostScriptUtilityProcessHostClient:: | |
744 PdfToPostScriptUtilityProcessHostClient( | |
745 base::WeakPtr<PdfToPostScriptConverterImpl> converter, | |
746 const PdfRenderSettings& settings) | |
747 : converter_(converter), settings_(settings) {} | |
748 | |
749 PdfToPostScriptUtilityProcessHostClient:: | |
750 ~PdfToPostScriptUtilityProcessHostClient() {} | |
751 | |
752 void PdfToPostScriptUtilityProcessHostClient::Start( | |
753 const scoped_refptr<base::RefCountedMemory>& data, | |
754 const PdfToPostScriptConverter::StartCallback& start_callback) { | |
755 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { | |
756 BrowserThread::PostTask( | |
757 BrowserThread::IO, FROM_HERE, | |
758 base::Bind(&PdfToPostScriptUtilityProcessHostClient::Start, this, data, | |
759 start_callback)); | |
760 return; | |
761 } | |
762 // Store callback before any OnFailed() call to make it called on failure. | |
763 start_callback_ = start_callback; | |
764 | |
765 // NOTE: This process _must_ be sandboxed, otherwise the pdf dll will load | |
766 // gdiplus.dll, change how rendering happens, and not be able to correctly | |
767 // generate when sent to a metafile DC. | |
768 utility_process_host_ = content::UtilityProcessHost::Create( | |
769 this, base::ThreadTaskRunnerHandle::Get()) | |
770 ->AsWeakPtr(); | |
771 utility_process_host_->SetName( | |
772 l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_PS_CONVERTOR_NAME)); | |
773 | |
774 BrowserThread::PostTaskAndReplyWithResult( | |
775 BrowserThread::FILE, FROM_HERE, | |
776 base::Bind(&CreateTempPdfFile, data, &temp_dir_), | |
777 base::Bind(&PdfToPostScriptUtilityProcessHostClient::OnTempPdfReady, | |
778 this)); | |
779 } | |
780 | |
781 void PdfToPostScriptUtilityProcessHostClient::OnTempPdfReady( | |
782 ScopedTempFile pdf) { | |
783 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
784 if (!utility_process_host_ || !pdf) | |
785 return OnFailed(); | |
786 // Should reply with OnPageCount(). | |
787 Send(new ChromeUtilityMsg_RenderPDFPagesToPostScript_Start( | |
788 IPC::GetPlatformFileForTransit(pdf->GetPlatformFile(), false), | |
789 settings_)); | |
790 } | |
791 | |
792 void PdfToPostScriptUtilityProcessHostClient::OnPageCount(int page_count) { | |
793 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
794 if (start_callback_.is_null()) | |
795 return OnFailed(); | |
796 BrowserThread::PostTask( | |
797 BrowserThread::UI, FROM_HERE, | |
798 base::Bind(&PdfToPostScriptConverterImpl::RunCallback, converter_, | |
799 base::Bind(start_callback_, page_count))); | |
800 start_callback_.Reset(); | |
801 } | |
802 | |
803 void PdfToPostScriptUtilityProcessHostClient::GetPage( | |
804 int page_number, | |
805 const PdfToPostScriptConverter::GetPageCallback& get_page_callback) { | |
806 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { | |
807 BrowserThread::PostTask( | |
808 BrowserThread::IO, FROM_HERE, | |
809 base::Bind(&PdfToPostScriptUtilityProcessHostClient::GetPage, this, | |
810 page_number, get_page_callback)); | |
811 return; | |
812 } | |
813 // Store callback before any OnFailed() call to make it called on failure. | |
814 get_page_callbacks_.push(GetPageCallbackData(page_number, get_page_callback)); | |
815 | |
816 if (!utility_process_host_) | |
817 return OnFailed(); | |
818 | |
819 BrowserThread::PostTaskAndReplyWithResult( | |
820 BrowserThread::FILE, FROM_HERE, base::Bind(&CreateTempFile, &temp_dir_), | |
821 base::Bind( | |
822 &PdfToPostScriptUtilityProcessHostClient::OnTempPostScriptReady, this, | |
823 &get_page_callbacks_.back())); | |
824 } | |
825 | |
826 void PdfToPostScriptUtilityProcessHostClient::OnTempPostScriptReady( | |
827 GetPageCallbackData* callback_data, | |
828 ScopedTempFile ps) { | |
829 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
830 if (!utility_process_host_ || !ps) | |
831 return OnFailed(); | |
832 IPC::PlatformFileForTransit transit = | |
833 IPC::GetPlatformFileForTransit(ps->GetPlatformFile(), false); | |
834 callback_data->set_ps(std::move(ps)); | |
835 // Should reply with OnPageDone(). | |
836 Send(new ChromeUtilityMsg_RenderPDFPagesToPostScript_GetPage( | |
837 callback_data->page_number(), transit)); | |
838 } | |
839 | |
840 void PdfToPostScriptUtilityProcessHostClient::OnPageDone(bool success) { | |
841 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
842 if (get_page_callbacks_.empty()) | |
843 return OnFailed(); | |
844 GetPageCallbackData& data = get_page_callbacks_.front(); | |
845 std::unique_ptr<PostScriptMetaFile> ps_metafile; | |
846 | |
847 if (success) { | |
848 ScopedTempFile temp_ps = data.TakePostScript(); | |
849 if (!temp_ps) // Unexpected message from utility process. | |
850 return OnFailed(); | |
851 ps_metafile = | |
852 base::MakeUnique<PostScriptMetaFile>(temp_dir_, std::move(temp_ps)); | |
853 } | |
854 | |
855 BrowserThread::PostTask( | |
856 BrowserThread::UI, FROM_HERE, | |
857 base::Bind(&PdfToPostScriptConverterImpl::RunCallback, converter_, | |
858 base::Bind(data.callback(), data.page_number(), | |
859 base::Passed(&ps_metafile)))); | |
860 get_page_callbacks_.pop(); | |
861 } | |
862 | |
863 void PdfToPostScriptUtilityProcessHostClient::Stop() { | |
864 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { | |
865 BrowserThread::PostTask( | |
866 BrowserThread::IO, FROM_HERE, | |
867 base::Bind(&PdfToPostScriptUtilityProcessHostClient::Stop, this)); | |
868 return; | |
869 } | |
870 Send(new ChromeUtilityMsg_RenderPDFPagesToPostScript_Stop()); | |
871 } | |
872 | |
873 void PdfToPostScriptUtilityProcessHostClient::OnProcessCrashed(int exit_code) { | |
874 OnFailed(); | |
875 } | |
876 | |
877 void PdfToPostScriptUtilityProcessHostClient::OnProcessLaunchFailed( | |
878 int exit_code) { | |
879 OnFailed(); | |
880 } | |
881 | |
882 bool PdfToPostScriptUtilityProcessHostClient::OnMessageReceived( | |
883 const IPC::Message& message) { | |
884 bool handled = true; | |
885 IPC_BEGIN_MESSAGE_MAP(PdfToPostScriptUtilityProcessHostClient, message) | |
886 IPC_MESSAGE_HANDLER( | |
887 ChromeUtilityHostMsg_RenderPDFPagesToPostScript_PageCount, OnPageCount) | |
888 IPC_MESSAGE_HANDLER( | |
889 ChromeUtilityHostMsg_RenderPDFPagesToPostScript_PageDone, OnPageDone) | |
890 IPC_MESSAGE_UNHANDLED(handled = false) | |
891 IPC_END_MESSAGE_MAP() | |
892 return handled; | |
893 } | |
894 | |
895 bool PdfToPostScriptUtilityProcessHostClient::Send(IPC::Message* msg) { | |
896 if (utility_process_host_) | |
897 return utility_process_host_->Send(msg); | |
898 delete msg; | |
899 return false; | |
900 } | |
901 | |
902 void PdfToPostScriptUtilityProcessHostClient::OnFailed() { | |
903 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
904 if (!start_callback_.is_null()) | |
905 OnPageCount(0); | |
906 while (!get_page_callbacks_.empty()) | |
907 OnPageDone(false); | |
908 utility_process_host_.reset(); | |
909 } | |
910 | |
911 PdfToPostScriptConverterImpl::PdfToPostScriptConverterImpl() | |
912 : weak_ptr_factory_(this) {} | |
913 | |
914 PdfToPostScriptConverterImpl::~PdfToPostScriptConverterImpl() { | |
915 if (utility_client_.get()) | |
916 utility_client_->Stop(); | |
917 } | |
918 | |
919 void PdfToPostScriptConverterImpl::Start( | |
920 const scoped_refptr<base::RefCountedMemory>& data, | |
921 const PdfRenderSettings& conversion_settings, | |
922 const StartCallback& start_callback) { | |
923 DCHECK(!utility_client_.get()); | |
924 utility_client_ = new PdfToPostScriptUtilityProcessHostClient( | |
925 weak_ptr_factory_.GetWeakPtr(), conversion_settings); | |
926 utility_client_->Start(data, start_callback); | |
927 } | |
928 | |
929 void PdfToPostScriptConverterImpl::GetPage( | |
930 int page_number, | |
931 const GetPageCallback& get_page_callback) { | |
932 utility_client_->GetPage(page_number, get_page_callback); | |
933 } | |
934 | |
935 void PdfToPostScriptConverterImpl::RunCallback(const base::Closure& callback) { | |
936 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
937 callback.Run(); | |
938 } | |
939 | |
540 } // namespace | 940 } // namespace |
541 | 941 |
542 PdfToEmfConverter::~PdfToEmfConverter() { | 942 PdfToEmfConverter::~PdfToEmfConverter() {} |
543 } | |
544 | 943 |
545 // static | 944 // static |
546 std::unique_ptr<PdfToEmfConverter> PdfToEmfConverter::CreateDefault() { | 945 std::unique_ptr<PdfToEmfConverter> PdfToEmfConverter::CreateDefault() { |
547 return std::unique_ptr<PdfToEmfConverter>(new PdfToEmfConverterImpl()); | 946 return base::MakeUnique<PdfToEmfConverterImpl>(); |
947 } | |
948 | |
949 PdfToPostScriptConverter::~PdfToPostScriptConverter() {} | |
950 | |
951 // static | |
952 std::unique_ptr<PdfToPostScriptConverter> | |
953 PdfToPostScriptConverter::CreateDefault() { | |
954 return base::MakeUnique<PdfToPostScriptConverterImpl>(); | |
548 } | 955 } |
549 | 956 |
550 } // namespace printing | 957 } // namespace printing |
OLD | NEW |