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..39259365ecb21b9e1539c0a171d658366031d7a0 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,469 @@ namespace { |
using content::BrowserThread; |
-class FileHandlers |
- : public base::RefCountedThreadSafe<FileHandlers, |
+class PdfToEmfConverterImpl; |
+ |
+// Allows to delete temporary directory after all temporary files created inside |
+// are closed. Windows cannot delete directory with opened files. Directory is |
+// used to store PDF and metafiles. PDF should be gone by the time utility |
+// process exits. Metafiles should be gone when all LazyEmf destroyed. |
+class RefCountedTempDir |
+ : 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_; |
+ DISALLOW_COPY_AND_ASSIGN(RefCountedTempDir); |
}; |
-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()); |