refactor: move printing out of chromium_src (#15023)
* remove printing related things from chromium_src * chore: add printing build flag and patch * fix: include PrintingService on other platforms too * fix: printing_handler is only needed on Windows * fix: format BUILD.gn properly * fix: rename printing build flag to avoid conflict with chromium * fix: place previously missed printing calls behind build flag * fix: accidentally renamed flag in patch file * fix: don't include all printing strings * fix: allow ShowItemInFolder and OpenItem to block, fixing a DCHECK crash * fix: make things compile, some changes got lost while rebasing * fix: remove rogue line from BUILD.gn * chore: update patch description * style: lint fix * chore: use chromium printing buildflag, move node related stuff out of patch * revert: remove ScopedAllowBlockingForTesting call * fix: fix my rebase blooper * fix: re-add header lost during rebase, update patch * fix: add <map> include, tweak the patch a bit * revert: remove rogue diff from patch * fix: clean up after rebase
This commit is contained in:
parent
c806c465fa
commit
a82bcc7e3c
53 changed files with 1398 additions and 7358 deletions
|
@ -5,10 +5,13 @@
|
|||
#include "chrome/browser/browser_process_impl.h"
|
||||
|
||||
#include "chrome/browser/printing/print_job_manager.h"
|
||||
#include "printing/buildflags/buildflags.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
|
||||
BrowserProcessImpl::BrowserProcessImpl()
|
||||
: print_job_manager_(new printing::PrintJobManager) {
|
||||
BrowserProcessImpl::BrowserProcessImpl() : print_job_manager_(nullptr) {
|
||||
#if BUILDFLAG(ENABLE_PRINTING)
|
||||
print_job_manager_.reset(new printing::PrintJobManager());
|
||||
#endif
|
||||
g_browser_process = this;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,684 +0,0 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/printing/pdf_to_emf_converter.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/files/file.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/files/scoped_temp_dir.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ref_counted_delete_on_sequence.h"
|
||||
#include "base/sequenced_task_runner.h"
|
||||
#include "base/task_scheduler/post_task.h"
|
||||
#include "base/threading/sequenced_task_runner_handle.h"
|
||||
#include "base/threading/thread_restrictions.h"
|
||||
#include "base/threading/thread_task_runner_handle.h"
|
||||
#include "chrome/common/chrome_utility_printing_messages.h"
|
||||
#include "content/browser/utility_process_host.h"
|
||||
#include "content/browser/utility_process_host_client.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/child_process_data.h"
|
||||
#include "printing/emf_win.h"
|
||||
#include "printing/pdf_render_settings.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace printing {
|
||||
|
||||
namespace {
|
||||
|
||||
void CloseFileOnBlockingTaskRunner(base::File temp_file) {
|
||||
base::AssertBlockingAllowed();
|
||||
temp_file.Close();
|
||||
}
|
||||
|
||||
class PdfConverterImpl;
|
||||
|
||||
// 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::RefCountedDeleteOnSequence<RefCountedTempDir> {
|
||||
public:
|
||||
RefCountedTempDir()
|
||||
: base::RefCountedDeleteOnSequence<RefCountedTempDir>(
|
||||
base::SequencedTaskRunnerHandle::Get()) {
|
||||
ignore_result(temp_dir_.CreateUniqueTempDir());
|
||||
}
|
||||
|
||||
bool IsValid() const { return temp_dir_.IsValid(); }
|
||||
const base::FilePath& GetPath() const { return temp_dir_.GetPath(); }
|
||||
|
||||
private:
|
||||
friend class base::RefCountedDeleteOnSequence<RefCountedTempDir>;
|
||||
friend class base::DeleteHelper<RefCountedTempDir>;
|
||||
|
||||
~RefCountedTempDir() {}
|
||||
|
||||
base::ScopedTempDir temp_dir_;
|
||||
DISALLOW_COPY_AND_ASSIGN(RefCountedTempDir);
|
||||
};
|
||||
|
||||
class TempFile {
|
||||
public:
|
||||
explicit TempFile(base::File file)
|
||||
: file_(std::move(file)),
|
||||
blocking_task_runner_(base::SequencedTaskRunnerHandle::Get()) {
|
||||
base::AssertBlockingAllowed();
|
||||
}
|
||||
~TempFile() {
|
||||
blocking_task_runner_->PostTask(
|
||||
FROM_HERE, base::BindOnce(&CloseFileOnBlockingTaskRunner,
|
||||
base::Passed(std::move(file_))));
|
||||
}
|
||||
|
||||
base::File& file() { return file_; }
|
||||
|
||||
private:
|
||||
base::File file_;
|
||||
const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TempFile);
|
||||
};
|
||||
|
||||
using ScopedTempFile = std::unique_ptr<TempFile>;
|
||||
|
||||
// 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_(std::move(file)) {
|
||||
CHECK(file_);
|
||||
}
|
||||
~LazyEmf() override { Close(); }
|
||||
|
||||
protected:
|
||||
// MetafilePlayer:
|
||||
bool SafePlayback(HDC hdc) const override;
|
||||
|
||||
void Close() const;
|
||||
bool LoadEmf(Emf* emf) const;
|
||||
|
||||
private:
|
||||
mutable scoped_refptr<RefCountedTempDir> temp_dir_;
|
||||
mutable ScopedTempFile file_; // Mutable because of consts in base class.
|
||||
|
||||
bool GetDataAsVector(std::vector<char>* buffer) const override;
|
||||
bool SaveTo(base::File* file) const override;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(LazyEmf);
|
||||
};
|
||||
|
||||
// Postscript metafile subclass to override SafePlayback.
|
||||
class PostScriptMetaFile : public LazyEmf {
|
||||
public:
|
||||
PostScriptMetaFile(const scoped_refptr<RefCountedTempDir>& temp_dir,
|
||||
ScopedTempFile file)
|
||||
: LazyEmf(temp_dir, std::move(file)) {}
|
||||
~PostScriptMetaFile() override;
|
||||
|
||||
private:
|
||||
// MetafilePlayer:
|
||||
bool SafePlayback(HDC hdc) const override;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PostScriptMetaFile);
|
||||
};
|
||||
|
||||
// Class for converting PDF to another format for printing (Emf, Postscript).
|
||||
// Class uses UI thread, IO thread and |blocking_task_runner_|.
|
||||
// Internal workflow is following:
|
||||
// 1. Create instance on the UI thread. (files_, settings_,)
|
||||
// 2. Create pdf file on |blocking_task_runner_|.
|
||||
// 3. Start utility process and start conversion on the IO 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 PdfConverterUtilityProcessHostClient
|
||||
: public content::UtilityProcessHostClient {
|
||||
public:
|
||||
PdfConverterUtilityProcessHostClient(
|
||||
base::WeakPtr<PdfConverterImpl> converter,
|
||||
const PdfRenderSettings& settings);
|
||||
|
||||
void Start(const scoped_refptr<base::RefCountedMemory>& data,
|
||||
const PdfConverter::StartCallback& start_callback);
|
||||
|
||||
void GetPage(int page_number,
|
||||
const PdfConverter::GetPageCallback& get_page_callback);
|
||||
|
||||
void Stop();
|
||||
|
||||
// UtilityProcessHostClient implementation.
|
||||
void OnProcessCrashed(int exit_code) override;
|
||||
void OnProcessLaunchFailed(int exit_code) override;
|
||||
|
||||
// Needs to be public to handle ChromeUtilityHostMsg_PreCacheFontCharacters
|
||||
// sync message replies.
|
||||
bool Send(IPC::Message* msg);
|
||||
|
||||
private:
|
||||
class GetPageCallbackData {
|
||||
public:
|
||||
GetPageCallbackData(int page_number, PdfConverter::GetPageCallback callback)
|
||||
: page_number_(page_number), callback_(callback) {}
|
||||
|
||||
GetPageCallbackData(GetPageCallbackData&& other) {
|
||||
*this = std::move(other);
|
||||
}
|
||||
|
||||
GetPageCallbackData& operator=(GetPageCallbackData&& rhs) {
|
||||
page_number_ = rhs.page_number_;
|
||||
callback_ = rhs.callback_;
|
||||
file_ = std::move(rhs.file_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
int page_number() const { return page_number_; }
|
||||
const PdfConverter::GetPageCallback& callback() const { return callback_; }
|
||||
ScopedTempFile TakeFile() { return std::move(file_); }
|
||||
void set_file(ScopedTempFile file) { file_ = std::move(file); }
|
||||
|
||||
private:
|
||||
int page_number_;
|
||||
|
||||
PdfConverter::GetPageCallback callback_;
|
||||
ScopedTempFile file_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(GetPageCallbackData);
|
||||
};
|
||||
|
||||
~PdfConverterUtilityProcessHostClient() override;
|
||||
|
||||
bool OnMessageReceived(const IPC::Message& message) override;
|
||||
|
||||
// Helper functions: must be overridden by subclasses
|
||||
// Set the process name
|
||||
base::string16 GetName() const;
|
||||
// Create a metafileplayer subclass file from a temporary file.
|
||||
std::unique_ptr<MetafilePlayer> GetFileFromTemp(ScopedTempFile temp_file);
|
||||
// Send the messages to Start, GetPage, and Stop.
|
||||
void SendStartMessage(IPC::PlatformFileForTransit transit);
|
||||
void SendGetPageMessage(int page_number, IPC::PlatformFileForTransit transit);
|
||||
void SendStopMessage();
|
||||
|
||||
// Message handlers:
|
||||
void OnPageCount(int page_count);
|
||||
void OnPageDone(bool success, float scale_factor);
|
||||
|
||||
void OnFailed();
|
||||
void OnTempPdfReady(ScopedTempFile pdf);
|
||||
void OnTempFileReady(GetPageCallbackData* callback_data,
|
||||
ScopedTempFile temp_file);
|
||||
|
||||
// Additional message handler needed for Pdf to Emf
|
||||
void OnPreCacheFontCharacters(const LOGFONT& log_font,
|
||||
const base::string16& characters);
|
||||
|
||||
scoped_refptr<RefCountedTempDir> temp_dir_;
|
||||
|
||||
// Used to suppress callbacks after PdfConverter is deleted.
|
||||
base::WeakPtr<PdfConverterImpl> converter_;
|
||||
PdfRenderSettings settings_;
|
||||
|
||||
// Document loaded callback.
|
||||
PdfConverter::StartCallback start_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().
|
||||
using GetPageCallbacks = std::queue<GetPageCallbackData>;
|
||||
GetPageCallbacks get_page_callbacks_;
|
||||
|
||||
const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PdfConverterUtilityProcessHostClient);
|
||||
};
|
||||
|
||||
std::unique_ptr<MetafilePlayer>
|
||||
PdfConverterUtilityProcessHostClient::GetFileFromTemp(
|
||||
ScopedTempFile temp_file) {
|
||||
if (settings_.mode == PdfRenderSettings::Mode::POSTSCRIPT_LEVEL2 ||
|
||||
settings_.mode == PdfRenderSettings::Mode::POSTSCRIPT_LEVEL3) {
|
||||
return std::make_unique<PostScriptMetaFile>(temp_dir_,
|
||||
std::move(temp_file));
|
||||
}
|
||||
return std::make_unique<LazyEmf>(temp_dir_, std::move(temp_file));
|
||||
}
|
||||
|
||||
class PdfConverterImpl : public PdfConverter {
|
||||
public:
|
||||
PdfConverterImpl();
|
||||
|
||||
~PdfConverterImpl() override;
|
||||
|
||||
base::WeakPtr<PdfConverterImpl> GetWeakPtr() {
|
||||
return weak_ptr_factory_.GetWeakPtr();
|
||||
}
|
||||
|
||||
void Start(const scoped_refptr<base::RefCountedMemory>& data,
|
||||
const PdfRenderSettings& conversion_settings,
|
||||
const StartCallback& start_callback);
|
||||
|
||||
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);
|
||||
|
||||
void Start(
|
||||
const scoped_refptr<PdfConverterUtilityProcessHostClient>& utility_client,
|
||||
const scoped_refptr<base::RefCountedMemory>& data,
|
||||
const StartCallback& start_callback);
|
||||
|
||||
private:
|
||||
scoped_refptr<PdfConverterUtilityProcessHostClient> utility_client_;
|
||||
|
||||
base::WeakPtrFactory<PdfConverterImpl> weak_ptr_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PdfConverterImpl);
|
||||
};
|
||||
|
||||
ScopedTempFile CreateTempFile(scoped_refptr<RefCountedTempDir>* temp_dir) {
|
||||
if (!temp_dir->get())
|
||||
*temp_dir = base::MakeRefCounted<RefCountedTempDir>();
|
||||
ScopedTempFile file;
|
||||
if (!(*temp_dir)->IsValid())
|
||||
return file;
|
||||
base::FilePath path;
|
||||
if (!base::CreateTemporaryFileInDir((*temp_dir)->GetPath(), &path)) {
|
||||
PLOG(ERROR) << "Failed to create file in "
|
||||
<< (*temp_dir)->GetPath().value();
|
||||
return file;
|
||||
}
|
||||
file = std::make_unique<TempFile>(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->file().IsValid()) {
|
||||
PLOG(ERROR) << "Failed to create " << path.value();
|
||||
file.reset();
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
ScopedTempFile CreateTempPdfFile(
|
||||
const scoped_refptr<base::RefCountedMemory>& data,
|
||||
scoped_refptr<RefCountedTempDir>* temp_dir) {
|
||||
ScopedTempFile pdf_file = CreateTempFile(temp_dir);
|
||||
if (!pdf_file || static_cast<int>(data->size()) !=
|
||||
pdf_file->file().WriteAtCurrentPos(
|
||||
data->front_as<char>(), data->size())) {
|
||||
pdf_file.reset();
|
||||
return pdf_file;
|
||||
}
|
||||
pdf_file->file().Seek(base::File::FROM_BEGIN, 0);
|
||||
return pdf_file;
|
||||
}
|
||||
|
||||
bool LazyEmf::SafePlayback(HDC hdc) const {
|
||||
Emf emf;
|
||||
bool result = LoadEmf(&emf) && emf.SafePlayback(hdc);
|
||||
// TODO(thestig): Fix destruction of metafiles. For some reasons
|
||||
// instances of Emf are not deleted. https://crbug.com/260806
|
||||
// It's known that the Emf going to be played just once to a printer. So just
|
||||
// release |file_| here.
|
||||
Close();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LazyEmf::GetDataAsVector(std::vector<char>* buffer) const {
|
||||
NOTREACHED();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LazyEmf::SaveTo(base::File* file) const {
|
||||
Emf emf;
|
||||
return LoadEmf(&emf) && emf.SaveTo(file);
|
||||
}
|
||||
|
||||
void LazyEmf::Close() const {
|
||||
file_.reset();
|
||||
temp_dir_ = nullptr;
|
||||
}
|
||||
|
||||
bool LazyEmf::LoadEmf(Emf* emf) const {
|
||||
file_->file().Seek(base::File::FROM_BEGIN, 0);
|
||||
int64_t size = file_->file().GetLength();
|
||||
if (size <= 0)
|
||||
return false;
|
||||
std::vector<char> data(size);
|
||||
if (file_->file().ReadAtCurrentPos(data.data(), data.size()) != size)
|
||||
return false;
|
||||
return emf->InitFromData(data.data(), data.size());
|
||||
}
|
||||
|
||||
PostScriptMetaFile::~PostScriptMetaFile() {}
|
||||
|
||||
bool PostScriptMetaFile::SafePlayback(HDC hdc) const {
|
||||
// TODO(thestig): Fix destruction of metafiles. For some reasons
|
||||
// instances of Emf are not deleted. https://crbug.com/260806
|
||||
// It's known that the Emf going to be played just once to a printer. So just
|
||||
// release |file_| before returning.
|
||||
Emf emf;
|
||||
if (!LoadEmf(&emf)) {
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
// Ensure enumerator destruction before calling Close() below.
|
||||
Emf::Enumerator emf_enum(emf, nullptr, nullptr);
|
||||
for (const Emf::Record& record : emf_enum) {
|
||||
auto* emf_record = record.record();
|
||||
if (emf_record->iType != EMR_GDICOMMENT)
|
||||
continue;
|
||||
|
||||
const EMRGDICOMMENT* comment =
|
||||
reinterpret_cast<const EMRGDICOMMENT*>(emf_record);
|
||||
const char* data = reinterpret_cast<const char*>(comment->Data);
|
||||
const uint16_t* ptr = reinterpret_cast<const uint16_t*>(data);
|
||||
int ret = ExtEscape(hdc, PASSTHROUGH, 2 + *ptr, data, 0, nullptr);
|
||||
DCHECK_EQ(*ptr, ret);
|
||||
}
|
||||
}
|
||||
Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
PdfConverterUtilityProcessHostClient::PdfConverterUtilityProcessHostClient(
|
||||
base::WeakPtr<PdfConverterImpl> converter,
|
||||
const PdfRenderSettings& settings)
|
||||
: converter_(converter),
|
||||
settings_(settings),
|
||||
blocking_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
|
||||
{base::MayBlock(), base::TaskPriority::USER_VISIBLE,
|
||||
base::TaskShutdownBehavior::BLOCK_SHUTDOWN})) {}
|
||||
|
||||
PdfConverterUtilityProcessHostClient::~PdfConverterUtilityProcessHostClient() {}
|
||||
|
||||
void PdfConverterUtilityProcessHostClient::Start(
|
||||
const scoped_refptr<base::RefCountedMemory>& data,
|
||||
const PdfConverter::StartCallback& start_callback) {
|
||||
CHECK(false) << "Printing doesn't work yet.";
|
||||
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&PdfConverterUtilityProcessHostClient::Start, this, data,
|
||||
start_callback));
|
||||
return;
|
||||
}
|
||||
|
||||
// Store callback before any OnFailed() call to make it called on failure.
|
||||
start_callback_ = start_callback;
|
||||
|
||||
// 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_ = content::UtilityProcessHost::Create(
|
||||
this, base::ThreadTaskRunnerHandle::Get())
|
||||
->AsWeakPtr();
|
||||
*/
|
||||
utility_process_host_ = nullptr;
|
||||
utility_process_host_->SetName(GetName());
|
||||
|
||||
base::PostTaskAndReplyWithResult(
|
||||
blocking_task_runner_.get(), FROM_HERE,
|
||||
base::Bind(&CreateTempPdfFile, data, &temp_dir_),
|
||||
base::Bind(&PdfConverterUtilityProcessHostClient::OnTempPdfReady, this));
|
||||
}
|
||||
|
||||
void PdfConverterUtilityProcessHostClient::OnTempPdfReady(ScopedTempFile pdf) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
if (!utility_process_host_ || !pdf)
|
||||
return OnFailed();
|
||||
// Should reply with OnPageCount().
|
||||
SendStartMessage(
|
||||
IPC::GetPlatformFileForTransit(pdf->file().GetPlatformFile(), false));
|
||||
}
|
||||
|
||||
void PdfConverterUtilityProcessHostClient::OnPageCount(int page_count) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
if (start_callback_.is_null())
|
||||
return OnFailed();
|
||||
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&PdfConverterImpl::RunCallback, converter_,
|
||||
base::Bind(start_callback_, page_count)));
|
||||
start_callback_.Reset();
|
||||
}
|
||||
|
||||
void PdfConverterUtilityProcessHostClient::GetPage(
|
||||
int page_number,
|
||||
const PdfConverter::GetPageCallback& get_page_callback) {
|
||||
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&PdfConverterUtilityProcessHostClient::GetPage, this,
|
||||
page_number, get_page_callback));
|
||||
return;
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
base::PostTaskAndReplyWithResult(
|
||||
blocking_task_runner_.get(), FROM_HERE,
|
||||
base::Bind(&CreateTempFile, &temp_dir_),
|
||||
base::Bind(&PdfConverterUtilityProcessHostClient::OnTempFileReady, this,
|
||||
&get_page_callbacks_.back()));
|
||||
}
|
||||
|
||||
void PdfConverterUtilityProcessHostClient::OnTempFileReady(
|
||||
GetPageCallbackData* callback_data,
|
||||
ScopedTempFile temp_file) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
if (!utility_process_host_ || !temp_file)
|
||||
return OnFailed();
|
||||
IPC::PlatformFileForTransit transit = IPC::GetPlatformFileForTransit(
|
||||
temp_file->file().GetPlatformFile(), false);
|
||||
callback_data->set_file(std::move(temp_file));
|
||||
// Should reply with OnPageDone().
|
||||
SendGetPageMessage(callback_data->page_number(), transit);
|
||||
}
|
||||
|
||||
void PdfConverterUtilityProcessHostClient::OnPageDone(bool success,
|
||||
float scale_factor) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
if (get_page_callbacks_.empty())
|
||||
return OnFailed();
|
||||
GetPageCallbackData& data = get_page_callbacks_.front();
|
||||
std::unique_ptr<MetafilePlayer> file;
|
||||
|
||||
if (success) {
|
||||
ScopedTempFile temp_file = data.TakeFile();
|
||||
if (!temp_file) // Unexpected message from utility process.
|
||||
return OnFailed();
|
||||
file = GetFileFromTemp(std::move(temp_file));
|
||||
}
|
||||
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&PdfConverterImpl::RunCallback, converter_,
|
||||
base::Bind(data.callback(), data.page_number(), scale_factor,
|
||||
base::Passed(&file))));
|
||||
get_page_callbacks_.pop();
|
||||
}
|
||||
|
||||
void PdfConverterUtilityProcessHostClient::Stop() {
|
||||
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&PdfConverterUtilityProcessHostClient::Stop, this));
|
||||
return;
|
||||
}
|
||||
SendStopMessage();
|
||||
}
|
||||
|
||||
void PdfConverterUtilityProcessHostClient::OnProcessCrashed(int exit_code) {
|
||||
OnFailed();
|
||||
}
|
||||
|
||||
void PdfConverterUtilityProcessHostClient::OnProcessLaunchFailed(
|
||||
int exit_code) {
|
||||
OnFailed();
|
||||
}
|
||||
|
||||
bool PdfConverterUtilityProcessHostClient::Send(IPC::Message* msg) {
|
||||
if (utility_process_host_)
|
||||
#if 1
|
||||
CHECK(false) << "Printing is broken.";
|
||||
#else
|
||||
return utility_process_host_->Send(msg);
|
||||
#endif
|
||||
delete msg;
|
||||
return false;
|
||||
}
|
||||
|
||||
void PdfConverterUtilityProcessHostClient::OnFailed() {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
if (!start_callback_.is_null())
|
||||
OnPageCount(0);
|
||||
while (!get_page_callbacks_.empty())
|
||||
OnPageDone(false, 0.0f);
|
||||
utility_process_host_.reset();
|
||||
}
|
||||
|
||||
void PdfConverterUtilityProcessHostClient::OnPreCacheFontCharacters(
|
||||
const LOGFONT& font,
|
||||
const base::string16& str) {
|
||||
// TODO(scottmg): pdf/ppapi still require the renderer to be able to precache
|
||||
// GDI fonts (http://crbug.com/383227), even when using DirectWrite.
|
||||
// Eventually this shouldn't be added and should be moved to
|
||||
// FontCacheDispatcher too. http://crbug.com/356346.
|
||||
|
||||
// First, comments from FontCacheDispatcher::OnPreCacheFont do apply here too.
|
||||
// Except that for True Type fonts,
|
||||
// GetTextMetrics will not load the font in memory.
|
||||
// The only way windows seem to load properly, it is to create a similar
|
||||
// device (like the one in which we print), then do an ExtTextOut,
|
||||
// as we do in the printing thread, which is sandboxed.
|
||||
HDC hdc = CreateEnhMetaFile(nullptr, nullptr, nullptr, nullptr);
|
||||
HFONT font_handle = CreateFontIndirect(&font);
|
||||
DCHECK(font_handle != nullptr);
|
||||
|
||||
HGDIOBJ old_font = SelectObject(hdc, font_handle);
|
||||
DCHECK(old_font != nullptr);
|
||||
|
||||
ExtTextOut(hdc, 0, 0, ETO_GLYPH_INDEX, 0, str.c_str(), str.length(), nullptr);
|
||||
|
||||
SelectObject(hdc, old_font);
|
||||
DeleteObject(font_handle);
|
||||
|
||||
HENHMETAFILE metafile = CloseEnhMetaFile(hdc);
|
||||
|
||||
if (metafile)
|
||||
DeleteEnhMetaFile(metafile);
|
||||
}
|
||||
|
||||
bool PdfConverterUtilityProcessHostClient::OnMessageReceived(
|
||||
const IPC::Message& message) {
|
||||
bool handled = true;
|
||||
IPC_BEGIN_MESSAGE_MAP(PdfConverterUtilityProcessHostClient, message)
|
||||
IPC_MESSAGE_HANDLER(
|
||||
ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageCount, OnPageCount)
|
||||
IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToMetafiles_PageDone,
|
||||
OnPageDone)
|
||||
#if 0
|
||||
IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_PreCacheFontCharacters,
|
||||
OnPreCacheFontCharacters)
|
||||
#endif
|
||||
IPC_MESSAGE_UNHANDLED(handled = false)
|
||||
IPC_END_MESSAGE_MAP()
|
||||
return handled;
|
||||
}
|
||||
|
||||
base::string16 PdfConverterUtilityProcessHostClient::GetName() const {
|
||||
return L"ChromeUtilityProcessPDFConvertor";
|
||||
}
|
||||
|
||||
void PdfConverterUtilityProcessHostClient::SendGetPageMessage(
|
||||
int page_number,
|
||||
IPC::PlatformFileForTransit transit) {
|
||||
Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_GetPage(page_number,
|
||||
transit));
|
||||
}
|
||||
|
||||
void PdfConverterUtilityProcessHostClient::SendStartMessage(
|
||||
IPC::PlatformFileForTransit transit) {
|
||||
Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles(transit, settings_));
|
||||
}
|
||||
|
||||
void PdfConverterUtilityProcessHostClient::SendStopMessage() {
|
||||
Send(new ChromeUtilityMsg_RenderPDFPagesToMetafiles_Stop());
|
||||
}
|
||||
|
||||
// Pdf Converter Impl and subclasses
|
||||
PdfConverterImpl::PdfConverterImpl() : weak_ptr_factory_(this) {}
|
||||
|
||||
PdfConverterImpl::~PdfConverterImpl() {
|
||||
if (utility_client_.get())
|
||||
utility_client_->Stop();
|
||||
}
|
||||
|
||||
void PdfConverterImpl::Start(
|
||||
const scoped_refptr<PdfConverterUtilityProcessHostClient>& utility_client,
|
||||
const scoped_refptr<base::RefCountedMemory>& data,
|
||||
const StartCallback& start_callback) {
|
||||
DCHECK(!utility_client_);
|
||||
utility_client_ = utility_client;
|
||||
utility_client_->Start(data, start_callback);
|
||||
}
|
||||
|
||||
void PdfConverterImpl::GetPage(int page_number,
|
||||
const GetPageCallback& get_page_callback) {
|
||||
utility_client_->GetPage(page_number, get_page_callback);
|
||||
}
|
||||
|
||||
void PdfConverterImpl::RunCallback(const base::Closure& callback) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
callback.Run();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PdfConverter::~PdfConverter() {}
|
||||
|
||||
// static
|
||||
std::unique_ptr<PdfConverter> PdfConverter::StartPdfConverter(
|
||||
const scoped_refptr<base::RefCountedMemory>& data,
|
||||
const PdfRenderSettings& conversion_settings,
|
||||
const StartCallback& start_callback) {
|
||||
std::unique_ptr<PdfConverterImpl> converter =
|
||||
std::make_unique<PdfConverterImpl>();
|
||||
converter->Start(new PdfConverterUtilityProcessHostClient(
|
||||
converter->GetWeakPtr(), conversion_settings),
|
||||
data, start_callback);
|
||||
return std::move(converter);
|
||||
}
|
||||
|
||||
} // namespace printing
|
|
@ -1,43 +0,0 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_PRINTING_PDF_TO_EMF_CONVERTER_H_
|
||||
#define CHROME_BROWSER_PRINTING_PDF_TO_EMF_CONVERTER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/memory/ref_counted_memory.h"
|
||||
|
||||
namespace printing {
|
||||
|
||||
class MetafilePlayer;
|
||||
struct PdfRenderSettings;
|
||||
|
||||
class PdfConverter {
|
||||
public:
|
||||
using StartCallback = base::Callback<void(int page_count)>;
|
||||
using GetPageCallback =
|
||||
base::Callback<void(int page_number,
|
||||
float scale_factor,
|
||||
std::unique_ptr<MetafilePlayer> file)>;
|
||||
virtual ~PdfConverter();
|
||||
|
||||
// Starts conversion of PDF provided as |data|. Calls |start_callback|
|
||||
// with positive |page_count|. |page_count| is 0 if initialization failed.
|
||||
static std::unique_ptr<PdfConverter> StartPdfConverter(
|
||||
const scoped_refptr<base::RefCountedMemory>& data,
|
||||
const PdfRenderSettings& conversion_settings,
|
||||
const StartCallback& start_callback);
|
||||
|
||||
// Requests conversion of the page. |page_number| is 0-base page number in
|
||||
// PDF provided in Start() call.
|
||||
// Calls |get_page_callback| after conversion. |emf| of callback in not NULL
|
||||
// if conversion succeeded.
|
||||
virtual void GetPage(int page_number,
|
||||
const GetPageCallback& get_page_callback) = 0;
|
||||
};
|
||||
} // namespace printing
|
||||
|
||||
#endif // CHROME_BROWSER_PRINTING_PDF_TO_EMF_CONVERTER_H_
|
|
@ -1,468 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/printing/print_job.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/bind_helpers.h"
|
||||
#include "base/location.h"
|
||||
#include "base/message_loop/message_loop.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/single_thread_task_runner.h"
|
||||
#include "base/task_scheduler/post_task.h"
|
||||
#include "base/threading/thread_restrictions.h"
|
||||
#include "base/threading/thread_task_runner_handle.h"
|
||||
#include "build/build_config.h"
|
||||
#include "chrome/browser/chrome_notification_types.h"
|
||||
#include "chrome/browser/printing/print_job_worker.h"
|
||||
#include "content/public/browser/notification_service.h"
|
||||
#include "printing/printed_document.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "chrome/browser/printing/pdf_to_emf_converter.h"
|
||||
#include "printing/metafile.h"
|
||||
#include "printing/pdf_render_settings.h"
|
||||
#endif
|
||||
|
||||
using base::TimeDelta;
|
||||
|
||||
namespace printing {
|
||||
|
||||
namespace {
|
||||
|
||||
// Helper function to ensure |owner| is valid until at least |callback| returns.
|
||||
void HoldRefCallback(const scoped_refptr<PrintJobWorkerOwner>& owner,
|
||||
const base::Closure& callback) {
|
||||
callback.Run();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PrintJob::PrintJob()
|
||||
: is_job_pending_(false), is_canceling_(false), quit_factory_(this) {
|
||||
DCHECK(base::MessageLoopForUI::IsCurrent());
|
||||
}
|
||||
|
||||
PrintJob::~PrintJob() {
|
||||
// The job should be finished (or at least canceled) when it is destroyed.
|
||||
DCHECK(!is_job_pending_);
|
||||
DCHECK(!is_canceling_);
|
||||
DCHECK(!worker_ || !worker_->IsRunning());
|
||||
DCHECK(RunsTasksInCurrentSequence());
|
||||
}
|
||||
|
||||
void PrintJob::Initialize(PrintJobWorkerOwner* job,
|
||||
const base::string16& name,
|
||||
int page_count) {
|
||||
DCHECK(!worker_);
|
||||
DCHECK(!is_job_pending_);
|
||||
DCHECK(!is_canceling_);
|
||||
DCHECK(!document_.get());
|
||||
worker_ = job->DetachWorker(this);
|
||||
settings_ = job->settings();
|
||||
|
||||
PrintedDocument* new_doc =
|
||||
new PrintedDocument(settings_, name, job->cookie());
|
||||
new_doc->set_page_count(page_count);
|
||||
UpdatePrintedDocument(new_doc);
|
||||
|
||||
// Don't forget to register to our own messages.
|
||||
registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT,
|
||||
content::Source<PrintJob>(this));
|
||||
}
|
||||
|
||||
void PrintJob::Observe(int type,
|
||||
const content::NotificationSource& source,
|
||||
const content::NotificationDetails& details) {
|
||||
DCHECK(RunsTasksInCurrentSequence());
|
||||
DCHECK_EQ(chrome::NOTIFICATION_PRINT_JOB_EVENT, type);
|
||||
|
||||
OnNotifyPrintJobEvent(*content::Details<JobEventDetails>(details).ptr());
|
||||
}
|
||||
|
||||
void PrintJob::GetSettingsDone(const PrintSettings& new_settings,
|
||||
PrintingContext::Result result) {
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
std::unique_ptr<PrintJobWorker> PrintJob::DetachWorker(
|
||||
PrintJobWorkerOwner* new_owner) {
|
||||
NOTREACHED();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const PrintSettings& PrintJob::settings() const {
|
||||
return settings_;
|
||||
}
|
||||
|
||||
int PrintJob::cookie() const {
|
||||
// Always use an invalid cookie in this case.
|
||||
if (!document_.get())
|
||||
return 0;
|
||||
return document_->cookie();
|
||||
}
|
||||
|
||||
void PrintJob::StartPrinting() {
|
||||
DCHECK(RunsTasksInCurrentSequence());
|
||||
if (!worker_->IsRunning() || is_job_pending_) {
|
||||
NOTREACHED();
|
||||
return;
|
||||
}
|
||||
|
||||
// Real work is done in PrintJobWorker::StartPrinting().
|
||||
worker_->PostTask(FROM_HERE,
|
||||
base::Bind(&HoldRefCallback, WrapRefCounted(this),
|
||||
base::Bind(&PrintJobWorker::StartPrinting,
|
||||
base::Unretained(worker_.get()),
|
||||
base::RetainedRef(document_))));
|
||||
// Set the flag right now.
|
||||
is_job_pending_ = true;
|
||||
|
||||
// Tell everyone!
|
||||
scoped_refptr<JobEventDetails> details(
|
||||
new JobEventDetails(JobEventDetails::NEW_DOC, 0, document_.get()));
|
||||
content::NotificationService::current()->Notify(
|
||||
chrome::NOTIFICATION_PRINT_JOB_EVENT, content::Source<PrintJob>(this),
|
||||
content::Details<JobEventDetails>(details.get()));
|
||||
}
|
||||
|
||||
void PrintJob::Stop() {
|
||||
DCHECK(RunsTasksInCurrentSequence());
|
||||
|
||||
if (quit_factory_.HasWeakPtrs()) {
|
||||
// In case we're running a nested message loop to wait for a job to finish,
|
||||
// and we finished before the timeout, quit the nested loop right away.
|
||||
Quit();
|
||||
quit_factory_.InvalidateWeakPtrs();
|
||||
}
|
||||
|
||||
// Be sure to live long enough.
|
||||
scoped_refptr<PrintJob> handle(this);
|
||||
|
||||
if (worker_->IsRunning()) {
|
||||
ControlledWorkerShutdown();
|
||||
} else {
|
||||
// Flush the cached document.
|
||||
is_job_pending_ = false;
|
||||
UpdatePrintedDocument(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintJob::Cancel() {
|
||||
if (is_canceling_)
|
||||
return;
|
||||
is_canceling_ = true;
|
||||
|
||||
// Be sure to live long enough.
|
||||
scoped_refptr<PrintJob> handle(this);
|
||||
|
||||
DCHECK(RunsTasksInCurrentSequence());
|
||||
if (worker_ && worker_->IsRunning()) {
|
||||
// Call this right now so it renders the context invalid. Do not use
|
||||
// InvokeLater since it would take too much time.
|
||||
worker_->Cancel();
|
||||
}
|
||||
// Make sure a Cancel() is broadcast.
|
||||
scoped_refptr<JobEventDetails> details(
|
||||
new JobEventDetails(JobEventDetails::FAILED, 0, nullptr));
|
||||
content::NotificationService::current()->Notify(
|
||||
chrome::NOTIFICATION_PRINT_JOB_EVENT, content::Source<PrintJob>(this),
|
||||
content::Details<JobEventDetails>(details.get()));
|
||||
Stop();
|
||||
is_canceling_ = false;
|
||||
}
|
||||
|
||||
bool PrintJob::FlushJob(base::TimeDelta timeout) {
|
||||
// Make sure the object outlive this message loop.
|
||||
scoped_refptr<PrintJob> handle(this);
|
||||
|
||||
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
|
||||
FROM_HERE, base::Bind(&PrintJob::Quit, quit_factory_.GetWeakPtr()),
|
||||
timeout);
|
||||
|
||||
base::MessageLoop::ScopedNestableTaskAllower allow;
|
||||
base::RunLoop().Run();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PrintJob::is_job_pending() const {
|
||||
return is_job_pending_;
|
||||
}
|
||||
|
||||
PrintedDocument* PrintJob::document() const {
|
||||
return document_.get();
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
class PrintJob::PdfConversionState {
|
||||
public:
|
||||
PdfConversionState(gfx::Size page_size, gfx::Rect content_area)
|
||||
: page_count_(0),
|
||||
current_page_(0),
|
||||
pages_in_progress_(0),
|
||||
page_size_(page_size),
|
||||
content_area_(content_area) {}
|
||||
|
||||
void Start(const scoped_refptr<base::RefCountedMemory>& data,
|
||||
const PdfRenderSettings& conversion_settings,
|
||||
const PdfConverter::StartCallback& start_callback) {
|
||||
converter_ = PdfConverter::StartPdfConverter(data, conversion_settings,
|
||||
start_callback);
|
||||
}
|
||||
|
||||
void GetMorePages(const PdfConverter::GetPageCallback& get_page_callback) {
|
||||
const int kMaxNumberOfTempFilesPerDocument = 3;
|
||||
while (pages_in_progress_ < kMaxNumberOfTempFilesPerDocument &&
|
||||
current_page_ < page_count_) {
|
||||
++pages_in_progress_;
|
||||
converter_->GetPage(current_page_++, get_page_callback);
|
||||
}
|
||||
}
|
||||
|
||||
void OnPageProcessed(const PdfConverter::GetPageCallback& get_page_callback) {
|
||||
--pages_in_progress_;
|
||||
GetMorePages(get_page_callback);
|
||||
// Release converter if we don't need this any more.
|
||||
if (!pages_in_progress_ && current_page_ >= page_count_)
|
||||
converter_.reset();
|
||||
}
|
||||
|
||||
void set_page_count(int page_count) { page_count_ = page_count; }
|
||||
gfx::Size page_size() const { return page_size_; }
|
||||
gfx::Rect content_area() const { return content_area_; }
|
||||
|
||||
private:
|
||||
int page_count_;
|
||||
int current_page_;
|
||||
int pages_in_progress_;
|
||||
gfx::Size page_size_;
|
||||
gfx::Rect content_area_;
|
||||
std::unique_ptr<PdfConverter> converter_;
|
||||
};
|
||||
|
||||
void PrintJob::AppendPrintedPage(int page_number) {
|
||||
pdf_page_mapping_.push_back(page_number);
|
||||
}
|
||||
|
||||
void PrintJob::StartPdfToEmfConversion(
|
||||
const scoped_refptr<base::RefCountedMemory>& bytes,
|
||||
const gfx::Size& page_size,
|
||||
const gfx::Rect& content_area,
|
||||
bool print_text_with_gdi) {
|
||||
DCHECK(!pdf_conversion_state_);
|
||||
pdf_conversion_state_ =
|
||||
std::make_unique<PdfConversionState>(page_size, content_area);
|
||||
const int kPrinterDpi = settings().dpi();
|
||||
PdfRenderSettings settings(
|
||||
content_area, gfx::Point(0, 0), gfx::Size(kPrinterDpi, kPrinterDpi),
|
||||
/*autorotate=*/true, settings_.color() == COLOR,
|
||||
print_text_with_gdi ? PdfRenderSettings::Mode::GDI_TEXT
|
||||
: PdfRenderSettings::Mode::NORMAL);
|
||||
pdf_conversion_state_->Start(
|
||||
bytes, settings, base::Bind(&PrintJob::OnPdfConversionStarted, this));
|
||||
}
|
||||
|
||||
void PrintJob::OnPdfConversionStarted(int page_count) {
|
||||
if (page_count <= 0) {
|
||||
pdf_conversion_state_.reset();
|
||||
Cancel();
|
||||
return;
|
||||
}
|
||||
pdf_conversion_state_->set_page_count(page_count);
|
||||
pdf_conversion_state_->GetMorePages(
|
||||
base::Bind(&PrintJob::OnPdfPageConverted, this));
|
||||
}
|
||||
|
||||
void PrintJob::OnPdfPageConverted(int page_number,
|
||||
float scale_factor,
|
||||
std::unique_ptr<MetafilePlayer> metafile) {
|
||||
DCHECK(pdf_conversion_state_);
|
||||
if (!document_.get() || !metafile || page_number < 0 ||
|
||||
static_cast<size_t>(page_number) >= pdf_page_mapping_.size()) {
|
||||
pdf_conversion_state_.reset();
|
||||
Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the rendered document. It will send notifications to the listener.
|
||||
document_->SetPage(pdf_page_mapping_[page_number], std::move(metafile),
|
||||
scale_factor, pdf_conversion_state_->page_size(),
|
||||
pdf_conversion_state_->content_area());
|
||||
|
||||
pdf_conversion_state_->GetMorePages(
|
||||
base::Bind(&PrintJob::OnPdfPageConverted, this));
|
||||
}
|
||||
|
||||
void PrintJob::StartPdfToPostScriptConversion(
|
||||
const scoped_refptr<base::RefCountedMemory>& bytes,
|
||||
const gfx::Rect& content_area,
|
||||
const gfx::Point& physical_offsets,
|
||||
bool ps_level2) {
|
||||
DCHECK(!pdf_conversion_state_);
|
||||
pdf_conversion_state_ =
|
||||
std::make_unique<PdfConversionState>(gfx::Size(), gfx::Rect());
|
||||
const int kPrinterDpi = settings().dpi();
|
||||
PdfRenderSettings settings(
|
||||
content_area, physical_offsets, gfx::Size(kPrinterDpi, kPrinterDpi),
|
||||
true /* autorotate? */, settings_.color() == COLOR,
|
||||
ps_level2 ? PdfRenderSettings::Mode::POSTSCRIPT_LEVEL2
|
||||
: PdfRenderSettings::Mode::POSTSCRIPT_LEVEL3);
|
||||
pdf_conversion_state_->Start(
|
||||
bytes, settings, base::Bind(&PrintJob::OnPdfConversionStarted, this));
|
||||
}
|
||||
|
||||
#endif // defined(OS_WIN)
|
||||
|
||||
void PrintJob::UpdatePrintedDocument(PrintedDocument* new_document) {
|
||||
if (document_.get() == new_document)
|
||||
return;
|
||||
|
||||
document_ = new_document;
|
||||
|
||||
if (document_.get())
|
||||
settings_ = document_->settings();
|
||||
|
||||
if (worker_) {
|
||||
DCHECK(!is_job_pending_);
|
||||
// Sync the document with the worker.
|
||||
worker_->PostTask(FROM_HERE,
|
||||
base::Bind(&HoldRefCallback, WrapRefCounted(this),
|
||||
base::Bind(&PrintJobWorker::OnDocumentChanged,
|
||||
base::Unretained(worker_.get()),
|
||||
base::RetainedRef(document_))));
|
||||
}
|
||||
}
|
||||
|
||||
void PrintJob::OnNotifyPrintJobEvent(const JobEventDetails& event_details) {
|
||||
switch (event_details.type()) {
|
||||
case JobEventDetails::FAILED: {
|
||||
settings_.Clear();
|
||||
// No need to cancel since the worker already canceled itself.
|
||||
Stop();
|
||||
break;
|
||||
}
|
||||
case JobEventDetails::USER_INIT_DONE:
|
||||
case JobEventDetails::DEFAULT_INIT_DONE:
|
||||
case JobEventDetails::USER_INIT_CANCELED: {
|
||||
DCHECK_EQ(event_details.document(), document_.get());
|
||||
break;
|
||||
}
|
||||
case JobEventDetails::NEW_DOC:
|
||||
case JobEventDetails::NEW_PAGE:
|
||||
case JobEventDetails::JOB_DONE:
|
||||
case JobEventDetails::ALL_PAGES_REQUESTED: {
|
||||
// Don't care.
|
||||
break;
|
||||
}
|
||||
case JobEventDetails::DOC_DONE: {
|
||||
// This will call Stop() and broadcast a JOB_DONE message.
|
||||
base::ThreadTaskRunnerHandle::Get()->PostTask(
|
||||
FROM_HERE, base::Bind(&PrintJob::OnDocumentDone, this));
|
||||
break;
|
||||
}
|
||||
case JobEventDetails::PAGE_DONE:
|
||||
#if defined(OS_WIN)
|
||||
if (pdf_conversion_state_) {
|
||||
pdf_conversion_state_->OnPageProcessed(
|
||||
base::Bind(&PrintJob::OnPdfPageConverted, this));
|
||||
}
|
||||
#endif // defined(OS_WIN)
|
||||
break;
|
||||
default: {
|
||||
NOTREACHED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrintJob::OnDocumentDone() {
|
||||
// Be sure to live long enough. The instance could be destroyed by the
|
||||
// JOB_DONE broadcast.
|
||||
scoped_refptr<PrintJob> handle(this);
|
||||
|
||||
// Stop the worker thread.
|
||||
Stop();
|
||||
|
||||
scoped_refptr<JobEventDetails> details(
|
||||
new JobEventDetails(JobEventDetails::JOB_DONE, 0, document_.get()));
|
||||
content::NotificationService::current()->Notify(
|
||||
chrome::NOTIFICATION_PRINT_JOB_EVENT, content::Source<PrintJob>(this),
|
||||
content::Details<JobEventDetails>(details.get()));
|
||||
}
|
||||
|
||||
void PrintJob::ControlledWorkerShutdown() {
|
||||
DCHECK(RunsTasksInCurrentSequence());
|
||||
|
||||
// The deadlock this code works around is specific to window messaging on
|
||||
// Windows, so we aren't likely to need it on any other platforms.
|
||||
#if defined(OS_WIN)
|
||||
// We could easily get into a deadlock case if worker_->Stop() is used; the
|
||||
// printer driver created a window as a child of the browser window. By
|
||||
// canceling the job, the printer driver initiated dialog box is destroyed,
|
||||
// which sends a blocking message to its parent window. If the browser window
|
||||
// thread is not processing messages, a deadlock occurs.
|
||||
//
|
||||
// This function ensures that the dialog box will be destroyed in a timely
|
||||
// manner by the mere fact that the thread will terminate. So the potential
|
||||
// deadlock is eliminated.
|
||||
worker_->StopSoon();
|
||||
|
||||
// Delay shutdown until the worker terminates. We want this code path
|
||||
// to wait on the thread to quit before continuing.
|
||||
if (worker_->IsRunning()) {
|
||||
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
|
||||
FROM_HERE, base::Bind(&PrintJob::ControlledWorkerShutdown, this),
|
||||
base::TimeDelta::FromMilliseconds(100));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Now make sure the thread object is cleaned up. Do this on a worker
|
||||
// thread because it may block.
|
||||
base::PostTaskAndReply(
|
||||
FROM_HERE,
|
||||
base::Bind(&PrintJobWorker::Stop, base::Unretained(worker_.get())),
|
||||
base::Bind(&PrintJob::HoldUntilStopIsCalled, this));
|
||||
|
||||
is_job_pending_ = false;
|
||||
registrar_.RemoveAll();
|
||||
UpdatePrintedDocument(nullptr);
|
||||
}
|
||||
|
||||
void PrintJob::HoldUntilStopIsCalled() {}
|
||||
|
||||
void PrintJob::Quit() {
|
||||
base::RunLoop::QuitCurrentWhenIdleDeprecated();
|
||||
}
|
||||
|
||||
// Takes settings_ ownership and will be deleted in the receiving thread.
|
||||
#if defined(OS_WIN)
|
||||
JobEventDetails::JobEventDetails(Type type,
|
||||
int job_id,
|
||||
PrintedDocument* document,
|
||||
PrintedPage* page)
|
||||
: document_(document), page_(page), type_(type), job_id_(job_id) {}
|
||||
#endif
|
||||
|
||||
JobEventDetails::JobEventDetails(Type type,
|
||||
int job_id,
|
||||
PrintedDocument* document)
|
||||
: document_(document), type_(type), job_id_(job_id) {}
|
||||
|
||||
JobEventDetails::~JobEventDetails() {}
|
||||
|
||||
PrintedDocument* JobEventDetails::document() const {
|
||||
return document_.get();
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
PrintedPage* JobEventDetails::page() const {
|
||||
return page_.get();
|
||||
}
|
||||
#endif
|
||||
} // namespace printing
|
|
@ -1,246 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_PRINTING_PRINT_JOB_H_
|
||||
#define CHROME_BROWSER_PRINTING_PRINT_JOB_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "build/build_config.h"
|
||||
#include "chrome/browser/printing/print_job_worker_owner.h"
|
||||
#include "content/public/browser/notification_observer.h"
|
||||
#include "content/public/browser/notification_registrar.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "printing/printed_page_win.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
class RefCountedMemory;
|
||||
}
|
||||
|
||||
namespace printing {
|
||||
|
||||
class JobEventDetails;
|
||||
class MetafilePlayer;
|
||||
class PdfToEmfConverter;
|
||||
class PrintJobWorker;
|
||||
class PrintedDocument;
|
||||
#if defined(OS_WIN)
|
||||
class PrintedPage;
|
||||
#endif
|
||||
class PrinterQuery;
|
||||
|
||||
// Manages the print work for a specific document. Talks to the printer through
|
||||
// PrintingContext through PrintJobWorker. Hides access to PrintingContext in a
|
||||
// worker thread so the caller never blocks. PrintJob will send notifications on
|
||||
// any state change. While printing, the PrintJobManager instance keeps a
|
||||
// reference to the job to be sure it is kept alive. All the code in this class
|
||||
// runs in the UI thread.
|
||||
class PrintJob : public PrintJobWorkerOwner,
|
||||
public content::NotificationObserver {
|
||||
public:
|
||||
// Create a empty PrintJob. When initializing with this constructor,
|
||||
// post-constructor initialization must be done with Initialize().
|
||||
PrintJob();
|
||||
|
||||
// Grabs the ownership of the PrintJobWorker from another job, which is
|
||||
// usually a PrinterQuery. Set the expected page count of the print job.
|
||||
void Initialize(PrintJobWorkerOwner* job,
|
||||
const base::string16& name,
|
||||
int page_count);
|
||||
|
||||
// content::NotificationObserver implementation.
|
||||
void Observe(int type,
|
||||
const content::NotificationSource& source,
|
||||
const content::NotificationDetails& details) override;
|
||||
|
||||
// PrintJobWorkerOwner implementation.
|
||||
void GetSettingsDone(const PrintSettings& new_settings,
|
||||
PrintingContext::Result result) override;
|
||||
std::unique_ptr<PrintJobWorker> DetachWorker(
|
||||
PrintJobWorkerOwner* new_owner) override;
|
||||
const PrintSettings& settings() const override;
|
||||
int cookie() const override;
|
||||
|
||||
// Starts the actual printing. Signals the worker that it should begin to
|
||||
// spool as soon as data is available.
|
||||
void StartPrinting();
|
||||
|
||||
// Asks for the worker thread to finish its queued tasks and disconnects the
|
||||
// delegate object. The PrintJobManager will remove its reference. This may
|
||||
// have the side-effect of destroying the object if the caller doesn't have a
|
||||
// handle to the object. Use PrintJob::is_stopped() to check whether the
|
||||
// worker thread has actually stopped.
|
||||
void Stop();
|
||||
|
||||
// Cancels printing job and stops the worker thread. Takes effect immediately.
|
||||
void Cancel();
|
||||
|
||||
// Synchronously wait for the job to finish. It is mainly useful when the
|
||||
// process is about to be shut down and we're waiting for the spooler to eat
|
||||
// our data.
|
||||
bool FlushJob(base::TimeDelta timeout);
|
||||
|
||||
// Returns true if the print job is pending, i.e. between a StartPrinting()
|
||||
// and the end of the spooling.
|
||||
bool is_job_pending() const;
|
||||
|
||||
// Access the current printed document. Warning: may be NULL.
|
||||
PrintedDocument* document() const;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Let the PrintJob know the 0-based |page_number| of a given printed page.
|
||||
void AppendPrintedPage(int page_number);
|
||||
|
||||
void StartPdfToEmfConversion(
|
||||
const scoped_refptr<base::RefCountedMemory>& bytes,
|
||||
const gfx::Size& page_size,
|
||||
const gfx::Rect& content_area,
|
||||
bool print_text_with_gdi);
|
||||
|
||||
void StartPdfToPostScriptConversion(
|
||||
const scoped_refptr<base::RefCountedMemory>& bytes,
|
||||
const gfx::Rect& content_area,
|
||||
const gfx::Point& physical_offset,
|
||||
bool ps_level2);
|
||||
#endif // defined(OS_WIN)
|
||||
|
||||
protected:
|
||||
~PrintJob() override;
|
||||
|
||||
private:
|
||||
// Updates |document_| to a new instance.
|
||||
void UpdatePrintedDocument(PrintedDocument* new_document);
|
||||
|
||||
// Processes a NOTIFY_PRINT_JOB_EVENT notification.
|
||||
void OnNotifyPrintJobEvent(const JobEventDetails& event_details);
|
||||
|
||||
// Releases the worker thread by calling Stop(), then broadcasts a JOB_DONE
|
||||
// notification.
|
||||
void OnDocumentDone();
|
||||
|
||||
// Terminates the worker thread in a very controlled way, to work around any
|
||||
// eventual deadlock.
|
||||
void ControlledWorkerShutdown();
|
||||
|
||||
// Called at shutdown when running a nested message loop.
|
||||
void Quit();
|
||||
|
||||
void HoldUntilStopIsCalled();
|
||||
|
||||
#if defined(OS_WIN)
|
||||
void OnPdfConversionStarted(int page_count);
|
||||
void OnPdfPageConverted(int page_number,
|
||||
float scale_factor,
|
||||
std::unique_ptr<MetafilePlayer> emf);
|
||||
#endif // defined(OS_WIN)
|
||||
|
||||
content::NotificationRegistrar registrar_;
|
||||
|
||||
// All the UI is done in a worker thread because many Win32 print functions
|
||||
// are blocking and enters a message loop without your consent. There is one
|
||||
// worker thread per print job.
|
||||
std::unique_ptr<PrintJobWorker> worker_;
|
||||
|
||||
// Cache of the print context settings for access in the UI thread.
|
||||
PrintSettings settings_;
|
||||
|
||||
// The printed document.
|
||||
scoped_refptr<PrintedDocument> document_;
|
||||
|
||||
// Is the worker thread printing.
|
||||
bool is_job_pending_;
|
||||
|
||||
// Is Canceling? If so, try to not cause recursion if on FAILED notification,
|
||||
// the notified calls Cancel() again.
|
||||
bool is_canceling_;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
class PdfConversionState;
|
||||
std::unique_ptr<PdfConversionState> pdf_conversion_state_;
|
||||
std::vector<int> pdf_page_mapping_;
|
||||
#endif // defined(OS_WIN)
|
||||
|
||||
// Used at shutdown so that we can quit a nested message loop.
|
||||
base::WeakPtrFactory<PrintJob> quit_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PrintJob);
|
||||
};
|
||||
|
||||
// Details for a NOTIFY_PRINT_JOB_EVENT notification. The members may be NULL.
|
||||
class JobEventDetails : public base::RefCountedThreadSafe<JobEventDetails> {
|
||||
public:
|
||||
// Event type.
|
||||
enum Type {
|
||||
// Print... dialog box has been closed with OK button.
|
||||
USER_INIT_DONE,
|
||||
|
||||
// Print... dialog box has been closed with CANCEL button.
|
||||
USER_INIT_CANCELED,
|
||||
|
||||
// An automated initialization has been done, e.g. Init(false, NULL).
|
||||
DEFAULT_INIT_DONE,
|
||||
|
||||
// A new document started printing.
|
||||
NEW_DOC,
|
||||
|
||||
// A new page started printing.
|
||||
NEW_PAGE,
|
||||
|
||||
// A page is done printing.
|
||||
PAGE_DONE,
|
||||
|
||||
// A document is done printing. The worker thread is still alive. Warning:
|
||||
// not a good moment to release the handle to PrintJob.
|
||||
DOC_DONE,
|
||||
|
||||
// The worker thread is finished. A good moment to release the handle to
|
||||
// PrintJob.
|
||||
JOB_DONE,
|
||||
|
||||
// All missing pages have been requested.
|
||||
ALL_PAGES_REQUESTED,
|
||||
|
||||
// An error occured. Printing is canceled.
|
||||
FAILED,
|
||||
};
|
||||
|
||||
#if defined(OS_WIN)
|
||||
JobEventDetails(Type type,
|
||||
int job_id,
|
||||
PrintedDocument* document,
|
||||
PrintedPage* page);
|
||||
#endif
|
||||
JobEventDetails(Type type, int job_id, PrintedDocument* document);
|
||||
|
||||
// Getters.
|
||||
PrintedDocument* document() const;
|
||||
#if defined(OS_WIN)
|
||||
PrintedPage* page() const;
|
||||
#endif
|
||||
Type type() const { return type_; }
|
||||
int job_id() const { return job_id_; }
|
||||
|
||||
private:
|
||||
friend class base::RefCountedThreadSafe<JobEventDetails>;
|
||||
|
||||
~JobEventDetails();
|
||||
|
||||
scoped_refptr<PrintedDocument> document_;
|
||||
#if defined(OS_WIN)
|
||||
scoped_refptr<PrintedPage> page_;
|
||||
#endif
|
||||
const Type type_;
|
||||
int job_id_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(JobEventDetails);
|
||||
};
|
||||
|
||||
} // namespace printing
|
||||
|
||||
#endif // CHROME_BROWSER_PRINTING_PRINT_JOB_H_
|
|
@ -1,151 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/printing/print_job_manager.h"
|
||||
|
||||
#include "chrome/browser/chrome_notification_types.h"
|
||||
#include "chrome/browser/printing/print_job.h"
|
||||
#include "chrome/browser/printing/printer_query.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/notification_service.h"
|
||||
#include "printing/printed_document.h"
|
||||
|
||||
namespace printing {
|
||||
|
||||
PrintQueriesQueue::PrintQueriesQueue() {}
|
||||
|
||||
PrintQueriesQueue::~PrintQueriesQueue() {
|
||||
base::AutoLock lock(lock_);
|
||||
queued_queries_.clear();
|
||||
}
|
||||
|
||||
void PrintQueriesQueue::QueuePrinterQuery(PrinterQuery* job) {
|
||||
base::AutoLock lock(lock_);
|
||||
DCHECK(job);
|
||||
queued_queries_.push_back(WrapRefCounted(job));
|
||||
DCHECK(job->is_valid());
|
||||
}
|
||||
|
||||
scoped_refptr<PrinterQuery> PrintQueriesQueue::PopPrinterQuery(
|
||||
int document_cookie) {
|
||||
base::AutoLock lock(lock_);
|
||||
for (PrinterQueries::iterator itr = queued_queries_.begin();
|
||||
itr != queued_queries_.end(); ++itr) {
|
||||
if ((*itr)->cookie() == document_cookie && !(*itr)->is_callback_pending()) {
|
||||
scoped_refptr<PrinterQuery> current_query(*itr);
|
||||
queued_queries_.erase(itr);
|
||||
DCHECK(current_query->is_valid());
|
||||
return current_query;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
scoped_refptr<PrinterQuery> PrintQueriesQueue::CreatePrinterQuery(
|
||||
int render_process_id,
|
||||
int render_frame_id) {
|
||||
return WrapRefCounted(new PrinterQuery(render_process_id, render_frame_id));
|
||||
}
|
||||
|
||||
void PrintQueriesQueue::Shutdown() {
|
||||
PrinterQueries queries_to_stop;
|
||||
{
|
||||
base::AutoLock lock(lock_);
|
||||
queued_queries_.swap(queries_to_stop);
|
||||
}
|
||||
// Stop all pending queries, requests to generate print preview do not have
|
||||
// corresponding PrintJob, so any pending preview requests are not covered
|
||||
// by PrintJobManager::StopJobs and should be stopped explicitly.
|
||||
for (PrinterQueries::iterator itr = queries_to_stop.begin();
|
||||
itr != queries_to_stop.end(); ++itr) {
|
||||
(*itr)->PostTask(FROM_HERE, base::Bind(&PrinterQuery::StopWorker, *itr));
|
||||
}
|
||||
}
|
||||
|
||||
PrintJobManager::PrintJobManager() : is_shutdown_(false) {
|
||||
registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT,
|
||||
content::NotificationService::AllSources());
|
||||
}
|
||||
|
||||
PrintJobManager::~PrintJobManager() {}
|
||||
|
||||
scoped_refptr<PrintQueriesQueue> PrintJobManager::queue() {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
if (!queue_.get())
|
||||
queue_ = new PrintQueriesQueue();
|
||||
return queue_;
|
||||
}
|
||||
|
||||
void PrintJobManager::Shutdown() {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
DCHECK(!is_shutdown_);
|
||||
is_shutdown_ = true;
|
||||
registrar_.RemoveAll();
|
||||
StopJobs(true);
|
||||
if (queue_.get())
|
||||
queue_->Shutdown();
|
||||
queue_ = NULL;
|
||||
}
|
||||
|
||||
void PrintJobManager::StopJobs(bool wait_for_finish) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
// Copy the array since it can be modified in transit.
|
||||
PrintJobs to_stop;
|
||||
to_stop.swap(current_jobs_);
|
||||
|
||||
for (PrintJobs::const_iterator job = to_stop.begin(); job != to_stop.end();
|
||||
++job) {
|
||||
// Wait for two minutes for the print job to be spooled.
|
||||
if (wait_for_finish)
|
||||
(*job)->FlushJob(base::TimeDelta::FromMinutes(2));
|
||||
(*job)->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
void PrintJobManager::Observe(int type,
|
||||
const content::NotificationSource& source,
|
||||
const content::NotificationDetails& details) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
||||
DCHECK_EQ(chrome::NOTIFICATION_PRINT_JOB_EVENT, type);
|
||||
|
||||
OnPrintJobEvent(content::Source<PrintJob>(source).ptr(),
|
||||
*content::Details<JobEventDetails>(details).ptr());
|
||||
}
|
||||
|
||||
void PrintJobManager::OnPrintJobEvent(PrintJob* print_job,
|
||||
const JobEventDetails& event_details) {
|
||||
switch (event_details.type()) {
|
||||
case JobEventDetails::NEW_DOC: {
|
||||
DCHECK(current_jobs_.end() == current_jobs_.find(print_job));
|
||||
// Causes a AddRef().
|
||||
current_jobs_.insert(print_job);
|
||||
break;
|
||||
}
|
||||
case JobEventDetails::JOB_DONE: {
|
||||
DCHECK(current_jobs_.end() != current_jobs_.find(print_job));
|
||||
current_jobs_.erase(print_job);
|
||||
break;
|
||||
}
|
||||
case JobEventDetails::FAILED: {
|
||||
current_jobs_.erase(print_job);
|
||||
break;
|
||||
}
|
||||
case JobEventDetails::USER_INIT_DONE:
|
||||
case JobEventDetails::USER_INIT_CANCELED:
|
||||
case JobEventDetails::DEFAULT_INIT_DONE:
|
||||
case JobEventDetails::NEW_PAGE:
|
||||
case JobEventDetails::PAGE_DONE:
|
||||
case JobEventDetails::DOC_DONE:
|
||||
case JobEventDetails::ALL_PAGES_REQUESTED: {
|
||||
// Don't care.
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
NOTREACHED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace printing
|
|
@ -1,100 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_PRINTING_PRINT_JOB_MANAGER_H_
|
||||
#define CHROME_BROWSER_PRINTING_PRINT_JOB_MANAGER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "content/public/browser/notification_observer.h"
|
||||
#include "content/public/browser/notification_registrar.h"
|
||||
|
||||
namespace printing {
|
||||
|
||||
class JobEventDetails;
|
||||
class PrintJob;
|
||||
class PrinterQuery;
|
||||
|
||||
class PrintQueriesQueue : public base::RefCountedThreadSafe<PrintQueriesQueue> {
|
||||
public:
|
||||
PrintQueriesQueue();
|
||||
|
||||
// Queues a semi-initialized worker thread. Can be called from any thread.
|
||||
// Current use case is queuing from the I/O thread.
|
||||
// TODO(maruel): Have them vanish after a timeout (~5 minutes?)
|
||||
void QueuePrinterQuery(PrinterQuery* job);
|
||||
|
||||
// Pops a queued PrintJobWorkerOwner object that was previously queued or
|
||||
// create new one. Can be called from any thread.
|
||||
scoped_refptr<PrinterQuery> PopPrinterQuery(int document_cookie);
|
||||
|
||||
// Creates new query.
|
||||
scoped_refptr<PrinterQuery> CreatePrinterQuery(int render_process_id,
|
||||
int render_frame_id);
|
||||
|
||||
void Shutdown();
|
||||
|
||||
private:
|
||||
friend class base::RefCountedThreadSafe<PrintQueriesQueue>;
|
||||
typedef std::vector<scoped_refptr<PrinterQuery>> PrinterQueries;
|
||||
|
||||
virtual ~PrintQueriesQueue();
|
||||
|
||||
// Used to serialize access to queued_workers_.
|
||||
base::Lock lock_;
|
||||
|
||||
PrinterQueries queued_queries_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PrintQueriesQueue);
|
||||
};
|
||||
|
||||
class PrintJobManager : public content::NotificationObserver {
|
||||
public:
|
||||
PrintJobManager();
|
||||
~PrintJobManager() override;
|
||||
|
||||
// On browser quit, we should wait to have the print job finished.
|
||||
void Shutdown();
|
||||
|
||||
// content::NotificationObserver
|
||||
void Observe(int type,
|
||||
const content::NotificationSource& source,
|
||||
const content::NotificationDetails& details) override;
|
||||
|
||||
// Returns queries queue. Never returns NULL. Must be called on Browser UI
|
||||
// Thread. Reference could be stored and used from any thread.
|
||||
scoped_refptr<PrintQueriesQueue> queue();
|
||||
|
||||
private:
|
||||
typedef std::set<scoped_refptr<PrintJob>> PrintJobs;
|
||||
|
||||
// Processes a NOTIFY_PRINT_JOB_EVENT notification.
|
||||
void OnPrintJobEvent(PrintJob* print_job,
|
||||
const JobEventDetails& event_details);
|
||||
|
||||
// Stops all printing jobs. If wait_for_finish is true, tries to give jobs
|
||||
// a chance to complete before stopping them.
|
||||
void StopJobs(bool wait_for_finish);
|
||||
|
||||
content::NotificationRegistrar registrar_;
|
||||
|
||||
// Current print jobs that are active.
|
||||
PrintJobs current_jobs_;
|
||||
|
||||
scoped_refptr<PrintQueriesQueue> queue_;
|
||||
|
||||
bool is_shutdown_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PrintJobManager);
|
||||
};
|
||||
|
||||
} // namespace printing
|
||||
|
||||
#endif // CHROME_BROWSER_PRINTING_PRINT_JOB_MANAGER_H_
|
|
@ -1,558 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/printing/print_job_worker.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/bind_helpers.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/location.h"
|
||||
#include "base/message_loop/message_loop.h"
|
||||
#include "base/single_thread_task_runner.h"
|
||||
#include "base/threading/thread_task_runner_handle.h"
|
||||
#include "base/values.h"
|
||||
#include "build/build_config.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "chrome/browser/chrome_notification_types.h"
|
||||
#include "chrome/browser/printing/print_job.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/notification_service.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "printing/print_job_constants.h"
|
||||
#include "printing/printed_document.h"
|
||||
#include "printing/printing_utils.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/time/time.h"
|
||||
#include "printing/page_size_margins.h"
|
||||
#include "printing/print_job_constants.h"
|
||||
#include "printing/print_settings.h"
|
||||
#include "printing/units.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "printing/printed_page_win.h"
|
||||
#endif
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace printing {
|
||||
|
||||
namespace {
|
||||
|
||||
// Helper function to ensure |owner| is valid until at least |callback| returns.
|
||||
void HoldRefCallback(const scoped_refptr<PrintJobWorkerOwner>& owner,
|
||||
base::OnceClosure callback) {
|
||||
std::move(callback).Run();
|
||||
}
|
||||
|
||||
void SetCustomMarginsToJobSettings(const PageSizeMargins& page_size_margins,
|
||||
base::DictionaryValue* settings) {
|
||||
std::unique_ptr<base::DictionaryValue> custom_margins(
|
||||
new base::DictionaryValue());
|
||||
custom_margins->SetDouble(kSettingMarginTop, page_size_margins.margin_top);
|
||||
custom_margins->SetDouble(kSettingMarginBottom,
|
||||
page_size_margins.margin_bottom);
|
||||
custom_margins->SetDouble(kSettingMarginLeft, page_size_margins.margin_left);
|
||||
custom_margins->SetDouble(kSettingMarginRight,
|
||||
page_size_margins.margin_right);
|
||||
settings->Set(kSettingMarginsCustom, std::move(custom_margins));
|
||||
}
|
||||
|
||||
void PrintSettingsToJobSettings(const PrintSettings& settings,
|
||||
base::DictionaryValue* job_settings) {
|
||||
// header footer
|
||||
job_settings->SetBoolean(kSettingHeaderFooterEnabled,
|
||||
settings.display_header_footer());
|
||||
job_settings->SetString(kSettingHeaderFooterTitle, settings.title());
|
||||
job_settings->SetString(kSettingHeaderFooterURL, settings.url());
|
||||
|
||||
// bg
|
||||
job_settings->SetBoolean(kSettingShouldPrintBackgrounds,
|
||||
settings.should_print_backgrounds());
|
||||
job_settings->SetBoolean(kSettingShouldPrintSelectionOnly,
|
||||
settings.selection_only());
|
||||
|
||||
// margin
|
||||
auto margin_type = settings.margin_type();
|
||||
job_settings->SetInteger(kSettingMarginsType, settings.margin_type());
|
||||
if (margin_type == CUSTOM_MARGINS) {
|
||||
const auto& margins_in_points =
|
||||
settings.requested_custom_margins_in_points();
|
||||
|
||||
PageSizeMargins page_size_margins;
|
||||
|
||||
page_size_margins.margin_top = margins_in_points.top;
|
||||
page_size_margins.margin_bottom = margins_in_points.bottom;
|
||||
page_size_margins.margin_left = margins_in_points.left;
|
||||
page_size_margins.margin_right = margins_in_points.right;
|
||||
SetCustomMarginsToJobSettings(page_size_margins, job_settings);
|
||||
}
|
||||
job_settings->SetInteger(kSettingPreviewPageCount, 1);
|
||||
|
||||
// range
|
||||
|
||||
if (!settings.ranges().empty()) {
|
||||
auto page_range_array = std::make_unique<base::ListValue>();
|
||||
job_settings->Set(kSettingPageRange, std::move(page_range_array));
|
||||
for (size_t i = 0; i < settings.ranges().size(); ++i) {
|
||||
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
|
||||
dict->SetInteger(kSettingPageRangeFrom, settings.ranges()[i].from + 1);
|
||||
dict->SetInteger(kSettingPageRangeTo, settings.ranges()[i].to + 1);
|
||||
page_range_array->Append(std::move(dict));
|
||||
}
|
||||
}
|
||||
|
||||
job_settings->SetBoolean(kSettingCollate, settings.collate());
|
||||
job_settings->SetInteger(kSettingCopies, 1);
|
||||
job_settings->SetInteger(kSettingColor, settings.color());
|
||||
job_settings->SetInteger(kSettingDuplexMode, settings.duplex_mode());
|
||||
job_settings->SetBoolean(kSettingLandscape, settings.landscape());
|
||||
job_settings->SetString(kSettingDeviceName, settings.device_name());
|
||||
job_settings->SetInteger(kSettingScaleFactor, 100);
|
||||
job_settings->SetBoolean("rasterizePDF", false);
|
||||
|
||||
job_settings->SetInteger("dpi", settings.dpi());
|
||||
job_settings->SetInteger("dpiHorizontal", 72);
|
||||
job_settings->SetInteger("dpiVertical", 72);
|
||||
|
||||
job_settings->SetBoolean(kSettingPrintToPDF, false);
|
||||
job_settings->SetBoolean(kSettingCloudPrintDialog, false);
|
||||
job_settings->SetBoolean(kSettingPrintWithPrivet, false);
|
||||
job_settings->SetBoolean(kSettingPrintWithExtension, false);
|
||||
|
||||
job_settings->SetBoolean(kSettingShowSystemDialog, false);
|
||||
job_settings->SetInteger(kSettingPreviewPageCount, 1);
|
||||
}
|
||||
|
||||
class PrintingContextDelegate : public PrintingContext::Delegate {
|
||||
public:
|
||||
PrintingContextDelegate(int render_process_id, int render_frame_id);
|
||||
~PrintingContextDelegate() override;
|
||||
|
||||
gfx::NativeView GetParentView() override;
|
||||
std::string GetAppLocale() override;
|
||||
|
||||
// Not exposed to PrintingContext::Delegate because of dependency issues.
|
||||
content::WebContents* GetWebContents();
|
||||
|
||||
private:
|
||||
const int render_process_id_;
|
||||
const int render_frame_id_;
|
||||
};
|
||||
|
||||
PrintingContextDelegate::PrintingContextDelegate(int render_process_id,
|
||||
int render_frame_id)
|
||||
: render_process_id_(render_process_id),
|
||||
render_frame_id_(render_frame_id) {}
|
||||
|
||||
PrintingContextDelegate::~PrintingContextDelegate() {}
|
||||
|
||||
gfx::NativeView PrintingContextDelegate::GetParentView() {
|
||||
content::WebContents* wc = GetWebContents();
|
||||
return wc ? wc->GetNativeView() : nullptr;
|
||||
}
|
||||
|
||||
content::WebContents* PrintingContextDelegate::GetWebContents() {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
auto* rfh =
|
||||
content::RenderFrameHost::FromID(render_process_id_, render_frame_id_);
|
||||
return rfh ? content::WebContents::FromRenderFrameHost(rfh) : nullptr;
|
||||
}
|
||||
|
||||
std::string PrintingContextDelegate::GetAppLocale() {
|
||||
return g_browser_process->GetApplicationLocale();
|
||||
}
|
||||
|
||||
void NotificationCallback(PrintJobWorkerOwner* print_job,
|
||||
JobEventDetails::Type detail_type,
|
||||
int job_id,
|
||||
PrintedDocument* document) {
|
||||
JobEventDetails* details = new JobEventDetails(detail_type, job_id, document);
|
||||
content::NotificationService::current()->Notify(
|
||||
chrome::NOTIFICATION_PRINT_JOB_EVENT,
|
||||
// We know that is is a PrintJob object in this circumstance.
|
||||
content::Source<PrintJob>(static_cast<PrintJob*>(print_job)),
|
||||
content::Details<JobEventDetails>(details));
|
||||
}
|
||||
|
||||
void PostOnOwnerThread(const scoped_refptr<PrintJobWorkerOwner>& owner,
|
||||
PrintingContext::PrintSettingsCallback callback,
|
||||
PrintingContext::Result result) {
|
||||
owner->PostTask(FROM_HERE,
|
||||
base::BindOnce(&HoldRefCallback, owner,
|
||||
base::BindOnce(std::move(callback), result)));
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
void PageNotificationCallback(PrintJobWorkerOwner* print_job,
|
||||
JobEventDetails::Type detail_type,
|
||||
int job_id,
|
||||
PrintedDocument* document,
|
||||
PrintedPage* page) {
|
||||
JobEventDetails* details =
|
||||
new JobEventDetails(detail_type, job_id, document, page);
|
||||
content::NotificationService::current()->Notify(
|
||||
chrome::NOTIFICATION_PRINT_JOB_EVENT,
|
||||
// We know that is is a PrintJob object in this circumstance.
|
||||
content::Source<PrintJob>(static_cast<PrintJob*>(print_job)),
|
||||
content::Details<JobEventDetails>(details));
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
PrintJobWorker::PrintJobWorker(int render_process_id,
|
||||
int render_frame_id,
|
||||
PrintJobWorkerOwner* owner)
|
||||
: owner_(owner), thread_("Printing_Worker"), weak_factory_(this) {
|
||||
// The object is created in the IO thread.
|
||||
DCHECK(owner_->RunsTasksInCurrentSequence());
|
||||
|
||||
printing_context_delegate_ = std::make_unique<PrintingContextDelegate>(
|
||||
render_process_id, render_frame_id);
|
||||
printing_context_ = PrintingContext::Create(printing_context_delegate_.get());
|
||||
}
|
||||
|
||||
PrintJobWorker::~PrintJobWorker() {
|
||||
// The object is normally deleted in the UI thread, but when the user
|
||||
// cancels printing or in the case of print preview, the worker is destroyed
|
||||
// on the I/O thread.
|
||||
DCHECK(owner_->RunsTasksInCurrentSequence());
|
||||
Stop();
|
||||
}
|
||||
|
||||
void PrintJobWorker::SetNewOwner(PrintJobWorkerOwner* new_owner) {
|
||||
DCHECK(page_number_ == PageNumber::npos());
|
||||
owner_ = new_owner;
|
||||
}
|
||||
|
||||
void PrintJobWorker::GetSettings(bool ask_user_for_settings,
|
||||
int document_page_count,
|
||||
bool has_selection,
|
||||
MarginType margin_type,
|
||||
bool is_scripted,
|
||||
bool is_modifiable,
|
||||
const base::string16& device_name) {
|
||||
DCHECK(task_runner_->RunsTasksInCurrentSequence());
|
||||
DCHECK_EQ(page_number_, PageNumber::npos());
|
||||
|
||||
// Recursive task processing is needed for the dialog in case it needs to be
|
||||
// destroyed by a task.
|
||||
// TODO(thestig): This code is wrong. SetNestableTasksAllowed(true) is needed
|
||||
// on the thread where the PrintDlgEx is called, and definitely both calls
|
||||
// should happen on the same thread. See http://crbug.com/73466
|
||||
// MessageLoop::current()->SetNestableTasksAllowed(true);
|
||||
printing_context_->set_margin_type(margin_type);
|
||||
printing_context_->set_is_modifiable(is_modifiable);
|
||||
|
||||
// When we delegate to a destination, we don't ask the user for settings.
|
||||
// TODO(mad): Ask the destination for settings.
|
||||
if (ask_user_for_settings) {
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::UI, FROM_HERE,
|
||||
base::BindOnce(
|
||||
&HoldRefCallback, WrapRefCounted(owner_),
|
||||
base::BindOnce(&PrintJobWorker::GetSettingsWithUI,
|
||||
base::Unretained(this), document_page_count,
|
||||
has_selection, is_scripted)));
|
||||
} else if (!device_name.empty()) {
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::UI, FROM_HERE,
|
||||
base::BindOnce(&HoldRefCallback, WrapRefCounted(owner_),
|
||||
base::BindOnce(&PrintJobWorker::InitWithDeviceName,
|
||||
base::Unretained(this), device_name)));
|
||||
} else {
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::UI, FROM_HERE,
|
||||
base::BindOnce(&HoldRefCallback, WrapRefCounted(owner_),
|
||||
base::BindOnce(&PrintJobWorker::UseDefaultSettings,
|
||||
base::Unretained(this))));
|
||||
}
|
||||
}
|
||||
|
||||
void PrintJobWorker::SetSettings(
|
||||
std::unique_ptr<base::DictionaryValue> new_settings) {
|
||||
DCHECK(task_runner_->RunsTasksInCurrentSequence());
|
||||
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::UI, FROM_HERE,
|
||||
base::BindOnce(
|
||||
&HoldRefCallback, WrapRefCounted(owner_),
|
||||
base::BindOnce(&PrintJobWorker::UpdatePrintSettings,
|
||||
base::Unretained(this), base::Passed(&new_settings))));
|
||||
}
|
||||
|
||||
void PrintJobWorker::UpdatePrintSettings(
|
||||
std::unique_ptr<base::DictionaryValue> new_settings) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
PrintingContext::Result result =
|
||||
printing_context_->UpdatePrintSettings(*new_settings);
|
||||
GetSettingsDone(result);
|
||||
}
|
||||
|
||||
void PrintJobWorker::GetSettingsDone(PrintingContext::Result result) {
|
||||
// Most PrintingContext functions may start a message loop and process
|
||||
// message recursively, so disable recursive task processing.
|
||||
// TODO(thestig): See above comment. SetNestableTasksAllowed(false) needs to
|
||||
// be called on the same thread as the previous call. See
|
||||
// http://crbug.com/73466
|
||||
// MessageLoop::current()->SetNestableTasksAllowed(false);
|
||||
|
||||
// We can't use OnFailure() here since owner_ may not support notifications.
|
||||
|
||||
// PrintJob will create the new PrintedDocument.
|
||||
owner_->PostTask(
|
||||
FROM_HERE,
|
||||
base::Bind(&PrintJobWorkerOwner::GetSettingsDone, WrapRefCounted(owner_),
|
||||
printing_context_->settings(), result));
|
||||
}
|
||||
|
||||
void PrintJobWorker::GetSettingsWithUI(int document_page_count,
|
||||
bool has_selection,
|
||||
bool is_scripted) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
|
||||
// weak_factory_ creates pointers valid only on owner_ thread.
|
||||
printing_context_->AskUserForSettings(
|
||||
document_page_count, has_selection, is_scripted,
|
||||
base::BindOnce(&PostOnOwnerThread, WrapRefCounted(owner_),
|
||||
base::BindOnce(&PrintJobWorker::GetSettingsDone,
|
||||
weak_factory_.GetWeakPtr())));
|
||||
}
|
||||
|
||||
void PrintJobWorker::UseDefaultSettings() {
|
||||
PrintingContext::Result result = printing_context_->UseDefaultSettings();
|
||||
GetSettingsDone(result);
|
||||
}
|
||||
|
||||
void PrintJobWorker::InitWithDeviceName(const base::string16& device_name) {
|
||||
const auto& settings = printing_context_->settings();
|
||||
std::unique_ptr<base::DictionaryValue> dic(new base::DictionaryValue);
|
||||
PrintSettingsToJobSettings(settings, dic.get());
|
||||
dic->SetString(kSettingDeviceName, device_name);
|
||||
UpdatePrintSettings(std::move(dic));
|
||||
}
|
||||
|
||||
void PrintJobWorker::StartPrinting(PrintedDocument* new_document) {
|
||||
DCHECK(task_runner_->RunsTasksInCurrentSequence());
|
||||
DCHECK_EQ(page_number_, PageNumber::npos());
|
||||
DCHECK_EQ(document_.get(), new_document);
|
||||
DCHECK(document_.get());
|
||||
|
||||
if (!document_.get() || page_number_ != PageNumber::npos() ||
|
||||
document_.get() != new_document) {
|
||||
return;
|
||||
}
|
||||
|
||||
base::string16 document_name =
|
||||
printing::SimplifyDocumentTitle(document_->name());
|
||||
PrintingContext::Result result =
|
||||
printing_context_->NewDocument(document_name);
|
||||
if (result != PrintingContext::OK) {
|
||||
OnFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to print already cached data. It may already have been generated for
|
||||
// the print preview.
|
||||
OnNewPage();
|
||||
// Don't touch this anymore since the instance could be destroyed. It happens
|
||||
// if all the pages are printed a one sweep and the client doesn't have a
|
||||
// handle to us anymore. There's a timing issue involved between the worker
|
||||
// thread and the UI thread. Take no chance.
|
||||
}
|
||||
|
||||
void PrintJobWorker::OnDocumentChanged(PrintedDocument* new_document) {
|
||||
DCHECK(task_runner_->RunsTasksInCurrentSequence());
|
||||
DCHECK_EQ(page_number_, PageNumber::npos());
|
||||
|
||||
if (page_number_ != PageNumber::npos())
|
||||
return;
|
||||
|
||||
document_ = new_document;
|
||||
}
|
||||
|
||||
void PrintJobWorker::PostWaitForPage() {
|
||||
// We need to wait for the page to be available.
|
||||
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(&PrintJobWorker::OnNewPage, weak_factory_.GetWeakPtr()),
|
||||
base::TimeDelta::FromMilliseconds(500));
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
void PrintJobWorker::OnNewPage() {
|
||||
if (!document_.get()) // Spurious message.
|
||||
return;
|
||||
|
||||
// message_loop() could return NULL when the print job is cancelled.
|
||||
DCHECK(task_runner_->RunsTasksInCurrentSequence());
|
||||
|
||||
if (page_number_ == PageNumber::npos()) {
|
||||
// Find first page to print.
|
||||
int page_count = document_->page_count();
|
||||
if (!page_count) {
|
||||
// We still don't know how many pages the document contains. We can't
|
||||
// start to print the document yet since the header/footer may refer to
|
||||
// the document's page count.
|
||||
return;
|
||||
}
|
||||
// We have enough information to initialize page_number_.
|
||||
page_number_.Init(document_->settings(), page_count);
|
||||
}
|
||||
|
||||
DCHECK_NE(page_number_, PageNumber::npos());
|
||||
while (true) {
|
||||
// Is the page available?
|
||||
scoped_refptr<PrintedPage> page = document_->GetPage(page_number_.ToInt());
|
||||
if (!page.get()) {
|
||||
PostWaitForPage();
|
||||
break;
|
||||
}
|
||||
// The page is there, print it.
|
||||
SpoolPage(page.get());
|
||||
++page_number_;
|
||||
if (page_number_ == PageNumber::npos()) {
|
||||
OnDocumentDone();
|
||||
// Don't touch this anymore since the instance could be destroyed.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
void PrintJobWorker::OnNewPage() {
|
||||
if (!document_.get()) // Spurious message.
|
||||
return;
|
||||
|
||||
// message_loop() could return NULL when the print job is cancelled.
|
||||
DCHECK(task_runner_->RunsTasksInCurrentSequence());
|
||||
|
||||
const MetafilePlayer* metafile = document_->GetMetafile();
|
||||
if (!metafile) {
|
||||
PostWaitForPage();
|
||||
return;
|
||||
}
|
||||
SpoolJob();
|
||||
// Don't touch this anymore since the instance could be destroyed.
|
||||
OnDocumentDone();
|
||||
}
|
||||
#endif // defined(OS_WIN)
|
||||
|
||||
void PrintJobWorker::Cancel() {
|
||||
// This is the only function that can be called from any thread.
|
||||
printing_context_->Cancel();
|
||||
// Cannot touch any member variable since we don't know in which thread
|
||||
// context we run.
|
||||
}
|
||||
|
||||
bool PrintJobWorker::IsRunning() const {
|
||||
return thread_.IsRunning();
|
||||
}
|
||||
|
||||
bool PrintJobWorker::PostTask(const base::Location& from_here,
|
||||
const base::Closure& task) {
|
||||
if (task_runner_.get())
|
||||
return task_runner_->PostTask(from_here, task);
|
||||
return false;
|
||||
}
|
||||
|
||||
void PrintJobWorker::StopSoon() {
|
||||
thread_.StopSoon();
|
||||
}
|
||||
|
||||
void PrintJobWorker::Stop() {
|
||||
thread_.Stop();
|
||||
}
|
||||
|
||||
bool PrintJobWorker::Start() {
|
||||
bool result = thread_.Start();
|
||||
task_runner_ = thread_.task_runner();
|
||||
return result;
|
||||
}
|
||||
|
||||
void PrintJobWorker::OnDocumentDone() {
|
||||
DCHECK(task_runner_->RunsTasksInCurrentSequence());
|
||||
DCHECK_EQ(page_number_, PageNumber::npos());
|
||||
DCHECK(document_.get());
|
||||
|
||||
int job_id = printing_context_->job_id();
|
||||
if (printing_context_->DocumentDone() != PrintingContext::OK) {
|
||||
OnFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
owner_->PostTask(FROM_HERE,
|
||||
base::Bind(&NotificationCallback, base::RetainedRef(owner_),
|
||||
JobEventDetails::DOC_DONE, job_id,
|
||||
base::RetainedRef(document_)));
|
||||
|
||||
// Makes sure the variables are reinitialized.
|
||||
document_ = NULL;
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
void PrintJobWorker::SpoolPage(PrintedPage* page) {
|
||||
DCHECK(task_runner_->RunsTasksInCurrentSequence());
|
||||
DCHECK_NE(page_number_, PageNumber::npos());
|
||||
|
||||
// Preprocess.
|
||||
if (printing_context_->NewPage() != PrintingContext::OK) {
|
||||
OnFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
// Actual printing.
|
||||
document_->RenderPrintedPage(*page, printing_context_->context());
|
||||
|
||||
// Postprocess.
|
||||
if (printing_context_->PageDone() != PrintingContext::OK) {
|
||||
OnFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
// Signal everyone that the page is printed.
|
||||
owner_->PostTask(FROM_HERE,
|
||||
base::BindRepeating(
|
||||
&PageNotificationCallback, base::RetainedRef(owner_),
|
||||
JobEventDetails::PAGE_DONE, printing_context_->job_id(),
|
||||
base::RetainedRef(document_), base::RetainedRef(page)));
|
||||
}
|
||||
#else
|
||||
void PrintJobWorker::SpoolJob() {
|
||||
DCHECK(task_runner_->RunsTasksInCurrentSequence());
|
||||
if (!document_->RenderPrintedDocument(printing_context_.get()))
|
||||
OnFailure();
|
||||
}
|
||||
#endif
|
||||
|
||||
void PrintJobWorker::OnFailure() {
|
||||
DCHECK(task_runner_->RunsTasksInCurrentSequence());
|
||||
|
||||
// We may loose our last reference by broadcasting the FAILED event.
|
||||
scoped_refptr<PrintJobWorkerOwner> handle(owner_);
|
||||
|
||||
owner_->PostTask(
|
||||
FROM_HERE, base::BindRepeating(
|
||||
&NotificationCallback, base::RetainedRef(owner_),
|
||||
JobEventDetails::FAILED, 0, base::RetainedRef(document_)));
|
||||
Cancel();
|
||||
|
||||
// Makes sure the variables are reinitialized.
|
||||
document_ = NULL;
|
||||
page_number_ = PageNumber::npos();
|
||||
}
|
||||
|
||||
} // namespace printing
|
|
@ -1,174 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_H_
|
||||
#define CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/location.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/threading/thread.h"
|
||||
#include "build/build_config.h"
|
||||
#include "chrome/browser/printing/printer_query.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "printing/page_number.h"
|
||||
#include "printing/print_job_constants.h"
|
||||
#include "printing/printing_context.h"
|
||||
|
||||
namespace base {
|
||||
class DictionaryValue;
|
||||
}
|
||||
|
||||
namespace printing {
|
||||
|
||||
class PrintJob;
|
||||
class PrintJobWorkerOwner;
|
||||
class PrintedDocument;
|
||||
class PrintedPage;
|
||||
|
||||
// Worker thread code. It manages the PrintingContext, which can be blocking
|
||||
// and/or run a message loop. This is the object that generates most
|
||||
// NOTIFY_PRINT_JOB_EVENT notifications, but they are generated through a
|
||||
// NotificationTask task to be executed from the right thread, the UI thread.
|
||||
// PrintJob always outlives its worker instance.
|
||||
class PrintJobWorker {
|
||||
public:
|
||||
PrintJobWorker(int render_process_id,
|
||||
int render_frame_id,
|
||||
PrintJobWorkerOwner* owner);
|
||||
virtual ~PrintJobWorker();
|
||||
|
||||
void SetNewOwner(PrintJobWorkerOwner* new_owner);
|
||||
|
||||
// Initializes the print settings. If |ask_user_for_settings| is true, a
|
||||
// Print... dialog box will be shown to ask the user their preference.
|
||||
// |is_scripted| should be true for calls coming straight from window.print().
|
||||
// |is_modifiable| implies HTML and not other formats like PDF.
|
||||
void GetSettings(bool ask_user_for_settings,
|
||||
int document_page_count,
|
||||
bool has_selection,
|
||||
MarginType margin_type,
|
||||
bool is_scripted,
|
||||
bool is_modifiable,
|
||||
const base::string16& device_name);
|
||||
|
||||
// Set the new print settings.
|
||||
void SetSettings(std::unique_ptr<base::DictionaryValue> new_settings);
|
||||
|
||||
// Starts the printing loop. Every pages are printed as soon as the data is
|
||||
// available. Makes sure the new_document is the right one.
|
||||
void StartPrinting(PrintedDocument* new_document);
|
||||
|
||||
// Updates the printed document.
|
||||
void OnDocumentChanged(PrintedDocument* new_document);
|
||||
|
||||
// Dequeues waiting pages. Called when PrintJob receives a
|
||||
// NOTIFY_PRINTED_DOCUMENT_UPDATED notification. It's time to look again if
|
||||
// the next page can be printed.
|
||||
void OnNewPage();
|
||||
|
||||
// This is the only function that can be called in a thread.
|
||||
void Cancel();
|
||||
|
||||
// Returns true if the thread has been started, and not yet stopped.
|
||||
bool IsRunning() const;
|
||||
|
||||
// Posts the given task to be run.
|
||||
bool PostTask(const base::Location& from_here, const base::Closure& task);
|
||||
|
||||
// Signals the thread to exit in the near future.
|
||||
void StopSoon();
|
||||
|
||||
// Signals the thread to exit and returns once the thread has exited.
|
||||
void Stop();
|
||||
|
||||
// Starts the thread.
|
||||
bool Start();
|
||||
|
||||
protected:
|
||||
// Retrieves the context for testing only.
|
||||
PrintingContext* printing_context() { return printing_context_.get(); }
|
||||
|
||||
private:
|
||||
// The shared NotificationService service can only be accessed from the UI
|
||||
// thread, so this class encloses the necessary information to send the
|
||||
// notification from the right thread. Most NOTIFY_PRINT_JOB_EVENT
|
||||
// notifications are sent this way, except USER_INIT_DONE, USER_INIT_CANCELED
|
||||
// and DEFAULT_INIT_DONE. These three are sent through PrintJob::InitDone().
|
||||
class NotificationTask;
|
||||
|
||||
// Posts a task to call OnNewPage(). Used to wait for pages/document to be
|
||||
// available.
|
||||
void PostWaitForPage();
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Renders a page in the printer.
|
||||
void SpoolPage(PrintedPage* page);
|
||||
#else
|
||||
// Renders the document to the printer.
|
||||
void SpoolJob();
|
||||
#endif
|
||||
|
||||
// Closes the job since spooling is done.
|
||||
void OnDocumentDone();
|
||||
|
||||
// Discards the current document, the current page and cancels the printing
|
||||
// context.
|
||||
void OnFailure();
|
||||
|
||||
// Asks the user for print settings. Must be called on the UI thread.
|
||||
// Required on Mac and Linux. Windows can display UI from non-main threads,
|
||||
// but sticks with this for consistency.
|
||||
void GetSettingsWithUI(int document_page_count,
|
||||
bool has_selection,
|
||||
bool is_scripted);
|
||||
|
||||
// Called on the UI thread to update the print settings.
|
||||
void UpdatePrintSettings(std::unique_ptr<base::DictionaryValue> new_settings);
|
||||
|
||||
// Reports settings back to owner_.
|
||||
void GetSettingsDone(PrintingContext::Result result);
|
||||
|
||||
// Use the default settings. When using GTK+ or Mac, this can still end up
|
||||
// displaying a dialog. So this needs to happen from the UI thread on these
|
||||
// systems.
|
||||
void UseDefaultSettings();
|
||||
|
||||
// set the printer name
|
||||
void InitWithDeviceName(const base::string16& device_name);
|
||||
|
||||
// Printing context delegate.
|
||||
std::unique_ptr<PrintingContext::Delegate> printing_context_delegate_;
|
||||
|
||||
// Information about the printer setting.
|
||||
std::unique_ptr<PrintingContext> printing_context_;
|
||||
|
||||
// The printed document. Only has read-only access.
|
||||
scoped_refptr<PrintedDocument> document_;
|
||||
|
||||
// The print job owning this worker thread. It is guaranteed to outlive this
|
||||
// object.
|
||||
PrintJobWorkerOwner* owner_;
|
||||
|
||||
// Current page number to print.
|
||||
PageNumber page_number_;
|
||||
|
||||
// Thread to run worker tasks.
|
||||
base::Thread thread_;
|
||||
|
||||
// Tread-safe pointer to task runner of the |thread_|.
|
||||
scoped_refptr<base::SequencedTaskRunner> task_runner_;
|
||||
|
||||
// Used to generate a WeakPtr for callbacks.
|
||||
base::WeakPtrFactory<PrintJobWorker> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PrintJobWorker);
|
||||
};
|
||||
|
||||
} // namespace printing
|
||||
|
||||
#endif // CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_H_
|
|
@ -1,27 +0,0 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/printing/print_job_worker_owner.h"
|
||||
|
||||
#include "base/location.h"
|
||||
#include "base/single_thread_task_runner.h"
|
||||
#include "base/threading/thread_task_runner_handle.h"
|
||||
|
||||
namespace printing {
|
||||
|
||||
PrintJobWorkerOwner::PrintJobWorkerOwner()
|
||||
: task_runner_(base::ThreadTaskRunnerHandle::Get()) {}
|
||||
|
||||
PrintJobWorkerOwner::~PrintJobWorkerOwner() {}
|
||||
|
||||
bool PrintJobWorkerOwner::RunsTasksInCurrentSequence() const {
|
||||
return task_runner_->RunsTasksInCurrentSequence();
|
||||
}
|
||||
|
||||
bool PrintJobWorkerOwner::PostTask(const base::Location& from_here,
|
||||
base::OnceClosure task) {
|
||||
return task_runner_->PostTask(from_here, std::move(task));
|
||||
}
|
||||
|
||||
} // namespace printing
|
|
@ -1,65 +0,0 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_OWNER_H__
|
||||
#define CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_OWNER_H__
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/location.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "printing/printing_context.h"
|
||||
|
||||
namespace base {
|
||||
class MessageLoop;
|
||||
class SequencedTaskRunner;
|
||||
} // namespace base
|
||||
|
||||
namespace printing {
|
||||
|
||||
class PrintJobWorker;
|
||||
class PrintSettings;
|
||||
|
||||
class PrintJobWorkerOwner
|
||||
: public base::RefCountedThreadSafe<PrintJobWorkerOwner> {
|
||||
public:
|
||||
PrintJobWorkerOwner();
|
||||
|
||||
// Finishes the initialization began by PrintJobWorker::GetSettings().
|
||||
// Creates a new PrintedDocument if necessary. Solely meant to be called by
|
||||
// PrintJobWorker.
|
||||
virtual void GetSettingsDone(const PrintSettings& new_settings,
|
||||
PrintingContext::Result result) = 0;
|
||||
|
||||
// Detach the PrintJobWorker associated to this object.
|
||||
virtual std::unique_ptr<PrintJobWorker> DetachWorker(
|
||||
PrintJobWorkerOwner* new_owner) = 0;
|
||||
|
||||
// Access the current settings.
|
||||
virtual const PrintSettings& settings() const = 0;
|
||||
|
||||
// Cookie uniquely identifying the PrintedDocument and/or loaded settings.
|
||||
virtual int cookie() const = 0;
|
||||
|
||||
// Returns true if the current thread is a thread on which a task
|
||||
// may be run, and false if no task will be run on the current
|
||||
// thread.
|
||||
bool RunsTasksInCurrentSequence() const;
|
||||
|
||||
// Posts the given task to be run.
|
||||
bool PostTask(const base::Location& from_here, base::OnceClosure task);
|
||||
|
||||
protected:
|
||||
friend class base::RefCountedThreadSafe<PrintJobWorkerOwner>;
|
||||
|
||||
virtual ~PrintJobWorkerOwner();
|
||||
|
||||
// Task runner reference. Used to send notifications in the right
|
||||
// thread.
|
||||
scoped_refptr<base::SequencedTaskRunner> task_runner_;
|
||||
};
|
||||
|
||||
} // namespace printing
|
||||
|
||||
#endif // CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_OWNER_H__
|
|
@ -1,142 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/printing/print_preview_message_handler.h"
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/memory/shared_memory.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "chrome/browser/printing/print_job_manager.h"
|
||||
#include "chrome/browser/printing/printer_query.h"
|
||||
#include "chrome/common/print_messages.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "content/public/browser/render_view_host.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "printing/page_size_margins.h"
|
||||
#include "printing/pdf_metafile_skia.h"
|
||||
#include "printing/print_job_constants.h"
|
||||
|
||||
#include "atom/common/node_includes.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
using content::WebContents;
|
||||
|
||||
DEFINE_WEB_CONTENTS_USER_DATA_KEY(printing::PrintPreviewMessageHandler);
|
||||
|
||||
namespace {
|
||||
|
||||
void StopWorker(int document_cookie) {
|
||||
if (document_cookie <= 0)
|
||||
return;
|
||||
scoped_refptr<printing::PrintQueriesQueue> queue =
|
||||
g_browser_process->print_job_manager()->queue();
|
||||
scoped_refptr<printing::PrinterQuery> printer_query =
|
||||
queue->PopPrinterQuery(document_cookie);
|
||||
if (printer_query.get()) {
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&printing::PrinterQuery::StopWorker, printer_query));
|
||||
}
|
||||
}
|
||||
|
||||
char* CopyPDFDataOnIOThread(
|
||||
const PrintHostMsg_DidPreviewDocument_Params& params) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
std::unique_ptr<base::SharedMemory> shared_buf(
|
||||
new base::SharedMemory(params.metafile_data_handle, true));
|
||||
if (!shared_buf->Map(params.data_size))
|
||||
return nullptr;
|
||||
char* pdf_data = new char[params.data_size];
|
||||
memcpy(pdf_data, shared_buf->memory(), params.data_size);
|
||||
return pdf_data;
|
||||
}
|
||||
|
||||
void FreeNodeBufferData(char* data, void* hint) {
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace printing {
|
||||
|
||||
PrintPreviewMessageHandler::PrintPreviewMessageHandler(
|
||||
WebContents* web_contents)
|
||||
: content::WebContentsObserver(web_contents) {
|
||||
DCHECK(web_contents);
|
||||
}
|
||||
|
||||
PrintPreviewMessageHandler::~PrintPreviewMessageHandler() {}
|
||||
|
||||
void PrintPreviewMessageHandler::OnMetafileReadyForPrinting(
|
||||
const PrintHostMsg_DidPreviewDocument_Params& params) {
|
||||
// Always try to stop the worker.
|
||||
StopWorker(params.document_cookie);
|
||||
|
||||
if (params.expected_pages_count <= 0) {
|
||||
NOTREACHED();
|
||||
return;
|
||||
}
|
||||
|
||||
BrowserThread::PostTaskAndReplyWithResult(
|
||||
BrowserThread::IO, FROM_HERE, base::Bind(&CopyPDFDataOnIOThread, params),
|
||||
base::Bind(&PrintPreviewMessageHandler::RunPrintToPDFCallback,
|
||||
base::Unretained(this), params.preview_request_id,
|
||||
params.data_size));
|
||||
}
|
||||
|
||||
void PrintPreviewMessageHandler::OnPrintPreviewFailed(int document_cookie,
|
||||
int request_id) {
|
||||
StopWorker(document_cookie);
|
||||
RunPrintToPDFCallback(request_id, 0, nullptr);
|
||||
}
|
||||
|
||||
bool PrintPreviewMessageHandler::OnMessageReceived(
|
||||
const IPC::Message& message,
|
||||
content::RenderFrameHost* render_frame_host) {
|
||||
bool handled = true;
|
||||
IPC_BEGIN_MESSAGE_MAP(PrintPreviewMessageHandler, message)
|
||||
IPC_MESSAGE_HANDLER(PrintHostMsg_MetafileReadyForPrinting,
|
||||
OnMetafileReadyForPrinting)
|
||||
IPC_MESSAGE_HANDLER(PrintHostMsg_PrintPreviewFailed, OnPrintPreviewFailed)
|
||||
IPC_MESSAGE_UNHANDLED(handled = false)
|
||||
IPC_END_MESSAGE_MAP()
|
||||
return handled;
|
||||
}
|
||||
|
||||
void PrintPreviewMessageHandler::PrintToPDF(
|
||||
const base::DictionaryValue& options,
|
||||
const atom::api::WebContents::PrintToPDFCallback& callback) {
|
||||
int request_id;
|
||||
options.GetInteger(printing::kPreviewRequestID, &request_id);
|
||||
print_to_pdf_callback_map_[request_id] = callback;
|
||||
|
||||
content::RenderFrameHost* rfh = web_contents()->GetMainFrame();
|
||||
rfh->Send(new PrintMsg_PrintPreview(rfh->GetRoutingID(), options));
|
||||
}
|
||||
|
||||
void PrintPreviewMessageHandler::RunPrintToPDFCallback(int request_id,
|
||||
uint32_t data_size,
|
||||
char* data) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
|
||||
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||
v8::Locker locker(isolate);
|
||||
v8::HandleScope handle_scope(isolate);
|
||||
if (data) {
|
||||
v8::Local<v8::Value> buffer =
|
||||
node::Buffer::New(isolate, data, static_cast<size_t>(data_size),
|
||||
&FreeNodeBufferData, nullptr)
|
||||
.ToLocalChecked();
|
||||
print_to_pdf_callback_map_[request_id].Run(v8::Null(isolate), buffer);
|
||||
} else {
|
||||
v8::Local<v8::String> error_message =
|
||||
v8::String::NewFromUtf8(isolate, "Failed to generate PDF");
|
||||
print_to_pdf_callback_map_[request_id].Run(
|
||||
v8::Exception::Error(error_message), v8::Null(isolate));
|
||||
}
|
||||
print_to_pdf_callback_map_.erase(request_id);
|
||||
}
|
||||
|
||||
} // namespace printing
|
|
@ -1,60 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_PRINTING_PRINT_PREVIEW_MESSAGE_HANDLER_H_
|
||||
#define CHROME_BROWSER_PRINTING_PRINT_PREVIEW_MESSAGE_HANDLER_H_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "atom/browser/api/atom_api_web_contents.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
#include "content/public/browser/web_contents_user_data.h"
|
||||
|
||||
struct PrintHostMsg_DidPreviewDocument_Params;
|
||||
|
||||
namespace content {
|
||||
class WebContents;
|
||||
}
|
||||
|
||||
namespace printing {
|
||||
|
||||
struct PageSizeMargins;
|
||||
|
||||
// Manages the print preview handling for a WebContents.
|
||||
class PrintPreviewMessageHandler
|
||||
: public content::WebContentsObserver,
|
||||
public content::WebContentsUserData<PrintPreviewMessageHandler> {
|
||||
public:
|
||||
~PrintPreviewMessageHandler() override;
|
||||
|
||||
// content::WebContentsObserver implementation.
|
||||
bool OnMessageReceived(const IPC::Message& message,
|
||||
content::RenderFrameHost* render_frame_host) override;
|
||||
|
||||
void PrintToPDF(const base::DictionaryValue& options,
|
||||
const atom::api::WebContents::PrintToPDFCallback& callback);
|
||||
|
||||
private:
|
||||
typedef std::map<int, atom::api::WebContents::PrintToPDFCallback>
|
||||
PrintToPDFCallbackMap;
|
||||
|
||||
explicit PrintPreviewMessageHandler(content::WebContents* web_contents);
|
||||
friend class content::WebContentsUserData<PrintPreviewMessageHandler>;
|
||||
|
||||
// Message handlers.
|
||||
void OnMetafileReadyForPrinting(
|
||||
const PrintHostMsg_DidPreviewDocument_Params& params);
|
||||
void OnPrintPreviewFailed(int document_cookie, int request_id);
|
||||
|
||||
void RunPrintToPDFCallback(int request_id, uint32_t data_size, char* data);
|
||||
|
||||
PrintToPDFCallbackMap print_to_pdf_callback_map_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PrintPreviewMessageHandler);
|
||||
};
|
||||
|
||||
} // namespace printing
|
||||
|
||||
#endif // CHROME_BROWSER_PRINTING_PRINT_PREVIEW_MESSAGE_HANDLER_H_
|
|
@ -1,508 +0,0 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/printing/print_view_manager_base.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/memory/ref_counted_memory.h"
|
||||
#include "base/message_loop/message_loop.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/timer/timer.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "chrome/browser/chrome_notification_types.h"
|
||||
#include "chrome/browser/printing/print_job.h"
|
||||
#include "chrome/browser/printing/print_job_manager.h"
|
||||
#include "chrome/browser/printing/printer_query.h"
|
||||
#include "chrome/browser/profiles/profile.h"
|
||||
#include "chrome/browser/ui/simple_message_box.h"
|
||||
#include "chrome/common/pref_names.h"
|
||||
#include "chrome/common/print_messages.h"
|
||||
#include "components/prefs/pref_service.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/notification_details.h"
|
||||
#include "content/public/browser/notification_service.h"
|
||||
#include "content/public/browser/notification_source.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "content/public/browser/render_view_host.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "printing/pdf_metafile_skia.h"
|
||||
#include "printing/printed_document.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
|
||||
#if defined(ENABLE_FULL_PRINTING)
|
||||
#include "chrome/browser/printing/print_error_dialog.h"
|
||||
#endif
|
||||
|
||||
using base::TimeDelta;
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace printing {
|
||||
|
||||
namespace {} // namespace
|
||||
|
||||
PrintViewManagerBase::PrintViewManagerBase(content::WebContents* web_contents)
|
||||
: content::WebContentsObserver(web_contents),
|
||||
number_pages_(0),
|
||||
printing_succeeded_(false),
|
||||
inside_inner_message_loop_(false),
|
||||
cookie_(0),
|
||||
queue_(g_browser_process->print_job_manager()->queue()) {
|
||||
DCHECK(queue_.get());
|
||||
expecting_first_page_ = true;
|
||||
printing_enabled_ = true;
|
||||
}
|
||||
|
||||
PrintViewManagerBase::~PrintViewManagerBase() {
|
||||
ReleasePrinterQuery();
|
||||
DisconnectFromCurrentPrintJob();
|
||||
}
|
||||
|
||||
#if !defined(DISABLE_BASIC_PRINTING)
|
||||
bool PrintViewManagerBase::PrintNow(content::RenderFrameHost* rfh,
|
||||
bool silent,
|
||||
bool print_background,
|
||||
const base::string16& device_name) {
|
||||
int32_t id = rfh->GetRoutingID();
|
||||
return PrintNowInternal(rfh, std::make_unique<PrintMsg_PrintPages>(
|
||||
id, silent, print_background, device_name));
|
||||
}
|
||||
#endif // !DISABLE_BASIC_PRINTING
|
||||
|
||||
void PrintViewManagerBase::NavigationStopped() {
|
||||
// Cancel the current job, wait for the worker to finish.
|
||||
TerminatePrintJob(true);
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::RenderProcessGone(base::TerminationStatus status) {
|
||||
ReleasePrinterQuery();
|
||||
|
||||
if (!print_job_.get())
|
||||
return;
|
||||
|
||||
scoped_refptr<PrintedDocument> document(print_job_->document());
|
||||
if (document.get()) {
|
||||
// If IsComplete() returns false, the document isn't completely rendered.
|
||||
// Since our renderer is gone, there's nothing to do, cancel it. Otherwise,
|
||||
// the print job may finish without problem.
|
||||
TerminatePrintJob(!document->IsComplete());
|
||||
}
|
||||
}
|
||||
|
||||
base::string16 PrintViewManagerBase::RenderSourceName() {
|
||||
base::string16 name(web_contents()->GetTitle());
|
||||
return name;
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::OnDidGetPrintedPagesCount(int cookie,
|
||||
int number_pages) {
|
||||
DCHECK_GT(cookie, 0);
|
||||
DCHECK_GT(number_pages, 0);
|
||||
number_pages_ = number_pages;
|
||||
OpportunisticallyCreatePrintJob(cookie);
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::OnDidGetDocumentCookie(int cookie) {
|
||||
cookie_ = cookie;
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::OnDidPrintPage(
|
||||
const PrintHostMsg_DidPrintPage_Params& params) {
|
||||
// TODO(rbpotter): Remove this check once there are no more spurious
|
||||
// DidPrintPage messages.
|
||||
#if !defined(OS_WIN)
|
||||
if (!expecting_first_page_)
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (!OpportunisticallyCreatePrintJob(params.document_cookie))
|
||||
return;
|
||||
|
||||
PrintedDocument* document = print_job_->document();
|
||||
if (!document || params.document_cookie != document->cookie()) {
|
||||
// Out of sync. It may happen since we are completely asynchronous. Old
|
||||
// spurious messages can be received if one of the processes is overloaded.
|
||||
return;
|
||||
}
|
||||
|
||||
const bool metafile_must_be_valid = expecting_first_page_;
|
||||
expecting_first_page_ = false;
|
||||
|
||||
base::SharedMemory shared_buf(params.metafile_data_handle, true);
|
||||
if (metafile_must_be_valid) {
|
||||
if (!shared_buf.Map(params.data_size)) {
|
||||
NOTREACHED() << "couldn't map";
|
||||
web_contents()->Stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto metafile = std::make_unique<PdfMetafileSkia>();
|
||||
if (metafile_must_be_valid) {
|
||||
if (!metafile->InitFromData(shared_buf.memory(), params.data_size)) {
|
||||
NOTREACHED() << "Invalid metafile header";
|
||||
web_contents()->Stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(OS_WIN)
|
||||
// Update the rendered document. It will send notifications to the listener.
|
||||
document->SetDocument(std::move(metafile), params.page_size,
|
||||
params.content_area);
|
||||
|
||||
ShouldQuitFromInnerMessageLoop();
|
||||
#else
|
||||
print_job_->AppendPrintedPage(params.page_number);
|
||||
if (metafile_must_be_valid) {
|
||||
bool print_text_with_gdi = document->settings().print_text_with_gdi() &&
|
||||
!document->settings().printer_is_xps();
|
||||
|
||||
scoped_refptr<base::RefCountedBytes> bytes = new base::RefCountedBytes(
|
||||
reinterpret_cast<const unsigned char*>(shared_buf.memory()),
|
||||
params.data_size);
|
||||
|
||||
document->DebugDumpData(bytes.get(), FILE_PATH_LITERAL(".pdf"));
|
||||
print_job_->StartPdfToEmfConversion(
|
||||
bytes, params.page_size, params.content_area, print_text_with_gdi);
|
||||
}
|
||||
#endif // !OS_WIN
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::OnPrintingFailed(int cookie) {
|
||||
if (cookie != cookie_) {
|
||||
NOTREACHED();
|
||||
return;
|
||||
}
|
||||
|
||||
ReleasePrinterQuery();
|
||||
|
||||
content::NotificationService::current()->Notify(
|
||||
chrome::NOTIFICATION_PRINT_JOB_RELEASED,
|
||||
content::Source<content::WebContents>(web_contents()),
|
||||
content::NotificationService::NoDetails());
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::OnShowInvalidPrinterSettingsError() {
|
||||
LOG(ERROR) << "Invalid printer settings";
|
||||
}
|
||||
|
||||
bool PrintViewManagerBase::OnMessageReceived(
|
||||
const IPC::Message& message,
|
||||
content::RenderFrameHost* render_frame_host) {
|
||||
bool handled = true;
|
||||
IPC_BEGIN_MESSAGE_MAP(PrintViewManagerBase, message)
|
||||
IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetPrintedPagesCount,
|
||||
OnDidGetPrintedPagesCount)
|
||||
IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetDocumentCookie,
|
||||
OnDidGetDocumentCookie)
|
||||
IPC_MESSAGE_HANDLER(PrintHostMsg_DidPrintPage, OnDidPrintPage)
|
||||
IPC_MESSAGE_HANDLER(PrintHostMsg_PrintingFailed, OnPrintingFailed)
|
||||
IPC_MESSAGE_HANDLER(PrintHostMsg_ShowInvalidPrinterSettingsError,
|
||||
OnShowInvalidPrinterSettingsError);
|
||||
IPC_MESSAGE_UNHANDLED(handled = false)
|
||||
IPC_END_MESSAGE_MAP()
|
||||
return handled;
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::Observe(
|
||||
int type,
|
||||
const content::NotificationSource& source,
|
||||
const content::NotificationDetails& details) {
|
||||
switch (type) {
|
||||
case chrome::NOTIFICATION_PRINT_JOB_EVENT: {
|
||||
OnNotifyPrintJobEvent(*content::Details<JobEventDetails>(details).ptr());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
NOTREACHED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::OnNotifyPrintJobEvent(
|
||||
const JobEventDetails& event_details) {
|
||||
switch (event_details.type()) {
|
||||
case JobEventDetails::FAILED: {
|
||||
TerminatePrintJob(true);
|
||||
|
||||
content::NotificationService::current()->Notify(
|
||||
chrome::NOTIFICATION_PRINT_JOB_RELEASED,
|
||||
content::Source<content::WebContents>(web_contents()),
|
||||
content::NotificationService::NoDetails());
|
||||
break;
|
||||
}
|
||||
case JobEventDetails::USER_INIT_DONE:
|
||||
case JobEventDetails::DEFAULT_INIT_DONE:
|
||||
case JobEventDetails::USER_INIT_CANCELED: {
|
||||
NOTREACHED();
|
||||
break;
|
||||
}
|
||||
case JobEventDetails::ALL_PAGES_REQUESTED: {
|
||||
ShouldQuitFromInnerMessageLoop();
|
||||
break;
|
||||
}
|
||||
case JobEventDetails::NEW_DOC:
|
||||
case JobEventDetails::NEW_PAGE:
|
||||
case JobEventDetails::PAGE_DONE:
|
||||
case JobEventDetails::DOC_DONE: {
|
||||
// Don't care about the actual printing process.
|
||||
break;
|
||||
}
|
||||
case JobEventDetails::JOB_DONE: {
|
||||
// Printing is done, we don't need it anymore.
|
||||
// print_job_->is_job_pending() may still be true, depending on the order
|
||||
// of object registration.
|
||||
printing_succeeded_ = true;
|
||||
ReleasePrintJob();
|
||||
|
||||
content::NotificationService::current()->Notify(
|
||||
chrome::NOTIFICATION_PRINT_JOB_RELEASED,
|
||||
content::Source<content::WebContents>(web_contents()),
|
||||
content::NotificationService::NoDetails());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
NOTREACHED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PrintViewManagerBase::RenderAllMissingPagesNow() {
|
||||
if (!print_job_.get() || !print_job_->is_job_pending())
|
||||
return false;
|
||||
|
||||
// We can't print if there is no renderer.
|
||||
if (!web_contents() || !web_contents()->GetRenderViewHost() ||
|
||||
!web_contents()->GetRenderViewHost()->IsRenderViewLive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is the document already complete?
|
||||
if (print_job_->document() && print_job_->document()->IsComplete()) {
|
||||
printing_succeeded_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// WebContents is either dying or a second consecutive request to print
|
||||
// happened before the first had time to finish. We need to render all the
|
||||
// pages in an hurry if a print_job_ is still pending. No need to wait for it
|
||||
// to actually spool the pages, only to have the renderer generate them. Run
|
||||
// a message loop until we get our signal that the print job is satisfied.
|
||||
// PrintJob will send a ALL_PAGES_REQUESTED after having received all the
|
||||
// pages it needs. MessageLoop::current()->Quit() will be called as soon as
|
||||
// print_job_->document()->IsComplete() is true on either ALL_PAGES_REQUESTED
|
||||
// or in DidPrintPage(). The check is done in
|
||||
// ShouldQuitFromInnerMessageLoop().
|
||||
// BLOCKS until all the pages are received. (Need to enable recursive task)
|
||||
if (!RunInnerMessageLoop()) {
|
||||
// This function is always called from DisconnectFromCurrentPrintJob() so we
|
||||
// know that the job will be stopped/canceled in any case.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::ShouldQuitFromInnerMessageLoop() {
|
||||
// Look at the reason.
|
||||
DCHECK(print_job_->document());
|
||||
if (print_job_->document() && print_job_->document()->IsComplete() &&
|
||||
inside_inner_message_loop_) {
|
||||
// We are in a message loop created by RenderAllMissingPagesNow. Quit from
|
||||
// it.
|
||||
base::RunLoop::QuitCurrentWhenIdleDeprecated();
|
||||
inside_inner_message_loop_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool PrintViewManagerBase::CreateNewPrintJob(PrintJobWorkerOwner* job) {
|
||||
DCHECK(!inside_inner_message_loop_);
|
||||
|
||||
// Disconnect the current print_job_.
|
||||
DisconnectFromCurrentPrintJob();
|
||||
|
||||
// We can't print if there is no renderer.
|
||||
if (!web_contents()->GetRenderViewHost() ||
|
||||
!web_contents()->GetRenderViewHost()->IsRenderViewLive()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ask the renderer to generate the print preview, create the print preview
|
||||
// view and switch to it, initialize the printer and show the print dialog.
|
||||
DCHECK(!print_job_.get());
|
||||
DCHECK(job);
|
||||
if (!job)
|
||||
return false;
|
||||
|
||||
print_job_ = new PrintJob();
|
||||
print_job_->Initialize(job, RenderSourceName(), number_pages_);
|
||||
registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT,
|
||||
content::Source<PrintJob>(print_job_.get()));
|
||||
printing_succeeded_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::DisconnectFromCurrentPrintJob() {
|
||||
// Make sure all the necessary rendered page are done. Don't bother with the
|
||||
// return value.
|
||||
bool result = RenderAllMissingPagesNow();
|
||||
|
||||
// Verify that assertion.
|
||||
if (print_job_.get() && print_job_->document() &&
|
||||
!print_job_->document()->IsComplete()) {
|
||||
DCHECK(!result);
|
||||
// That failed.
|
||||
TerminatePrintJob(true);
|
||||
} else {
|
||||
// DO NOT wait for the job to finish.
|
||||
ReleasePrintJob();
|
||||
}
|
||||
expecting_first_page_ = true;
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::PrintingDone(bool success) {
|
||||
auto* host = web_contents()->GetRenderViewHost();
|
||||
if (print_job_.get()) {
|
||||
if (host)
|
||||
host->Send(new PrintMsg_PrintingDone(host->GetRoutingID(), success));
|
||||
}
|
||||
if (!callback.is_null()) {
|
||||
callback.Run(success && print_job_);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::TerminatePrintJob(bool cancel) {
|
||||
if (!print_job_.get())
|
||||
return;
|
||||
|
||||
if (cancel) {
|
||||
// We don't need the metafile data anymore because the printing is canceled.
|
||||
print_job_->Cancel();
|
||||
inside_inner_message_loop_ = false;
|
||||
} else {
|
||||
DCHECK(!inside_inner_message_loop_);
|
||||
DCHECK(!print_job_->document() || print_job_->document()->IsComplete());
|
||||
|
||||
// WebContents is either dying or navigating elsewhere. We need to render
|
||||
// all the pages in an hurry if a print job is still pending. This does the
|
||||
// trick since it runs a blocking message loop:
|
||||
print_job_->Stop();
|
||||
}
|
||||
ReleasePrintJob();
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::ReleasePrintJob() {
|
||||
if (!print_job_.get())
|
||||
return;
|
||||
|
||||
PrintingDone(printing_succeeded_);
|
||||
|
||||
registrar_.Remove(this, chrome::NOTIFICATION_PRINT_JOB_EVENT,
|
||||
content::Source<PrintJob>(print_job_.get()));
|
||||
// Don't close the worker thread.
|
||||
print_job_ = NULL;
|
||||
}
|
||||
|
||||
bool PrintViewManagerBase::RunInnerMessageLoop() {
|
||||
// This value may actually be too low:
|
||||
//
|
||||
// - If we're looping because of printer settings initialization, the premise
|
||||
// here is that some poor users have their print server away on a VPN over a
|
||||
// slow connection. In this situation, the simple fact of opening the printer
|
||||
// can be dead slow. On the other side, we don't want to die infinitely for a
|
||||
// real network error. Give the printer 60 seconds to comply.
|
||||
//
|
||||
// - If we're looping because of renderer page generation, the renderer could
|
||||
// be CPU bound, the page overly complex/large or the system just
|
||||
// memory-bound.
|
||||
static const int kPrinterSettingsTimeout = 60000;
|
||||
base::OneShotTimer quit_timer;
|
||||
base::RunLoop run_loop;
|
||||
quit_timer.Start(FROM_HERE,
|
||||
TimeDelta::FromMilliseconds(kPrinterSettingsTimeout),
|
||||
run_loop.QuitWhenIdleClosure());
|
||||
|
||||
inside_inner_message_loop_ = true;
|
||||
|
||||
// Need to enable recursive task.
|
||||
{
|
||||
base::MessageLoop::ScopedNestableTaskAllower allow;
|
||||
base::RunLoop().Run();
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
if (inside_inner_message_loop_) {
|
||||
// Ok we timed out. That's sad.
|
||||
inside_inner_message_loop_ = false;
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool PrintViewManagerBase::OpportunisticallyCreatePrintJob(int cookie) {
|
||||
if (print_job_.get())
|
||||
return true;
|
||||
|
||||
if (!cookie) {
|
||||
// Out of sync. It may happens since we are completely asynchronous. Old
|
||||
// spurious message can happen if one of the processes is overloaded.
|
||||
return false;
|
||||
}
|
||||
|
||||
// The job was initiated by a script. Time to get the corresponding worker
|
||||
// thread.
|
||||
scoped_refptr<PrinterQuery> queued_query = queue_->PopPrinterQuery(cookie);
|
||||
if (!queued_query.get()) {
|
||||
NOTREACHED();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CreateNewPrintJob(queued_query.get())) {
|
||||
// Don't kill anything.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Settings are already loaded. Go ahead. This will set
|
||||
// print_job_->is_job_pending() to true.
|
||||
print_job_->StartPrinting();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PrintViewManagerBase::PrintNowInternal(
|
||||
content::RenderFrameHost* rfh,
|
||||
std::unique_ptr<IPC::Message> message) {
|
||||
// Don't print / print preview interstitials or crashed tabs.
|
||||
if (web_contents()->ShowingInterstitialPage() || web_contents()->IsCrashed())
|
||||
return false;
|
||||
return rfh->Send(message.release());
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::ReleasePrinterQuery() {
|
||||
if (!cookie_)
|
||||
return;
|
||||
|
||||
int cookie = cookie_;
|
||||
cookie_ = 0;
|
||||
|
||||
printing::PrintJobManager* print_job_manager =
|
||||
g_browser_process->print_job_manager();
|
||||
// May be NULL in tests.
|
||||
if (!print_job_manager)
|
||||
return;
|
||||
|
||||
scoped_refptr<printing::PrinterQuery> printer_query;
|
||||
printer_query = queue_->PopPrinterQuery(cookie);
|
||||
if (!printer_query.get())
|
||||
return;
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&PrinterQuery::StopWorker, printer_query.get()));
|
||||
}
|
||||
|
||||
} // namespace printing
|
|
@ -1,171 +0,0 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASE_H_
|
||||
#define CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASE_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/strings/string16.h"
|
||||
#include "components/prefs/pref_member.h"
|
||||
#include "content/public/browser/notification_observer.h"
|
||||
#include "content/public/browser/notification_registrar.h"
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
#include "content/public/browser/web_contents_user_data.h"
|
||||
|
||||
struct PrintHostMsg_DidPrintPage_Params;
|
||||
|
||||
namespace content {
|
||||
class RenderViewHost;
|
||||
}
|
||||
|
||||
namespace printing {
|
||||
|
||||
class JobEventDetails;
|
||||
class MetafilePlayer;
|
||||
class PrintJob;
|
||||
class PrintJobWorkerOwner;
|
||||
class PrintQueriesQueue;
|
||||
|
||||
// Base class for managing the print commands for a WebContents.
|
||||
class PrintViewManagerBase : public content::NotificationObserver,
|
||||
public content::WebContentsObserver {
|
||||
public:
|
||||
~PrintViewManagerBase() override;
|
||||
|
||||
#if !defined(DISABLE_BASIC_PRINTING)
|
||||
// Prints the current document immediately. Since the rendering is
|
||||
// asynchronous, the actual printing will not be completed on the return of
|
||||
// this function. Returns false if printing is impossible at the moment.
|
||||
virtual bool PrintNow(content::RenderFrameHost* rfh,
|
||||
bool silent,
|
||||
bool print_background,
|
||||
const base::string16& device_name);
|
||||
#endif // !DISABLE_BASIC_PRINTING
|
||||
|
||||
// PrintedPagesSource implementation.
|
||||
base::string16 RenderSourceName();
|
||||
|
||||
void SetCallback(const base::Callback<void(bool)>& cb) { callback = cb; };
|
||||
|
||||
protected:
|
||||
explicit PrintViewManagerBase(content::WebContents* web_contents);
|
||||
|
||||
// Helper method for Print*Now().
|
||||
bool PrintNowInternal(content::RenderFrameHost* rfh,
|
||||
std::unique_ptr<IPC::Message> message);
|
||||
|
||||
// Terminates or cancels the print job if one was pending.
|
||||
void RenderProcessGone(base::TerminationStatus status) override;
|
||||
|
||||
// content::WebContentsObserver implementation.
|
||||
bool OnMessageReceived(const IPC::Message& message,
|
||||
content::RenderFrameHost* render_frame_host) override;
|
||||
|
||||
// IPC Message handlers.
|
||||
virtual void OnPrintingFailed(int cookie);
|
||||
|
||||
private:
|
||||
// content::NotificationObserver implementation.
|
||||
void Observe(int type,
|
||||
const content::NotificationSource& source,
|
||||
const content::NotificationDetails& details) override;
|
||||
|
||||
// Cancels the print job.
|
||||
void NavigationStopped() override;
|
||||
|
||||
// IPC Message handlers.
|
||||
void OnDidGetPrintedPagesCount(int cookie, int number_pages);
|
||||
void OnDidGetDocumentCookie(int cookie);
|
||||
void OnDidPrintPage(const PrintHostMsg_DidPrintPage_Params& params);
|
||||
void OnShowInvalidPrinterSettingsError();
|
||||
|
||||
// Processes a NOTIFY_PRINT_JOB_EVENT notification.
|
||||
void OnNotifyPrintJobEvent(const JobEventDetails& event_details);
|
||||
|
||||
// Requests the RenderView to render all the missing pages for the print job.
|
||||
// No-op if no print job is pending. Returns true if at least one page has
|
||||
// been requested to the renderer.
|
||||
bool RenderAllMissingPagesNow();
|
||||
|
||||
// Quits the current message loop if these conditions hold true: a document is
|
||||
// loaded and is complete and waiting_for_pages_to_be_rendered_ is true. This
|
||||
// function is called in DidPrintPage() or on ALL_PAGES_REQUESTED
|
||||
// notification. The inner message loop is created was created by
|
||||
// RenderAllMissingPagesNow().
|
||||
void ShouldQuitFromInnerMessageLoop();
|
||||
|
||||
// Creates a new empty print job. It has no settings loaded. If there is
|
||||
// currently a print job, safely disconnect from it. Returns false if it is
|
||||
// impossible to safely disconnect from the current print job or it is
|
||||
// impossible to create a new print job.
|
||||
bool CreateNewPrintJob(PrintJobWorkerOwner* job);
|
||||
|
||||
// Makes sure the current print_job_ has all its data before continuing, and
|
||||
// disconnect from it.
|
||||
void DisconnectFromCurrentPrintJob();
|
||||
|
||||
// Notify that the printing is done.
|
||||
void PrintingDone(bool success);
|
||||
|
||||
// Terminates the print job. No-op if no print job has been created. If
|
||||
// |cancel| is true, cancel it instead of waiting for the job to finish. Will
|
||||
// call ReleasePrintJob().
|
||||
void TerminatePrintJob(bool cancel);
|
||||
|
||||
// Releases print_job_. Correctly deregisters from notifications. No-op if
|
||||
// no print job has been created.
|
||||
void ReleasePrintJob();
|
||||
|
||||
// Runs an inner message loop. It will set inside_inner_message_loop_ to true
|
||||
// while the blocking inner message loop is running. This is useful in cases
|
||||
// where the RenderView is about to be destroyed while a printing job isn't
|
||||
// finished.
|
||||
bool RunInnerMessageLoop();
|
||||
|
||||
// In the case of Scripted Printing, where the renderer is controlling the
|
||||
// control flow, print_job_ is initialized whenever possible. No-op is
|
||||
// print_job_ is initialized.
|
||||
bool OpportunisticallyCreatePrintJob(int cookie);
|
||||
|
||||
// Release the PrinterQuery associated with our |cookie_|.
|
||||
void ReleasePrinterQuery();
|
||||
|
||||
content::NotificationRegistrar registrar_;
|
||||
|
||||
// Manages the low-level talk to the printer.
|
||||
scoped_refptr<PrintJob> print_job_;
|
||||
|
||||
// Number of pages to print in the print job.
|
||||
int number_pages_;
|
||||
|
||||
// Indication of success of the print job.
|
||||
bool printing_succeeded_;
|
||||
|
||||
// Running an inner message loop inside RenderAllMissingPagesNow(). This means
|
||||
// we are _blocking_ until all the necessary pages have been rendered or the
|
||||
// print settings are being loaded.
|
||||
bool inside_inner_message_loop_;
|
||||
|
||||
// Set to true when OnDidPrintPage() should be expecting the first page.
|
||||
bool expecting_first_page_;
|
||||
|
||||
// The document cookie of the current PrinterQuery.
|
||||
int cookie_;
|
||||
|
||||
// Whether printing is enabled.
|
||||
bool printing_enabled_;
|
||||
|
||||
scoped_refptr<printing::PrintQueriesQueue> queue_;
|
||||
|
||||
base::Callback<void(bool)> callback;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PrintViewManagerBase);
|
||||
};
|
||||
|
||||
} // namespace printing
|
||||
|
||||
#endif // CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASE_H_
|
|
@ -1,46 +0,0 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/printing/print_view_manager_basic.h"
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
#include "base/file_descriptor_posix.h"
|
||||
#include "chrome/common/print_messages.h"
|
||||
#include "printing/printing_context_android.h"
|
||||
#endif
|
||||
|
||||
DEFINE_WEB_CONTENTS_USER_DATA_KEY(printing::PrintViewManagerBasic);
|
||||
|
||||
namespace printing {
|
||||
|
||||
PrintViewManagerBasic::PrintViewManagerBasic(content::WebContents* web_contents)
|
||||
: PrintViewManagerBase(web_contents) {}
|
||||
|
||||
PrintViewManagerBasic::~PrintViewManagerBasic() {}
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
void PrintViewManagerBasic::RenderProcessGone(base::TerminationStatus status) {
|
||||
PrintingContextAndroid::PdfWritingDone(file_descriptor_.fd, false);
|
||||
file_descriptor_ = base::FileDescriptor(-1, false);
|
||||
PrintViewManagerBase::RenderProcessGone(status);
|
||||
}
|
||||
|
||||
void PrintViewManagerBasic::OnPrintingFailed(int cookie) {
|
||||
PrintingContextAndroid::PdfWritingDone(file_descriptor_.fd, false);
|
||||
file_descriptor_ = base::FileDescriptor(-1, false);
|
||||
PrintViewManagerBase::OnPrintingFailed(cookie);
|
||||
}
|
||||
|
||||
bool PrintViewManagerBasic::OnMessageReceived(const IPC::Message& message) {
|
||||
bool handled = true;
|
||||
IPC_BEGIN_MESSAGE_MAP(PrintViewManagerBasic, message)
|
||||
IPC_MESSAGE_HANDLER(PrintHostMsg_PrintingFailed, OnPrintingFailed)
|
||||
IPC_MESSAGE_UNHANDLED(handled = false)
|
||||
IPC_END_MESSAGE_MAP()
|
||||
|
||||
return handled ? true : PrintViewManagerBase::OnMessageReceived(message);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace printing
|
|
@ -1,57 +0,0 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASIC_H_
|
||||
#define CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASIC_H_
|
||||
|
||||
#include "chrome/browser/printing/print_view_manager_base.h"
|
||||
#include "content/public/browser/web_contents_user_data.h"
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
#include "base/file_descriptor_posix.h"
|
||||
#endif
|
||||
|
||||
namespace printing {
|
||||
|
||||
// Manages the print commands for a WebContents - basic version.
|
||||
class PrintViewManagerBasic
|
||||
: public PrintViewManagerBase,
|
||||
public content::WebContentsUserData<PrintViewManagerBasic> {
|
||||
public:
|
||||
~PrintViewManagerBasic() override;
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
// Sets the file descriptor into which the PDF will be written.
|
||||
void set_file_descriptor(const base::FileDescriptor& file_descriptor) {
|
||||
file_descriptor_ = file_descriptor;
|
||||
}
|
||||
|
||||
// Gets the file descriptor into which the PDF will be written.
|
||||
base::FileDescriptor file_descriptor() const { return file_descriptor_; }
|
||||
|
||||
// content::WebContentsObserver implementation.
|
||||
// Terminates or cancels the print job if one was pending.
|
||||
virtual void RenderProcessGone(base::TerminationStatus status) override;
|
||||
|
||||
// content::WebContentsObserver implementation.
|
||||
virtual bool OnMessageReceived(const IPC::Message& message) override;
|
||||
#endif
|
||||
|
||||
private:
|
||||
explicit PrintViewManagerBasic(content::WebContents* web_contents);
|
||||
friend class content::WebContentsUserData<PrintViewManagerBasic>;
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
virtual void OnPrintingFailed(int cookie) override;
|
||||
|
||||
// The file descriptor into which the PDF of the page will be written.
|
||||
base::FileDescriptor file_descriptor_;
|
||||
#endif
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PrintViewManagerBasic);
|
||||
};
|
||||
|
||||
} // namespace printing
|
||||
|
||||
#endif // CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASIC_H_
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_OBSERVER_H_
|
||||
#define CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_OBSERVER_H_
|
||||
|
||||
namespace printing {
|
||||
|
||||
// An interface the PrintViewManager uses to notify an observer when the print
|
||||
// dialog is shown. Register the observer via PrintViewManager::set_observer.
|
||||
class PrintViewManagerObserver {
|
||||
public:
|
||||
// Notifies the observer that the print dialog was shown.
|
||||
virtual void OnPrintDialogShown() = 0;
|
||||
|
||||
protected:
|
||||
virtual ~PrintViewManagerObserver() {}
|
||||
};
|
||||
|
||||
} // namespace printing
|
||||
|
||||
#endif // CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_OBSERVER_H_
|
|
@ -1,152 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/printing/printer_query.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/bind_helpers.h"
|
||||
#include "base/message_loop/message_loop.h"
|
||||
#include "base/threading/thread_restrictions.h"
|
||||
#include "base/values.h"
|
||||
#include "chrome/browser/printing/print_job_worker.h"
|
||||
|
||||
namespace printing {
|
||||
|
||||
PrinterQuery::PrinterQuery(int render_process_id, int render_frame_id)
|
||||
: worker_(new PrintJobWorker(render_process_id, render_frame_id, this)),
|
||||
is_print_dialog_box_shown_(false),
|
||||
cookie_(PrintSettings::NewCookie()),
|
||||
last_status_(PrintingContext::FAILED) {
|
||||
DCHECK(base::MessageLoopForIO::IsCurrent());
|
||||
}
|
||||
|
||||
PrinterQuery::~PrinterQuery() {
|
||||
// The job should be finished (or at least canceled) when it is destroyed.
|
||||
DCHECK(!is_print_dialog_box_shown_);
|
||||
// If this fires, it is that this pending printer context has leaked.
|
||||
DCHECK(!worker_);
|
||||
}
|
||||
|
||||
void PrinterQuery::GetSettingsDone(const PrintSettings& new_settings,
|
||||
PrintingContext::Result result) {
|
||||
is_print_dialog_box_shown_ = false;
|
||||
last_status_ = result;
|
||||
if (result != PrintingContext::FAILED) {
|
||||
settings_ = new_settings;
|
||||
cookie_ = PrintSettings::NewCookie();
|
||||
} else {
|
||||
// Failure.
|
||||
cookie_ = 0;
|
||||
}
|
||||
|
||||
if (!callback_.is_null()) {
|
||||
// This may cause reentrancy like to call StopWorker().
|
||||
callback_.Run();
|
||||
callback_.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<PrintJobWorker> PrinterQuery::DetachWorker(
|
||||
PrintJobWorkerOwner* new_owner) {
|
||||
DCHECK(callback_.is_null());
|
||||
DCHECK(worker_);
|
||||
|
||||
worker_->SetNewOwner(new_owner);
|
||||
return std::move(worker_);
|
||||
}
|
||||
|
||||
const PrintSettings& PrinterQuery::settings() const {
|
||||
return settings_;
|
||||
}
|
||||
|
||||
int PrinterQuery::cookie() const {
|
||||
return cookie_;
|
||||
}
|
||||
|
||||
void PrinterQuery::GetSettings(GetSettingsAskParam ask_user_for_settings,
|
||||
int expected_page_count,
|
||||
bool has_selection,
|
||||
MarginType margin_type,
|
||||
bool is_scripted,
|
||||
bool is_modifiable,
|
||||
const base::Closure& callback) {
|
||||
DCHECK(RunsTasksInCurrentSequence());
|
||||
DCHECK(!is_print_dialog_box_shown_ || !is_scripted);
|
||||
|
||||
StartWorker(callback);
|
||||
|
||||
// Real work is done in PrintJobWorker::GetSettings().
|
||||
is_print_dialog_box_shown_ =
|
||||
ask_user_for_settings == GetSettingsAskParam::ASK_USER;
|
||||
worker_->PostTask(
|
||||
FROM_HERE,
|
||||
base::Bind(&PrintJobWorker::GetSettings, base::Unretained(worker_.get()),
|
||||
is_print_dialog_box_shown_, expected_page_count, has_selection,
|
||||
margin_type, is_scripted, is_modifiable, base::string16()));
|
||||
}
|
||||
|
||||
void PrinterQuery::GetSettings(GetSettingsAskParam ask_user_for_settings,
|
||||
int expected_page_count,
|
||||
bool has_selection,
|
||||
MarginType margin_type,
|
||||
bool is_scripted,
|
||||
bool is_modifiable,
|
||||
const base::string16& device_name,
|
||||
const base::Closure& callback) {
|
||||
DCHECK(RunsTasksInCurrentSequence());
|
||||
DCHECK(!is_print_dialog_box_shown_);
|
||||
StartWorker(callback);
|
||||
|
||||
is_print_dialog_box_shown_ = false;
|
||||
worker_->PostTask(
|
||||
FROM_HERE,
|
||||
base::Bind(&PrintJobWorker::GetSettings, base::Unretained(worker_.get()),
|
||||
is_print_dialog_box_shown_, expected_page_count, has_selection,
|
||||
margin_type, is_scripted, is_modifiable, device_name));
|
||||
}
|
||||
|
||||
void PrinterQuery::SetSettings(
|
||||
std::unique_ptr<base::DictionaryValue> new_settings,
|
||||
const base::Closure& callback) {
|
||||
StartWorker(callback);
|
||||
|
||||
worker_->PostTask(FROM_HERE, base::Bind(&PrintJobWorker::SetSettings,
|
||||
base::Unretained(worker_.get()),
|
||||
base::Passed(&new_settings)));
|
||||
}
|
||||
|
||||
void PrinterQuery::StartWorker(const base::Closure& callback) {
|
||||
DCHECK(callback_.is_null());
|
||||
DCHECK(worker_);
|
||||
|
||||
// Lazily create the worker thread. There is one worker thread per print job.
|
||||
if (!worker_->IsRunning())
|
||||
worker_->Start();
|
||||
|
||||
callback_ = callback;
|
||||
}
|
||||
|
||||
void PrinterQuery::StopWorker() {
|
||||
if (worker_) {
|
||||
// http://crbug.com/66082: We're blocking on the PrinterQuery's worker
|
||||
// thread. It's not clear to me if this may result in blocking the current
|
||||
// thread for an unacceptable time. We should probably fix it.
|
||||
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
||||
worker_->Stop();
|
||||
worker_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool PrinterQuery::is_callback_pending() const {
|
||||
return !callback_.is_null();
|
||||
}
|
||||
|
||||
bool PrinterQuery::is_valid() const {
|
||||
return !!worker_;
|
||||
}
|
||||
|
||||
} // namespace printing
|
|
@ -1,111 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_PRINTING_PRINTER_QUERY_H_
|
||||
#define CHROME_BROWSER_PRINTING_PRINTER_QUERY_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/macros.h"
|
||||
#include "chrome/browser/printing/print_job_worker_owner.h"
|
||||
#include "printing/print_job_constants.h"
|
||||
|
||||
namespace base {
|
||||
class DictionaryValue;
|
||||
}
|
||||
|
||||
namespace printing {
|
||||
|
||||
class PrintDestinationInterface;
|
||||
class PrintJobWorker;
|
||||
|
||||
// Query the printer for settings.
|
||||
class PrinterQuery : public PrintJobWorkerOwner {
|
||||
public:
|
||||
// GetSettings() UI parameter.
|
||||
enum class GetSettingsAskParam {
|
||||
DEFAULTS,
|
||||
ASK_USER,
|
||||
};
|
||||
|
||||
PrinterQuery(int render_process_id, int render_frame_id);
|
||||
|
||||
// PrintJobWorkerOwner implementation.
|
||||
void GetSettingsDone(const PrintSettings& new_settings,
|
||||
PrintingContext::Result result) override;
|
||||
std::unique_ptr<PrintJobWorker> DetachWorker(
|
||||
PrintJobWorkerOwner* new_owner) override;
|
||||
const PrintSettings& settings() const override;
|
||||
int cookie() const override;
|
||||
|
||||
// Initializes the printing context. It is fine to call this function multiple
|
||||
// times to reinitialize the settings. |web_contents_observer| can be queried
|
||||
// to find the owner of the print setting dialog box. It is unused when
|
||||
// |ask_for_user_settings| is DEFAULTS.
|
||||
void GetSettings(GetSettingsAskParam ask_user_for_settings,
|
||||
int expected_page_count,
|
||||
bool has_selection,
|
||||
MarginType margin_type,
|
||||
bool is_scripted,
|
||||
bool is_modifiable,
|
||||
const base::Closure& callback);
|
||||
|
||||
void GetSettings(GetSettingsAskParam ask_user_for_settings,
|
||||
int expected_page_count,
|
||||
bool has_selection,
|
||||
MarginType margin_type,
|
||||
bool is_scripted,
|
||||
bool is_modifiable,
|
||||
const base::string16& device_name,
|
||||
const base::Closure& callback);
|
||||
|
||||
// Updates the current settings with |new_settings| dictionary values.
|
||||
void SetSettings(std::unique_ptr<base::DictionaryValue> new_settings,
|
||||
const base::Closure& callback);
|
||||
|
||||
// Stops the worker thread since the client is done with this object.
|
||||
void StopWorker();
|
||||
|
||||
// Returns true if a GetSettings() call is pending completion.
|
||||
bool is_callback_pending() const;
|
||||
|
||||
PrintingContext::Result last_status() const { return last_status_; }
|
||||
|
||||
// Returns if a worker thread is still associated to this instance.
|
||||
bool is_valid() const;
|
||||
|
||||
private:
|
||||
~PrinterQuery() override;
|
||||
|
||||
// Lazy create the worker thread. There is one worker thread per print job.
|
||||
void StartWorker(const base::Closure& callback);
|
||||
|
||||
// All the UI is done in a worker thread because many Win32 print functions
|
||||
// are blocking and enters a message loop without your consent. There is one
|
||||
// worker thread per print job.
|
||||
std::unique_ptr<PrintJobWorker> worker_;
|
||||
|
||||
// Cache of the print context settings for access in the UI thread.
|
||||
PrintSettings settings_;
|
||||
|
||||
// Is the Print... dialog box currently shown.
|
||||
bool is_print_dialog_box_shown_;
|
||||
|
||||
// Cookie that make this instance unique.
|
||||
int cookie_;
|
||||
|
||||
// Results from the last GetSettingsDone() callback.
|
||||
PrintingContext::Result last_status_;
|
||||
|
||||
// Callback waiting to be run.
|
||||
base::Closure callback_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PrinterQuery);
|
||||
};
|
||||
|
||||
} // namespace printing
|
||||
|
||||
#endif // CHROME_BROWSER_PRINTING_PRINTER_QUERY_H_
|
|
@ -1,328 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/printing/printing_message_filter.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "chrome/browser/printing/print_job_manager.h"
|
||||
#include "chrome/browser/printing/printer_query.h"
|
||||
#include "chrome/browser/profiles/profile.h"
|
||||
#include "chrome/browser/profiles/profile_io_data.h"
|
||||
#include "chrome/common/print_messages.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/common/child_process_host.h"
|
||||
#include "printing/buildflags/buildflags.h"
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "chrome/browser/printing/print_view_manager_basic.h"
|
||||
#include "printing/printing_context_android.h"
|
||||
#endif
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace printing {
|
||||
|
||||
namespace {
|
||||
|
||||
void RenderParamsFromPrintSettings(const PrintSettings& settings,
|
||||
PrintMsg_Print_Params* params) {
|
||||
params->page_size = settings.page_setup_device_units().physical_size();
|
||||
params->content_size.SetSize(
|
||||
settings.page_setup_device_units().content_area().width(),
|
||||
settings.page_setup_device_units().content_area().height());
|
||||
params->printable_area.SetRect(
|
||||
settings.page_setup_device_units().printable_area().x(),
|
||||
settings.page_setup_device_units().printable_area().y(),
|
||||
settings.page_setup_device_units().printable_area().width(),
|
||||
settings.page_setup_device_units().printable_area().height());
|
||||
params->margin_top = settings.page_setup_device_units().content_area().y();
|
||||
params->margin_left = settings.page_setup_device_units().content_area().x();
|
||||
params->dpi = settings.dpi();
|
||||
params->scale_factor = settings.scale_factor();
|
||||
// Always use an invalid cookie.
|
||||
params->document_cookie = 0;
|
||||
params->selection_only = settings.selection_only();
|
||||
params->supports_alpha_blend = settings.supports_alpha_blend();
|
||||
params->should_print_backgrounds = settings.should_print_backgrounds();
|
||||
params->display_header_footer = settings.display_header_footer();
|
||||
params->title = settings.title();
|
||||
params->url = settings.url();
|
||||
}
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
content::WebContents* GetWebContentsForRenderFrame(int render_process_id,
|
||||
int render_frame_id) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
content::RenderFrameHost* frame =
|
||||
content::RenderFrameHost::FromID(render_process_id, render_frame_id);
|
||||
return frame ? content::WebContents::FromRenderFrameHost(frame) : nullptr;
|
||||
}
|
||||
|
||||
PrintViewManagerBasic* GetPrintManager(int render_process_id,
|
||||
int render_frame_id) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
content::WebContents* web_contents =
|
||||
GetWebContentsForRenderFrame(render_process_id, render_frame_id);
|
||||
return web_contents ? PrintViewManagerBasic::FromWebContents(web_contents)
|
||||
: nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
PrintingMessageFilter::PrintingMessageFilter(int render_process_id)
|
||||
: BrowserMessageFilter(PrintMsgStart),
|
||||
render_process_id_(render_process_id),
|
||||
queue_(g_browser_process->print_job_manager()->queue()) {
|
||||
DCHECK(queue_.get());
|
||||
}
|
||||
|
||||
PrintingMessageFilter::~PrintingMessageFilter() {}
|
||||
|
||||
void PrintingMessageFilter::OnDestruct() const {
|
||||
BrowserThread::DeleteOnUIThread::Destruct(this);
|
||||
}
|
||||
|
||||
void PrintingMessageFilter::OverrideThreadForMessage(
|
||||
const IPC::Message& message,
|
||||
BrowserThread::ID* thread) {
|
||||
#if defined(OS_ANDROID)
|
||||
if (message.type() == PrintHostMsg_AllocateTempFileForPrinting::ID ||
|
||||
message.type() == PrintHostMsg_TempFileForPrintingWritten::ID) {
|
||||
*thread = BrowserThread::UI;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool PrintingMessageFilter::OnMessageReceived(const IPC::Message& message) {
|
||||
bool handled = true;
|
||||
IPC_BEGIN_MESSAGE_MAP(PrintingMessageFilter, message)
|
||||
#if defined(OS_ANDROID)
|
||||
IPC_MESSAGE_HANDLER(PrintHostMsg_AllocateTempFileForPrinting,
|
||||
OnAllocateTempFileForPrinting)
|
||||
IPC_MESSAGE_HANDLER(PrintHostMsg_TempFileForPrintingWritten,
|
||||
OnTempFileForPrintingWritten)
|
||||
#endif
|
||||
IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_GetDefaultPrintSettings,
|
||||
OnGetDefaultPrintSettings)
|
||||
IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_InitSettingWithDeviceName,
|
||||
OnInitSettingWithDeviceName)
|
||||
IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_ScriptedPrint, OnScriptedPrint)
|
||||
IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_UpdatePrintSettings,
|
||||
OnUpdatePrintSettings)
|
||||
IPC_MESSAGE_UNHANDLED(handled = false)
|
||||
IPC_END_MESSAGE_MAP()
|
||||
return handled;
|
||||
}
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
void PrintingMessageFilter::OnAllocateTempFileForPrinting(
|
||||
int render_frame_id,
|
||||
base::FileDescriptor* temp_file_fd,
|
||||
int* sequence_number) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
PrintViewManagerBasic* print_view_manager =
|
||||
GetPrintManager(render_process_id_, render_frame_id);
|
||||
if (!print_view_manager)
|
||||
return;
|
||||
|
||||
// The file descriptor is originally created in & passed from the Android
|
||||
// side, and it will handle the closing.
|
||||
temp_file_fd->fd = print_view_manager->file_descriptor().fd;
|
||||
temp_file_fd->auto_close = false;
|
||||
}
|
||||
|
||||
void PrintingMessageFilter::OnTempFileForPrintingWritten(int render_frame_id,
|
||||
int sequence_number) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
PrintViewManagerBasic* print_view_manager =
|
||||
GetPrintManager(render_process_id_, render_frame_id);
|
||||
if (print_view_manager)
|
||||
print_view_manager->PdfWritingDone(true);
|
||||
}
|
||||
#endif // defined(OS_ANDROID)
|
||||
|
||||
void PrintingMessageFilter::OnGetDefaultPrintSettings(IPC::Message* reply_msg) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
scoped_refptr<PrinterQuery> printer_query;
|
||||
if (false) {
|
||||
// Reply with NULL query.
|
||||
OnGetDefaultPrintSettingsReply(printer_query, reply_msg);
|
||||
return;
|
||||
}
|
||||
printer_query = queue_->PopPrinterQuery(0);
|
||||
if (!printer_query.get()) {
|
||||
printer_query =
|
||||
queue_->CreatePrinterQuery(render_process_id_, reply_msg->routing_id());
|
||||
}
|
||||
|
||||
// Loads default settings. This is asynchronous, only the IPC message sender
|
||||
// will hang until the settings are retrieved.
|
||||
printer_query->GetSettings(
|
||||
PrinterQuery::GetSettingsAskParam::DEFAULTS, 0, false, DEFAULT_MARGINS,
|
||||
false, false,
|
||||
base::Bind(&PrintingMessageFilter::OnGetDefaultPrintSettingsReply, this,
|
||||
printer_query, reply_msg));
|
||||
}
|
||||
|
||||
void PrintingMessageFilter::OnInitSettingWithDeviceName(
|
||||
const base::string16& device_name,
|
||||
IPC::Message* reply_msg) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
scoped_refptr<PrinterQuery> printer_query;
|
||||
printer_query = queue_->PopPrinterQuery(0);
|
||||
if (!printer_query.get()) {
|
||||
printer_query =
|
||||
queue_->CreatePrinterQuery(render_process_id_, reply_msg->routing_id());
|
||||
}
|
||||
|
||||
// Loads default settings. This is asynchronous, only the IPC message sender
|
||||
// will hang until the settings are retrieved.
|
||||
printer_query->GetSettings(
|
||||
PrinterQuery::GetSettingsAskParam::DEFAULTS, 0, false, DEFAULT_MARGINS,
|
||||
true, true, device_name,
|
||||
base::Bind(&PrintingMessageFilter::OnGetDefaultPrintSettingsReply, this,
|
||||
printer_query, reply_msg));
|
||||
}
|
||||
|
||||
void PrintingMessageFilter::OnGetDefaultPrintSettingsReply(
|
||||
scoped_refptr<PrinterQuery> printer_query,
|
||||
IPC::Message* reply_msg) {
|
||||
PrintMsg_Print_Params params;
|
||||
if (!printer_query.get() ||
|
||||
printer_query->last_status() != PrintingContext::OK) {
|
||||
params.Reset();
|
||||
} else {
|
||||
RenderParamsFromPrintSettings(printer_query->settings(), ¶ms);
|
||||
params.document_cookie = printer_query->cookie();
|
||||
}
|
||||
PrintHostMsg_GetDefaultPrintSettings::WriteReplyParams(reply_msg, params);
|
||||
Send(reply_msg);
|
||||
// If printing was enabled.
|
||||
if (printer_query.get()) {
|
||||
// If user hasn't cancelled.
|
||||
if (printer_query->cookie() && printer_query->settings().dpi()) {
|
||||
queue_->QueuePrinterQuery(printer_query.get());
|
||||
} else {
|
||||
printer_query->StopWorker();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrintingMessageFilter::OnScriptedPrint(
|
||||
const PrintHostMsg_ScriptedPrint_Params& params,
|
||||
IPC::Message* reply_msg) {
|
||||
scoped_refptr<PrinterQuery> printer_query =
|
||||
queue_->PopPrinterQuery(params.cookie);
|
||||
if (!printer_query.get()) {
|
||||
printer_query =
|
||||
queue_->CreatePrinterQuery(render_process_id_, reply_msg->routing_id());
|
||||
}
|
||||
printer_query->GetSettings(
|
||||
PrinterQuery::GetSettingsAskParam::ASK_USER, params.expected_pages_count,
|
||||
params.has_selection, params.margin_type, true, true,
|
||||
base::Bind(&PrintingMessageFilter::OnScriptedPrintReply, this,
|
||||
printer_query, reply_msg));
|
||||
}
|
||||
|
||||
void PrintingMessageFilter::OnScriptedPrintReply(
|
||||
scoped_refptr<PrinterQuery> printer_query,
|
||||
IPC::Message* reply_msg) {
|
||||
PrintMsg_PrintPages_Params params;
|
||||
#if defined(OS_ANDROID)
|
||||
// We need to save the routing ID here because Send method below deletes the
|
||||
// |reply_msg| before we can get the routing ID for the Android code.
|
||||
int routing_id = reply_msg->routing_id();
|
||||
#endif
|
||||
if (printer_query->last_status() != PrintingContext::OK ||
|
||||
!printer_query->settings().dpi()) {
|
||||
params.Reset();
|
||||
} else {
|
||||
RenderParamsFromPrintSettings(printer_query->settings(), ¶ms.params);
|
||||
params.params.document_cookie = printer_query->cookie();
|
||||
params.pages = PageRange::GetPages(printer_query->settings().ranges());
|
||||
}
|
||||
PrintHostMsg_ScriptedPrint::WriteReplyParams(reply_msg, params);
|
||||
Send(reply_msg);
|
||||
if (params.params.dpi && params.params.document_cookie) {
|
||||
#if defined(OS_ANDROID)
|
||||
int file_descriptor;
|
||||
const base::string16& device_name = printer_query->settings().device_name();
|
||||
if (base::StringToInt(device_name, &file_descriptor)) {
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&PrintingMessageFilter::UpdateFileDescriptor, this,
|
||||
routing_id, file_descriptor));
|
||||
}
|
||||
#endif
|
||||
queue_->QueuePrinterQuery(printer_query.get());
|
||||
} else {
|
||||
printer_query->StopWorker();
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
void PrintingMessageFilter::UpdateFileDescriptor(int render_view_id, int fd) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
content::WebContents* wc = GetWebContentsForRenderView(render_view_id);
|
||||
if (!wc)
|
||||
return;
|
||||
PrintViewManagerBasic* print_view_manager =
|
||||
PrintViewManagerBasic::FromWebContents(wc);
|
||||
print_view_manager->set_file_descriptor(base::FileDescriptor(fd, false));
|
||||
}
|
||||
#endif
|
||||
|
||||
void PrintingMessageFilter::OnUpdatePrintSettings(
|
||||
int document_cookie,
|
||||
const base::DictionaryValue& job_settings,
|
||||
IPC::Message* reply_msg) {
|
||||
std::unique_ptr<base::DictionaryValue> new_settings(job_settings.DeepCopy());
|
||||
|
||||
scoped_refptr<PrinterQuery> printer_query;
|
||||
printer_query = queue_->PopPrinterQuery(document_cookie);
|
||||
if (!printer_query.get()) {
|
||||
printer_query = queue_->CreatePrinterQuery(
|
||||
content::ChildProcessHost::kInvalidUniqueID, MSG_ROUTING_NONE);
|
||||
}
|
||||
printer_query->SetSettings(
|
||||
std::move(new_settings),
|
||||
base::Bind(&PrintingMessageFilter::OnUpdatePrintSettingsReply, this,
|
||||
printer_query, reply_msg));
|
||||
}
|
||||
|
||||
void PrintingMessageFilter::OnUpdatePrintSettingsReply(
|
||||
scoped_refptr<PrinterQuery> printer_query,
|
||||
IPC::Message* reply_msg) {
|
||||
PrintMsg_PrintPages_Params params;
|
||||
if (!printer_query.get() ||
|
||||
printer_query->last_status() != PrintingContext::OK) {
|
||||
params.Reset();
|
||||
} else {
|
||||
RenderParamsFromPrintSettings(printer_query->settings(), ¶ms.params);
|
||||
params.params.document_cookie = printer_query->cookie();
|
||||
params.pages = PageRange::GetPages(printer_query->settings().ranges());
|
||||
}
|
||||
bool canceled = printer_query.get() &&
|
||||
(printer_query->last_status() == PrintingContext::CANCEL);
|
||||
PrintHostMsg_UpdatePrintSettings::WriteReplyParams(reply_msg, params,
|
||||
canceled);
|
||||
Send(reply_msg);
|
||||
// If user hasn't cancelled.
|
||||
if (printer_query.get()) {
|
||||
if (printer_query->cookie() && printer_query->settings().dpi()) {
|
||||
queue_->QueuePrinterQuery(printer_query.get());
|
||||
} else {
|
||||
printer_query->StopWorker();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace printing
|
|
@ -1,97 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_
|
||||
#define CHROME_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/compiler_specific.h"
|
||||
#include "content/public/browser/browser_message_filter.h"
|
||||
|
||||
struct PrintHostMsg_ScriptedPrint_Params;
|
||||
|
||||
namespace base {
|
||||
class DictionaryValue;
|
||||
class FilePath;
|
||||
} // namespace base
|
||||
|
||||
namespace content {
|
||||
class WebContents;
|
||||
}
|
||||
|
||||
namespace printing {
|
||||
|
||||
class PrintQueriesQueue;
|
||||
class PrinterQuery;
|
||||
|
||||
// This class filters out incoming printing related IPC messages for the
|
||||
// renderer process on the IPC thread.
|
||||
class PrintingMessageFilter : public content::BrowserMessageFilter {
|
||||
public:
|
||||
PrintingMessageFilter(int render_process_id);
|
||||
|
||||
// content::BrowserMessageFilter methods.
|
||||
void OverrideThreadForMessage(const IPC::Message& message,
|
||||
content::BrowserThread::ID* thread) override;
|
||||
bool OnMessageReceived(const IPC::Message& message) override;
|
||||
|
||||
private:
|
||||
friend class base::DeleteHelper<PrintingMessageFilter>;
|
||||
friend class content::BrowserThread;
|
||||
|
||||
~PrintingMessageFilter() override;
|
||||
|
||||
void OnDestruct() const override;
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
// Used to ask the browser allocate a temporary file for the renderer
|
||||
// to fill in resulting PDF in renderer.
|
||||
void OnAllocateTempFileForPrinting(int render_frame_id,
|
||||
base::FileDescriptor* temp_file_fd,
|
||||
int* sequence_number);
|
||||
void OnTempFileForPrintingWritten(int render_frame_id, int sequence_number);
|
||||
|
||||
// Updates the file descriptor for the PrintViewManagerBasic of a given
|
||||
// render_frame_id.
|
||||
void UpdateFileDescriptor(int render_frame_id, int fd);
|
||||
#endif
|
||||
|
||||
// Get the default print setting.
|
||||
void OnGetDefaultPrintSettings(IPC::Message* reply_msg);
|
||||
|
||||
// Set deviceName
|
||||
void OnInitSettingWithDeviceName(const base::string16& device_name,
|
||||
IPC::Message* reply_msg);
|
||||
|
||||
void OnGetDefaultPrintSettingsReply(scoped_refptr<PrinterQuery> printer_query,
|
||||
IPC::Message* reply_msg);
|
||||
|
||||
// The renderer host have to show to the user the print dialog and returns
|
||||
// the selected print settings. The task is handled by the print worker
|
||||
// thread and the UI thread. The reply occurs on the IO thread.
|
||||
void OnScriptedPrint(const PrintHostMsg_ScriptedPrint_Params& params,
|
||||
IPC::Message* reply_msg);
|
||||
void OnScriptedPrintReply(scoped_refptr<PrinterQuery> printer_query,
|
||||
IPC::Message* reply_msg);
|
||||
|
||||
// Modify the current print settings based on |job_settings|. The task is
|
||||
// handled by the print worker thread and the UI thread. The reply occurs on
|
||||
// the IO thread.
|
||||
void OnUpdatePrintSettings(int document_cookie,
|
||||
const base::DictionaryValue& job_settings,
|
||||
IPC::Message* reply_msg);
|
||||
void OnUpdatePrintSettingsReply(scoped_refptr<PrinterQuery> printer_query,
|
||||
IPC::Message* reply_msg);
|
||||
|
||||
const int render_process_id_;
|
||||
|
||||
scoped_refptr<PrintQueriesQueue> queue_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PrintingMessageFilter);
|
||||
};
|
||||
|
||||
} // namespace printing
|
||||
|
||||
#endif // CHROME_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/printing/printing_ui_web_contents_observer.h"
|
||||
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
|
||||
PrintingUIWebContentsObserver::PrintingUIWebContentsObserver(
|
||||
content::WebContents* web_contents)
|
||||
: content::WebContentsObserver(web_contents) {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
||||
}
|
||||
|
||||
gfx::NativeView PrintingUIWebContentsObserver::GetParentView() {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
||||
return web_contents() ? web_contents()->GetNativeView() : NULL;
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_PRINTING_PRINTING_UI_WEB_CONTENTS_OBSERVER_H_
|
||||
#define CHROME_BROWSER_PRINTING_PRINTING_UI_WEB_CONTENTS_OBSERVER_H_
|
||||
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
#include "ui/gfx/native_widget_types.h"
|
||||
|
||||
// Wrapper used to keep track of the lifetime of a WebContents.
|
||||
// Lives on the UI thread.
|
||||
class PrintingUIWebContentsObserver : public content::WebContentsObserver {
|
||||
public:
|
||||
explicit PrintingUIWebContentsObserver(content::WebContents* web_contents);
|
||||
|
||||
// Return the parent NativeView of the observed WebContents.
|
||||
gfx::NativeView GetParentView();
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(PrintingUIWebContentsObserver);
|
||||
};
|
||||
|
||||
#endif // CHROME_BROWSER_PRINTING_PRINTING_UI_WEB_CONTENTS_OBSERVER_H_
|
Loading…
Add table
Add a link
Reference in a new issue