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