Chromium Code Reviews| Index: chrome/browser/local_discovery/pwg_raster_converter.cc |
| diff --git a/chrome/browser/local_discovery/pwg_raster_converter.cc b/chrome/browser/local_discovery/pwg_raster_converter.cc |
| index cd50d95f03faebf7797602321002e96b39bcff8b..215cbee91ae575b5f2a032829c9ba825cc92470e 100644 |
| --- a/chrome/browser/local_discovery/pwg_raster_converter.cc |
| +++ b/chrome/browser/local_discovery/pwg_raster_converter.cc |
| @@ -4,12 +4,245 @@ |
| #include "chrome/browser/local_discovery/pwg_raster_converter.h" |
| +#include "base/cancelable_callback.h" |
| +#include "base/file_util.h" |
| +#include "base/files/scoped_temp_dir.h" |
| #include "base/logging.h" |
| +#include "chrome/common/chrome_utility_messages.h" |
| +#include "content/public/browser/browser_thread.h" |
| +#include "content/public/browser/child_process_data.h" |
| +#include "content/public/browser/utility_process_host.h" |
| +#include "content/public/browser/utility_process_host_client.h" |
| namespace local_discovery { |
| namespace { |
| +using content::BrowserThread; |
| + |
| +class FileHandlers { |
| + public: |
| + FileHandlers() : pdf_file_(base::kInvalidPlatformFileValue), |
| + pwg_file_(base::kInvalidPlatformFileValue) { } |
| + |
| + ~FileHandlers() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + if (pdf_file_ != base::kInvalidPlatformFileValue) |
| + base::ClosePlatformFile(pdf_file_); |
| + if (pwg_file_ != base::kInvalidPlatformFileValue) |
| + base::ClosePlatformFile(pwg_file_); |
| + } |
| + |
| + void Init(base::RefCountedMemory* data); |
| + bool IsValid(); |
| + |
| + base::FilePath GetPwgPath() const { |
| + return temp_dir_.path().AppendASCII("output.pwg"); |
| + } |
| + |
| + base::FilePath GetPdfPath() const { |
| + return temp_dir_.path().AppendASCII("input.pdf"); |
| + } |
| + |
| + IPC::PlatformFileForTransit GetPdfForProcess(base::ProcessHandle process) { |
| + DCHECK_NE(pdf_file_, base::kInvalidPlatformFileValue); |
| + IPC::PlatformFileForTransit transit = |
| + IPC::GetFileHandleForProcess(pdf_file_, process, true); |
| + pdf_file_ = base::kInvalidPlatformFileValue; |
| + return transit; |
| + } |
| + |
| + IPC::PlatformFileForTransit GetPwgForProcess(base::ProcessHandle process) { |
| + DCHECK_NE(pwg_file_, base::kInvalidPlatformFileValue); |
| + IPC::PlatformFileForTransit transit = |
| + IPC::GetFileHandleForProcess(pwg_file_, process, true); |
| + pwg_file_ = base::kInvalidPlatformFileValue; |
| + return transit; |
| + } |
| + |
| + private: |
| + base::ScopedTempDir temp_dir_; |
| + base::PlatformFile pdf_file_; |
| + base::PlatformFile pwg_file_; |
| +}; |
| + |
| +void FileHandlers::Init(base::RefCountedMemory* data) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| + |
| + if (!temp_dir_.CreateUniqueTempDir()) { |
| + return; |
| + } |
| + |
| + if (static_cast<int>(data->size()) != |
| + file_util::WriteFile(GetPdfPath(), |
| + reinterpret_cast<const char*>(data->front()), |
| + data->size())) { |
| + return; |
| + } |
| + |
| + // Reopen in read only mode. |
| + pdf_file_ = base::CreatePlatformFile(GetPdfPath(), base::PLATFORM_FILE_OPEN | |
| + base::PLATFORM_FILE_READ, |
| + NULL, NULL); |
| + pwg_file_ = base::CreatePlatformFile(GetPwgPath(), |
| + base::PLATFORM_FILE_CREATE_ALWAYS | |
| + base::PLATFORM_FILE_APPEND, NULL, NULL); |
| +} |
| + |
| +bool FileHandlers::IsValid() { |
| + return pdf_file_ != base::kInvalidPlatformFileValue && |
| + pwg_file_ != base::kInvalidPlatformFileValue; |
| +} |
| + |
| +// Converts PDF into PWG raster. |
| +// Class uses 3 threads: UI, IO and FILE. |
| +// Internal workflow is following: |
| +// 1. Create instance on the UI thread. (files_, settings_,) |
| +// 2. Create file on the FILE thread. |
| +// 3. Start utility process and start conversion on the IO thread. |
| +// 4. Run result callback on the UI thread. |
| +// 5. Instance is destroyed from any thread that has the last reference. |
| +// 6. FileHandlers destroyed on the FILE thread. |
| +// This step posts |FileHandlers| to be destroyed on the FILE thread. |
| +// All these steps work sequentially, so no data should be accessed |
| +// simultaneously by several threads. |
| +class PwgUtilityProcessHostClient : public content::UtilityProcessHostClient { |
| + public: |
| + explicit PwgUtilityProcessHostClient( |
| + const printing::PdfRenderSettings& settings); |
| + virtual ~PwgUtilityProcessHostClient(); |
| + |
| + void Convert(base::RefCountedMemory* data, |
| + const PWGRasterConverter::ResultCallback& callback); |
| + |
| + // UtilityProcessHostClient implementation. |
| + virtual void OnProcessCrashed(int exit_code); |
| + virtual bool OnMessageReceived(const IPC::Message& message); |
| + |
| + private: |
| + // Message handlers. |
| + void OnProcessStarted(); |
| + void OnSucceeded(); |
| + void OnFailed(); |
| + |
| + void RunCallback(bool success); |
| + |
| + void StartProcessOnIOThread(); |
| + |
| + void RunCallbackOnUIThread(bool success); |
| + void OnFilesReadyOnUIThread(); |
| + |
| + scoped_ptr<FileHandlers> files_; |
| + printing::PdfRenderSettings settings_; |
| + PWGRasterConverter::ResultCallback callback_; |
| + base::WeakPtr<content::UtilityProcessHost> utility_process_host_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(PwgUtilityProcessHostClient); |
| +}; |
| + |
| +PwgUtilityProcessHostClient::PwgUtilityProcessHostClient( |
| + const printing::PdfRenderSettings& settings) : settings_(settings) { |
| +} |
| + |
| +PwgUtilityProcessHostClient::~PwgUtilityProcessHostClient() { |
| + // Delete temp directory. |
| + BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, files_.release()); |
| +} |
| + |
| +void PwgUtilityProcessHostClient::Convert( |
| + base::RefCountedMemory* data, |
| + const PWGRasterConverter::ResultCallback& callback) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + callback_ = callback; |
| + CHECK(!files_); |
| + files_.reset(new FileHandlers()); |
| + BrowserThread::PostTaskAndReply( |
| + BrowserThread::FILE, FROM_HERE, |
| + base::Bind(&FileHandlers::Init, base::Unretained(files_.get()), data), |
| + base::Bind(&PwgUtilityProcessHostClient::OnFilesReadyOnUIThread, this)); |
| +} |
| + |
| +void PwgUtilityProcessHostClient::OnProcessCrashed(int exit_code) { |
| + OnFailed(); |
| +} |
| + |
| +bool PwgUtilityProcessHostClient::OnMessageReceived( |
| + const IPC::Message& message) { |
| + bool handled = true; |
| + IPC_BEGIN_MESSAGE_MAP(PwgUtilityProcessHostClient, message) |
| + IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted) |
| + IPC_MESSAGE_HANDLER( |
| + ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Succeeded, OnSucceeded) |
| + IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Failed, |
| + OnFailed) |
| + IPC_MESSAGE_UNHANDLED(handled = false) |
| + IPC_END_MESSAGE_MAP() |
| + return handled; |
| +} |
| + |
| +void PwgUtilityProcessHostClient::OnProcessStarted() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + if (!utility_process_host_) { |
| + RunCallbackOnUIThread(false); |
| + return; |
| + } |
| + |
| + base::ProcessHandle process = utility_process_host_->GetData().handle; |
| + utility_process_host_->Send( |
| + new ChromeUtilityMsg_RenderPDFPagesToPWGRaster( |
| + files_->GetPdfForProcess(process), |
| + settings_, |
| + files_->GetPwgForProcess(process))); |
| + utility_process_host_.reset(); |
|
Lei Zhang
2013/11/22 01:57:24
Actually, is this suppose to be there?
Vitaly Buka (NO REVIEWS)
2013/11/22 08:10:05
I guess it's safer. it's weak ptr, so it does not
|
| +} |
| + |
| +void PwgUtilityProcessHostClient::OnSucceeded() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + RunCallback(true); |
| +} |
| + |
| +void PwgUtilityProcessHostClient::OnFailed() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + RunCallback(false); |
| +} |
| + |
| +void PwgUtilityProcessHostClient::OnFilesReadyOnUIThread() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + if (!files_->IsValid()) { |
| + RunCallbackOnUIThread(false); |
| + return; |
| + } |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, FROM_HERE, |
| + base::Bind(&PwgUtilityProcessHostClient::StartProcessOnIOThread, this)); |
| +} |
| + |
| +void PwgUtilityProcessHostClient::StartProcessOnIOThread() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + utility_process_host_ = |
| + content::UtilityProcessHost::Create( |
| + this, |
| + base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr(); |
| + utility_process_host_->Send(new ChromeUtilityMsg_StartupPing); |
| +} |
| + |
| +void PwgUtilityProcessHostClient::RunCallback(bool success) { |
| + BrowserThread::PostTask( |
| + BrowserThread::UI, FROM_HERE, |
| + base::Bind(&PwgUtilityProcessHostClient::RunCallbackOnUIThread, this, |
| + success)); |
| +} |
| + |
| +void PwgUtilityProcessHostClient::RunCallbackOnUIThread(bool success) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + if (!callback_.is_null()) { |
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| + base::Bind(callback_, success, |
| + files_->GetPwgPath())); |
| + callback_.Reset(); |
| + } |
| +} |
| + |
| class PWGRasterConverterImpl : public PWGRasterConverter { |
| public: |
| PWGRasterConverterImpl(); |
| @@ -20,6 +253,9 @@ class PWGRasterConverterImpl : public PWGRasterConverter { |
| const printing::PdfRenderSettings& conversion_settings, |
| const ResultCallback& callback) OVERRIDE; |
| private: |
| + scoped_refptr<PwgUtilityProcessHostClient> utility_client_; |
| + base::CancelableCallback<ResultCallback::RunType> callback_; |
| + |
| DISALLOW_COPY_AND_ASSIGN(PWGRasterConverterImpl); |
| }; |
| @@ -33,7 +269,11 @@ void PWGRasterConverterImpl::Start( |
| base::RefCountedMemory* data, |
| const printing::PdfRenderSettings& conversion_settings, |
| const ResultCallback& callback) { |
| - NOTIMPLEMENTED(); |
| + // Rebind cancelable callback to avoid calling callback if |
| + // PWGRasterConverterImpl is destroyed. |
| + callback_.Reset(callback); |
| + utility_client_ = new PwgUtilityProcessHostClient(conversion_settings); |
| + utility_client_->Convert(data, callback_.callback()); |
| } |
| } // namespace |