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