Add printing related source codes from chrome.
This commit is contained in:
parent
cab546cbb7
commit
d934526bb3
34 changed files with 7794 additions and 0 deletions
363
chromium_src/chrome/browser/printing/print_job.cc
Normal file
363
chromium_src/chrome/browser/printing/print_job.cc
Normal file
|
@ -0,0 +1,363 @@
|
|||
// 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 "base/bind.h"
|
||||
#include "base/bind_helpers.h"
|
||||
#include "base/message_loop/message_loop.h"
|
||||
#include "base/threading/thread_restrictions.h"
|
||||
#include "base/threading/worker_pool.h"
|
||||
#include "base/timer/timer.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"
|
||||
#include "printing/printed_page.h"
|
||||
|
||||
using base::TimeDelta;
|
||||
|
||||
namespace {
|
||||
|
||||
// Helper function to ensure |owner| is valid until at least |callback| returns.
|
||||
void HoldRefCallback(const scoped_refptr<printing::PrintJobWorkerOwner>& owner,
|
||||
const base::Closure& callback) {
|
||||
callback.Run();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace printing {
|
||||
|
||||
PrintJob::PrintJob()
|
||||
: ui_message_loop_(base::MessageLoop::current()),
|
||||
source_(NULL),
|
||||
worker_(),
|
||||
settings_(),
|
||||
is_job_pending_(false),
|
||||
is_canceling_(false),
|
||||
quit_factory_(this) {
|
||||
DCHECK(ui_message_loop_);
|
||||
// This is normally a UI message loop, but in unit tests, the message loop is
|
||||
// of the 'default' type.
|
||||
DCHECK(base::MessageLoopForUI::IsCurrent() ||
|
||||
ui_message_loop_->type() == base::MessageLoop::TYPE_DEFAULT);
|
||||
ui_message_loop_->AddDestructionObserver(this);
|
||||
}
|
||||
|
||||
PrintJob::~PrintJob() {
|
||||
ui_message_loop_->RemoveDestructionObserver(this);
|
||||
// The job should be finished (or at least canceled) when it is destroyed.
|
||||
DCHECK(!is_job_pending_);
|
||||
DCHECK(!is_canceling_);
|
||||
if (worker_.get())
|
||||
DCHECK(worker_->message_loop() == NULL);
|
||||
DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
|
||||
}
|
||||
|
||||
void PrintJob::Initialize(PrintJobWorkerOwner* job,
|
||||
PrintedPagesSource* source,
|
||||
int page_count) {
|
||||
DCHECK(!source_);
|
||||
DCHECK(!worker_.get());
|
||||
DCHECK(!is_job_pending_);
|
||||
DCHECK(!is_canceling_);
|
||||
DCHECK(!document_.get());
|
||||
source_ = source;
|
||||
worker_.reset(job->DetachWorker(this));
|
||||
settings_ = job->settings();
|
||||
|
||||
PrintedDocument* new_doc =
|
||||
new PrintedDocument(settings_, source_, 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_EQ(ui_message_loop_, base::MessageLoop::current());
|
||||
switch (type) {
|
||||
case chrome::NOTIFICATION_PRINT_JOB_EVENT: {
|
||||
OnNotifyPrintJobEvent(*content::Details<JobEventDetails>(details).ptr());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrintJob::GetSettingsDone(const PrintSettings& new_settings,
|
||||
PrintingContext::Result result) {
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
PrintJobWorker* PrintJob::DetachWorker(PrintJobWorkerOwner* new_owner) {
|
||||
NOTREACHED();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
base::MessageLoop* PrintJob::message_loop() {
|
||||
return ui_message_loop_;
|
||||
}
|
||||
|
||||
const PrintSettings& PrintJob::settings() const {
|
||||
return settings_;
|
||||
}
|
||||
|
||||
int PrintJob::cookie() const {
|
||||
if (!document_.get())
|
||||
// Always use an invalid cookie in this case.
|
||||
return 0;
|
||||
return document_->cookie();
|
||||
}
|
||||
|
||||
void PrintJob::WillDestroyCurrentMessageLoop() {
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
void PrintJob::StartPrinting() {
|
||||
DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
|
||||
DCHECK(worker_->message_loop());
|
||||
DCHECK(!is_job_pending_);
|
||||
if (!worker_->message_loop() || is_job_pending_)
|
||||
return;
|
||||
|
||||
// Real work is done in PrintJobWorker::StartPrinting().
|
||||
worker_->message_loop()->PostTask(
|
||||
FROM_HERE,
|
||||
base::Bind(&HoldRefCallback, make_scoped_refptr(this),
|
||||
base::Bind(&PrintJobWorker::StartPrinting,
|
||||
base::Unretained(worker_.get()), document_)));
|
||||
// Set the flag right now.
|
||||
is_job_pending_ = true;
|
||||
|
||||
// Tell everyone!
|
||||
scoped_refptr<JobEventDetails> details(
|
||||
new JobEventDetails(JobEventDetails::NEW_DOC, document_.get(), NULL));
|
||||
content::NotificationService::current()->Notify(
|
||||
chrome::NOTIFICATION_PRINT_JOB_EVENT,
|
||||
content::Source<PrintJob>(this),
|
||||
content::Details<JobEventDetails>(details.get()));
|
||||
}
|
||||
|
||||
void PrintJob::Stop() {
|
||||
DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
|
||||
|
||||
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_->message_loop()) {
|
||||
ControlledWorkerShutdown();
|
||||
} else {
|
||||
// Flush the cached document.
|
||||
UpdatePrintedDocument(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintJob::Cancel() {
|
||||
if (is_canceling_)
|
||||
return;
|
||||
is_canceling_ = true;
|
||||
|
||||
// Be sure to live long enough.
|
||||
scoped_refptr<PrintJob> handle(this);
|
||||
|
||||
DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
|
||||
base::MessageLoop* worker_loop =
|
||||
worker_.get() ? worker_->message_loop() : NULL;
|
||||
if (worker_loop) {
|
||||
// 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, NULL, NULL));
|
||||
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::MessageLoop::current()->PostDelayedTask(FROM_HERE,
|
||||
base::Bind(&PrintJob::Quit, quit_factory_.GetWeakPtr()), timeout);
|
||||
|
||||
base::MessageLoop::ScopedNestableTaskAllower allow(
|
||||
base::MessageLoop::current());
|
||||
base::MessageLoop::current()->Run();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PrintJob::DisconnectSource() {
|
||||
source_ = NULL;
|
||||
if (document_.get())
|
||||
document_->DisconnectSource();
|
||||
}
|
||||
|
||||
bool PrintJob::is_job_pending() const {
|
||||
return is_job_pending_;
|
||||
}
|
||||
|
||||
PrintedDocument* PrintJob::document() const {
|
||||
return document_.get();
|
||||
}
|
||||
|
||||
void PrintJob::UpdatePrintedDocument(PrintedDocument* new_document) {
|
||||
if (document_.get() == new_document)
|
||||
return;
|
||||
|
||||
document_ = new_document;
|
||||
|
||||
if (document_.get()) {
|
||||
settings_ = document_->settings();
|
||||
}
|
||||
|
||||
if (worker_.get() && worker_->message_loop()) {
|
||||
DCHECK(!is_job_pending_);
|
||||
// Sync the document with the worker.
|
||||
worker_->message_loop()->PostTask(
|
||||
FROM_HERE,
|
||||
base::Bind(&HoldRefCallback, make_scoped_refptr(this),
|
||||
base::Bind(&PrintJobWorker::OnDocumentChanged,
|
||||
base::Unretained(worker_.get()), 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::PAGE_DONE:
|
||||
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::MessageLoop::current()->PostTask(
|
||||
FROM_HERE, base::Bind(&PrintJob::OnDocumentDone, this));
|
||||
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, document_.get(), NULL));
|
||||
content::NotificationService::current()->Notify(
|
||||
chrome::NOTIFICATION_PRINT_JOB_EVENT,
|
||||
content::Source<PrintJob>(this),
|
||||
content::Details<JobEventDetails>(details.get()));
|
||||
}
|
||||
|
||||
void PrintJob::ControlledWorkerShutdown() {
|
||||
DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
|
||||
|
||||
// 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::MessageLoop::current()->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::WorkerPool::PostTaskAndReply(
|
||||
FROM_HERE,
|
||||
base::Bind(&PrintJobWorker::Stop, base::Unretained(worker_.get())),
|
||||
base::Bind(&PrintJob::HoldUntilStopIsCalled, this),
|
||||
false);
|
||||
|
||||
is_job_pending_ = false;
|
||||
registrar_.RemoveAll();
|
||||
UpdatePrintedDocument(NULL);
|
||||
}
|
||||
|
||||
void PrintJob::HoldUntilStopIsCalled() {
|
||||
}
|
||||
|
||||
void PrintJob::Quit() {
|
||||
base::MessageLoop::current()->Quit();
|
||||
}
|
||||
|
||||
// Takes settings_ ownership and will be deleted in the receiving thread.
|
||||
JobEventDetails::JobEventDetails(Type type,
|
||||
PrintedDocument* document,
|
||||
PrintedPage* page)
|
||||
: document_(document),
|
||||
page_(page),
|
||||
type_(type) {
|
||||
}
|
||||
|
||||
JobEventDetails::~JobEventDetails() {
|
||||
}
|
||||
|
||||
PrintedDocument* JobEventDetails::document() const { return document_.get(); }
|
||||
|
||||
PrintedPage* JobEventDetails::page() const { return page_.get(); }
|
||||
|
||||
} // namespace printing
|
212
chromium_src/chrome/browser/printing/print_job.h
Normal file
212
chromium_src/chrome/browser/printing/print_job.h
Normal file
|
@ -0,0 +1,212 @@
|
|||
// 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 "base/basictypes.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/message_loop/message_loop.h"
|
||||
#include "chrome/browser/printing/print_job_worker_owner.h"
|
||||
#include "content/public/browser/notification_observer.h"
|
||||
#include "content/public/browser/notification_registrar.h"
|
||||
|
||||
class Thread;
|
||||
|
||||
namespace printing {
|
||||
|
||||
// See definition below.
|
||||
class JobEventDetails;
|
||||
|
||||
class PrintedDocument;
|
||||
class PrintedPage;
|
||||
class PrintedPagesSource;
|
||||
class PrintJobWorker;
|
||||
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 base::MessageLoop::DestructionObserver {
|
||||
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, PrintedPagesSource* source,
|
||||
int page_count);
|
||||
|
||||
// content::NotificationObserver implementation.
|
||||
virtual void Observe(int type,
|
||||
const content::NotificationSource& source,
|
||||
const content::NotificationDetails& details) OVERRIDE;
|
||||
|
||||
// PrintJobWorkerOwner implementation.
|
||||
virtual void GetSettingsDone(const PrintSettings& new_settings,
|
||||
PrintingContext::Result result) OVERRIDE;
|
||||
virtual PrintJobWorker* DetachWorker(PrintJobWorkerOwner* new_owner) OVERRIDE;
|
||||
virtual base::MessageLoop* message_loop() OVERRIDE;
|
||||
virtual const PrintSettings& settings() const OVERRIDE;
|
||||
virtual int cookie() const OVERRIDE;
|
||||
|
||||
// DestructionObserver implementation.
|
||||
virtual void WillDestroyCurrentMessageLoop() 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);
|
||||
|
||||
// Disconnects the PrintedPage source (PrintedPagesSource). It is done when
|
||||
// the source is being destroyed.
|
||||
void DisconnectSource();
|
||||
|
||||
// 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;
|
||||
|
||||
protected:
|
||||
virtual ~PrintJob();
|
||||
|
||||
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();
|
||||
|
||||
content::NotificationRegistrar registrar_;
|
||||
|
||||
// Main message loop reference. Used to send notifications in the right
|
||||
// thread.
|
||||
base::MessageLoop* const ui_message_loop_;
|
||||
|
||||
// Source that generates the PrintedPage's (i.e. a WebContents). It will be
|
||||
// set back to NULL if the source is deleted before this object.
|
||||
PrintedPagesSource* source_;
|
||||
|
||||
// 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.
|
||||
scoped_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_;
|
||||
|
||||
// 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,
|
||||
};
|
||||
|
||||
JobEventDetails(Type type, PrintedDocument* document, PrintedPage* page);
|
||||
|
||||
// Getters.
|
||||
PrintedDocument* document() const;
|
||||
PrintedPage* page() const;
|
||||
Type type() const {
|
||||
return type_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class base::RefCountedThreadSafe<JobEventDetails>;
|
||||
|
||||
~JobEventDetails();
|
||||
|
||||
scoped_refptr<PrintedDocument> document_;
|
||||
scoped_refptr<PrintedPage> page_;
|
||||
const Type type_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(JobEventDetails);
|
||||
};
|
||||
|
||||
} // namespace printing
|
||||
|
||||
#endif // CHROME_BROWSER_PRINTING_PRINT_JOB_H_
|
159
chromium_src/chrome/browser/printing/print_job_manager.cc
Normal file
159
chromium_src/chrome/browser/printing/print_job_manager.cc
Normal file
|
@ -0,0 +1,159 @@
|
|||
// 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"
|
||||
#include "printing/printed_page.h"
|
||||
|
||||
namespace printing {
|
||||
|
||||
PrintQueriesQueue::PrintQueriesQueue() {
|
||||
}
|
||||
|
||||
PrintQueriesQueue::~PrintQueriesQueue() {
|
||||
base::AutoLock lock(lock_);
|
||||
queued_queries_.clear();
|
||||
}
|
||||
|
||||
void PrintQueriesQueue::SetDestination(PrintDestinationInterface* destination) {
|
||||
base::AutoLock lock(lock_);
|
||||
destination_ = destination;
|
||||
}
|
||||
|
||||
void PrintQueriesQueue::QueuePrinterQuery(PrinterQuery* job) {
|
||||
base::AutoLock lock(lock_);
|
||||
DCHECK(job);
|
||||
queued_queries_.push_back(make_scoped_refptr(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<printing::PrinterQuery> current_query(*itr);
|
||||
queued_queries_.erase(itr);
|
||||
DCHECK(current_query->is_valid());
|
||||
return current_query;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
scoped_refptr<PrinterQuery> PrintQueriesQueue::CreatePrinterQuery() {
|
||||
scoped_refptr<PrinterQuery> job = new printing::PrinterQuery;
|
||||
base::AutoLock lock(lock_);
|
||||
job->SetWorkerDestination(destination_);
|
||||
return job;
|
||||
}
|
||||
|
||||
void PrintQueriesQueue::Shutdown() {
|
||||
base::AutoLock lock(lock_);
|
||||
queued_queries_.clear();
|
||||
destination_ = NULL;
|
||||
}
|
||||
|
||||
PrintJobManager::PrintJobManager() : is_shutdown_(false) {
|
||||
registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT,
|
||||
content::NotificationService::AllSources());
|
||||
}
|
||||
|
||||
PrintJobManager::~PrintJobManager() {
|
||||
}
|
||||
|
||||
scoped_refptr<PrintQueriesQueue> PrintJobManager::queue() {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
||||
if (!queue_)
|
||||
queue_ = new PrintQueriesQueue();
|
||||
return queue_;
|
||||
}
|
||||
|
||||
void PrintJobManager::Shutdown() {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
||||
DCHECK(!is_shutdown_);
|
||||
is_shutdown_ = true;
|
||||
registrar_.RemoveAll();
|
||||
StopJobs(true);
|
||||
if (queue_)
|
||||
queue_->Shutdown();
|
||||
queue_ = NULL;
|
||||
}
|
||||
|
||||
void PrintJobManager::StopJobs(bool wait_for_finish) {
|
||||
DCHECK(content::BrowserThread::CurrentlyOn(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(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
||||
switch (type) {
|
||||
case chrome::NOTIFICATION_PRINT_JOB_EVENT: {
|
||||
OnPrintJobEvent(content::Source<PrintJob>(source).ptr(),
|
||||
*content::Details<JobEventDetails>(details).ptr());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
NOTREACHED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
104
chromium_src/chrome/browser/printing/print_job_manager.h
Normal file
104
chromium_src/chrome/browser/printing/print_job_manager.h
Normal file
|
@ -0,0 +1,104 @@
|
|||
// 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 <set>
|
||||
#include <vector>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "base/threading/non_thread_safe.h"
|
||||
#include "content/public/browser/notification_observer.h"
|
||||
#include "content/public/browser/notification_registrar.h"
|
||||
#include "printing/print_destination_interface.h"
|
||||
|
||||
namespace printing {
|
||||
|
||||
class JobEventDetails;
|
||||
class PrintJob;
|
||||
class PrinterQuery;
|
||||
|
||||
class PrintQueriesQueue : public base::RefCountedThreadSafe<PrintQueriesQueue> {
|
||||
public:
|
||||
PrintQueriesQueue();
|
||||
|
||||
// Sets the print destination to be set on the next print job.
|
||||
void SetDestination(PrintDestinationInterface* destination);
|
||||
|
||||
// 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();
|
||||
|
||||
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_;
|
||||
|
||||
scoped_refptr<PrintDestinationInterface> destination_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PrintQueriesQueue);
|
||||
};
|
||||
|
||||
class PrintJobManager : public content::NotificationObserver {
|
||||
public:
|
||||
PrintJobManager();
|
||||
virtual ~PrintJobManager();
|
||||
|
||||
// On browser quit, we should wait to have the print job finished.
|
||||
void Shutdown();
|
||||
|
||||
// content::NotificationObserver
|
||||
virtual 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_
|
387
chromium_src/chrome/browser/printing/print_job_worker.cc
Normal file
387
chromium_src/chrome/browser/printing/print_job_worker.cc
Normal file
|
@ -0,0 +1,387 @@
|
|||
// 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 "base/bind.h"
|
||||
#include "base/bind_helpers.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/message_loop/message_loop.h"
|
||||
#include "base/values.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/printing_ui_web_contents_observer.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/notification_service.h"
|
||||
#include "grit/generated_resources.h"
|
||||
#include "printing/print_job_constants.h"
|
||||
#include "printing/printed_document.h"
|
||||
#include "printing/printed_page.h"
|
||||
#include "printing/printing_utils.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace {
|
||||
|
||||
// Helper function to ensure |owner| is valid until at least |callback| returns.
|
||||
void HoldRefCallback(const scoped_refptr<printing::PrintJobWorkerOwner>& owner,
|
||||
const base::Closure& callback) {
|
||||
callback.Run();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace printing {
|
||||
|
||||
void NotificationCallback(PrintJobWorkerOwner* print_job,
|
||||
JobEventDetails::Type detail_type,
|
||||
PrintedDocument* document,
|
||||
PrintedPage* page) {
|
||||
JobEventDetails* details = new JobEventDetails(detail_type, 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));
|
||||
}
|
||||
|
||||
PrintJobWorker::PrintJobWorker(PrintJobWorkerOwner* owner)
|
||||
: Thread("Printing_Worker"),
|
||||
owner_(owner),
|
||||
weak_factory_(this) {
|
||||
// The object is created in the IO thread.
|
||||
DCHECK_EQ(owner_->message_loop(), base::MessageLoop::current());
|
||||
|
||||
printing_context_.reset(PrintingContext::Create(
|
||||
g_browser_process->GetApplicationLocale()));
|
||||
}
|
||||
|
||||
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_EQ(owner_->message_loop(), base::MessageLoop::current());
|
||||
Stop();
|
||||
}
|
||||
|
||||
void PrintJobWorker::SetNewOwner(PrintJobWorkerOwner* new_owner) {
|
||||
DCHECK(page_number_ == PageNumber::npos());
|
||||
owner_ = new_owner;
|
||||
}
|
||||
|
||||
void PrintJobWorker::SetPrintDestination(
|
||||
PrintDestinationInterface* destination) {
|
||||
destination_ = destination;
|
||||
}
|
||||
|
||||
void PrintJobWorker::GetSettings(
|
||||
bool ask_user_for_settings,
|
||||
scoped_ptr<PrintingUIWebContentsObserver> web_contents_observer,
|
||||
int document_page_count,
|
||||
bool has_selection,
|
||||
MarginType margin_type) {
|
||||
DCHECK_EQ(message_loop(), base::MessageLoop::current());
|
||||
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);
|
||||
|
||||
// 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 && destination_.get() == NULL) {
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
|
||||
base::Bind(&PrintJobWorker::GetSettingsWithUI,
|
||||
base::Unretained(this),
|
||||
base::Passed(&web_contents_observer),
|
||||
document_page_count,
|
||||
has_selection)));
|
||||
} else {
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
|
||||
base::Bind(&PrintJobWorker::UseDefaultSettings,
|
||||
base::Unretained(this))));
|
||||
}
|
||||
}
|
||||
|
||||
void PrintJobWorker::SetSettings(
|
||||
const base::DictionaryValue* const new_settings) {
|
||||
DCHECK_EQ(message_loop(), base::MessageLoop::current());
|
||||
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
|
||||
base::Bind(&PrintJobWorker::UpdatePrintSettings,
|
||||
base::Unretained(this), new_settings)));
|
||||
}
|
||||
|
||||
void PrintJobWorker::UpdatePrintSettings(
|
||||
const base::DictionaryValue* const new_settings) {
|
||||
// Create new PageRanges based on |new_settings|.
|
||||
PageRanges new_ranges;
|
||||
const base::ListValue* page_range_array;
|
||||
if (new_settings->GetList(kSettingPageRange, &page_range_array)) {
|
||||
for (size_t index = 0; index < page_range_array->GetSize(); ++index) {
|
||||
const base::DictionaryValue* dict;
|
||||
if (!page_range_array->GetDictionary(index, &dict))
|
||||
continue;
|
||||
|
||||
PageRange range;
|
||||
if (!dict->GetInteger(kSettingPageRangeFrom, &range.from) ||
|
||||
!dict->GetInteger(kSettingPageRangeTo, &range.to)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Page numbers are 1-based in the dictionary.
|
||||
// Page numbers are 0-based for the printing context.
|
||||
range.from--;
|
||||
range.to--;
|
||||
new_ranges.push_back(range);
|
||||
}
|
||||
}
|
||||
PrintingContext::Result result =
|
||||
printing_context_->UpdatePrintSettings(*new_settings, new_ranges);
|
||||
delete 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_->message_loop()->PostTask(
|
||||
FROM_HERE,
|
||||
base::Bind(&PrintJobWorkerOwner::GetSettingsDone,
|
||||
make_scoped_refptr(owner_), printing_context_->settings(),
|
||||
result));
|
||||
}
|
||||
|
||||
void PrintJobWorker::GetSettingsWithUI(
|
||||
scoped_ptr<PrintingUIWebContentsObserver> web_contents_observer,
|
||||
int document_page_count,
|
||||
bool has_selection) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
|
||||
gfx::NativeView parent_view = web_contents_observer->GetParentView();
|
||||
if (!parent_view) {
|
||||
GetSettingsWithUIDone(printing::PrintingContext::FAILED);
|
||||
return;
|
||||
}
|
||||
printing_context_->AskUserForSettings(
|
||||
parent_view, document_page_count, has_selection,
|
||||
base::Bind(&PrintJobWorker::GetSettingsWithUIDone,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
|
||||
void PrintJobWorker::GetSettingsWithUIDone(PrintingContext::Result result) {
|
||||
message_loop()->PostTask(
|
||||
FROM_HERE,
|
||||
base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
|
||||
base::Bind(&PrintJobWorker::GetSettingsDone,
|
||||
base::Unretained(this), result)));
|
||||
}
|
||||
|
||||
void PrintJobWorker::UseDefaultSettings() {
|
||||
PrintingContext::Result result = printing_context_->UseDefaultSettings();
|
||||
GetSettingsDone(result);
|
||||
}
|
||||
|
||||
void PrintJobWorker::StartPrinting(PrintedDocument* new_document) {
|
||||
DCHECK_EQ(message_loop(), base::MessageLoop::current());
|
||||
DCHECK_EQ(page_number_, PageNumber::npos());
|
||||
DCHECK_EQ(document_, 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());
|
||||
if (document_name.empty()) {
|
||||
// document_name = printing::SimplifyDocumentTitle(
|
||||
// l10n_util::GetStringUTF16(IDS_DEFAULT_PRINT_DOCUMENT_TITLE));
|
||||
}
|
||||
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_EQ(message_loop(), base::MessageLoop::current());
|
||||
DCHECK_EQ(page_number_, PageNumber::npos());
|
||||
|
||||
if (page_number_ != PageNumber::npos())
|
||||
return;
|
||||
|
||||
document_ = new_document;
|
||||
}
|
||||
|
||||
void PrintJobWorker::OnNewPage() {
|
||||
if (!document_.get()) // Spurious message.
|
||||
return;
|
||||
|
||||
// message_loop() could return NULL when the print job is cancelled.
|
||||
DCHECK_EQ(message_loop(), base::MessageLoop::current());
|
||||
|
||||
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);
|
||||
if (destination_.get() != NULL)
|
||||
destination_->SetPageCount(page_count);
|
||||
}
|
||||
DCHECK_NE(page_number_, PageNumber::npos());
|
||||
|
||||
while (true) {
|
||||
// Is the page available?
|
||||
scoped_refptr<PrintedPage> page;
|
||||
if (!document_->GetPage(page_number_.ToInt(), &page)) {
|
||||
// We need to wait for the page to be available.
|
||||
base::MessageLoop::current()->PostDelayedTask(
|
||||
FROM_HERE,
|
||||
base::Bind(&PrintJobWorker::OnNewPage, weak_factory_.GetWeakPtr()),
|
||||
base::TimeDelta::FromMilliseconds(500));
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
void PrintJobWorker::OnDocumentDone() {
|
||||
DCHECK_EQ(message_loop(), base::MessageLoop::current());
|
||||
DCHECK_EQ(page_number_, PageNumber::npos());
|
||||
DCHECK(document_.get());
|
||||
|
||||
if (printing_context_->DocumentDone() != PrintingContext::OK) {
|
||||
OnFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
owner_->message_loop()->PostTask(
|
||||
FROM_HERE, base::Bind(NotificationCallback, make_scoped_refptr(owner_),
|
||||
JobEventDetails::DOC_DONE, document_,
|
||||
scoped_refptr<PrintedPage>()));
|
||||
|
||||
// Makes sure the variables are reinitialized.
|
||||
document_ = NULL;
|
||||
}
|
||||
|
||||
void PrintJobWorker::SpoolPage(PrintedPage* page) {
|
||||
DCHECK_EQ(message_loop(), base::MessageLoop::current());
|
||||
DCHECK_NE(page_number_, PageNumber::npos());
|
||||
|
||||
// Signal everyone that the page is about to be printed.
|
||||
owner_->message_loop()->PostTask(
|
||||
FROM_HERE, base::Bind(NotificationCallback, make_scoped_refptr(owner_),
|
||||
JobEventDetails::NEW_PAGE, document_,
|
||||
make_scoped_refptr(page)));
|
||||
|
||||
// Preprocess.
|
||||
if (printing_context_->NewPage() != PrintingContext::OK) {
|
||||
OnFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
if (destination_.get() != NULL) {
|
||||
std::vector<uint8> metabytes(page->metafile()->GetDataSize());
|
||||
bool success = page->metafile()->GetData(
|
||||
reinterpret_cast<void*>(&metabytes[0]), metabytes.size());
|
||||
DCHECK(success) << "Failed to get metafile data.";
|
||||
destination_->SetPageContent(
|
||||
page->page_number(),
|
||||
reinterpret_cast<void*>(&metabytes[0]),
|
||||
metabytes.size());
|
||||
return;
|
||||
}
|
||||
|
||||
// Actual printing.
|
||||
#if defined(OS_WIN) || defined(OS_MACOSX)
|
||||
document_->RenderPrintedPage(*page, printing_context_->context());
|
||||
#elif defined(OS_POSIX)
|
||||
document_->RenderPrintedPage(*page, printing_context_.get());
|
||||
#endif
|
||||
|
||||
// Postprocess.
|
||||
if (printing_context_->PageDone() != PrintingContext::OK) {
|
||||
OnFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
// Signal everyone that the page is printed.
|
||||
owner_->message_loop()->PostTask(
|
||||
FROM_HERE,
|
||||
base::Bind(NotificationCallback, make_scoped_refptr(owner_),
|
||||
JobEventDetails::PAGE_DONE, document_,
|
||||
make_scoped_refptr(page)));
|
||||
}
|
||||
|
||||
void PrintJobWorker::OnFailure() {
|
||||
DCHECK_EQ(message_loop(), base::MessageLoop::current());
|
||||
|
||||
// We may loose our last reference by broadcasting the FAILED event.
|
||||
scoped_refptr<PrintJobWorkerOwner> handle(owner_);
|
||||
|
||||
owner_->message_loop()->PostTask(
|
||||
FROM_HERE, base::Bind(NotificationCallback, make_scoped_refptr(owner_),
|
||||
JobEventDetails::FAILED, document_,
|
||||
scoped_refptr<PrintedPage>()));
|
||||
Cancel();
|
||||
|
||||
// Makes sure the variables are reinitialized.
|
||||
document_ = NULL;
|
||||
page_number_ = PageNumber::npos();
|
||||
}
|
||||
|
||||
} // namespace printing
|
145
chromium_src/chrome/browser/printing/print_job_worker.h
Normal file
145
chromium_src/chrome/browser/printing/print_job_worker.h
Normal file
|
@ -0,0 +1,145 @@
|
|||
// 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 "base/memory/ref_counted.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/threading/thread.h"
|
||||
#include "printing/page_number.h"
|
||||
#include "printing/print_destination_interface.h"
|
||||
#include "printing/printing_context.h"
|
||||
#include "printing/print_job_constants.h"
|
||||
|
||||
class PrintingUIWebContentsObserver;
|
||||
|
||||
namespace base {
|
||||
class DictionaryValue;
|
||||
}
|
||||
|
||||
namespace printing {
|
||||
|
||||
class PrintedDocument;
|
||||
class PrintedPage;
|
||||
class PrintJob;
|
||||
class PrintJobWorkerOwner;
|
||||
|
||||
// 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 base::Thread {
|
||||
public:
|
||||
explicit PrintJobWorker(PrintJobWorkerOwner* owner);
|
||||
virtual ~PrintJobWorker();
|
||||
|
||||
void SetNewOwner(PrintJobWorkerOwner* new_owner);
|
||||
|
||||
// Set a destination for print.
|
||||
// This supercedes the document's rendering destination.
|
||||
void SetPrintDestination(PrintDestinationInterface* destination);
|
||||
|
||||
// Initializes the print settings. If |ask_user_for_settings| is true, a
|
||||
// Print... dialog box will be shown to ask the user his preference.
|
||||
void GetSettings(
|
||||
bool ask_user_for_settings,
|
||||
scoped_ptr<PrintingUIWebContentsObserver> web_contents_observer,
|
||||
int document_page_count,
|
||||
bool has_selection,
|
||||
MarginType margin_type);
|
||||
|
||||
// Set the new print settings. This function takes ownership of
|
||||
// |new_settings|.
|
||||
void SetSettings(const base::DictionaryValue* const 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();
|
||||
|
||||
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;
|
||||
|
||||
// Renders a page in the printer.
|
||||
void SpoolPage(PrintedPage* page);
|
||||
|
||||
// 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(
|
||||
scoped_ptr<PrintingUIWebContentsObserver> web_contents_observer,
|
||||
int document_page_count,
|
||||
bool has_selection);
|
||||
|
||||
// The callback used by PrintingContext::GetSettingsWithUI() to notify this
|
||||
// object that the print settings are set. This is needed in order to bounce
|
||||
// back into the IO thread for GetSettingsDone().
|
||||
void GetSettingsWithUIDone(PrintingContext::Result result);
|
||||
|
||||
// Called on the UI thread to update the print settings. This function takes
|
||||
// the ownership of |new_settings|.
|
||||
void UpdatePrintSettings(const base::DictionaryValue* const 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();
|
||||
|
||||
// Information about the printer setting.
|
||||
scoped_ptr<PrintingContext> printing_context_;
|
||||
|
||||
// The printed document. Only has read-only access.
|
||||
scoped_refptr<PrintedDocument> document_;
|
||||
|
||||
// The print destination, may be NULL.
|
||||
scoped_refptr<PrintDestinationInterface> destination_;
|
||||
|
||||
// The print job owning this worker thread. It is guaranteed to outlive this
|
||||
// object.
|
||||
PrintJobWorkerOwner* owner_;
|
||||
|
||||
// Current page number to print.
|
||||
PageNumber page_number_;
|
||||
|
||||
// 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_
|
|
@ -0,0 +1,50 @@
|
|||
// 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 "base/memory/ref_counted.h"
|
||||
#include "printing/printing_context.h"
|
||||
|
||||
namespace base {
|
||||
class MessageLoop;
|
||||
}
|
||||
|
||||
|
||||
namespace printing {
|
||||
|
||||
class PrintJobWorker;
|
||||
class PrintSettings;
|
||||
|
||||
class PrintJobWorkerOwner
|
||||
: public base::RefCountedThreadSafe<PrintJobWorkerOwner> {
|
||||
public:
|
||||
// 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 PrintJobWorker* DetachWorker(PrintJobWorkerOwner* new_owner) = 0;
|
||||
|
||||
// Retrieves the message loop that is expected to process GetSettingsDone.
|
||||
virtual base::MessageLoop* message_loop() = 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;
|
||||
|
||||
protected:
|
||||
friend class base::RefCountedThreadSafe<PrintJobWorkerOwner>;
|
||||
|
||||
virtual ~PrintJobWorkerOwner() {}
|
||||
};
|
||||
|
||||
} // namespace printing
|
||||
|
||||
#endif // CHROME_BROWSER_PRINTING_PRINT_JOB_WORKER_OWNER_H__
|
528
chromium_src/chrome/browser/printing/print_view_manager_base.cc
Normal file
528
chromium_src/chrome/browser/printing/print_view_manager_base.cc
Normal file
|
@ -0,0 +1,528 @@
|
|||
// 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 <map>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/prefs/pref_service.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 "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_view_host.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "grit/generated_resources.h"
|
||||
#include "printing/metafile_impl.h"
|
||||
#include "printing/printed_document.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "base/command_line.h"
|
||||
#include "chrome/common/chrome_switches.h"
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_FULL_PRINTING)
|
||||
#include "chrome/browser/printing/print_error_dialog.h"
|
||||
#endif
|
||||
|
||||
using base::TimeDelta;
|
||||
using content::BrowserThread;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Limits memory usage by raster to 64 MiB.
|
||||
const int kMaxRasterSizeInPixels = 16*1024*1024;
|
||||
#endif
|
||||
|
||||
namespace printing {
|
||||
|
||||
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_);
|
||||
#if defined(OS_POSIX) && !defined(OS_MACOSX)
|
||||
expecting_first_page_ = true;
|
||||
#endif
|
||||
printing_enabled_ = true;
|
||||
}
|
||||
|
||||
PrintViewManagerBase::~PrintViewManagerBase() {
|
||||
ReleasePrinterQuery();
|
||||
DisconnectFromCurrentPrintJob();
|
||||
}
|
||||
|
||||
bool PrintViewManagerBase::PrintNow() {
|
||||
return PrintNowInternal(new PrintMsg_PrintPages(routing_id()));
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::UpdateScriptedPrintingBlocked() {
|
||||
Send(new PrintMsg_SetScriptedPrintingBlocked(
|
||||
routing_id(),
|
||||
!printing_enabled_));
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
#if defined(OS_WIN) || defined(OS_MACOSX)
|
||||
const bool metafile_must_be_valid = true;
|
||||
#elif defined(OS_POSIX)
|
||||
const bool metafile_must_be_valid = expecting_first_page_;
|
||||
expecting_first_page_ = false;
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
scoped_ptr<NativeMetafile> metafile(new NativeMetafile);
|
||||
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)
|
||||
bool big_emf = (params.data_size && params.data_size >= kMetafileMaxSize);
|
||||
int raster_size = std::min(params.page_size.GetArea(),
|
||||
kMaxRasterSizeInPixels);
|
||||
if (big_emf) {
|
||||
scoped_ptr<NativeMetafile> raster_metafile(
|
||||
metafile->RasterizeMetafile(raster_size));
|
||||
if (raster_metafile.get()) {
|
||||
metafile.swap(raster_metafile);
|
||||
} else if (big_emf) {
|
||||
// Don't fall back to emf here.
|
||||
NOTREACHED() << "size:" << params.data_size;
|
||||
TerminatePrintJob(true);
|
||||
web_contents()->Stop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Update the rendered document. It will send notifications to the listener.
|
||||
document->SetPage(params.page_number,
|
||||
metafile.release(),
|
||||
params.actual_shrink,
|
||||
params.page_size,
|
||||
params.content_area);
|
||||
|
||||
ShouldQuitFromInnerMessageLoop();
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::OnPrintingFailed(int cookie) {
|
||||
if (cookie != cookie_) {
|
||||
NOTREACHED();
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(ENABLE_FULL_PRINTING)
|
||||
chrome::ShowPrintErrorDialog();
|
||||
#endif
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::DidStartLoading(
|
||||
content::RenderViewHost* render_view_host) {
|
||||
UpdateScriptedPrintingBlocked();
|
||||
}
|
||||
|
||||
bool PrintViewManagerBase::OnMessageReceived(const IPC::Message& message) {
|
||||
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::MessageLoop::current()->Quit();
|
||||
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, this, 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();
|
||||
}
|
||||
#if defined(OS_POSIX) && !defined(OS_MACOSX)
|
||||
expecting_first_page_ = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::PrintingDone(bool success) {
|
||||
if (!print_job_.get())
|
||||
return;
|
||||
Send(new PrintMsg_PrintingDone(routing_id(), success));
|
||||
}
|
||||
|
||||
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()));
|
||||
print_job_->DisconnectSource();
|
||||
// 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<base::MessageLoop> quit_timer;
|
||||
quit_timer.Start(FROM_HERE,
|
||||
TimeDelta::FromMilliseconds(kPrinterSettingsTimeout),
|
||||
base::MessageLoop::current(), &base::MessageLoop::Quit);
|
||||
|
||||
inside_inner_message_loop_ = true;
|
||||
|
||||
// Need to enable recursive task.
|
||||
{
|
||||
base::MessageLoop::ScopedNestableTaskAllower allow(
|
||||
base::MessageLoop::current());
|
||||
base::MessageLoop::current()->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) {
|
||||
NOTREACHED();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CreateNewPrintJob(queued_query)) {
|
||||
// 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(IPC::Message* message) {
|
||||
// Don't print / print preview interstitials.
|
||||
if (web_contents()->ShowingInterstitialPage()) {
|
||||
delete message;
|
||||
return false;
|
||||
}
|
||||
return Send(message);
|
||||
}
|
||||
|
||||
void PrintViewManagerBase::ReleasePrinterQuery() {
|
||||
if (!cookie_)
|
||||
return;
|
||||
|
||||
int cookie = cookie_;
|
||||
cookie_ = 0;
|
||||
queue_->SetDestination(NULL);
|
||||
|
||||
|
||||
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)
|
||||
return;
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&PrinterQuery::StopWorker, printer_query.get()));
|
||||
}
|
||||
|
||||
} // namespace printing
|
167
chromium_src/chrome/browser/printing/print_view_manager_base.h
Normal file
167
chromium_src/chrome/browser/printing/print_view_manager_base.h
Normal file
|
@ -0,0 +1,167 @@
|
|||
// 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 "base/memory/ref_counted.h"
|
||||
#include "base/prefs/pref_member.h"
|
||||
#include "base/strings/string16.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"
|
||||
#include "printing/printed_pages_source.h"
|
||||
|
||||
struct PrintHostMsg_DidPrintPage_Params;
|
||||
|
||||
namespace content {
|
||||
class RenderViewHost;
|
||||
}
|
||||
|
||||
namespace printing {
|
||||
|
||||
class JobEventDetails;
|
||||
class PrintJob;
|
||||
class PrintJobWorkerOwner;
|
||||
class PrintQueriesQueue;
|
||||
|
||||
// Base class for managing the print commands for a WebContents.
|
||||
class PrintViewManagerBase : public content::NotificationObserver,
|
||||
public PrintedPagesSource,
|
||||
public content::WebContentsObserver {
|
||||
public:
|
||||
virtual ~PrintViewManagerBase();
|
||||
|
||||
// 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();
|
||||
|
||||
// Whether to block scripted printing for our tab or not.
|
||||
void UpdateScriptedPrintingBlocked();
|
||||
|
||||
// PrintedPagesSource implementation.
|
||||
virtual base::string16 RenderSourceName() OVERRIDE;
|
||||
|
||||
protected:
|
||||
explicit PrintViewManagerBase(content::WebContents* web_contents);
|
||||
|
||||
// Helper method for Print*Now().
|
||||
bool PrintNowInternal(IPC::Message* message);
|
||||
|
||||
// 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;
|
||||
|
||||
// IPC Message handlers.
|
||||
virtual void OnPrintingFailed(int cookie);
|
||||
|
||||
private:
|
||||
// content::NotificationObserver implementation.
|
||||
virtual void Observe(int type,
|
||||
const content::NotificationSource& source,
|
||||
const content::NotificationDetails& details) OVERRIDE;
|
||||
|
||||
// content::WebContentsObserver implementation.
|
||||
virtual void DidStartLoading(
|
||||
content::RenderViewHost* render_view_host) OVERRIDE;
|
||||
|
||||
// Cancels the print job.
|
||||
virtual 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_;
|
||||
|
||||
#if defined(OS_POSIX) && !defined(OS_MACOSX)
|
||||
// Set to true when OnDidPrintPage() should be expecting the first page.
|
||||
bool expecting_first_page_;
|
||||
#endif
|
||||
|
||||
// The document cookie of the current PrinterQuery.
|
||||
int cookie_;
|
||||
|
||||
// Whether printing is enabled.
|
||||
bool printing_enabled_;
|
||||
|
||||
scoped_refptr<printing::PrintQueriesQueue> queue_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PrintViewManagerBase);
|
||||
};
|
||||
|
||||
} // namespace printing
|
||||
|
||||
#endif // CHROME_BROWSER_PRINTING_PRINT_VIEW_MANAGER_BASE_H_
|
|
@ -0,0 +1,48 @@
|
|||
// 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
|
|
@ -0,0 +1,57 @@
|
|||
// 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:
|
||||
virtual ~PrintViewManagerBasic();
|
||||
|
||||
#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_
|
|
@ -0,0 +1,23 @@
|
|||
// 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_
|
143
chromium_src/chrome/browser/printing/printer_query.cc
Normal file
143
chromium_src/chrome/browser/printing/printer_query.cc
Normal file
|
@ -0,0 +1,143 @@
|
|||
// 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 "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"
|
||||
#include "chrome/browser/printing/printing_ui_web_contents_observer.h"
|
||||
|
||||
namespace printing {
|
||||
|
||||
PrinterQuery::PrinterQuery()
|
||||
: io_message_loop_(base::MessageLoop::current()),
|
||||
worker_(new PrintJobWorker(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_.get());
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
PrintJobWorker* PrinterQuery::DetachWorker(PrintJobWorkerOwner* new_owner) {
|
||||
DCHECK(callback_.is_null());
|
||||
DCHECK(worker_.get());
|
||||
|
||||
worker_->SetNewOwner(new_owner);
|
||||
return worker_.release();
|
||||
}
|
||||
|
||||
base::MessageLoop* PrinterQuery::message_loop() {
|
||||
return io_message_loop_;
|
||||
}
|
||||
|
||||
const PrintSettings& PrinterQuery::settings() const {
|
||||
return settings_;
|
||||
}
|
||||
|
||||
int PrinterQuery::cookie() const {
|
||||
return cookie_;
|
||||
}
|
||||
|
||||
void PrinterQuery::GetSettings(
|
||||
GetSettingsAskParam ask_user_for_settings,
|
||||
scoped_ptr<PrintingUIWebContentsObserver> web_contents_observer,
|
||||
int expected_page_count,
|
||||
bool has_selection,
|
||||
MarginType margin_type,
|
||||
const base::Closure& callback) {
|
||||
DCHECK_EQ(io_message_loop_, base::MessageLoop::current());
|
||||
DCHECK(!is_print_dialog_box_shown_);
|
||||
|
||||
StartWorker(callback);
|
||||
|
||||
// Real work is done in PrintJobWorker::GetSettings().
|
||||
is_print_dialog_box_shown_ = ask_user_for_settings == ASK_USER;
|
||||
worker_->message_loop()->PostTask(
|
||||
FROM_HERE,
|
||||
base::Bind(&PrintJobWorker::GetSettings,
|
||||
base::Unretained(worker_.get()),
|
||||
is_print_dialog_box_shown_,
|
||||
base::Passed(&web_contents_observer),
|
||||
expected_page_count,
|
||||
has_selection,
|
||||
margin_type));
|
||||
}
|
||||
|
||||
void PrinterQuery::SetSettings(const base::DictionaryValue& new_settings,
|
||||
const base::Closure& callback) {
|
||||
StartWorker(callback);
|
||||
|
||||
worker_->message_loop()->PostTask(
|
||||
FROM_HERE,
|
||||
base::Bind(&PrintJobWorker::SetSettings,
|
||||
base::Unretained(worker_.get()),
|
||||
new_settings.DeepCopy()));
|
||||
}
|
||||
|
||||
void PrinterQuery::SetWorkerDestination(
|
||||
PrintDestinationInterface* destination) {
|
||||
worker_->SetPrintDestination(destination);
|
||||
}
|
||||
|
||||
void PrinterQuery::StartWorker(const base::Closure& callback) {
|
||||
DCHECK(callback_.is_null());
|
||||
DCHECK(worker_.get());
|
||||
|
||||
// Lazily create the worker thread. There is one worker thread per print job.
|
||||
if (!worker_->message_loop())
|
||||
worker_->Start();
|
||||
|
||||
callback_ = callback;
|
||||
}
|
||||
|
||||
void PrinterQuery::StopWorker() {
|
||||
if (worker_.get()) {
|
||||
// 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_.get() != NULL;
|
||||
}
|
||||
|
||||
} // namespace printing
|
110
chromium_src/chrome/browser/printing/printer_query.h
Normal file
110
chromium_src/chrome/browser/printing/printer_query.h
Normal file
|
@ -0,0 +1,110 @@
|
|||
// 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 "base/callback.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "chrome/browser/printing/print_job_worker_owner.h"
|
||||
#include "printing/print_job_constants.h"
|
||||
|
||||
class PrintingUIWebContentsObserver;
|
||||
|
||||
namespace base {
|
||||
class DictionaryValue;
|
||||
class MessageLoop;
|
||||
}
|
||||
|
||||
namespace printing {
|
||||
|
||||
class PrintDestinationInterface;
|
||||
class PrintJobWorker;
|
||||
|
||||
// Query the printer for settings.
|
||||
class PrinterQuery : public PrintJobWorkerOwner {
|
||||
public:
|
||||
// GetSettings() UI parameter.
|
||||
enum GetSettingsAskParam {
|
||||
DEFAULTS,
|
||||
ASK_USER,
|
||||
};
|
||||
|
||||
PrinterQuery();
|
||||
|
||||
// PrintJobWorkerOwner implementation.
|
||||
virtual void GetSettingsDone(const PrintSettings& new_settings,
|
||||
PrintingContext::Result result) OVERRIDE;
|
||||
virtual PrintJobWorker* DetachWorker(PrintJobWorkerOwner* new_owner) OVERRIDE;
|
||||
virtual base::MessageLoop* message_loop() OVERRIDE;
|
||||
virtual const PrintSettings& settings() const OVERRIDE;
|
||||
virtual 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,
|
||||
scoped_ptr<PrintingUIWebContentsObserver> web_contents_observer,
|
||||
int expected_page_count,
|
||||
bool has_selection,
|
||||
MarginType margin_type,
|
||||
const base::Closure& callback);
|
||||
|
||||
// Updates the current settings with |new_settings| dictionary values.
|
||||
void SetSettings(const base::DictionaryValue& new_settings,
|
||||
const base::Closure& callback);
|
||||
|
||||
// Set a destination for the worker.
|
||||
void SetWorkerDestination(PrintDestinationInterface* destination);
|
||||
|
||||
// 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:
|
||||
virtual ~PrinterQuery();
|
||||
|
||||
// Lazy create the worker thread. There is one worker thread per print job.
|
||||
void StartWorker(const base::Closure& callback);
|
||||
|
||||
// Main message loop reference. Used to send notifications in the right
|
||||
// thread.
|
||||
base::MessageLoop* const io_message_loop_;
|
||||
|
||||
// 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.
|
||||
scoped_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_
|
479
chromium_src/chrome/browser/printing/printing_message_filter.cc
Normal file
479
chromium_src/chrome/browser/printing/printing_message_filter.cc
Normal file
|
@ -0,0 +1,479 @@
|
|||
// 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/printer_query.h"
|
||||
#include "chrome/browser/printing/print_job_manager.h"
|
||||
#include "chrome/browser/printing/printing_ui_web_contents_observer.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_view_host.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
|
||||
#if defined(ENABLE_FULL_PRINTING)
|
||||
#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
|
||||
#endif
|
||||
|
||||
#if defined(OS_CHROMEOS)
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "base/file_util.h"
|
||||
#include "base/lazy_instance.h"
|
||||
#include "chrome/browser/printing/print_dialog_cloud.h"
|
||||
#endif
|
||||
|
||||
#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 {
|
||||
|
||||
#if defined(OS_CHROMEOS)
|
||||
typedef std::map<int, base::FilePath> SequenceToPathMap;
|
||||
|
||||
struct PrintingSequencePathMap {
|
||||
SequenceToPathMap map;
|
||||
int sequence;
|
||||
};
|
||||
|
||||
// No locking, only access on the FILE thread.
|
||||
static base::LazyInstance<PrintingSequencePathMap>
|
||||
g_printing_file_descriptor_map = LAZY_INSTANCE_INITIALIZER;
|
||||
#endif
|
||||
|
||||
void RenderParamsFromPrintSettings(const printing::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();
|
||||
// Currently hardcoded at 1.25. See PrintSettings' constructor.
|
||||
params->min_shrink = settings.min_shrink();
|
||||
// Currently hardcoded at 2.0. See PrintSettings' constructor.
|
||||
params->max_shrink = settings.max_shrink();
|
||||
// Currently hardcoded at 72dpi. See PrintSettings' constructor.
|
||||
params->desired_dpi = settings.desired_dpi();
|
||||
// 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();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PrintingMessageFilter::PrintingMessageFilter(int render_process_id,
|
||||
Profile* profile)
|
||||
: BrowserMessageFilter(PrintMsgStart),
|
||||
// profile_io_data_(ProfileIOData::FromResourceContext(
|
||||
// profile->GetResourceContext())),
|
||||
render_process_id_(render_process_id),
|
||||
queue_(g_browser_process->print_job_manager()->queue()) {
|
||||
DCHECK(queue_);
|
||||
}
|
||||
|
||||
PrintingMessageFilter::~PrintingMessageFilter() {
|
||||
}
|
||||
|
||||
void PrintingMessageFilter::OverrideThreadForMessage(
|
||||
const IPC::Message& message, BrowserThread::ID* thread) {
|
||||
#if defined(OS_CHROMEOS)
|
||||
if (message.type() == PrintHostMsg_AllocateTempFileForPrinting::ID ||
|
||||
message.type() == PrintHostMsg_TempFileForPrintingWritten::ID) {
|
||||
*thread = BrowserThread::FILE;
|
||||
}
|
||||
#elif 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* message_was_ok) {
|
||||
bool handled = true;
|
||||
IPC_BEGIN_MESSAGE_MAP_EX(PrintingMessageFilter, message, *message_was_ok)
|
||||
#if defined(OS_WIN)
|
||||
IPC_MESSAGE_HANDLER(PrintHostMsg_DuplicateSection, OnDuplicateSection)
|
||||
#endif
|
||||
#if defined(OS_CHROMEOS) || defined(OS_ANDROID)
|
||||
IPC_MESSAGE_HANDLER(PrintHostMsg_AllocateTempFileForPrinting,
|
||||
OnAllocateTempFileForPrinting)
|
||||
IPC_MESSAGE_HANDLER(PrintHostMsg_TempFileForPrintingWritten,
|
||||
OnTempFileForPrintingWritten)
|
||||
#endif
|
||||
IPC_MESSAGE_HANDLER(PrintHostMsg_IsPrintingEnabled, OnIsPrintingEnabled)
|
||||
IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_GetDefaultPrintSettings,
|
||||
OnGetDefaultPrintSettings)
|
||||
IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_ScriptedPrint, OnScriptedPrint)
|
||||
IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_UpdatePrintSettings,
|
||||
OnUpdatePrintSettings)
|
||||
#if defined(ENABLE_FULL_PRINTING)
|
||||
IPC_MESSAGE_HANDLER(PrintHostMsg_CheckForCancel, OnCheckForCancel)
|
||||
#endif
|
||||
IPC_MESSAGE_UNHANDLED(handled = false)
|
||||
IPC_END_MESSAGE_MAP()
|
||||
return handled;
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
void PrintingMessageFilter::OnDuplicateSection(
|
||||
base::SharedMemoryHandle renderer_handle,
|
||||
base::SharedMemoryHandle* browser_handle) {
|
||||
// Duplicate the handle in this process right now so the memory is kept alive
|
||||
// (even if it is not mapped)
|
||||
base::SharedMemory shared_buf(renderer_handle, true, PeerHandle());
|
||||
shared_buf.GiveToProcess(base::GetCurrentProcessHandle(), browser_handle);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(OS_CHROMEOS) || defined(OS_ANDROID)
|
||||
void PrintingMessageFilter::OnAllocateTempFileForPrinting(
|
||||
int render_view_id,
|
||||
base::FileDescriptor* temp_file_fd,
|
||||
int* sequence_number) {
|
||||
#if defined(OS_CHROMEOS)
|
||||
// TODO(thestig): Use |render_view_id| for Chrome OS.
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::FILE);
|
||||
temp_file_fd->fd = *sequence_number = -1;
|
||||
temp_file_fd->auto_close = false;
|
||||
|
||||
SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map;
|
||||
*sequence_number = g_printing_file_descriptor_map.Get().sequence++;
|
||||
|
||||
base::FilePath path;
|
||||
if (base::CreateTemporaryFile(&path)) {
|
||||
int fd = open(path.value().c_str(), O_WRONLY);
|
||||
if (fd >= 0) {
|
||||
SequenceToPathMap::iterator it = map->find(*sequence_number);
|
||||
if (it != map->end()) {
|
||||
NOTREACHED() << "Sequence number already in use. seq=" <<
|
||||
*sequence_number;
|
||||
} else {
|
||||
(*map)[*sequence_number] = path;
|
||||
temp_file_fd->fd = fd;
|
||||
temp_file_fd->auto_close = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif defined(OS_ANDROID)
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
content::WebContents* wc = GetWebContentsForRenderView(render_view_id);
|
||||
if (!wc)
|
||||
return;
|
||||
printing::PrintViewManagerBasic* print_view_manager =
|
||||
printing::PrintViewManagerBasic::FromWebContents(wc);
|
||||
// The file descriptor is originally created in & passed from the Android
|
||||
// side, and it will handle the closing.
|
||||
const base::FileDescriptor& file_descriptor =
|
||||
print_view_manager->file_descriptor();
|
||||
temp_file_fd->fd = file_descriptor.fd;
|
||||
temp_file_fd->auto_close = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void PrintingMessageFilter::OnTempFileForPrintingWritten(int render_view_id,
|
||||
int sequence_number) {
|
||||
#if defined(OS_CHROMEOS)
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::FILE);
|
||||
SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map;
|
||||
SequenceToPathMap::iterator it = map->find(sequence_number);
|
||||
if (it == map->end()) {
|
||||
NOTREACHED() << "Got a sequence that we didn't pass to the "
|
||||
"renderer: " << sequence_number;
|
||||
return;
|
||||
}
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&PrintingMessageFilter::CreatePrintDialogForFile,
|
||||
this, render_view_id, it->second));
|
||||
|
||||
// Erase the entry in the map.
|
||||
map->erase(it);
|
||||
#elif defined(OS_ANDROID)
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
content::WebContents* wc = GetWebContentsForRenderView(render_view_id);
|
||||
if (!wc)
|
||||
return;
|
||||
printing::PrintViewManagerBasic* print_view_manager =
|
||||
printing::PrintViewManagerBasic::FromWebContents(wc);
|
||||
const base::FileDescriptor& file_descriptor =
|
||||
print_view_manager->file_descriptor();
|
||||
printing::PrintingContextAndroid::PdfWritingDone(file_descriptor.fd, true);
|
||||
// Invalidate the file descriptor so it doesn't accidentally get reused.
|
||||
print_view_manager->set_file_descriptor(base::FileDescriptor(-1, false));
|
||||
#endif
|
||||
}
|
||||
#endif // defined(OS_CHROMEOS) || defined(OS_ANDROID)
|
||||
|
||||
#if defined(OS_CHROMEOS)
|
||||
void PrintingMessageFilter::CreatePrintDialogForFile(
|
||||
int render_view_id,
|
||||
const base::FilePath& path) {
|
||||
content::WebContents* wc = GetWebContentsForRenderView(render_view_id);
|
||||
if (!wc)
|
||||
return;
|
||||
print_dialog_cloud::CreatePrintDialogForFile(
|
||||
wc->GetBrowserContext(),
|
||||
wc->GetTopLevelNativeWindow(),
|
||||
path,
|
||||
wc->GetTitle(),
|
||||
base::string16(),
|
||||
std::string("application/pdf"));
|
||||
}
|
||||
#endif // defined(OS_CHROMEOS)
|
||||
|
||||
content::WebContents* PrintingMessageFilter::GetWebContentsForRenderView(
|
||||
int render_view_id) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
content::RenderViewHost* view = content::RenderViewHost::FromID(
|
||||
render_process_id_, render_view_id);
|
||||
return view ? content::WebContents::FromRenderViewHost(view) : NULL;
|
||||
}
|
||||
|
||||
struct PrintingMessageFilter::GetPrintSettingsForRenderViewParams {
|
||||
printing::PrinterQuery::GetSettingsAskParam ask_user_for_settings;
|
||||
int expected_page_count;
|
||||
bool has_selection;
|
||||
printing::MarginType margin_type;
|
||||
};
|
||||
|
||||
void PrintingMessageFilter::GetPrintSettingsForRenderView(
|
||||
int render_view_id,
|
||||
GetPrintSettingsForRenderViewParams params,
|
||||
const base::Closure& callback,
|
||||
scoped_refptr<printing::PrinterQuery> printer_query) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
content::WebContents* wc = GetWebContentsForRenderView(render_view_id);
|
||||
if (wc) {
|
||||
scoped_ptr<PrintingUIWebContentsObserver> wc_observer(
|
||||
new PrintingUIWebContentsObserver(wc));
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&printing::PrinterQuery::GetSettings, printer_query,
|
||||
params.ask_user_for_settings, base::Passed(&wc_observer),
|
||||
params.expected_page_count, params.has_selection,
|
||||
params.margin_type, callback));
|
||||
} else {
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::IO, FROM_HERE,
|
||||
base::Bind(&PrintingMessageFilter::OnGetPrintSettingsFailed, this,
|
||||
callback, printer_query));
|
||||
}
|
||||
}
|
||||
|
||||
void PrintingMessageFilter::OnGetPrintSettingsFailed(
|
||||
const base::Closure& callback,
|
||||
scoped_refptr<printing::PrinterQuery> printer_query) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
printer_query->GetSettingsDone(printing::PrintSettings(),
|
||||
printing::PrintingContext::FAILED);
|
||||
callback.Run();
|
||||
}
|
||||
|
||||
void PrintingMessageFilter::OnIsPrintingEnabled(bool* is_enabled) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
*is_enabled = true;
|
||||
}
|
||||
|
||||
void PrintingMessageFilter::OnGetDefaultPrintSettings(IPC::Message* reply_msg) {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::IO);
|
||||
scoped_refptr<printing::PrinterQuery> printer_query;
|
||||
if (false) {
|
||||
// Reply with NULL query.
|
||||
OnGetDefaultPrintSettingsReply(printer_query, reply_msg);
|
||||
return;
|
||||
}
|
||||
printer_query = queue_->PopPrinterQuery(0);
|
||||
if (!printer_query)
|
||||
printer_query = queue_->CreatePrinterQuery();
|
||||
|
||||
// Loads default settings. This is asynchronous, only the IPC message sender
|
||||
// will hang until the settings are retrieved.
|
||||
GetPrintSettingsForRenderViewParams params;
|
||||
params.ask_user_for_settings = printing::PrinterQuery::DEFAULTS;
|
||||
params.expected_page_count = 0;
|
||||
params.has_selection = false;
|
||||
params.margin_type = printing::DEFAULT_MARGINS;
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&PrintingMessageFilter::GetPrintSettingsForRenderView, this,
|
||||
reply_msg->routing_id(), params,
|
||||
base::Bind(&PrintingMessageFilter::OnGetDefaultPrintSettingsReply,
|
||||
this, printer_query, reply_msg),
|
||||
printer_query));
|
||||
}
|
||||
|
||||
void PrintingMessageFilter::OnGetDefaultPrintSettingsReply(
|
||||
scoped_refptr<printing::PrinterQuery> printer_query,
|
||||
IPC::Message* reply_msg) {
|
||||
PrintMsg_Print_Params params;
|
||||
if (!printer_query.get() ||
|
||||
printer_query->last_status() != printing::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<printing::PrinterQuery> printer_query =
|
||||
queue_->PopPrinterQuery(params.cookie);
|
||||
if (!printer_query)
|
||||
printer_query = queue_->CreatePrinterQuery();
|
||||
GetPrintSettingsForRenderViewParams settings_params;
|
||||
settings_params.ask_user_for_settings = printing::PrinterQuery::ASK_USER;
|
||||
settings_params.expected_page_count = params.expected_pages_count;
|
||||
settings_params.has_selection = params.has_selection;
|
||||
settings_params.margin_type = params.margin_type;
|
||||
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&PrintingMessageFilter::GetPrintSettingsForRenderView, this,
|
||||
reply_msg->routing_id(), settings_params,
|
||||
base::Bind(&PrintingMessageFilter::OnScriptedPrintReply, this,
|
||||
printer_query, reply_msg),
|
||||
printer_query));
|
||||
}
|
||||
|
||||
void PrintingMessageFilter::OnScriptedPrintReply(
|
||||
scoped_refptr<printing::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() != printing::PrintingContext::OK ||
|
||||
!printer_query->settings().dpi()) {
|
||||
params.Reset();
|
||||
} else {
|
||||
RenderParamsFromPrintSettings(printer_query->settings(), ¶ms.params);
|
||||
params.params.document_cookie = printer_query->cookie();
|
||||
params.pages =
|
||||
printing::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;
|
||||
printing::PrintViewManagerBasic* print_view_manager =
|
||||
printing::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) {
|
||||
scoped_refptr<printing::PrinterQuery> printer_query;
|
||||
if (false) {
|
||||
// Reply with NULL query.
|
||||
OnUpdatePrintSettingsReply(printer_query, reply_msg);
|
||||
return;
|
||||
}
|
||||
printer_query = queue_->PopPrinterQuery(document_cookie);
|
||||
if (!printer_query)
|
||||
printer_query = queue_->CreatePrinterQuery();
|
||||
printer_query->SetSettings(
|
||||
job_settings,
|
||||
base::Bind(&PrintingMessageFilter::OnUpdatePrintSettingsReply, this,
|
||||
printer_query, reply_msg));
|
||||
}
|
||||
|
||||
void PrintingMessageFilter::OnUpdatePrintSettingsReply(
|
||||
scoped_refptr<printing::PrinterQuery> printer_query,
|
||||
IPC::Message* reply_msg) {
|
||||
PrintMsg_PrintPages_Params params;
|
||||
if (!printer_query.get() ||
|
||||
printer_query->last_status() != printing::PrintingContext::OK) {
|
||||
params.Reset();
|
||||
} else {
|
||||
RenderParamsFromPrintSettings(printer_query->settings(), ¶ms.params);
|
||||
params.params.document_cookie = printer_query->cookie();
|
||||
params.pages =
|
||||
printing::PageRange::GetPages(printer_query->settings().ranges());
|
||||
}
|
||||
PrintHostMsg_UpdatePrintSettings::WriteReplyParams(reply_msg, params);
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(ENABLE_FULL_PRINTING)
|
||||
void PrintingMessageFilter::OnCheckForCancel(int32 preview_ui_id,
|
||||
int preview_request_id,
|
||||
bool* cancel) {
|
||||
PrintPreviewUI::GetCurrentPrintPreviewStatus(preview_ui_id,
|
||||
preview_request_id,
|
||||
cancel);
|
||||
}
|
||||
#endif
|
143
chromium_src/chrome/browser/printing/printing_message_filter.h
Normal file
143
chromium_src/chrome/browser/printing/printing_message_filter.h
Normal file
|
@ -0,0 +1,143 @@
|
|||
// 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"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "base/memory/shared_memory.h"
|
||||
#endif
|
||||
|
||||
struct PrintHostMsg_ScriptedPrint_Params;
|
||||
class Profile;
|
||||
class ProfileIOData;
|
||||
|
||||
namespace base {
|
||||
class DictionaryValue;
|
||||
class FilePath;
|
||||
}
|
||||
|
||||
namespace content {
|
||||
class WebContents;
|
||||
}
|
||||
|
||||
namespace printing {
|
||||
class PrinterQuery;
|
||||
class PrintJobManager;
|
||||
class PrintQueriesQueue;
|
||||
}
|
||||
|
||||
// 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, Profile* profile);
|
||||
|
||||
// content::BrowserMessageFilter methods.
|
||||
virtual void OverrideThreadForMessage(
|
||||
const IPC::Message& message,
|
||||
content::BrowserThread::ID* thread) OVERRIDE;
|
||||
virtual bool OnMessageReceived(const IPC::Message& message,
|
||||
bool* message_was_ok) OVERRIDE;
|
||||
|
||||
private:
|
||||
virtual ~PrintingMessageFilter();
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Used to pass resulting EMF from renderer to browser in printing.
|
||||
void OnDuplicateSection(base::SharedMemoryHandle renderer_handle,
|
||||
base::SharedMemoryHandle* browser_handle);
|
||||
#endif
|
||||
|
||||
#if defined(OS_CHROMEOS) || 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_view_id,
|
||||
base::FileDescriptor* temp_file_fd,
|
||||
int* sequence_number);
|
||||
void OnTempFileForPrintingWritten(int render_view_id, int sequence_number);
|
||||
#endif
|
||||
|
||||
#if defined(OS_CHROMEOS)
|
||||
void CreatePrintDialogForFile(int render_view_id, const base::FilePath& path);
|
||||
#endif
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
// Updates the file descriptor for the PrintViewManagerBasic of a given
|
||||
// render_view_id.
|
||||
void UpdateFileDescriptor(int render_view_id, int fd);
|
||||
#endif
|
||||
|
||||
// Given a render_view_id get the corresponding WebContents.
|
||||
// Must be called on the UI thread.
|
||||
content::WebContents* GetWebContentsForRenderView(int render_view_id);
|
||||
|
||||
// GetPrintSettingsForRenderView must be called via PostTask and
|
||||
// base::Bind. Collapse the settings-specific params into a
|
||||
// struct to avoid running into issues with too many params
|
||||
// to base::Bind.
|
||||
struct GetPrintSettingsForRenderViewParams;
|
||||
|
||||
// Retrieve print settings. Uses |render_view_id| to get a parent
|
||||
// for any UI created if needed.
|
||||
void GetPrintSettingsForRenderView(
|
||||
int render_view_id,
|
||||
GetPrintSettingsForRenderViewParams params,
|
||||
const base::Closure& callback,
|
||||
scoped_refptr<printing::PrinterQuery> printer_query);
|
||||
|
||||
void OnGetPrintSettingsFailed(
|
||||
const base::Closure& callback,
|
||||
scoped_refptr<printing::PrinterQuery> printer_query);
|
||||
|
||||
// Checks if printing is enabled.
|
||||
void OnIsPrintingEnabled(bool* is_enabled);
|
||||
|
||||
// Get the default print setting.
|
||||
void OnGetDefaultPrintSettings(IPC::Message* reply_msg);
|
||||
void OnGetDefaultPrintSettingsReply(
|
||||
scoped_refptr<printing::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<printing::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<printing::PrinterQuery> printer_query,
|
||||
IPC::Message* reply_msg);
|
||||
|
||||
#if defined(ENABLE_FULL_PRINTING)
|
||||
// Check to see if print preview has been cancelled.
|
||||
void OnCheckForCancel(int32 preview_ui_id,
|
||||
int preview_request_id,
|
||||
bool* cancel);
|
||||
#endif
|
||||
|
||||
// ProfileIOData* profile_io_data_;
|
||||
|
||||
const int render_process_id_;
|
||||
|
||||
scoped_refptr<printing::PrintQueriesQueue> queue_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PrintingMessageFilter);
|
||||
};
|
||||
|
||||
#endif // CHROME_BROWSER_PRINTING_PRINTING_MESSAGE_FILTER_H_
|
|
@ -0,0 +1,19 @@
|
|||
// 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;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// 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 "base/basictypes.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