summaryrefslogtreecommitdiffstats
path: root/chrome/browser/printing/print_job.cc
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/printing/print_job.cc
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/printing/print_job.cc')
-rw-r--r--chrome/browser/printing/print_job.cc454
1 files changed, 454 insertions, 0 deletions
diff --git a/chrome/browser/printing/print_job.cc b/chrome/browser/printing/print_job.cc
new file mode 100644
index 0000000..54713cf
--- /dev/null
+++ b/chrome/browser/printing/print_job.cc
@@ -0,0 +1,454 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "chrome/browser/printing/print_job.h"
+
+#include "base/message_loop.h"
+#include "chrome/browser/printing/print_job_worker.h"
+#include "chrome/browser/printing/printed_document.h"
+#include "chrome/browser/printing/printed_page.h"
+
+#ifdef _MSC_VER
+#pragma warning(disable:4355) // 'this' : used in base member initializer list
+#endif
+
+namespace printing {
+
+PrintJob::PrintJob(PrintedPagesSource* source)
+ : ui_message_loop_(MessageLoop::current()),
+ worker_(new PrintJobWorker(this)),
+ source_(source),
+ is_job_pending_(false),
+ is_print_dialog_box_shown_(false),
+ is_blocking_(false),
+ is_canceling_(false) {
+}
+
+PrintJob::PrintJob()
+ : ui_message_loop_(MessageLoop::current()),
+ worker_(),
+ source_(NULL),
+ settings_(),
+ is_job_pending_(false),
+ is_print_dialog_box_shown_(false),
+ is_blocking_(false),
+ is_canceling_(false) {
+}
+
+PrintJob::~PrintJob() {
+ // The job should be finished (or at least canceled) when it is destroyed.
+ DCHECK(!is_job_pending_);
+ DCHECK(!is_print_dialog_box_shown_);
+ DCHECK(!is_blocking_);
+ DCHECK(!is_canceling_);
+ DCHECK(worker_->message_loop() == NULL);
+ DCHECK_EQ(ui_message_loop_, MessageLoop::current());
+}
+
+void PrintJob::Initialize(PrintJobWorkerOwner* job,
+ PrintedPagesSource* source) {
+ DCHECK(!source_);
+ DCHECK(!worker_.get());
+ DCHECK(!is_job_pending_);
+ DCHECK(!is_print_dialog_box_shown_);
+ DCHECK(!is_blocking_);
+ DCHECK(!is_canceling_);
+ DCHECK(!document_.get());
+ source_ = source;
+ worker_.reset(job->DetachWorker(this));
+ settings_ = job->settings();
+
+ UpdatePrintedDocument(new PrintedDocument(settings_, source_, job->cookie()));
+
+ // Don't forget to register to our own messages.
+ NotificationService::current()->AddObserver(
+ this, NOTIFY_PRINT_JOB_EVENT, Source<PrintJob>(this));
+}
+
+void PrintJob::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK_EQ(ui_message_loop_, MessageLoop::current());
+ switch (type) {
+ case NOTIFY_PRINTED_DOCUMENT_UPDATED: {
+ DCHECK(Source<PrintedDocument>(source).ptr() ==
+ document_.get());
+
+ // This notification may happens even if no job is started (i.e. print
+ // preview)
+ if (is_job_pending_ == true &&
+ Source<PrintedDocument>(source).ptr() == document_.get() &&
+ Details<PrintedPage>(details).ptr() != NULL) {
+ // Are we waiting for a page to print? The worker will know.
+ worker_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ worker_.get(), &PrintJobWorker::OnNewPage));
+ }
+ break;
+ }
+ case NOTIFY_PRINT_JOB_EVENT: {
+ OnNotifyPrintJobEvent(*Details<JobEventDetails>(details).ptr());
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+void PrintJob::GetSettingsDone(const PrintSettings& new_settings,
+ PrintingContext::Result result) {
+ DCHECK(!is_job_pending_);
+ DCHECK(!is_blocking_);
+
+ if (!source_ || result == PrintingContext::FAILED) {
+ // The source is gone, there's nothing to do.
+ Cancel();
+ return;
+ }
+
+ // Only create a new PrintedDocument if the settings have changed or if
+ // there was no printed document.
+ if (!document_.get() || !new_settings.Equals(settings_)) {
+ UpdatePrintedDocument(new PrintedDocument(new_settings, source_,
+ PrintSettings::NewCookie()));
+ }
+
+ JobEventDetails::Type type;
+ if (is_print_dialog_box_shown_) {
+ type = (result == PrintingContext::OK) ?
+ JobEventDetails::USER_INIT_DONE :
+ JobEventDetails::USER_INIT_CANCELED;
+ // Dialog box is not shown anymore.
+ is_print_dialog_box_shown_ = false;
+ } else {
+ DCHECK_EQ(result, PrintingContext::OK);
+ type = JobEventDetails::DEFAULT_INIT_DONE;
+ }
+ scoped_refptr<JobEventDetails> details(
+ new JobEventDetails(type, document_.get(), NULL));
+ NotificationService::current()->Notify(
+ NOTIFY_PRINT_JOB_EVENT,
+ Source<PrintJob>(this),
+ Details<JobEventDetails>(details.get()));
+}
+
+PrintJobWorker* PrintJob::DetachWorker(PrintJobWorkerOwner* new_owner) {
+ NOTREACHED();
+ return NULL;
+}
+
+int PrintJob::cookie() const {
+ if (!document_.get())
+ // Always use an invalid cookie in this case.
+ return 0;
+ return document_->cookie();
+}
+
+void PrintJob::GetSettings(GetSettingsAskParam ask_user_for_settings,
+ HWND parent_window) {
+ DCHECK_EQ(ui_message_loop_, MessageLoop::current());
+ DCHECK(!is_job_pending_);
+ DCHECK(!is_print_dialog_box_shown_);
+ DCHECK(!is_blocking_);
+ // Is not reentrant.
+ if (is_job_pending_ || is_blocking_)
+ return;
+
+ // Lazy create the worker thread. There is one worker thread per print job.
+ if (!worker_->message_loop()) {
+ if (!worker_->Start())
+ return;
+
+ // Don't re-register if we were already registered.
+ NotificationService::current()->AddObserver(
+ this, NOTIFY_PRINT_JOB_EVENT, Source<PrintJob>(this));
+ }
+
+ int page_count = 0;
+ if (document_.get()) {
+ page_count = document_->page_count();
+ }
+
+ // Real work is done in PrintJobWorker::Init().
+ is_print_dialog_box_shown_ = ask_user_for_settings == ASK_USER;
+ worker_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ worker_.get(), &PrintJobWorker::GetSettings, is_print_dialog_box_shown_,
+ parent_window, page_count));
+}
+
+void PrintJob::StartPrinting() {
+ DCHECK_EQ(ui_message_loop_, MessageLoop::current());
+ DCHECK(worker_->message_loop());
+ DCHECK(!is_job_pending_);
+ DCHECK(!is_print_dialog_box_shown_);
+ DCHECK(!is_blocking_);
+ if (!worker_->message_loop() || is_job_pending_ || is_blocking_)
+ return;
+
+ // Real work is done in PrintJobWorker::StartPrinting().
+ worker_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ worker_.get(), &PrintJobWorker::StartPrinting, document_));
+ // Set the flag right now.
+ is_job_pending_ = true;
+
+ // Tell everyone!
+ scoped_refptr<JobEventDetails> details(
+ new JobEventDetails(JobEventDetails::NEW_DOC, document_.get(), NULL));
+ NotificationService::current()->Notify(
+ NOTIFY_PRINT_JOB_EVENT,
+ Source<PrintJob>(this),
+ Details<JobEventDetails>(details.get()));
+}
+
+void PrintJob::Stop() {
+ DCHECK_EQ(ui_message_loop_, MessageLoop::current());
+
+ // Be sure to live long enough.
+ scoped_refptr<PrintJob> handle(this);
+
+ MessageLoop* worker_loop = worker_->message_loop();
+ if (worker_loop) {
+ if (is_print_dialog_box_shown_) {
+ // Make sure there is no dialog box.
+ worker_loop->PostTask(FROM_HERE, NewRunnableMethod(
+ worker_.get(), &PrintJobWorker::DismissDialog));
+ is_print_dialog_box_shown_ = false;
+ }
+ // It will wait infinitely for the worker thread to quit.
+ worker_->NonBlockingStop();
+ is_job_pending_ = false;
+ NotificationService::current()->RemoveObserver(
+ this, NOTIFY_PRINT_JOB_EVENT, Source<PrintJob>(this));
+ }
+ // Flush the cached document.
+ UpdatePrintedDocument(NULL);
+
+ if (is_blocking_) {
+ // Make sure we don't get stuck in an inner message loop.
+ MessageLoop::current()->Quit();
+ is_blocking_ = false;
+ }
+}
+
+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_, MessageLoop::current());
+ 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));
+ NotificationService::current()->Notify(
+ NOTIFY_PRINT_JOB_EVENT,
+ Source<PrintJob>(this),
+ Details<JobEventDetails>(details.get()));
+ Stop();
+ is_canceling_ = false;
+}
+
+bool PrintJob::RequestMissingPages() {
+ DCHECK_EQ(ui_message_loop_, MessageLoop::current());
+ DCHECK(!is_print_dialog_box_shown_);
+ DCHECK(!is_blocking_);
+ if (!is_job_pending_ || is_print_dialog_box_shown_ || is_blocking_)
+ return false;
+
+ MessageLoop* worker_loop = worker_.get() ? worker_->message_loop() : NULL;
+ if (!worker_loop)
+ return false;
+
+ worker_loop->PostTask(FROM_HERE, NewRunnableMethod(
+ worker_.get(), &PrintJobWorker::RequestMissingPages));
+ return true;
+}
+
+bool PrintJob::FlushJob(int timeout_ms) {
+ if (!RequestMissingPages())
+ return false;
+
+ // Make sure the object outlive this message loop.
+ scoped_refptr<PrintJob> handle(this);
+
+ MessageLoop::QuitTask timeout_task;
+ scoped_ptr<Timer> timeout;
+ if (timeout_ms) {
+ timeout.reset(MessageLoop::current()->timer_manager()->StartTimer(
+ timeout_ms,
+ &timeout_task,
+ false));
+ }
+
+ is_blocking_ = true;
+ // Stop() will eventually be called, which will get out of the inner message
+ // loop. But, don't take it for granted and set a timer in case something goes
+ // wrong.
+
+ bool old_state = MessageLoop::current()->NestableTasksAllowed();
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ MessageLoop::current()->Run();
+ // Restore task state.
+ MessageLoop::current()->SetNestableTasksAllowed(old_state);
+
+ if (timeout.get()) {
+ MessageLoop::current()->timer_manager()->StopTimer(timeout.get());
+ }
+ return true;
+}
+
+void PrintJob::DisconnectSource() {
+ source_ = NULL;
+ if (document_.get())
+ document_->DisconnectSource();
+}
+
+bool PrintJob::is_job_pending() const {
+ return is_job_pending_;
+}
+
+bool PrintJob::is_print_dialog_box_shown() const {
+ return is_print_dialog_box_shown_;
+}
+
+PrintedDocument* PrintJob::document() const {
+ return document_.get();
+}
+
+void PrintJob::UpdatePrintedDocument(PrintedDocument* new_document) {
+ if (document_.get() == new_document)
+ return;
+ // Unregisters.
+ if (document_.get()) {
+ NotificationService::current()->
+ RemoveObserver(this,
+ NOTIFY_PRINTED_DOCUMENT_UPDATED,
+ Source<PrintedDocument>(document_.get()));
+ }
+ document_ = new_document;
+
+ // Registers.
+ if (document_.get()) {
+ NotificationService::current()->
+ AddObserver(this,
+ NOTIFY_PRINTED_DOCUMENT_UPDATED,
+ Source<PrintedDocument>(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, NewRunnableMethod(
+ worker_.get(), &PrintJobWorker::OnDocumentChanged, document_));
+ }
+}
+
+void PrintJob::OnNotifyPrintJobEvent(const JobEventDetails& event_details) {
+ switch (event_details.type()) {
+ case JobEventDetails::FAILED: {
+ settings_.Clear();
+ // Update internal state.
+ is_print_dialog_box_shown_ = false;
+ // 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.
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &PrintJob::OnDocumentDone));
+ 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));
+ NotificationService::current()->Notify(
+ NOTIFY_PRINT_JOB_EVENT,
+ Source<PrintJob>(this),
+ Details<JobEventDetails>(details.get()));
+}
+
+// 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_;
+}
+
+PrintedPage* JobEventDetails::page() const {
+ return page_;
+}
+
+} // namespace printing