Chromium Code Reviews| Index: chrome/browser/printing/pdf_to_emf_converter.cc |
| diff --git a/chrome/browser/printing/pdf_to_emf_converter.cc b/chrome/browser/printing/pdf_to_emf_converter.cc |
| index f0a8bab029aa923a92c9ca5788a0843d595a18ab..b9dc8a29a63dcb08380dbab6eb711912ff5f2997 100644 |
| --- a/chrome/browser/printing/pdf_to_emf_converter.cc |
| +++ b/chrome/browser/printing/pdf_to_emf_converter.cc |
| @@ -4,8 +4,8 @@ |
| #include "chrome/browser/printing/pdf_to_emf_converter.h" |
| -#include "base/bind_helpers.h" |
| -#include "base/cancelable_callback.h" |
| +#include <queue> |
| + |
| #include "base/files/file.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| @@ -17,7 +17,6 @@ |
| #include "content/public/browser/utility_process_host.h" |
| #include "content/public/browser/utility_process_host_client.h" |
| #include "printing/emf_win.h" |
| -#include "printing/page_range.h" |
| #include "printing/pdf_render_settings.h" |
| namespace printing { |
| @@ -26,319 +25,463 @@ namespace { |
| using content::BrowserThread; |
| -class FileHandlers |
| - : public base::RefCountedThreadSafe<FileHandlers, |
| +class PdfToEmfConverterImpl; |
| + |
| +class RefCountedTempDir |
|
Vitaly Buka (NO REVIEWS)
2014/09/16 23:08:10
Documented.
|
| + : public base::RefCountedThreadSafe<RefCountedTempDir, |
| BrowserThread::DeleteOnFileThread> { |
| public: |
| - FileHandlers() {} |
| - |
| - void Init(base::RefCountedMemory* data); |
| - bool IsValid(); |
| - |
| - base::FilePath GetEmfPath() const { |
| - return temp_dir_.path().AppendASCII("output.emf"); |
| - } |
| - |
| - base::FilePath GetEmfPagePath(int page_number) const { |
| - return GetEmfPath().InsertBeforeExtensionASCII( |
| - base::StringPrintf(".%d", page_number)); |
| - } |
| - |
| - base::FilePath GetPdfPath() const { |
| - return temp_dir_.path().AppendASCII("input.pdf"); |
| - } |
| - |
| - IPC::PlatformFileForTransit GetPdfForProcess(base::ProcessHandle process) { |
| - DCHECK(pdf_file_.IsValid()); |
| - IPC::PlatformFileForTransit transit = |
| - IPC::TakeFileHandleForProcess(pdf_file_.Pass(), process); |
| - return transit; |
| - } |
| - |
| - const base::FilePath& GetBasePath() const { |
| - return temp_dir_.path(); |
| - } |
| + RefCountedTempDir() { temp_dir_.CreateUniqueTempDir(); } |
| + bool IsValid() const { return temp_dir_.IsValid(); } |
| + const base::FilePath& GetPath() const { return temp_dir_.path(); } |
| private: |
| friend struct BrowserThread::DeleteOnThread<BrowserThread::FILE>; |
| - friend class base::DeleteHelper<FileHandlers>; |
| - |
| - ~FileHandlers() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); } |
| + friend class base::DeleteHelper<RefCountedTempDir>; |
| + ~RefCountedTempDir() {}; |
| base::ScopedTempDir temp_dir_; |
| - base::File pdf_file_; |
| }; |
|
Lei Zhang
2014/09/16 22:52:23
DISALLOW_COPY_AND_ASSIGN()
Vitaly Buka (NO REVIEWS)
2014/09/16 23:08:10
Done.
|
| -void FileHandlers::Init(base::RefCountedMemory* data) { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| +typedef scoped_ptr<base::File, BrowserThread::DeleteOnFileThread> |
| + ScopedTempFile; |
| - if (!temp_dir_.CreateUniqueTempDir()) { |
| - return; |
| - } |
| +// Wrapper for Emf to keep only file handle in memory, and load actual data only |
| +// on playback. Emf::InitFromFile() can play metafile directly from disk, but it |
| +// can't open file handles. We need file handles to reliably delete temporary |
| +// files, and to efficiently interact with utility process. |
| +class LazyEmf : public MetafilePlayer { |
| + public: |
| + LazyEmf(const scoped_refptr<RefCountedTempDir>& temp_dir, ScopedTempFile file) |
| + : temp_dir_(temp_dir), file_(file.Pass()) {} |
| + virtual ~LazyEmf() { Close(); } |
| - pdf_file_.Initialize(GetPdfPath(), |
| - base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE | |
| - base::File::FLAG_READ | |
| - base::File::FLAG_DELETE_ON_CLOSE); |
| - if (static_cast<int>(data->size()) != |
| - pdf_file_.WriteAtCurrentPos(data->front_as<char>(), data->size())) { |
| - pdf_file_.Close(); |
| - return; |
| - } |
| - pdf_file_.Seek(base::File::FROM_BEGIN, 0); |
| - pdf_file_.Flush(); |
| -} |
| + virtual bool SafePlayback(HDC hdc) const OVERRIDE; |
| + virtual bool SaveTo(base::File* file) const OVERRIDE; |
| -bool FileHandlers::IsValid() { |
| - return pdf_file_.IsValid(); |
| -} |
| + private: |
| + void Close() const; |
| + bool LoadEmf(Emf* emf) const; |
| -// Modification of Emf to keep references to |FileHandlers|. |
| -// |FileHandlers| must be deleted after the last metafile is closed because |
| -// Emf holds files locked. |
| -// Ideally we want to use FLAG_DELETE_ON_CLOSE, but it requires large changes. |
| -// It's going to be done for crbug.com/408184 |
| -class TempEmf : public Emf { |
| - public: |
| - explicit TempEmf(const scoped_refptr<FileHandlers>& files) : files_(files) {} |
| - virtual ~TempEmf() {} |
| - |
| - virtual bool SafePlayback(HDC hdc) const OVERRIDE { |
| - bool result = Emf::SafePlayback(hdc); |
| - TempEmf* this_mutable = const_cast<TempEmf*>(this); |
| - // TODO(vitalybuka): Fix destruction of metafiles. For some reasons |
| - // instances of Emf are not deleted. crbug.com/411683 |
| - // |files_| must be released as soon as possible to guarantee deletion. |
| - // It's know that this Emf file is going to be played just once to |
| - // a printer. So just release files here. |
| - this_mutable->Close(); |
| - this_mutable->files_ = NULL; |
| - return result; |
| - } |
| + mutable scoped_refptr<RefCountedTempDir> temp_dir_; |
| + mutable ScopedTempFile file_; // Mutable because of consts in base class. |
| - private: |
| - scoped_refptr<FileHandlers> files_; |
| - DISALLOW_COPY_AND_ASSIGN(TempEmf); |
| + DISALLOW_COPY_AND_ASSIGN(LazyEmf); |
| }; |
| // Converts PDF into EMF. |
| // 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. |
| +// 2. Create pdf 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. |
| +// 4. Utility process returns page count. |
| +// 5. For each page: |
| +// 1. Clients requests page with file handle to a temp file. |
| +// 2. Utility converts the page, save it to the file and reply. |
| +// |
| // All these steps work sequentially, so no data should be accessed |
| // simultaneously by several threads. |
| class PdfToEmfUtilityProcessHostClient |
| : public content::UtilityProcessHostClient { |
| public: |
| - explicit PdfToEmfUtilityProcessHostClient( |
| - const printing::PdfRenderSettings& settings); |
| + PdfToEmfUtilityProcessHostClient( |
| + base::WeakPtr<PdfToEmfConverterImpl> converter, |
| + const PdfRenderSettings& settings); |
| + |
| + void Start(const scoped_refptr<base::RefCountedMemory>& data, |
| + const PdfToEmfConverter::StartCallback& start_callback); |
| + |
| + void GetPage(int page_number, |
| + const PdfToEmfConverter::GetPageCallback& get_page_callback); |
| - void Convert(base::RefCountedMemory* data, |
| - const PdfToEmfConverter::ResultCallback& callback); |
| + void Stop(); |
| // UtilityProcessHostClient implementation. |
| virtual void OnProcessCrashed(int exit_code) OVERRIDE; |
| + virtual void OnProcessLaunchFailed() OVERRIDE; |
| virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; |
| private: |
| + class GetPageCallbackData { |
| + MOVE_ONLY_TYPE_FOR_CPP_03(GetPageCallbackData, RValue); |
| + |
| + public: |
| + GetPageCallbackData(int page_number, |
| + PdfToEmfConverter::GetPageCallback callback) |
| + : page_number_(page_number), callback_(callback) {} |
| + |
| + // Move constructor for STL. |
| + GetPageCallbackData(RValue other) { this->operator=(other); } |
| + |
| + // Move assignment for STL. |
| + GetPageCallbackData& operator=(RValue rhs) { |
| + page_number_ = rhs.object->page_number_; |
| + callback_ = rhs.object->callback_; |
| + emf_ = rhs.object->emf_.Pass(); |
| + return *this; |
| + } |
| + |
| + int page_number() const { return page_number_; } |
| + const PdfToEmfConverter::GetPageCallback& callback() const { |
| + return callback_; |
| + } |
| + ScopedTempFile emf() { return emf_.Pass(); } |
| + void set_emf(ScopedTempFile emf) { emf_ = emf.Pass(); } |
| + |
| + private: |
| + int page_number_; |
| + PdfToEmfConverter::GetPageCallback callback_; |
| + ScopedTempFile emf_; |
| + }; |
| + |
| virtual ~PdfToEmfUtilityProcessHostClient(); |
| + bool Send(IPC::Message* msg); |
| + |
| // Message handlers. |
| void OnProcessStarted(); |
| - void OnSucceeded(const std::vector<printing::PageRange>& page_ranges, |
| - double scale_factor); |
| + void OnPageCount(int page_count); |
| + void OnPageDone(bool success, double scale_factor); |
| + |
| void OnFailed(); |
| + void OnTempPdfReady(ScopedTempFile pdf); |
| + void OnTempEmfReady(GetPageCallbackData* callback_data, ScopedTempFile emf); |
| - void RunCallback(const std::vector<printing::PageRange>& page_ranges, |
| - double scale_factor); |
| + scoped_refptr<RefCountedTempDir> temp_dir_; |
| - void StartProcessOnIOThread(); |
| + // Used to suppress callbacks after PdfToEmfConverterImpl is deleted. |
| + base::WeakPtr<PdfToEmfConverterImpl> converter_; |
| + PdfRenderSettings settings_; |
| + scoped_refptr<base::RefCountedMemory> data_; |
| - void RunCallbackOnUIThread( |
| - const std::vector<printing::PageRange>& page_ranges, |
| - double scale_factor); |
| - void OnFilesReadyOnUIThread(); |
| + // Document loaded callback. |
| + PdfToEmfConverter::StartCallback start_callback_; |
| - scoped_refptr<FileHandlers> files_; |
| - printing::PdfRenderSettings settings_; |
| - PdfToEmfConverter::ResultCallback callback_; |
| + // Process host for IPC. |
| base::WeakPtr<content::UtilityProcessHost> utility_process_host_; |
| + // Queue of callbacks for GetPage() requests. Utility process should reply |
| + // with PageDone in the same order as requests were received. |
| + // Use containers that keeps element pointers valid after push() and pop(). |
| + typedef std::queue<GetPageCallbackData> GetPageCallbacks; |
| + GetPageCallbacks get_page_callbacks_; |
| + |
| DISALLOW_COPY_AND_ASSIGN(PdfToEmfUtilityProcessHostClient); |
| }; |
| -PdfToEmfUtilityProcessHostClient::PdfToEmfUtilityProcessHostClient( |
| - const printing::PdfRenderSettings& settings) |
| - : settings_(settings) {} |
| +class PdfToEmfConverterImpl : public PdfToEmfConverter { |
| + public: |
| + PdfToEmfConverterImpl(); |
| -PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() { |
| + virtual ~PdfToEmfConverterImpl(); |
| + |
| + virtual void Start(const scoped_refptr<base::RefCountedMemory>& data, |
| + const PdfRenderSettings& conversion_settings, |
| + const StartCallback& start_callback) OVERRIDE; |
| + |
| + virtual void GetPage(int page_number, |
| + const GetPageCallback& get_page_callback) OVERRIDE; |
| + |
| + // Helps to cancel callbacks if this object is destroyed. |
| + void RunCallback(const base::Closure& callback); |
| + |
| + private: |
| + scoped_refptr<PdfToEmfUtilityProcessHostClient> utility_client_; |
| + base::WeakPtrFactory<PdfToEmfConverterImpl> weak_ptr_factory_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(PdfToEmfConverterImpl); |
| +}; |
| + |
| +ScopedTempFile CreateTempFile(scoped_refptr<RefCountedTempDir>* temp_dir) { |
| + if (!(*temp_dir)) |
| + *temp_dir = new RefCountedTempDir(); |
| + ScopedTempFile file; |
| + if (!(*temp_dir)->IsValid()) |
| + return file.Pass(); |
| + base::FilePath path; |
| + if (!base::CreateTemporaryFileInDir((*temp_dir)->GetPath(), &path)) |
| + return file.Pass(); |
| + file.reset(new base::File(path, |
| + base::File::FLAG_CREATE_ALWAYS | |
| + base::File::FLAG_WRITE | base::File::FLAG_READ | |
| + base::File::FLAG_DELETE_ON_CLOSE | |
| + base::File::FLAG_TEMPORARY)); |
| + if (!file->IsValid()) |
| + file.reset(); |
| + return file.Pass(); |
| } |
| -void PdfToEmfUtilityProcessHostClient::Convert( |
| - base::RefCountedMemory* data, |
| - const PdfToEmfConverter::ResultCallback& callback) { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - callback_ = callback; |
| - CHECK(!files_.get()); |
| - files_ = new FileHandlers(); |
| - BrowserThread::PostTaskAndReply( |
| - BrowserThread::FILE, |
| - FROM_HERE, |
| - base::Bind(&FileHandlers::Init, files_, make_scoped_refptr(data)), |
| - base::Bind(&PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread, |
| - this)); |
| +ScopedTempFile CreateTempPdfFile( |
| + const scoped_refptr<base::RefCountedMemory>& data, |
| + scoped_refptr<RefCountedTempDir>* temp_dir) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| + |
| + ScopedTempFile pdf_file = CreateTempFile(temp_dir); |
| + if (!pdf_file || |
| + static_cast<int>(data->size()) != |
| + pdf_file->WriteAtCurrentPos(data->front_as<char>(), data->size())) { |
| + pdf_file.reset(); |
| + } |
| + pdf_file->Seek(base::File::FROM_BEGIN, 0); |
| + return pdf_file.Pass(); |
| } |
| -void PdfToEmfUtilityProcessHostClient::OnProcessCrashed(int exit_code) { |
| - OnFailed(); |
| +bool LazyEmf::SafePlayback(HDC hdc) const { |
| + Emf emf; |
| + bool result = LoadEmf(&emf) && emf.SafePlayback(hdc); |
| + // TODO(vitalybuka): Fix destruction of metafiles. For some reasons |
| + // instances of Emf are not deleted. crbug.com/411683 |
| + // It's known that the Emf going to be played just once to a printer. So just |
| + // release file here. |
| + Close(); |
| + return result; |
| } |
| -bool PdfToEmfUtilityProcessHostClient::OnMessageReceived( |
| - const IPC::Message& message) { |
| - bool handled = true; |
| - IPC_BEGIN_MESSAGE_MAP(PdfToEmfUtilityProcessHostClient, message) |
| - IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted) |
| - IPC_MESSAGE_HANDLER( |
| - ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_Succeeded, OnSucceeded) |
| - IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafile_Failed, |
| - OnFailed) |
| - IPC_MESSAGE_UNHANDLED(handled = false) |
| - IPC_END_MESSAGE_MAP() |
| - return handled; |
| +bool LazyEmf::SaveTo(base::File* file) const { |
| + Emf emf; |
| + return LoadEmf(&emf) && emf.SaveTo(file); |
| } |
| -void PdfToEmfUtilityProcessHostClient::OnProcessStarted() { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| - if (!utility_process_host_) { |
| - RunCallbackOnUIThread(std::vector<printing::PageRange>(), 0.0); |
| - return; |
| - } |
| +void LazyEmf::Close() const { |
| + file_.reset(); |
| + temp_dir_ = NULL; |
| +} |
| - base::ProcessHandle process = utility_process_host_->GetData().handle; |
| - utility_process_host_->Send( |
| - new ChromeUtilityMsg_RenderPDFPagesToMetafiles( |
| - files_->GetPdfForProcess(process), |
| - files_->GetEmfPath(), |
| - settings_, |
| - std::vector<printing::PageRange>())); |
| - utility_process_host_.reset(); |
| +bool LazyEmf::LoadEmf(Emf* emf) const { |
| + file_->Seek(base::File::FROM_BEGIN, 0); |
| + int64 size = file_->GetLength(); |
| + if (size <= 0) |
| + return false; |
| + std::vector<char> data(size); |
| + if (file_->ReadAtCurrentPos(data.data(), data.size()) != size) |
| + return false; |
| + return emf->InitFromData(data.data(), data.size()); |
| } |
| -void PdfToEmfUtilityProcessHostClient::OnSucceeded( |
| - const std::vector<printing::PageRange>& page_ranges, |
| - double scale_factor) { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| - RunCallback(page_ranges, scale_factor); |
| +PdfToEmfUtilityProcessHostClient::PdfToEmfUtilityProcessHostClient( |
| + base::WeakPtr<PdfToEmfConverterImpl> converter, |
| + const PdfRenderSettings& settings) |
| + : converter_(converter), settings_(settings) { |
| } |
| -void PdfToEmfUtilityProcessHostClient::OnFailed() { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| - RunCallback(std::vector<printing::PageRange>(), 0.0); |
| +PdfToEmfUtilityProcessHostClient::~PdfToEmfUtilityProcessHostClient() { |
| } |
| -void PdfToEmfUtilityProcessHostClient::OnFilesReadyOnUIThread() { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - if (!files_->IsValid()) { |
| - RunCallbackOnUIThread(std::vector<printing::PageRange>(), 0.0); |
| +void PdfToEmfUtilityProcessHostClient::Start( |
| + const scoped_refptr<base::RefCountedMemory>& data, |
| + const PdfToEmfConverter::StartCallback& start_callback) { |
| + if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { |
| + BrowserThread::PostTask(BrowserThread::IO, |
| + FROM_HERE, |
| + base::Bind(&PdfToEmfUtilityProcessHostClient::Start, |
| + this, |
| + data, |
| + start_callback)); |
| return; |
| } |
| - BrowserThread::PostTask( |
| - BrowserThread::IO, |
| - FROM_HERE, |
| - base::Bind(&PdfToEmfUtilityProcessHostClient::StartProcessOnIOThread, |
| - this)); |
| -} |
| + data_ = data; |
| + |
| + // Store callback before any OnFailed() call to make it called on failure. |
| + start_callback_ = start_callback; |
| -void PdfToEmfUtilityProcessHostClient::StartProcessOnIOThread() { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| - utility_process_host_ = |
| - content::UtilityProcessHost::Create( |
| - this, |
| - base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr(); |
| // NOTE: This process _must_ be sandboxed, otherwise the pdf dll will load |
| // gdiplus.dll, change how rendering happens, and not be able to correctly |
| // generate when sent to a metafile DC. |
| - utility_process_host_->SetExposedDir(files_->GetBasePath()); |
| - utility_process_host_->Send(new ChromeUtilityMsg_StartupPing); |
| + utility_process_host_ = |
| + content::UtilityProcessHost::Create( |
| + this, base::MessageLoop::current()->message_loop_proxy()) |
| + ->AsWeakPtr(); |
| + if (!utility_process_host_) |
| + return OnFailed(); |
| + // Should reply with OnProcessStarted(). |
| + Send(new ChromeUtilityMsg_StartupPing); |
| } |
| -void PdfToEmfUtilityProcessHostClient::RunCallback( |
| - const std::vector<printing::PageRange>& page_ranges, |
| - double scale_factor) { |
| - BrowserThread::PostTask( |
| - BrowserThread::UI, |
| +void PdfToEmfUtilityProcessHostClient::OnProcessStarted() { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + if (!utility_process_host_) |
| + return OnFailed(); |
| + |
| + base::ProcessHandle process = utility_process_host_->GetData().handle; |
| + scoped_refptr<base::RefCountedMemory> data = data_; |
| + data_ = NULL; |
| + BrowserThread::PostTaskAndReplyWithResult( |
| + BrowserThread::FILE, |
| FROM_HERE, |
| - base::Bind(&PdfToEmfUtilityProcessHostClient::RunCallbackOnUIThread, |
| - this, |
| - page_ranges, |
| - scale_factor)); |
| + base::Bind(&CreateTempPdfFile, data, &temp_dir_), |
| + base::Bind(&PdfToEmfUtilityProcessHostClient::OnTempPdfReady, this)); |
| } |
| -void PdfToEmfUtilityProcessHostClient::RunCallbackOnUIThread( |
| - const std::vector<printing::PageRange>& page_ranges, |
| - double scale_factor) { |
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - ScopedVector<MetafilePlayer> pages; |
| - std::vector<printing::PageRange>::const_iterator iter; |
| - for (iter = page_ranges.begin(); iter != page_ranges.end(); ++iter) { |
| - for (int page_number = iter->from; page_number <= iter->to; ++page_number) { |
| - scoped_ptr<TempEmf> metafile(new TempEmf(files_)); |
| - if (!metafile->InitFromFile(files_->GetEmfPagePath(page_number))) { |
| - NOTREACHED() << "Invalid metafile"; |
| - metafile.reset(); |
| - } |
| - pages.push_back(metafile.release()); |
| - } |
| +void PdfToEmfUtilityProcessHostClient::OnTempPdfReady(ScopedTempFile pdf) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + if (!utility_process_host_) |
| + return OnFailed(); |
| + base::ProcessHandle process = utility_process_host_->GetData().handle; |
| + // Should reply with OnPageCount(). |
| + Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles( |
| + IPC::GetFileHandleForProcess(pdf->GetPlatformFile(), process, false), |
| + settings_)); |
| +} |
| + |
| +void PdfToEmfUtilityProcessHostClient::OnPageCount(int page_count) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + if (start_callback_.is_null()) |
| + return OnFailed(); |
| + BrowserThread::PostTask(BrowserThread::UI, |
| + FROM_HERE, |
| + base::Bind(&PdfToEmfConverterImpl::RunCallback, |
| + converter_, |
| + base::Bind(start_callback_, page_count))); |
| + start_callback_.Reset(); |
| +} |
| + |
| +void PdfToEmfUtilityProcessHostClient::GetPage( |
| + int page_number, |
| + const PdfToEmfConverter::GetPageCallback& get_page_callback) { |
| + if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, |
| + FROM_HERE, |
| + base::Bind(&PdfToEmfUtilityProcessHostClient::GetPage, |
| + this, |
| + page_number, |
| + get_page_callback)); |
| + return; |
| } |
| - files_ = NULL; |
| - if (!callback_.is_null()) { |
| - callback_.Run(scale_factor, &pages); |
| - callback_.Reset(); |
| + |
| + // Store callback before any OnFailed() call to make it called on failure. |
| + get_page_callbacks_.push(GetPageCallbackData(page_number, get_page_callback)); |
| + |
| + if (!utility_process_host_) |
| + return OnFailed(); |
| + |
| + BrowserThread::PostTaskAndReplyWithResult( |
| + BrowserThread::FILE, |
| + FROM_HERE, |
| + base::Bind(&CreateTempFile, &temp_dir_), |
| + base::Bind(&PdfToEmfUtilityProcessHostClient::OnTempEmfReady, |
| + this, |
| + &get_page_callbacks_.back())); |
| +} |
| + |
| +void PdfToEmfUtilityProcessHostClient::OnTempEmfReady( |
| + GetPageCallbackData* callback_data, |
| + ScopedTempFile emf) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + if (!utility_process_host_) |
| + return OnFailed(); |
| + base::ProcessHandle process = utility_process_host_->GetData().handle; |
| + IPC::PlatformFileForTransit transit = |
| + IPC::GetFileHandleForProcess(emf->GetPlatformFile(), process, false); |
| + callback_data->set_emf(emf.Pass()); |
| + // Should reply with OnPageDone(). |
| + Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_GetPage( |
| + callback_data->page_number(), transit)); |
| +} |
| + |
| +void PdfToEmfUtilityProcessHostClient::OnPageDone(bool success, |
| + double scale_factor) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + if (get_page_callbacks_.empty()) |
| + return OnFailed(); |
| + scoped_ptr<LazyEmf> emf; |
| + GetPageCallbackData& data = get_page_callbacks_.front(); |
| + if (success) |
| + emf.reset(new LazyEmf(temp_dir_, data.emf().Pass())); |
| + BrowserThread::PostTask(BrowserThread::UI, |
| + FROM_HERE, |
| + base::Bind(&PdfToEmfConverterImpl::RunCallback, |
| + converter_, |
| + base::Bind(data.callback(), |
| + data.page_number(), |
| + scale_factor, |
| + base::Passed(&emf)))); |
| + get_page_callbacks_.pop(); |
| +} |
| + |
| +void PdfToEmfUtilityProcessHostClient::Stop() { |
| + if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, |
| + FROM_HERE, |
| + base::Bind(&PdfToEmfUtilityProcessHostClient::Stop, this)); |
| + return; |
| } |
| + Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop()); |
| } |
| -class PdfToEmfConverterImpl : public PdfToEmfConverter { |
| - public: |
| - PdfToEmfConverterImpl(); |
| +void PdfToEmfUtilityProcessHostClient::OnProcessCrashed(int exit_code) { |
| + OnFailed(); |
| +} |
| - virtual ~PdfToEmfConverterImpl(); |
| +void PdfToEmfUtilityProcessHostClient::OnProcessLaunchFailed() { |
| + OnFailed(); |
| +} |
| - virtual void Start(base::RefCountedMemory* data, |
| - const printing::PdfRenderSettings& conversion_settings, |
| - const ResultCallback& callback) OVERRIDE; |
| +bool PdfToEmfUtilityProcessHostClient::OnMessageReceived( |
| + const IPC::Message& message) { |
| + bool handled = true; |
| + IPC_BEGIN_MESSAGE_MAP(PdfToEmfUtilityProcessHostClient, message) |
| + IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted) |
| + IPC_MESSAGE_HANDLER( |
| + ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount, OnPageCount) |
| + IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone, |
| + OnPageDone) |
| + IPC_MESSAGE_UNHANDLED(handled = false) |
| + IPC_END_MESSAGE_MAP() |
| + return handled; |
| +} |
| - private: |
| - scoped_refptr<PdfToEmfUtilityProcessHostClient> utility_client_; |
| - base::CancelableCallback<ResultCallback::RunType> callback_; |
| +bool PdfToEmfUtilityProcessHostClient::Send(IPC::Message* msg) { |
| + if (utility_process_host_) |
| + return utility_process_host_->Send(msg); |
| + delete msg; |
| + return false; |
| +} |
| - DISALLOW_COPY_AND_ASSIGN(PdfToEmfConverterImpl); |
| -}; |
| +void PdfToEmfUtilityProcessHostClient::OnFailed() { |
| + DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| + if (!start_callback_.is_null()) |
| + OnPageCount(0); |
| + while (!get_page_callbacks_.empty()) |
| + OnPageDone(false, 0.0); |
| + utility_process_host_.reset(); |
| +} |
| -PdfToEmfConverterImpl::PdfToEmfConverterImpl() { |
| +PdfToEmfConverterImpl::PdfToEmfConverterImpl() : weak_ptr_factory_(this) { |
| } |
| PdfToEmfConverterImpl::~PdfToEmfConverterImpl() { |
| + if (utility_client_) |
| + utility_client_->Stop(); |
| } |
| void PdfToEmfConverterImpl::Start( |
| - base::RefCountedMemory* data, |
| - const printing::PdfRenderSettings& conversion_settings, |
| - const ResultCallback& callback) { |
| - // Rebind cancelable callback to avoid calling callback if |
| - // PdfToEmfConverterImpl is destroyed. |
| - callback_.Reset(callback); |
| - utility_client_ = new PdfToEmfUtilityProcessHostClient(conversion_settings); |
| - utility_client_->Convert(data, callback_.callback()); |
| + const scoped_refptr<base::RefCountedMemory>& data, |
| + const PdfRenderSettings& conversion_settings, |
| + const StartCallback& start_callback) { |
| + DCHECK(!utility_client_); |
| + utility_client_ = new PdfToEmfUtilityProcessHostClient( |
| + weak_ptr_factory_.GetWeakPtr(), conversion_settings); |
| + utility_client_->Start(data, start_callback); |
| +} |
| + |
| +void PdfToEmfConverterImpl::GetPage(int page_number, |
| + const GetPageCallback& get_page_callback) { |
| + utility_client_->GetPage(page_number, get_page_callback); |
| +} |
| + |
| +void PdfToEmfConverterImpl::RunCallback(const base::Closure& callback) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + callback.Run(); |
| } |
| } // namespace |
| +PdfToEmfConverter::~PdfToEmfConverter() { |
| +} |
| + |
| // static |
| scoped_ptr<PdfToEmfConverter> PdfToEmfConverter::CreateDefault() { |
| return scoped_ptr<PdfToEmfConverter>(new PdfToEmfConverterImpl()); |