pwg_raster_converter.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
1// Copyright 2013 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/local_discovery/pwg_raster_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/scoped_temp_dir.h" 11#include "base/logging.h" 12#include "chrome/common/chrome_utility_messages.h" 13#include "content/public/browser/browser_thread.h" 14#include "content/public/browser/child_process_data.h" 15#include "content/public/browser/utility_process_host.h" 16#include "content/public/browser/utility_process_host_client.h" 17 18namespace local_discovery { 19 20namespace { 21 22using content::BrowserThread; 23 24class FileHandlers { 25 public: 26 FileHandlers() : pdf_file_(base::kInvalidPlatformFileValue), 27 pwg_file_(base::kInvalidPlatformFileValue) { } 28 29 ~FileHandlers() { 30 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 31 if (pdf_file_ != base::kInvalidPlatformFileValue) 32 base::ClosePlatformFile(pdf_file_); 33 if (pwg_file_ != base::kInvalidPlatformFileValue) 34 base::ClosePlatformFile(pwg_file_); 35 } 36 37 void Init(base::RefCountedMemory* data); 38 bool IsValid(); 39 40 base::FilePath GetPwgPath() const { 41 return temp_dir_.path().AppendASCII("output.pwg"); 42 } 43 44 base::FilePath GetPdfPath() const { 45 return temp_dir_.path().AppendASCII("input.pdf"); 46 } 47 48 IPC::PlatformFileForTransit GetPdfForProcess(base::ProcessHandle process) { 49 DCHECK_NE(pdf_file_, base::kInvalidPlatformFileValue); 50 IPC::PlatformFileForTransit transit = 51 IPC::GetFileHandleForProcess(pdf_file_, process, true); 52 pdf_file_ = base::kInvalidPlatformFileValue; 53 return transit; 54 } 55 56 IPC::PlatformFileForTransit GetPwgForProcess(base::ProcessHandle process) { 57 DCHECK_NE(pwg_file_, base::kInvalidPlatformFileValue); 58 IPC::PlatformFileForTransit transit = 59 IPC::GetFileHandleForProcess(pwg_file_, process, true); 60 pwg_file_ = base::kInvalidPlatformFileValue; 61 return transit; 62 } 63 64 private: 65 base::ScopedTempDir temp_dir_; 66 base::PlatformFile pdf_file_; 67 base::PlatformFile pwg_file_; 68}; 69 70void FileHandlers::Init(base::RefCountedMemory* data) { 71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 72 73 if (!temp_dir_.CreateUniqueTempDir()) { 74 return; 75 } 76 77 if (static_cast<int>(data->size()) != 78 base::WriteFile(GetPdfPath(), data->front_as<char>(), data->size())) { 79 return; 80 } 81 82 // Reopen in read only mode. 83 pdf_file_ = base::CreatePlatformFile(GetPdfPath(), base::PLATFORM_FILE_OPEN | 84 base::PLATFORM_FILE_READ, 85 NULL, NULL); 86 pwg_file_ = base::CreatePlatformFile(GetPwgPath(), 87 base::PLATFORM_FILE_CREATE_ALWAYS | 88 base::PLATFORM_FILE_APPEND, NULL, NULL); 89} 90 91bool FileHandlers::IsValid() { 92 return pdf_file_ != base::kInvalidPlatformFileValue && 93 pwg_file_ != base::kInvalidPlatformFileValue; 94} 95 96// Converts PDF into PWG raster. 97// Class uses 3 threads: UI, IO and FILE. 98// Internal workflow is following: 99// 1. Create instance on the UI thread. (files_, settings_,) 100// 2. Create file on the FILE thread. 101// 3. Start utility process and start conversion on the IO thread. 102// 4. Run result callback on the UI thread. 103// 5. Instance is destroyed from any thread that has the last reference. 104// 6. FileHandlers destroyed on the FILE thread. 105// This step posts |FileHandlers| to be destroyed on the FILE thread. 106// All these steps work sequentially, so no data should be accessed 107// simultaneously by several threads. 108class PwgUtilityProcessHostClient : public content::UtilityProcessHostClient { 109 public: 110 explicit PwgUtilityProcessHostClient( 111 const printing::PdfRenderSettings& settings); 112 113 void Convert(base::RefCountedMemory* data, 114 const PWGRasterConverter::ResultCallback& callback); 115 116 // UtilityProcessHostClient implementation. 117 virtual void OnProcessCrashed(int exit_code) OVERRIDE; 118 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; 119 120 private: 121 virtual ~PwgUtilityProcessHostClient(); 122 123 // Message handlers. 124 void OnProcessStarted(); 125 void OnSucceeded(); 126 void OnFailed(); 127 128 void RunCallback(bool success); 129 130 void StartProcessOnIOThread(); 131 132 void RunCallbackOnUIThread(bool success); 133 void OnFilesReadyOnUIThread(); 134 135 scoped_ptr<FileHandlers> files_; 136 printing::PdfRenderSettings settings_; 137 PWGRasterConverter::ResultCallback callback_; 138 base::WeakPtr<content::UtilityProcessHost> utility_process_host_; 139 140 DISALLOW_COPY_AND_ASSIGN(PwgUtilityProcessHostClient); 141}; 142 143PwgUtilityProcessHostClient::PwgUtilityProcessHostClient( 144 const printing::PdfRenderSettings& settings) : settings_(settings) { 145} 146 147PwgUtilityProcessHostClient::~PwgUtilityProcessHostClient() { 148 // Delete temp directory. 149 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, files_.release()); 150} 151 152void PwgUtilityProcessHostClient::Convert( 153 base::RefCountedMemory* data, 154 const PWGRasterConverter::ResultCallback& callback) { 155 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 156 callback_ = callback; 157 CHECK(!files_); 158 files_.reset(new FileHandlers()); 159 BrowserThread::PostTaskAndReply( 160 BrowserThread::FILE, FROM_HERE, 161 base::Bind(&FileHandlers::Init, base::Unretained(files_.get()), 162 make_scoped_refptr(data)), 163 base::Bind(&PwgUtilityProcessHostClient::OnFilesReadyOnUIThread, this)); 164} 165 166void PwgUtilityProcessHostClient::OnProcessCrashed(int exit_code) { 167 OnFailed(); 168} 169 170bool PwgUtilityProcessHostClient::OnMessageReceived( 171 const IPC::Message& message) { 172 bool handled = true; 173 IPC_BEGIN_MESSAGE_MAP(PwgUtilityProcessHostClient, message) 174 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted) 175 IPC_MESSAGE_HANDLER( 176 ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Succeeded, OnSucceeded) 177 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Failed, 178 OnFailed) 179 IPC_MESSAGE_UNHANDLED(handled = false) 180 IPC_END_MESSAGE_MAP() 181 return handled; 182} 183 184void PwgUtilityProcessHostClient::OnProcessStarted() { 185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 186 if (!utility_process_host_) { 187 RunCallbackOnUIThread(false); 188 return; 189 } 190 191 base::ProcessHandle process = utility_process_host_->GetData().handle; 192 utility_process_host_->Send( 193 new ChromeUtilityMsg_RenderPDFPagesToPWGRaster( 194 files_->GetPdfForProcess(process), 195 settings_, 196 files_->GetPwgForProcess(process))); 197 utility_process_host_.reset(); 198} 199 200void PwgUtilityProcessHostClient::OnSucceeded() { 201 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 202 RunCallback(true); 203} 204 205void PwgUtilityProcessHostClient::OnFailed() { 206 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 207 RunCallback(false); 208} 209 210void PwgUtilityProcessHostClient::OnFilesReadyOnUIThread() { 211 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 212 if (!files_->IsValid()) { 213 RunCallbackOnUIThread(false); 214 return; 215 } 216 BrowserThread::PostTask( 217 BrowserThread::IO, FROM_HERE, 218 base::Bind(&PwgUtilityProcessHostClient::StartProcessOnIOThread, this)); 219} 220 221void PwgUtilityProcessHostClient::StartProcessOnIOThread() { 222 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 223 utility_process_host_ = 224 content::UtilityProcessHost::Create( 225 this, 226 base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr(); 227 utility_process_host_->Send(new ChromeUtilityMsg_StartupPing); 228} 229 230void PwgUtilityProcessHostClient::RunCallback(bool success) { 231 BrowserThread::PostTask( 232 BrowserThread::UI, FROM_HERE, 233 base::Bind(&PwgUtilityProcessHostClient::RunCallbackOnUIThread, this, 234 success)); 235} 236 237void PwgUtilityProcessHostClient::RunCallbackOnUIThread(bool success) { 238 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 239 if (!callback_.is_null()) { 240 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 241 base::Bind(callback_, success, 242 files_->GetPwgPath())); 243 callback_.Reset(); 244 } 245} 246 247class PWGRasterConverterImpl : public PWGRasterConverter { 248 public: 249 PWGRasterConverterImpl(); 250 251 virtual ~PWGRasterConverterImpl(); 252 253 virtual void Start(base::RefCountedMemory* data, 254 const printing::PdfRenderSettings& conversion_settings, 255 const ResultCallback& callback) OVERRIDE; 256 private: 257 scoped_refptr<PwgUtilityProcessHostClient> utility_client_; 258 base::CancelableCallback<ResultCallback::RunType> callback_; 259 260 DISALLOW_COPY_AND_ASSIGN(PWGRasterConverterImpl); 261}; 262 263PWGRasterConverterImpl::PWGRasterConverterImpl() { 264} 265 266PWGRasterConverterImpl::~PWGRasterConverterImpl() { 267} 268 269void PWGRasterConverterImpl::Start( 270 base::RefCountedMemory* data, 271 const printing::PdfRenderSettings& conversion_settings, 272 const ResultCallback& callback) { 273 // Rebind cancelable callback to avoid calling callback if 274 // PWGRasterConverterImpl is destroyed. 275 callback_.Reset(callback); 276 utility_client_ = new PwgUtilityProcessHostClient(conversion_settings); 277 utility_client_->Convert(data, callback_.callback()); 278} 279 280} // namespace 281 282// static 283scoped_ptr<PWGRasterConverter> PWGRasterConverter::CreateDefault() { 284 return scoped_ptr<PWGRasterConverter>(new PWGRasterConverterImpl()); 285} 286 287} // namespace local_discovery 288