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 file_util::WriteFile(GetPdfPath(), 79 reinterpret_cast<const char*>(data->front()), 80 data->size())) { 81 return; 82 } 83 84 // Reopen in read only mode. 85 pdf_file_ = base::CreatePlatformFile(GetPdfPath(), base::PLATFORM_FILE_OPEN | 86 base::PLATFORM_FILE_READ, 87 NULL, NULL); 88 pwg_file_ = base::CreatePlatformFile(GetPwgPath(), 89 base::PLATFORM_FILE_CREATE_ALWAYS | 90 base::PLATFORM_FILE_APPEND, NULL, NULL); 91} 92 93bool FileHandlers::IsValid() { 94 return pdf_file_ != base::kInvalidPlatformFileValue && 95 pwg_file_ != base::kInvalidPlatformFileValue; 96} 97 98// Converts PDF into PWG raster. 99// Class uses 3 threads: UI, IO and FILE. 100// Internal workflow is following: 101// 1. Create instance on the UI thread. (files_, settings_,) 102// 2. Create file on the FILE thread. 103// 3. Start utility process and start conversion on the IO thread. 104// 4. Run result callback on the UI thread. 105// 5. Instance is destroyed from any thread that has the last reference. 106// 6. FileHandlers destroyed on the FILE thread. 107// This step posts |FileHandlers| to be destroyed on the FILE thread. 108// All these steps work sequentially, so no data should be accessed 109// simultaneously by several threads. 110class PwgUtilityProcessHostClient : public content::UtilityProcessHostClient { 111 public: 112 explicit PwgUtilityProcessHostClient( 113 const printing::PdfRenderSettings& settings); 114 115 void Convert(base::RefCountedMemory* data, 116 const PWGRasterConverter::ResultCallback& callback); 117 118 // UtilityProcessHostClient implementation. 119 virtual void OnProcessCrashed(int exit_code) OVERRIDE; 120 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; 121 122 private: 123 virtual ~PwgUtilityProcessHostClient(); 124 125 // Message handlers. 126 void OnProcessStarted(); 127 void OnSucceeded(); 128 void OnFailed(); 129 130 void RunCallback(bool success); 131 132 void StartProcessOnIOThread(); 133 134 void RunCallbackOnUIThread(bool success); 135 void OnFilesReadyOnUIThread(); 136 137 scoped_ptr<FileHandlers> files_; 138 printing::PdfRenderSettings 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) : settings_(settings) { 147} 148 149PwgUtilityProcessHostClient::~PwgUtilityProcessHostClient() { 150 // Delete temp directory. 151 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, files_.release()); 152} 153 154void PwgUtilityProcessHostClient::Convert( 155 base::RefCountedMemory* data, 156 const PWGRasterConverter::ResultCallback& callback) { 157 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 158 callback_ = callback; 159 CHECK(!files_); 160 files_.reset(new FileHandlers()); 161 BrowserThread::PostTaskAndReply( 162 BrowserThread::FILE, FROM_HERE, 163 base::Bind(&FileHandlers::Init, base::Unretained(files_.get()), 164 make_scoped_refptr(data)), 165 base::Bind(&PwgUtilityProcessHostClient::OnFilesReadyOnUIThread, this)); 166} 167 168void PwgUtilityProcessHostClient::OnProcessCrashed(int exit_code) { 169 OnFailed(); 170} 171 172bool PwgUtilityProcessHostClient::OnMessageReceived( 173 const IPC::Message& message) { 174 bool handled = true; 175 IPC_BEGIN_MESSAGE_MAP(PwgUtilityProcessHostClient, message) 176 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted) 177 IPC_MESSAGE_HANDLER( 178 ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Succeeded, OnSucceeded) 179 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Failed, 180 OnFailed) 181 IPC_MESSAGE_UNHANDLED(handled = false) 182 IPC_END_MESSAGE_MAP() 183 return handled; 184} 185 186void PwgUtilityProcessHostClient::OnProcessStarted() { 187 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 188 if (!utility_process_host_) { 189 RunCallbackOnUIThread(false); 190 return; 191 } 192 193 base::ProcessHandle process = utility_process_host_->GetData().handle; 194 utility_process_host_->Send( 195 new ChromeUtilityMsg_RenderPDFPagesToPWGRaster( 196 files_->GetPdfForProcess(process), 197 settings_, 198 files_->GetPwgForProcess(process))); 199 utility_process_host_.reset(); 200} 201 202void PwgUtilityProcessHostClient::OnSucceeded() { 203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 204 RunCallback(true); 205} 206 207void PwgUtilityProcessHostClient::OnFailed() { 208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 209 RunCallback(false); 210} 211 212void PwgUtilityProcessHostClient::OnFilesReadyOnUIThread() { 213 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 214 if (!files_->IsValid()) { 215 RunCallbackOnUIThread(false); 216 return; 217 } 218 BrowserThread::PostTask( 219 BrowserThread::IO, FROM_HERE, 220 base::Bind(&PwgUtilityProcessHostClient::StartProcessOnIOThread, this)); 221} 222 223void PwgUtilityProcessHostClient::StartProcessOnIOThread() { 224 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 225 utility_process_host_ = 226 content::UtilityProcessHost::Create( 227 this, 228 base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr(); 229 utility_process_host_->Send(new ChromeUtilityMsg_StartupPing); 230} 231 232void PwgUtilityProcessHostClient::RunCallback(bool success) { 233 BrowserThread::PostTask( 234 BrowserThread::UI, FROM_HERE, 235 base::Bind(&PwgUtilityProcessHostClient::RunCallbackOnUIThread, this, 236 success)); 237} 238 239void PwgUtilityProcessHostClient::RunCallbackOnUIThread(bool success) { 240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 241 if (!callback_.is_null()) { 242 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 243 base::Bind(callback_, success, 244 files_->GetPwgPath())); 245 callback_.Reset(); 246 } 247} 248 249class PWGRasterConverterImpl : public PWGRasterConverter { 250 public: 251 PWGRasterConverterImpl(); 252 253 virtual ~PWGRasterConverterImpl(); 254 255 virtual void Start(base::RefCountedMemory* data, 256 const printing::PdfRenderSettings& conversion_settings, 257 const ResultCallback& callback) OVERRIDE; 258 private: 259 scoped_refptr<PwgUtilityProcessHostClient> utility_client_; 260 base::CancelableCallback<ResultCallback::RunType> callback_; 261 262 DISALLOW_COPY_AND_ASSIGN(PWGRasterConverterImpl); 263}; 264 265PWGRasterConverterImpl::PWGRasterConverterImpl() { 266} 267 268PWGRasterConverterImpl::~PWGRasterConverterImpl() { 269} 270 271void PWGRasterConverterImpl::Start( 272 base::RefCountedMemory* data, 273 const printing::PdfRenderSettings& conversion_settings, 274 const ResultCallback& callback) { 275 // Rebind cancelable callback to avoid calling callback if 276 // PWGRasterConverterImpl is destroyed. 277 callback_.Reset(callback); 278 utility_client_ = new PwgUtilityProcessHostClient(conversion_settings); 279 utility_client_->Convert(data, callback_.callback()); 280} 281 282} // namespace 283 284// static 285scoped_ptr<PWGRasterConverter> PWGRasterConverter::CreateDefault() { 286 return scoped_ptr<PWGRasterConverter>(new PWGRasterConverterImpl()); 287} 288 289} // namespace local_discovery 290