Chromium Code Reviews| 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 class RefCountedTempDir | |
|
Vitaly Buka (NO REVIEWS)
2014/09/16 23:08:10
Documented.
| |
| 31 : public base::RefCountedThreadSafe<RefCountedTempDir, | |
| 31 BrowserThread::DeleteOnFileThread> { | 32 BrowserThread::DeleteOnFileThread> { |
| 32 public: | 33 public: |
| 33 FileHandlers() {} | 34 RefCountedTempDir() { temp_dir_.CreateUniqueTempDir(); } |
| 34 | 35 bool IsValid() const { return temp_dir_.IsValid(); } |
| 35 void Init(base::RefCountedMemory* data); | 36 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 | 37 |
| 62 private: | 38 private: |
| 63 friend struct BrowserThread::DeleteOnThread<BrowserThread::FILE>; | 39 friend struct BrowserThread::DeleteOnThread<BrowserThread::FILE>; |
| 64 friend class base::DeleteHelper<FileHandlers>; | 40 friend class base::DeleteHelper<RefCountedTempDir>; |
| 65 | 41 ~RefCountedTempDir() {}; |
| 66 ~FileHandlers() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); } | |
| 67 | 42 |
| 68 base::ScopedTempDir temp_dir_; | 43 base::ScopedTempDir temp_dir_; |
| 69 base::File pdf_file_; | |
| 70 }; | 44 }; |
|
Lei Zhang
2014/09/16 22:52:23
DISALLOW_COPY_AND_ASSIGN()
Vitaly Buka (NO REVIEWS)
2014/09/16 23:08:10
Done.
| |
| 71 | 45 |
| 72 void FileHandlers::Init(base::RefCountedMemory* data) { | 46 typedef scoped_ptr<base::File, BrowserThread::DeleteOnFileThread> |
| 73 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 47 ScopedTempFile; |
| 74 | 48 |
| 75 if (!temp_dir_.CreateUniqueTempDir()) { | 49 // Wrapper for Emf to keep only file handle in memory, and load actual data only |
| 76 return; | 50 // on playback. Emf::InitFromFile() can play metafile directly from disk, but it |
| 77 } | 51 // can't open file handles. We need file handles to reliably delete temporary |
| 78 | 52 // files, and to efficiently interact with utility process. |
| 79 pdf_file_.Initialize(GetPdfPath(), | 53 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: | 54 public: |
| 103 explicit TempEmf(const scoped_refptr<FileHandlers>& files) : files_(files) {} | 55 LazyEmf(const scoped_refptr<RefCountedTempDir>& temp_dir, ScopedTempFile file) |
| 104 virtual ~TempEmf() {} | 56 : temp_dir_(temp_dir), file_(file.Pass()) {} |
| 105 | 57 virtual ~LazyEmf() { Close(); } |
| 106 virtual bool SafePlayback(HDC hdc) const OVERRIDE { | 58 |
| 107 bool result = Emf::SafePlayback(hdc); | 59 virtual bool SafePlayback(HDC hdc) const OVERRIDE; |
| 108 TempEmf* this_mutable = const_cast<TempEmf*>(this); | 60 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 | 61 |
| 119 private: | 62 private: |
| 120 scoped_refptr<FileHandlers> files_; | 63 void Close() const; |
| 121 DISALLOW_COPY_AND_ASSIGN(TempEmf); | 64 bool LoadEmf(Emf* emf) const; |
| 65 | |
| 66 mutable scoped_refptr<RefCountedTempDir> temp_dir_; | |
| 67 mutable ScopedTempFile file_; // Mutable because of consts in base class. | |
| 68 | |
| 69 DISALLOW_COPY_AND_ASSIGN(LazyEmf); | |
| 122 }; | 70 }; |
| 123 | 71 |
| 124 // Converts PDF into EMF. | 72 // Converts PDF into EMF. |
| 125 // Class uses 3 threads: UI, IO and FILE. | 73 // Class uses 3 threads: UI, IO and FILE. |
| 126 // Internal workflow is following: | 74 // Internal workflow is following: |
| 127 // 1. Create instance on the UI thread. (files_, settings_,) | 75 // 1. Create instance on the UI thread. (files_, settings_,) |
| 128 // 2. Create file on the FILE thread. | 76 // 2. Create pdf file on the FILE thread. |
| 129 // 3. Start utility process and start conversion on the IO thread. | 77 // 3. Start utility process and start conversion on the IO thread. |
| 130 // 4. Run result callback on the UI thread. | 78 // 4. Utility process returns page count. |
| 131 // 5. Instance is destroyed from any thread that has the last reference. | 79 // 5. For each page: |
| 132 // 6. FileHandlers destroyed on the FILE thread. | 80 // 1. Clients requests page with file handle to a temp file. |
| 133 // This step posts |FileHandlers| to be destroyed on the FILE thread. | 81 // 2. Utility converts the page, save it to the file and reply. |
| 82 // | |
| 134 // All these steps work sequentially, so no data should be accessed | 83 // All these steps work sequentially, so no data should be accessed |
| 135 // simultaneously by several threads. | 84 // simultaneously by several threads. |
| 136 class PdfToEmfUtilityProcessHostClient | 85 class PdfToEmfUtilityProcessHostClient |
| 137 : public content::UtilityProcessHostClient { | 86 : public content::UtilityProcessHostClient { |
| 138 public: | 87 public: |
| 139 explicit PdfToEmfUtilityProcessHostClient( | 88 PdfToEmfUtilityProcessHostClient( |
| 140 const printing::PdfRenderSettings& settings); | 89 base::WeakPtr<PdfToEmfConverterImpl> converter, |
| 141 | 90 const PdfRenderSettings& settings); |
| 142 void Convert(base::RefCountedMemory* data, | 91 |
| 143 const PdfToEmfConverter::ResultCallback& callback); | 92 void Start(const scoped_refptr<base::RefCountedMemory>& data, |
| 93 const PdfToEmfConverter::StartCallback& start_callback); | |
| 94 | |
| 95 void GetPage(int page_number, | |
| 96 const PdfToEmfConverter::GetPageCallback& get_page_callback); | |
| 97 | |
| 98 void Stop(); | |
| 144 | 99 |
| 145 // UtilityProcessHostClient implementation. | 100 // UtilityProcessHostClient implementation. |
| 146 virtual void OnProcessCrashed(int exit_code) OVERRIDE; | 101 virtual void OnProcessCrashed(int exit_code) OVERRIDE; |
| 102 virtual void OnProcessLaunchFailed() OVERRIDE; | |
| 147 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; | 103 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; |
| 148 | 104 |
| 149 private: | 105 private: |
| 106 class GetPageCallbackData { | |
| 107 MOVE_ONLY_TYPE_FOR_CPP_03(GetPageCallbackData, RValue); | |
| 108 | |
| 109 public: | |
| 110 GetPageCallbackData(int page_number, | |
| 111 PdfToEmfConverter::GetPageCallback callback) | |
| 112 : page_number_(page_number), callback_(callback) {} | |
| 113 | |
| 114 // Move constructor for STL. | |
| 115 GetPageCallbackData(RValue other) { this->operator=(other); } | |
| 116 | |
| 117 // Move assignment for STL. | |
| 118 GetPageCallbackData& operator=(RValue rhs) { | |
| 119 page_number_ = rhs.object->page_number_; | |
| 120 callback_ = rhs.object->callback_; | |
| 121 emf_ = rhs.object->emf_.Pass(); | |
| 122 return *this; | |
| 123 } | |
| 124 | |
| 125 int page_number() const { return page_number_; } | |
| 126 const PdfToEmfConverter::GetPageCallback& callback() const { | |
| 127 return callback_; | |
| 128 } | |
| 129 ScopedTempFile emf() { return emf_.Pass(); } | |
| 130 void set_emf(ScopedTempFile emf) { emf_ = emf.Pass(); } | |
| 131 | |
| 132 private: | |
| 133 int page_number_; | |
| 134 PdfToEmfConverter::GetPageCallback callback_; | |
| 135 ScopedTempFile emf_; | |
| 136 }; | |
| 137 | |
| 150 virtual ~PdfToEmfUtilityProcessHostClient(); | 138 virtual ~PdfToEmfUtilityProcessHostClient(); |
| 151 | 139 |
| 140 bool Send(IPC::Message* msg); | |
| 141 | |
| 152 // Message handlers. | 142 // Message handlers. |
| 153 void OnProcessStarted(); | 143 void OnProcessStarted(); |
| 154 void OnSucceeded(const std::vector<printing::PageRange>& page_ranges, | 144 void OnPageCount(int page_count); |
| 155 double scale_factor); | 145 void OnPageDone(bool success, double scale_factor); |
| 146 | |
| 156 void OnFailed(); | 147 void OnFailed(); |
| 157 | 148 void OnTempPdfReady(ScopedTempFile pdf); |
| 158 void RunCallback(const std::vector<printing::PageRange>& page_ranges, | 149 void OnTempEmfReady(GetPageCallbackData* callback_data, ScopedTempFile emf); |
| 159 double scale_factor); | 150 |
| 160 | 151 scoped_refptr<RefCountedTempDir> temp_dir_; |
| 161 void StartProcessOnIOThread(); | 152 |
| 162 | 153 // Used to suppress callbacks after PdfToEmfConverterImpl is deleted. |
| 163 void RunCallbackOnUIThread( | 154 base::WeakPtr<PdfToEmfConverterImpl> converter_; |
| 164 const std::vector<printing::PageRange>& page_ranges, | 155 PdfRenderSettings settings_; |
| 165 double scale_factor); | 156 scoped_refptr<base::RefCountedMemory> data_; |
| 166 void OnFilesReadyOnUIThread(); | 157 |
| 167 | 158 // Document loaded callback. |
| 168 scoped_refptr<FileHandlers> files_; | 159 PdfToEmfConverter::StartCallback start_callback_; |
| 169 printing::PdfRenderSettings settings_; | 160 |
| 170 PdfToEmfConverter::ResultCallback callback_; | 161 // Process host for IPC. |
| 171 base::WeakPtr<content::UtilityProcessHost> utility_process_host_; | 162 base::WeakPtr<content::UtilityProcessHost> utility_process_host_; |
| 172 | 163 |
| 164 // Queue of callbacks for GetPage() requests. Utility process should reply | |
| 165 // with PageDone in the same order as requests were received. | |
| 166 // Use containers that keeps element pointers valid after push() and pop(). | |
| 167 typedef std::queue<GetPageCallbackData> GetPageCallbacks; | |
| 168 GetPageCallbacks get_page_callbacks_; | |
| 169 | |
| 173 DISALLOW_COPY_AND_ASSIGN(PdfToEmfUtilityProcessHostClient); | 170 DISALLOW_COPY_AND_ASSIGN(PdfToEmfUtilityProcessHostClient); |
| 174 }; | 171 }; |
| 175 | 172 |
| 173 class PdfToEmfConverterImpl : public PdfToEmfConverter { | |
| 174 public: | |
| 175 PdfToEmfConverterImpl(); | |
| 176 | |
| 177 virtual ~PdfToEmfConverterImpl(); | |
| 178 | |
| 179 virtual void Start(const scoped_refptr<base::RefCountedMemory>& data, | |
| 180 const PdfRenderSettings& conversion_settings, | |
| 181 const StartCallback& start_callback) OVERRIDE; | |
| 182 | |
| 183 virtual void GetPage(int page_number, | |
| 184 const GetPageCallback& get_page_callback) OVERRIDE; | |
| 185 | |
| 186 // Helps to cancel callbacks if this object is destroyed. | |
| 187 void RunCallback(const base::Closure& callback); | |
| 188 | |
| 189 private: | |
| 190 scoped_refptr<PdfToEmfUtilityProcessHostClient> utility_client_; | |
| 191 base::WeakPtrFactory<PdfToEmfConverterImpl> weak_ptr_factory_; | |
| 192 | |
| 193 DISALLOW_COPY_AND_ASSIGN(PdfToEmfConverterImpl); | |
| 194 }; | |
| 195 | |
| 196 ScopedTempFile CreateTempFile(scoped_refptr<RefCountedTempDir>* temp_dir) { | |
| 197 if (!(*temp_dir)) | |
| 198 *temp_dir = new RefCountedTempDir(); | |
| 199 ScopedTempFile file; | |
| 200 if (!(*temp_dir)->IsValid()) | |
| 201 return file.Pass(); | |
| 202 base::FilePath path; | |
| 203 if (!base::CreateTemporaryFileInDir((*temp_dir)->GetPath(), &path)) | |
| 204 return file.Pass(); | |
| 205 file.reset(new base::File(path, | |
| 206 base::File::FLAG_CREATE_ALWAYS | | |
| 207 base::File::FLAG_WRITE | base::File::FLAG_READ | | |
| 208 base::File::FLAG_DELETE_ON_CLOSE | | |
| 209 base::File::FLAG_TEMPORARY)); | |
| 210 if (!file->IsValid()) | |
| 211 file.reset(); | |
| 212 return file.Pass(); | |
| 213 } | |
| 214 | |
| 215 ScopedTempFile CreateTempPdfFile( | |
| 216 const scoped_refptr<base::RefCountedMemory>& data, | |
| 217 scoped_refptr<RefCountedTempDir>* temp_dir) { | |
| 218 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | |
| 219 | |
| 220 ScopedTempFile pdf_file = CreateTempFile(temp_dir); | |
| 221 if (!pdf_file || | |
| 222 static_cast<int>(data->size()) != | |
| 223 pdf_file->WriteAtCurrentPos(data->front_as<char>(), data->size())) { | |
| 224 pdf_file.reset(); | |
| 225 } | |
| 226 pdf_file->Seek(base::File::FROM_BEGIN, 0); | |
| 227 return pdf_file.Pass(); | |
| 228 } | |
| 229 | |
| 230 bool LazyEmf::SafePlayback(HDC hdc) const { | |
| 231 Emf emf; | |
| 232 bool result = LoadEmf(&emf) && emf.SafePlayback(hdc); | |
| 233 // TODO(vitalybuka): Fix destruction of metafiles. For some reasons | |
| 234 // instances of Emf are not deleted. crbug.com/411683 | |
| 235 // It's known that the Emf going to be played just once to a printer. So just | |
| 236 // release file here. | |
| 237 Close(); | |
| 238 return result; | |
| 239 } | |
| 240 | |
| 241 bool LazyEmf::SaveTo(base::File* file) const { | |
| 242 Emf emf; | |
| 243 return LoadEmf(&emf) && emf.SaveTo(file); | |
| 244 } | |
| 245 | |
| 246 void LazyEmf::Close() const { | |
| 247 file_.reset(); | |
| 248 temp_dir_ = NULL; | |
| 249 } | |
| 250 | |
| 251 bool LazyEmf::LoadEmf(Emf* emf) const { | |
| 252 file_->Seek(base::File::FROM_BEGIN, 0); | |
| 253 int64 size = file_->GetLength(); | |
| 254 if (size <= 0) | |
| 255 return false; | |
| 256 std::vector<char> data(size); | |
| 257 if (file_->ReadAtCurrentPos(data.data(), data.size()) != size) | |
| 258 return false; | |
| 259 return emf->InitFromData(data.data(), data.size()); | |
| 260 } | |
| 261 | |
| 176 PdfToEmfUtilityProcessHostClient::PdfToEmfUtilityProcessHostClient( | 262 PdfToEmfUtilityProcessHostClient::PdfToEmfUtilityProcessHostClient( |
| 177 const printing::PdfRenderSettings& settings) | 263 base::WeakPtr<PdfToEmfConverterImpl> converter, |
| 178 : settings_(settings) {} | 264 const PdfRenderSettings& settings) |
| 265 : converter_(converter), settings_(settings) { | |
| 266 } | |
| 179 | 267 |
| 180 PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() { | 268 PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() { |
| 181 } | 269 } |
| 182 | 270 |
| 183 void PdfToEmfUtilityProcessHostClient::Convert( | 271 void PdfToEmfUtilityProcessHostClient::Start( |
| 184 base::RefCountedMemory* data, | 272 const scoped_refptr<base::RefCountedMemory>& data, |
| 185 const PdfToEmfConverter::ResultCallback& callback) { | 273 const PdfToEmfConverter::StartCallback& start_callback) { |
| 186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 274 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { |
| 187 callback_ = callback; | 275 BrowserThread::PostTask(BrowserThread::IO, |
| 188 CHECK(!files_.get()); | 276 FROM_HERE, |
| 189 files_ = new FileHandlers(); | 277 base::Bind(&PdfToEmfUtilityProcessHostClient::Start, |
| 190 BrowserThread::PostTaskAndReply( | 278 this, |
| 279 data, | |
| 280 start_callback)); | |
| 281 return; | |
| 282 } | |
| 283 data_ = data; | |
| 284 | |
| 285 // Store callback before any OnFailed() call to make it called on failure. | |
| 286 start_callback_ = start_callback; | |
| 287 | |
| 288 // NOTE: This process _must_ be sandboxed, otherwise the pdf dll will load | |
| 289 // gdiplus.dll, change how rendering happens, and not be able to correctly | |
| 290 // generate when sent to a metafile DC. | |
| 291 utility_process_host_ = | |
| 292 content::UtilityProcessHost::Create( | |
| 293 this, base::MessageLoop::current()->message_loop_proxy()) | |
| 294 ->AsWeakPtr(); | |
| 295 if (!utility_process_host_) | |
| 296 return OnFailed(); | |
| 297 // Should reply with OnProcessStarted(). | |
| 298 Send(new ChromeUtilityMsg_StartupPing); | |
| 299 } | |
| 300 | |
| 301 void PdfToEmfUtilityProcessHostClient::OnProcessStarted() { | |
| 302 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 303 if (!utility_process_host_) | |
| 304 return OnFailed(); | |
| 305 | |
| 306 base::ProcessHandle process = utility_process_host_->GetData().handle; | |
| 307 scoped_refptr<base::RefCountedMemory> data = data_; | |
| 308 data_ = NULL; | |
| 309 BrowserThread::PostTaskAndReplyWithResult( | |
| 191 BrowserThread::FILE, | 310 BrowserThread::FILE, |
| 192 FROM_HERE, | 311 FROM_HERE, |
| 193 base::Bind(&FileHandlers::Init, files_, make_scoped_refptr(data)), | 312 base::Bind(&CreateTempPdfFile, data, &temp_dir_), |
| 194 base::Bind(&PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread, | 313 base::Bind(&PdfToEmfUtilityProcessHostClient::OnTempPdfReady, this)); |
| 195 this)); | 314 } |
| 315 | |
| 316 void PdfToEmfUtilityProcessHostClient::OnTempPdfReady(ScopedTempFile pdf) { | |
| 317 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 318 if (!utility_process_host_) | |
| 319 return OnFailed(); | |
| 320 base::ProcessHandle process = utility_process_host_->GetData().handle; | |
| 321 // Should reply with OnPageCount(). | |
| 322 Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles( | |
| 323 IPC::GetFileHandleForProcess(pdf->GetPlatformFile(), process, false), | |
| 324 settings_)); | |
| 325 } | |
| 326 | |
| 327 void PdfToEmfUtilityProcessHostClient::OnPageCount(int page_count) { | |
| 328 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 329 if (start_callback_.is_null()) | |
| 330 return OnFailed(); | |
| 331 BrowserThread::PostTask(BrowserThread::UI, | |
| 332 FROM_HERE, | |
| 333 base::Bind(&PdfToEmfConverterImpl::RunCallback, | |
| 334 converter_, | |
| 335 base::Bind(start_callback_, page_count))); | |
| 336 start_callback_.Reset(); | |
| 337 } | |
| 338 | |
| 339 void PdfToEmfUtilityProcessHostClient::GetPage( | |
| 340 int page_number, | |
| 341 const PdfToEmfConverter::GetPageCallback& get_page_callback) { | |
| 342 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { | |
| 343 BrowserThread::PostTask( | |
| 344 BrowserThread::IO, | |
| 345 FROM_HERE, | |
| 346 base::Bind(&PdfToEmfUtilityProcessHostClient::GetPage, | |
| 347 this, | |
| 348 page_number, | |
| 349 get_page_callback)); | |
| 350 return; | |
| 351 } | |
| 352 | |
| 353 // Store callback before any OnFailed() call to make it called on failure. | |
| 354 get_page_callbacks_.push(GetPageCallbackData(page_number, get_page_callback)); | |
| 355 | |
| 356 if (!utility_process_host_) | |
| 357 return OnFailed(); | |
| 358 | |
| 359 BrowserThread::PostTaskAndReplyWithResult( | |
| 360 BrowserThread::FILE, | |
| 361 FROM_HERE, | |
| 362 base::Bind(&CreateTempFile, &temp_dir_), | |
| 363 base::Bind(&PdfToEmfUtilityProcessHostClient::OnTempEmfReady, | |
| 364 this, | |
| 365 &get_page_callbacks_.back())); | |
| 366 } | |
| 367 | |
| 368 void PdfToEmfUtilityProcessHostClient::OnTempEmfReady( | |
| 369 GetPageCallbackData* callback_data, | |
| 370 ScopedTempFile emf) { | |
| 371 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 372 if (!utility_process_host_) | |
| 373 return OnFailed(); | |
| 374 base::ProcessHandle process = utility_process_host_->GetData().handle; | |
| 375 IPC::PlatformFileForTransit transit = | |
| 376 IPC::GetFileHandleForProcess(emf->GetPlatformFile(), process, false); | |
| 377 callback_data->set_emf(emf.Pass()); | |
| 378 // Should reply with OnPageDone(). | |
| 379 Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_GetPage( | |
| 380 callback_data->page_number(), transit)); | |
| 381 } | |
| 382 | |
| 383 void PdfToEmfUtilityProcessHostClient::OnPageDone(bool success, | |
| 384 double scale_factor) { | |
| 385 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 386 if (get_page_callbacks_.empty()) | |
| 387 return OnFailed(); | |
| 388 scoped_ptr<LazyEmf> emf; | |
| 389 GetPageCallbackData& data = get_page_callbacks_.front(); | |
| 390 if (success) | |
| 391 emf.reset(new LazyEmf(temp_dir_, data.emf().Pass())); | |
| 392 BrowserThread::PostTask(BrowserThread::UI, | |
| 393 FROM_HERE, | |
| 394 base::Bind(&PdfToEmfConverterImpl::RunCallback, | |
| 395 converter_, | |
| 396 base::Bind(data.callback(), | |
| 397 data.page_number(), | |
| 398 scale_factor, | |
| 399 base::Passed(&emf)))); | |
| 400 get_page_callbacks_.pop(); | |
| 401 } | |
| 402 | |
| 403 void PdfToEmfUtilityProcessHostClient::Stop() { | |
| 404 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { | |
| 405 BrowserThread::PostTask( | |
| 406 BrowserThread::IO, | |
| 407 FROM_HERE, | |
| 408 base::Bind(&PdfToEmfUtilityProcessHostClient::Stop, this)); | |
| 409 return; | |
| 410 } | |
| 411 Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop()); | |
| 196 } | 412 } |
| 197 | 413 |
| 198 void PdfToEmfUtilityProcessHostClient::OnProcessCrashed(int exit_code) { | 414 void PdfToEmfUtilityProcessHostClient::OnProcessCrashed(int exit_code) { |
| 199 OnFailed(); | 415 OnFailed(); |
| 200 } | 416 } |
| 201 | 417 |
| 418 void PdfToEmfUtilityProcessHostClient::OnProcessLaunchFailed() { | |
| 419 OnFailed(); | |
| 420 } | |
| 421 | |
| 202 bool PdfToEmfUtilityProcessHostClient::OnMessageReceived( | 422 bool PdfToEmfUtilityProcessHostClient::OnMessageReceived( |
| 203 const IPC::Message& message) { | 423 const IPC::Message& message) { |
| 204 bool handled = true; | 424 bool handled = true; |
| 205 IPC_BEGIN_MESSAGE_MAP(PdfToEmfUtilityProcessHostClient, message) | 425 IPC_BEGIN_MESSAGE_MAP(PdfToEmfUtilityProcessHostClient, message) |
| 206 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted) | 426 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted) |
| 207 IPC_MESSAGE_HANDLER( | 427 IPC_MESSAGE_HANDLER( |
| 208 ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_Succeeded, OnSucceeded) | 428 ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount, OnPageCount) |
| 209 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed, | 429 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone, |
| 210 OnFailed) | 430 OnPageDone) |
| 211 IPC_MESSAGE_UNHANDLED(handled = false) | 431 IPC_MESSAGE_UNHANDLED(handled = false) |
| 212 IPC_END_MESSAGE_MAP() | 432 IPC_END_MESSAGE_MAP() |
| 213 return handled; | 433 return handled; |
| 214 } | 434 } |
| 215 | 435 |
| 216 void PdfToEmfUtilityProcessHostClient::OnProcessStarted() { | 436 bool PdfToEmfUtilityProcessHostClient::Send(IPC::Message* msg) { |
| 217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 437 if (utility_process_host_) |
| 218 if (!utility_process_host_) { | 438 return utility_process_host_->Send(msg); |
| 219 RunCallbackOnUIThread(std::vector<printing::PageRange>(), 0.0); | 439 delete msg; |
| 220 return; | 440 return false; |
| 221 } | 441 } |
| 222 | 442 |
| 223 base::ProcessHandle process = utility_process_host_->GetData().handle; | 443 void PdfToEmfUtilityProcessHostClient::OnFailed() { |
| 224 utility_process_host_->Send( | 444 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 225 new ChromeUtilityMsg_RenderPDFPagesToMetafiles( | 445 if (!start_callback_.is_null()) |
| 226 files_->GetPdfForProcess(process), | 446 OnPageCount(0); |
| 227 files_->GetEmfPath(), | 447 while (!get_page_callbacks_.empty()) |
| 228 settings_, | 448 OnPageDone(false, 0.0); |
| 229 std::vector<printing::PageRange>())); | |
| 230 utility_process_host_.reset(); | 449 utility_process_host_.reset(); |
| 231 } | 450 } |
| 232 | 451 |
| 233 void PdfToEmfUtilityProcessHostClient::OnSucceeded( | 452 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 } | 453 } |
| 325 | 454 |
| 326 PdfToEmfConverterImpl::~PdfToEmfConverterImpl() { | 455 PdfToEmfConverterImpl::~PdfToEmfConverterImpl() { |
| 456 if (utility_client_) | |
| 457 utility_client_->Stop(); | |
| 327 } | 458 } |
| 328 | 459 |
| 329 void PdfToEmfConverterImpl::Start( | 460 void PdfToEmfConverterImpl::Start( |
| 330 base::RefCountedMemory* data, | 461 const scoped_refptr<base::RefCountedMemory>& data, |
| 331 const printing::PdfRenderSettings& conversion_settings, | 462 const PdfRenderSettings& conversion_settings, |
| 332 const ResultCallback& callback) { | 463 const StartCallback& start_callback) { |
| 333 // Rebind cancelable callback to avoid calling callback if | 464 DCHECK(!utility_client_); |
| 334 // PdfToEmfConverterImpl is destroyed. | 465 utility_client_ = new PdfToEmfUtilityProcessHostClient( |
| 335 callback_.Reset(callback); | 466 weak_ptr_factory_.GetWeakPtr(), conversion_settings); |
| 336 utility_client_ = new PdfToEmfUtilityProcessHostClient(conversion_settings); | 467 utility_client_->Start(data, start_callback); |
| 337 utility_client_->Convert(data, callback_.callback()); | 468 } |
| 469 | |
| 470 void PdfToEmfConverterImpl::GetPage(int page_number, | |
| 471 const GetPageCallback& get_page_callback) { | |
| 472 utility_client_->GetPage(page_number, get_page_callback); | |
| 473 } | |
| 474 | |
| 475 void PdfToEmfConverterImpl::RunCallback(const base::Closure& callback) { | |
| 476 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 477 callback.Run(); | |
| 338 } | 478 } |
| 339 | 479 |
| 340 } // namespace | 480 } // namespace |
| 341 | 481 |
| 482 PdfToEmfConverter::~PdfToEmfConverter() { | |
| 483 } | |
| 484 | |
| 342 // static | 485 // static |
| 343 scoped_ptr<PdfToEmfConverter> PdfToEmfConverter::CreateDefault() { | 486 scoped_ptr<PdfToEmfConverter> PdfToEmfConverter::CreateDefault() { |
| 344 return scoped_ptr<PdfToEmfConverter>(new PdfToEmfConverterImpl()); | 487 return scoped_ptr<PdfToEmfConverter>(new PdfToEmfConverterImpl()); |
| 345 } | 488 } |
| 346 | 489 |
| 347 } // namespace printing | 490 } // namespace printing |
| OLD | NEW |