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 "base/bind_helpers.h" | 7 #include <queue> |
8 #include "base/cancelable_callback.h" | 8 |
9 #include "base/files/file.h" | 9 #include "base/files/file.h" |
10 #include "base/files/file_util.h" | 10 #include "base/files/file_util.h" |
11 #include "base/files/scoped_temp_dir.h" | 11 #include "base/files/scoped_temp_dir.h" |
12 #include "base/logging.h" | 12 #include "base/logging.h" |
13 #include "chrome/common/chrome_utility_messages.h" | 13 #include "chrome/common/chrome_utility_messages.h" |
14 #include "chrome/common/chrome_utility_printing_messages.h" | 14 #include "chrome/common/chrome_utility_printing_messages.h" |
15 #include "content/public/browser/browser_thread.h" | 15 #include "content/public/browser/browser_thread.h" |
16 #include "content/public/browser/child_process_data.h" | 16 #include "content/public/browser/child_process_data.h" |
17 #include "content/public/browser/utility_process_host.h" | 17 #include "content/public/browser/utility_process_host.h" |
18 #include "content/public/browser/utility_process_host_client.h" | 18 #include "content/public/browser/utility_process_host_client.h" |
19 #include "printing/emf_win.h" | 19 #include "printing/emf_win.h" |
20 #include "printing/page_range.h" | |
21 #include "printing/pdf_render_settings.h" | 20 #include "printing/pdf_render_settings.h" |
22 | 21 |
23 namespace printing { | 22 namespace printing { |
24 | 23 |
25 namespace { | 24 namespace { |
26 | 25 |
27 using content::BrowserThread; | 26 using content::BrowserThread; |
28 | 27 |
29 class FileHandlers | 28 class PdfToEmfConverterImpl; |
30 : public base::RefCountedThreadSafe<FileHandlers, | 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, |
31 BrowserThread::DeleteOnFileThread> { | 36 BrowserThread::DeleteOnFileThread> { |
32 public: | 37 public: |
33 FileHandlers() {} | 38 RefCountedTempDir() { temp_dir_.CreateUniqueTempDir(); } |
34 | 39 bool IsValid() const { return temp_dir_.IsValid(); } |
35 void Init(base::RefCountedMemory* data); | 40 const base::FilePath& GetPath() const { return temp_dir_.path(); } |
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 | 41 |
62 private: | 42 private: |
63 friend struct BrowserThread::DeleteOnThread<BrowserThread::FILE>; | 43 friend struct BrowserThread::DeleteOnThread<BrowserThread::FILE>; |
64 friend class base::DeleteHelper<FileHandlers>; | 44 friend class base::DeleteHelper<RefCountedTempDir>; |
65 | 45 ~RefCountedTempDir() {}; |
66 ~FileHandlers() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); } | |
67 | 46 |
68 base::ScopedTempDir temp_dir_; | 47 base::ScopedTempDir temp_dir_; |
69 base::File pdf_file_; | 48 DISALLOW_COPY_AND_ASSIGN(RefCountedTempDir); |
70 }; | 49 }; |
71 | 50 |
72 void FileHandlers::Init(base::RefCountedMemory* data) { | 51 typedef scoped_ptr<base::File, BrowserThread::DeleteOnFileThread> |
73 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 52 ScopedTempFile; |
74 | 53 |
75 if (!temp_dir_.CreateUniqueTempDir()) { | 54 // Wrapper for Emf to keep only file handle in memory, and load actual data only |
76 return; | 55 // on playback. Emf::InitFromFile() can play metafile directly from disk, but it |
77 } | 56 // can't open file handles. We need file handles to reliably delete temporary |
78 | 57 // files, and to efficiently interact with utility process. |
79 pdf_file_.Initialize(GetPdfPath(), | 58 class LazyEmf : public MetafilePlayer { |
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: | 59 public: |
103 explicit TempEmf(const scoped_refptr<FileHandlers>& files) : files_(files) {} | 60 LazyEmf(const scoped_refptr<RefCountedTempDir>& temp_dir, ScopedTempFile file) |
104 virtual ~TempEmf() {} | 61 : temp_dir_(temp_dir), file_(file.Pass()) {} |
105 | 62 virtual ~LazyEmf() { Close(); } |
106 virtual bool SafePlayback(HDC hdc) const OVERRIDE { | 63 |
107 bool result = Emf::SafePlayback(hdc); | 64 virtual bool SafePlayback(HDC hdc) const OVERRIDE; |
108 TempEmf* this_mutable = const_cast<TempEmf*>(this); | 65 virtual bool SaveTo(base::File* file) const OVERRIDE; |
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 | 66 |
119 private: | 67 private: |
120 scoped_refptr<FileHandlers> files_; | 68 void Close() const; |
121 DISALLOW_COPY_AND_ASSIGN(TempEmf); | 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); |
122 }; | 75 }; |
123 | 76 |
124 // Converts PDF into EMF. | 77 // Converts PDF into EMF. |
125 // Class uses 3 threads: UI, IO and FILE. | 78 // Class uses 3 threads: UI, IO and FILE. |
126 // Internal workflow is following: | 79 // Internal workflow is following: |
127 // 1. Create instance on the UI thread. (files_, settings_,) | 80 // 1. Create instance on the UI thread. (files_, settings_,) |
128 // 2. Create file on the FILE thread. | 81 // 2. Create pdf file on the FILE thread. |
129 // 3. Start utility process and start conversion on the IO thread. | 82 // 3. Start utility process and start conversion on the IO thread. |
130 // 4. Run result callback on the UI thread. | 83 // 4. Utility process returns page count. |
131 // 5. Instance is destroyed from any thread that has the last reference. | 84 // 5. For each page: |
132 // 6. FileHandlers destroyed on the FILE thread. | 85 // 1. Clients requests page with file handle to a temp file. |
133 // This step posts |FileHandlers| to be destroyed on the FILE thread. | 86 // 2. Utility converts the page, save it to the file and reply. |
| 87 // |
134 // All these steps work sequentially, so no data should be accessed | 88 // All these steps work sequentially, so no data should be accessed |
135 // simultaneously by several threads. | 89 // simultaneously by several threads. |
136 class PdfToEmfUtilityProcessHostClient | 90 class PdfToEmfUtilityProcessHostClient |
137 : public content::UtilityProcessHostClient { | 91 : public content::UtilityProcessHostClient { |
138 public: | 92 public: |
139 explicit PdfToEmfUtilityProcessHostClient( | 93 PdfToEmfUtilityProcessHostClient( |
140 const printing::PdfRenderSettings& settings); | 94 base::WeakPtr<PdfToEmfConverterImpl> converter, |
141 | 95 const PdfRenderSettings& settings); |
142 void Convert(base::RefCountedMemory* data, | 96 |
143 const PdfToEmfConverter::ResultCallback& callback); | 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(); |
144 | 104 |
145 // UtilityProcessHostClient implementation. | 105 // UtilityProcessHostClient implementation. |
146 virtual void OnProcessCrashed(int exit_code) OVERRIDE; | 106 virtual void OnProcessCrashed(int exit_code) OVERRIDE; |
| 107 virtual void OnProcessLaunchFailed() OVERRIDE; |
147 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; | 108 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; |
148 | 109 |
149 private: | 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 |
150 virtual ~PdfToEmfUtilityProcessHostClient(); | 143 virtual ~PdfToEmfUtilityProcessHostClient(); |
151 | 144 |
| 145 bool Send(IPC::Message* msg); |
| 146 |
152 // Message handlers. | 147 // Message handlers. |
153 void OnProcessStarted(); | 148 void OnProcessStarted(); |
154 void OnSucceeded(const std::vector<printing::PageRange>& page_ranges, | 149 void OnPageCount(int page_count); |
155 double scale_factor); | 150 void OnPageDone(bool success, double scale_factor); |
| 151 |
156 void OnFailed(); | 152 void OnFailed(); |
157 | 153 void OnTempPdfReady(ScopedTempFile pdf); |
158 void RunCallback(const std::vector<printing::PageRange>& page_ranges, | 154 void OnTempEmfReady(GetPageCallbackData* callback_data, ScopedTempFile emf); |
159 double scale_factor); | 155 |
160 | 156 scoped_refptr<RefCountedTempDir> temp_dir_; |
161 void StartProcessOnIOThread(); | 157 |
162 | 158 // Used to suppress callbacks after PdfToEmfConverterImpl is deleted. |
163 void RunCallbackOnUIThread( | 159 base::WeakPtr<PdfToEmfConverterImpl> converter_; |
164 const std::vector<printing::PageRange>& page_ranges, | 160 PdfRenderSettings settings_; |
165 double scale_factor); | 161 scoped_refptr<base::RefCountedMemory> data_; |
166 void OnFilesReadyOnUIThread(); | 162 |
167 | 163 // Document loaded callback. |
168 scoped_refptr<FileHandlers> files_; | 164 PdfToEmfConverter::StartCallback start_callback_; |
169 printing::PdfRenderSettings settings_; | 165 |
170 PdfToEmfConverter::ResultCallback callback_; | 166 // Process host for IPC. |
171 base::WeakPtr<content::UtilityProcessHost> utility_process_host_; | 167 base::WeakPtr<content::UtilityProcessHost> utility_process_host_; |
172 | 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 |
173 DISALLOW_COPY_AND_ASSIGN(PdfToEmfUtilityProcessHostClient); | 175 DISALLOW_COPY_AND_ASSIGN(PdfToEmfUtilityProcessHostClient); |
174 }; | 176 }; |
175 | 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)) |
| 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 |
176 PdfToEmfUtilityProcessHostClient::PdfToEmfUtilityProcessHostClient( | 268 PdfToEmfUtilityProcessHostClient::PdfToEmfUtilityProcessHostClient( |
177 const printing::PdfRenderSettings& settings) | 269 base::WeakPtr<PdfToEmfConverterImpl> converter, |
178 : settings_(settings) {} | 270 const PdfRenderSettings& settings) |
| 271 : converter_(converter), settings_(settings) { |
| 272 } |
179 | 273 |
180 PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() { | 274 PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() { |
181 } | 275 } |
182 | 276 |
183 void PdfToEmfUtilityProcessHostClient::Convert( | 277 void PdfToEmfUtilityProcessHostClient::Start( |
184 base::RefCountedMemory* data, | 278 const scoped_refptr<base::RefCountedMemory>& data, |
185 const PdfToEmfConverter::ResultCallback& callback) { | 279 const PdfToEmfConverter::StartCallback& start_callback) { |
186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 280 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { |
187 callback_ = callback; | 281 BrowserThread::PostTask(BrowserThread::IO, |
188 CHECK(!files_.get()); | 282 FROM_HERE, |
189 files_ = new FileHandlers(); | 283 base::Bind(&PdfToEmfUtilityProcessHostClient::Start, |
190 BrowserThread::PostTaskAndReply( | 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 base::ProcessHandle process = utility_process_host_->GetData().handle; |
| 313 scoped_refptr<base::RefCountedMemory> data = data_; |
| 314 data_ = NULL; |
| 315 BrowserThread::PostTaskAndReplyWithResult( |
191 BrowserThread::FILE, | 316 BrowserThread::FILE, |
192 FROM_HERE, | 317 FROM_HERE, |
193 base::Bind(&FileHandlers::Init, files_, make_scoped_refptr(data)), | 318 base::Bind(&CreateTempPdfFile, data, &temp_dir_), |
194 base::Bind(&PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread, | 319 base::Bind(&PdfToEmfUtilityProcessHostClient::OnTempPdfReady, this)); |
195 this)); | 320 } |
| 321 |
| 322 void PdfToEmfUtilityProcessHostClient::OnTempPdfReady(ScopedTempFile pdf) { |
| 323 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 324 if (!utility_process_host_) |
| 325 return OnFailed(); |
| 326 base::ProcessHandle process = utility_process_host_->GetData().handle; |
| 327 // Should reply with OnPageCount(). |
| 328 Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles( |
| 329 IPC::GetFileHandleForProcess(pdf->GetPlatformFile(), process, false), |
| 330 settings_)); |
| 331 } |
| 332 |
| 333 void PdfToEmfUtilityProcessHostClient::OnPageCount(int page_count) { |
| 334 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 335 if (start_callback_.is_null()) |
| 336 return OnFailed(); |
| 337 BrowserThread::PostTask(BrowserThread::UI, |
| 338 FROM_HERE, |
| 339 base::Bind(&PdfToEmfConverterImpl::RunCallback, |
| 340 converter_, |
| 341 base::Bind(start_callback_, page_count))); |
| 342 start_callback_.Reset(); |
| 343 } |
| 344 |
| 345 void PdfToEmfUtilityProcessHostClient::GetPage( |
| 346 int page_number, |
| 347 const PdfToEmfConverter::GetPageCallback& get_page_callback) { |
| 348 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { |
| 349 BrowserThread::PostTask( |
| 350 BrowserThread::IO, |
| 351 FROM_HERE, |
| 352 base::Bind(&PdfToEmfUtilityProcessHostClient::GetPage, |
| 353 this, |
| 354 page_number, |
| 355 get_page_callback)); |
| 356 return; |
| 357 } |
| 358 |
| 359 // Store callback before any OnFailed() call to make it called on failure. |
| 360 get_page_callbacks_.push(GetPageCallbackData(page_number, get_page_callback)); |
| 361 |
| 362 if (!utility_process_host_) |
| 363 return OnFailed(); |
| 364 |
| 365 BrowserThread::PostTaskAndReplyWithResult( |
| 366 BrowserThread::FILE, |
| 367 FROM_HERE, |
| 368 base::Bind(&CreateTempFile, &temp_dir_), |
| 369 base::Bind(&PdfToEmfUtilityProcessHostClient::OnTempEmfReady, |
| 370 this, |
| 371 &get_page_callbacks_.back())); |
| 372 } |
| 373 |
| 374 void PdfToEmfUtilityProcessHostClient::OnTempEmfReady( |
| 375 GetPageCallbackData* callback_data, |
| 376 ScopedTempFile emf) { |
| 377 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 378 if (!utility_process_host_) |
| 379 return OnFailed(); |
| 380 base::ProcessHandle process = utility_process_host_->GetData().handle; |
| 381 IPC::PlatformFileForTransit transit = |
| 382 IPC::GetFileHandleForProcess(emf->GetPlatformFile(), process, false); |
| 383 callback_data->set_emf(emf.Pass()); |
| 384 // Should reply with OnPageDone(). |
| 385 Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_GetPage( |
| 386 callback_data->page_number(), transit)); |
| 387 } |
| 388 |
| 389 void PdfToEmfUtilityProcessHostClient::OnPageDone(bool success, |
| 390 double scale_factor) { |
| 391 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 392 if (get_page_callbacks_.empty()) |
| 393 return OnFailed(); |
| 394 scoped_ptr<LazyEmf> emf; |
| 395 GetPageCallbackData& data = get_page_callbacks_.front(); |
| 396 if (success) |
| 397 emf.reset(new LazyEmf(temp_dir_, data.emf().Pass())); |
| 398 BrowserThread::PostTask(BrowserThread::UI, |
| 399 FROM_HERE, |
| 400 base::Bind(&PdfToEmfConverterImpl::RunCallback, |
| 401 converter_, |
| 402 base::Bind(data.callback(), |
| 403 data.page_number(), |
| 404 scale_factor, |
| 405 base::Passed(&emf)))); |
| 406 get_page_callbacks_.pop(); |
| 407 } |
| 408 |
| 409 void PdfToEmfUtilityProcessHostClient::Stop() { |
| 410 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { |
| 411 BrowserThread::PostTask( |
| 412 BrowserThread::IO, |
| 413 FROM_HERE, |
| 414 base::Bind(&PdfToEmfUtilityProcessHostClient::Stop, this)); |
| 415 return; |
| 416 } |
| 417 Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop()); |
196 } | 418 } |
197 | 419 |
198 void PdfToEmfUtilityProcessHostClient::OnProcessCrashed(int exit_code) { | 420 void PdfToEmfUtilityProcessHostClient::OnProcessCrashed(int exit_code) { |
199 OnFailed(); | 421 OnFailed(); |
200 } | 422 } |
201 | 423 |
| 424 void PdfToEmfUtilityProcessHostClient::OnProcessLaunchFailed() { |
| 425 OnFailed(); |
| 426 } |
| 427 |
202 bool PdfToEmfUtilityProcessHostClient::OnMessageReceived( | 428 bool PdfToEmfUtilityProcessHostClient::OnMessageReceived( |
203 const IPC::Message& message) { | 429 const IPC::Message& message) { |
204 bool handled = true; | 430 bool handled = true; |
205 IPC_BEGIN_MESSAGE_MAP(PdfToEmfUtilityProcessHostClient, message) | 431 IPC_BEGIN_MESSAGE_MAP(PdfToEmfUtilityProcessHostClient, message) |
206 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted) | 432 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted) |
207 IPC_MESSAGE_HANDLER( | 433 IPC_MESSAGE_HANDLER( |
208 ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_Succeeded, OnSucceeded) | 434 ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount, OnPageCount) |
209 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed, | 435 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone, |
210 OnFailed) | 436 OnPageDone) |
211 IPC_MESSAGE_UNHANDLED(handled = false) | 437 IPC_MESSAGE_UNHANDLED(handled = false) |
212 IPC_END_MESSAGE_MAP() | 438 IPC_END_MESSAGE_MAP() |
213 return handled; | 439 return handled; |
214 } | 440 } |
215 | 441 |
216 void PdfToEmfUtilityProcessHostClient::OnProcessStarted() { | 442 bool PdfToEmfUtilityProcessHostClient::Send(IPC::Message* msg) { |
217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 443 if (utility_process_host_) |
218 if (!utility_process_host_) { | 444 return utility_process_host_->Send(msg); |
219 RunCallbackOnUIThread(std::vector<printing::PageRange>(), 0.0); | 445 delete msg; |
220 return; | 446 return false; |
221 } | 447 } |
222 | 448 |
223 base::ProcessHandle process = utility_process_host_->GetData().handle; | 449 void PdfToEmfUtilityProcessHostClient::OnFailed() { |
224 utility_process_host_->Send( | 450 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
225 new ChromeUtilityMsg_RenderPDFPagesToMetafiles( | 451 if (!start_callback_.is_null()) |
226 files_->GetPdfForProcess(process), | 452 OnPageCount(0); |
227 files_->GetEmfPath(), | 453 while (!get_page_callbacks_.empty()) |
228 settings_, | 454 OnPageDone(false, 0.0); |
229 std::vector<printing::PageRange>())); | |
230 utility_process_host_.reset(); | 455 utility_process_host_.reset(); |
231 } | 456 } |
232 | 457 |
233 void PdfToEmfUtilityProcessHostClient::OnSucceeded( | 458 PdfToEmfConverterImpl::PdfToEmfConverterImpl() : weak_ptr_factory_(this) { |
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<MetafilePlayer> 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 } | 459 } |
325 | 460 |
326 PdfToEmfConverterImpl::~PdfToEmfConverterImpl() { | 461 PdfToEmfConverterImpl::~PdfToEmfConverterImpl() { |
| 462 if (utility_client_) |
| 463 utility_client_->Stop(); |
327 } | 464 } |
328 | 465 |
329 void PdfToEmfConverterImpl::Start( | 466 void PdfToEmfConverterImpl::Start( |
330 base::RefCountedMemory* data, | 467 const scoped_refptr<base::RefCountedMemory>& data, |
331 const printing::PdfRenderSettings& conversion_settings, | 468 const PdfRenderSettings& conversion_settings, |
332 const ResultCallback& callback) { | 469 const StartCallback& start_callback) { |
333 // Rebind cancelable callback to avoid calling callback if | 470 DCHECK(!utility_client_); |
334 // PdfToEmfConverterImpl is destroyed. | 471 utility_client_ = new PdfToEmfUtilityProcessHostClient( |
335 callback_.Reset(callback); | 472 weak_ptr_factory_.GetWeakPtr(), conversion_settings); |
336 utility_client_ = new PdfToEmfUtilityProcessHostClient(conversion_settings); | 473 utility_client_->Start(data, start_callback); |
337 utility_client_->Convert(data, callback_.callback()); | 474 } |
| 475 |
| 476 void PdfToEmfConverterImpl::GetPage(int page_number, |
| 477 const GetPageCallback& get_page_callback) { |
| 478 utility_client_->GetPage(page_number, get_page_callback); |
| 479 } |
| 480 |
| 481 void PdfToEmfConverterImpl::RunCallback(const base::Closure& callback) { |
| 482 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 483 callback.Run(); |
338 } | 484 } |
339 | 485 |
340 } // namespace | 486 } // namespace |
341 | 487 |
| 488 PdfToEmfConverter::~PdfToEmfConverter() { |
| 489 } |
| 490 |
342 // static | 491 // static |
343 scoped_ptr<PdfToEmfConverter> PdfToEmfConverter::CreateDefault() { | 492 scoped_ptr<PdfToEmfConverter> PdfToEmfConverter::CreateDefault() { |
344 return scoped_ptr<PdfToEmfConverter>(new PdfToEmfConverterImpl()); | 493 return scoped_ptr<PdfToEmfConverter>(new PdfToEmfConverterImpl()); |
345 } | 494 } |
346 | 495 |
347 } // namespace printing | 496 } // namespace printing |
OLD | NEW |