| 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());
|
|
|