pwg_raster_converter.cc revision e5d81f57cb97b3b6b7fccc9c5610d21eb81db09d
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 const printing::PwgRasterSettings& bitmap_settings); 113 114 void Convert(base::RefCountedMemory* data, 115 const PWGRasterConverter::ResultCallback& callback); 116 117 // UtilityProcessHostClient implementation. 118 virtual void OnProcessCrashed(int exit_code) OVERRIDE; 119 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; 120 121 private: 122 virtual ~PwgUtilityProcessHostClient(); 123 124 // Message handlers. 125 void OnProcessStarted(); 126 void OnSucceeded(); 127 void OnFailed(); 128 129 void RunCallback(bool success); 130 131 void StartProcessOnIOThread(); 132 133 void RunCallbackOnUIThread(bool success); 134 void OnFilesReadyOnUIThread(); 135 136 scoped_ptr<FileHandlers> files_; 137 printing::PdfRenderSettings settings_; 138 printing::PwgRasterSettings bitmap_settings_; 139 PWGRasterConverter::ResultCallback callback_; 140 base::WeakPtr<content::UtilityProcessHost> utility_process_host_; 141 142 DISALLOW_COPY_AND_ASSIGN(PwgUtilityProcessHostClient); 143}; 144 145PwgUtilityProcessHostClient::PwgUtilityProcessHostClient( 146 const printing::PdfRenderSettings& settings, 147 const printing::PwgRasterSettings& bitmap_settings) 148 : settings_(settings), bitmap_settings_(bitmap_settings) {} 149 150PwgUtilityProcessHostClient::~PwgUtilityProcessHostClient() { 151 // Delete temp directory. 152 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, files_.release()); 153} 154 155void PwgUtilityProcessHostClient::Convert( 156 base::RefCountedMemory* data, 157 const PWGRasterConverter::ResultCallback& callback) { 158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 159 callback_ = callback; 160 CHECK(!files_); 161 files_.reset(new FileHandlers()); 162 BrowserThread::PostTaskAndReply( 163 BrowserThread::FILE, FROM_HERE, 164 base::Bind(&FileHandlers::Init, base::Unretained(files_.get()), 165 make_scoped_refptr(data)), 166 base::Bind(&PwgUtilityProcessHostClient::OnFilesReadyOnUIThread, this)); 167} 168 169void PwgUtilityProcessHostClient::OnProcessCrashed(int exit_code) { 170 OnFailed(); 171} 172 173bool PwgUtilityProcessHostClient::OnMessageReceived( 174 const IPC::Message& message) { 175 bool handled = true; 176 IPC_BEGIN_MESSAGE_MAP(PwgUtilityProcessHostClient, message) 177 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted) 178 IPC_MESSAGE_HANDLER( 179 ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Succeeded, OnSucceeded) 180 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Failed, 181 OnFailed) 182 IPC_MESSAGE_UNHANDLED(handled = false) 183 IPC_END_MESSAGE_MAP() 184 return handled; 185} 186 187void PwgUtilityProcessHostClient::OnProcessStarted() { 188 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 189 if (!utility_process_host_) { 190 RunCallbackOnUIThread(false); 191 return; 192 } 193 194 base::ProcessHandle process = utility_process_host_->GetData().handle; 195 utility_process_host_->Send(new ChromeUtilityMsg_RenderPDFPagesToPWGRaster( 196 files_->GetPdfForProcess(process), 197 settings_, 198 bitmap_settings_, 199 files_->GetPwgForProcess(process))); 200 utility_process_host_.reset(); 201} 202 203void PwgUtilityProcessHostClient::OnSucceeded() { 204 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 205 RunCallback(true); 206} 207 208void PwgUtilityProcessHostClient::OnFailed() { 209 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 210 RunCallback(false); 211} 212 213void PwgUtilityProcessHostClient::OnFilesReadyOnUIThread() { 214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 215 if (!files_->IsValid()) { 216 RunCallbackOnUIThread(false); 217 return; 218 } 219 BrowserThread::PostTask( 220 BrowserThread::IO, FROM_HERE, 221 base::Bind(&PwgUtilityProcessHostClient::StartProcessOnIOThread, this)); 222} 223 224void PwgUtilityProcessHostClient::StartProcessOnIOThread() { 225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 226 utility_process_host_ = 227 content::UtilityProcessHost::Create( 228 this, 229 base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr(); 230 utility_process_host_->Send(new ChromeUtilityMsg_StartupPing); 231} 232 233void PwgUtilityProcessHostClient::RunCallback(bool success) { 234 BrowserThread::PostTask( 235 BrowserThread::UI, FROM_HERE, 236 base::Bind(&PwgUtilityProcessHostClient::RunCallbackOnUIThread, this, 237 success)); 238} 239 240void PwgUtilityProcessHostClient::RunCallbackOnUIThread(bool success) { 241 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 242 if (!callback_.is_null()) { 243 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 244 base::Bind(callback_, success, 245 files_->GetPwgPath())); 246 callback_.Reset(); 247 } 248} 249 250class PWGRasterConverterImpl : public PWGRasterConverter { 251 public: 252 PWGRasterConverterImpl(); 253 254 virtual ~PWGRasterConverterImpl(); 255 256 virtual void Start(base::RefCountedMemory* data, 257 const printing::PdfRenderSettings& conversion_settings, 258 const printing::PwgRasterSettings& bitmap_settings, 259 const ResultCallback& callback) OVERRIDE; 260 261 private: 262 scoped_refptr<PwgUtilityProcessHostClient> utility_client_; 263 base::CancelableCallback<ResultCallback::RunType> callback_; 264 265 DISALLOW_COPY_AND_ASSIGN(PWGRasterConverterImpl); 266}; 267 268PWGRasterConverterImpl::PWGRasterConverterImpl() { 269} 270 271PWGRasterConverterImpl::~PWGRasterConverterImpl() { 272} 273 274void PWGRasterConverterImpl::Start( 275 base::RefCountedMemory* data, 276 const printing::PdfRenderSettings& conversion_settings, 277 const printing::PwgRasterSettings& bitmap_settings, 278 const ResultCallback& callback) { 279 // Rebind cancelable callback to avoid calling callback if 280 // PWGRasterConverterImpl is destroyed. 281 callback_.Reset(callback); 282 utility_client_ = 283 new PwgUtilityProcessHostClient(conversion_settings, bitmap_settings); 284 utility_client_->Convert(data, callback_.callback()); 285} 286 287} // namespace 288 289// static 290scoped_ptr<PWGRasterConverter> PWGRasterConverter::CreateDefault() { 291 return scoped_ptr<PWGRasterConverter>(new PWGRasterConverterImpl()); 292} 293 294} // namespace local_discovery 295