print_job_worker.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
1// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/printing/print_job_worker.h" 6 7#include "base/message_loop.h" 8#include "chrome/browser/browser_process.h" 9#include "chrome/browser/browser_thread.h" 10#include "chrome/browser/printing/print_job.h" 11#include "chrome/common/notification_service.h" 12#include "printing/printed_document.h" 13#include "printing/printed_page.h" 14 15namespace printing { 16 17class PrintJobWorker::NotificationTask : public Task { 18 public: 19 NotificationTask() : print_job_(NULL), details_(NULL) { 20 } 21 ~NotificationTask() { 22 } 23 24 // Initializes the object. This object can't be initialized in the constructor 25 // since it is not created directly. 26 void Init(PrintJobWorkerOwner* print_job, 27 JobEventDetails::Type detail_type, 28 PrintedDocument* document, 29 PrintedPage* page) { 30 DCHECK(!print_job_); 31 DCHECK(!details_); 32 print_job_ = print_job; 33 details_ = new JobEventDetails(detail_type, document, page); 34 } 35 36 virtual void Run() { 37 // Send the notification in the right thread. 38 NotificationService::current()->Notify( 39 NotificationType::PRINT_JOB_EVENT, 40 // We know that is is a PrintJob object in this circumstance. 41 Source<PrintJob>(static_cast<PrintJob*>(print_job_.get())), 42 Details<JobEventDetails>(details_)); 43 } 44 45 // The job which originates this notification. 46 scoped_refptr<PrintJobWorkerOwner> print_job_; 47 scoped_refptr<JobEventDetails> details_; 48}; 49 50 51PrintJobWorker::PrintJobWorker(PrintJobWorkerOwner* owner) 52 : Thread("Printing_Worker"), 53 owner_(owner) { 54 // The object is created in the IO thread. 55 DCHECK_EQ(owner_->message_loop(), MessageLoop::current()); 56 57 printing_context_.reset(PrintingContext::Create( 58 g_browser_process->GetApplicationLocale())); 59} 60 61PrintJobWorker::~PrintJobWorker() { 62 // The object is normally deleted in the UI thread, but when the user 63 // cancels printing or in the case of print preview, the worker is destroyed 64 // on the I/O thread. 65 DCHECK_EQ(owner_->message_loop(), MessageLoop::current()); 66} 67 68void PrintJobWorker::SetNewOwner(PrintJobWorkerOwner* new_owner) { 69 DCHECK(page_number_ == PageNumber::npos()); 70 owner_ = new_owner; 71} 72 73void PrintJobWorker::GetSettings(bool ask_user_for_settings, 74 gfx::NativeView parent_view, 75 int document_page_count, 76 bool has_selection, 77 bool use_overlays) { 78 DCHECK_EQ(message_loop(), MessageLoop::current()); 79 DCHECK_EQ(page_number_, PageNumber::npos()); 80 81 // Recursive task processing is needed for the dialog in case it needs to be 82 // destroyed by a task. 83 MessageLoop::current()->SetNestableTasksAllowed(true); 84 printing_context_->set_use_overlays(use_overlays); 85 86 if (ask_user_for_settings) { 87#if defined(OS_POSIX) 88 BrowserThread::PostTask( 89 BrowserThread::UI, FROM_HERE, 90 NewRunnableMethod(this, &PrintJobWorker::GetSettingsWithUI, 91 parent_view, document_page_count, 92 has_selection)); 93#else 94 printing_context_->AskUserForSettings( 95 parent_view, 96 document_page_count, 97 has_selection, 98 NewCallback(this, &PrintJobWorker::GetSettingsDone)); 99#endif // defined(OS_POSIX) 100 } else { 101#if defined(OS_POSIX) 102 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 103 NewRunnableMethod(this, &PrintJobWorker::UseDefaultSettings)); 104#else 105 UseDefaultSettings(); 106#endif // defined(OS_POSIX) 107 } 108} 109 110void PrintJobWorker::GetSettingsDone(PrintingContext::Result result) { 111 // Most PrintingContext functions may start a message loop and process 112 // message recursively, so disable recursive task processing. 113 MessageLoop::current()->SetNestableTasksAllowed(false); 114 115 // We can't use OnFailure() here since owner_ may not support notifications. 116 117 // PrintJob will create the new PrintedDocument. 118 owner_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( 119 owner_, 120 &PrintJobWorkerOwner::GetSettingsDone, 121 printing_context_->settings(), 122 result)); 123} 124 125#if defined(OS_POSIX) 126void PrintJobWorker::GetSettingsWithUI(gfx::NativeView parent_view, 127 int document_page_count, 128 bool has_selection) { 129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 130 131 printing_context_->AskUserForSettings( 132 parent_view, 133 document_page_count, 134 has_selection, 135 NewCallback(this, &PrintJobWorker::GetSettingsWithUIDone)); 136} 137 138void PrintJobWorker::GetSettingsWithUIDone(PrintingContext::Result result) { 139 message_loop()->PostTask(FROM_HERE, NewRunnableMethod( 140 this, &PrintJobWorker::GetSettingsDone, result)); 141} 142#endif // defined(OS_POSIX) 143 144void PrintJobWorker::UseDefaultSettings() { 145 PrintingContext::Result result = printing_context_->UseDefaultSettings(); 146 GetSettingsDone(result); 147} 148 149void PrintJobWorker::StartPrinting(PrintedDocument* new_document) { 150 DCHECK_EQ(message_loop(), MessageLoop::current()); 151 DCHECK_EQ(page_number_, PageNumber::npos()); 152 DCHECK_EQ(document_, new_document); 153 DCHECK(document_.get()); 154 DCHECK(new_document->settings().Equals(printing_context_->settings())); 155 156 if (!document_.get() || page_number_ != PageNumber::npos() || 157 document_ != new_document) { 158 return; 159 } 160 161 PrintingContext::Result result = 162 printing_context_->NewDocument(document_->name()); 163 if (result != PrintingContext::OK) { 164 OnFailure(); 165 return; 166 } 167 168 // Try to print already cached data. It may already have been generated for 169 // the print preview. 170 OnNewPage(); 171 // Don't touch this anymore since the instance could be destroyed. It happens 172 // if all the pages are printed a one sweep and the client doesn't have a 173 // handle to us anymore. There's a timing issue involved between the worker 174 // thread and the UI thread. Take no chance. 175} 176 177void PrintJobWorker::OnDocumentChanged(PrintedDocument* new_document) { 178 DCHECK_EQ(message_loop(), MessageLoop::current()); 179 DCHECK_EQ(page_number_, PageNumber::npos()); 180 DCHECK(!new_document || 181 new_document->settings().Equals(printing_context_->settings())); 182 183 if (page_number_ != PageNumber::npos()) 184 return; 185 186 document_ = new_document; 187} 188 189void PrintJobWorker::OnNewPage() { 190 if (!document_.get()) { 191 // Spurious message. 192 return; 193 } 194 // message_loop() could return NULL when the print job is cancelled. 195 DCHECK_EQ(message_loop(), MessageLoop::current()); 196 197 if (page_number_ == PageNumber::npos()) { 198 // Find first page to print. 199 int page_count = document_->page_count(); 200 if (!page_count) { 201 // We still don't know how many pages the document contains. We can't 202 // start to print the document yet since the header/footer may refer to 203 // the document's page count. 204 return; 205 } 206 // We have enough information to initialize page_number_. 207 page_number_.Init(document_->settings(), page_count); 208 } 209 DCHECK_NE(page_number_, PageNumber::npos()); 210 211 for (;;) { 212 // Is the page available? 213 scoped_refptr<PrintedPage> page; 214 if (!document_->GetPage(page_number_.ToInt(), &page)) { 215 // We need to wait for the page to be available. 216 MessageLoop::current()->PostDelayedTask( 217 FROM_HERE, 218 NewRunnableMethod(this, &PrintJobWorker::OnNewPage), 219 500); 220 break; 221 } 222 // The page is there, print it. 223 SpoolPage(*page); 224 ++page_number_; 225 if (page_number_ == PageNumber::npos()) { 226 OnDocumentDone(); 227 // Don't touch this anymore since the instance could be destroyed. 228 break; 229 } 230 } 231} 232 233void PrintJobWorker::Cancel() { 234 // This is the only function that can be called from any thread. 235 printing_context_->Cancel(); 236 // Cannot touch any member variable since we don't know in which thread 237 // context we run. 238} 239 240void PrintJobWorker::DismissDialog() { 241 printing_context_->DismissDialog(); 242} 243 244void PrintJobWorker::OnDocumentDone() { 245 DCHECK_EQ(message_loop(), MessageLoop::current()); 246 DCHECK_EQ(page_number_, PageNumber::npos()); 247 DCHECK(document_.get()); 248 249 if (printing_context_->DocumentDone() != PrintingContext::OK) { 250 OnFailure(); 251 return; 252 } 253 254 // Tell everyone! 255 NotificationTask* task = new NotificationTask(); 256 task->Init(owner_, 257 JobEventDetails::DOC_DONE, 258 document_.get(), 259 NULL); 260 owner_->message_loop()->PostTask(FROM_HERE, task); 261 262 // Makes sure the variables are reinitialized. 263 document_ = NULL; 264} 265 266void PrintJobWorker::SpoolPage(PrintedPage& page) { 267 DCHECK_EQ(message_loop(), MessageLoop::current()); 268 DCHECK_NE(page_number_, PageNumber::npos()); 269 270 // Signal everyone that the page is about to be printed. 271 NotificationTask* task = new NotificationTask(); 272 task->Init(owner_, 273 JobEventDetails::NEW_PAGE, 274 document_.get(), 275 &page); 276 owner_->message_loop()->PostTask(FROM_HERE, task); 277 278 // Preprocess. 279 if (printing_context_->NewPage() != PrintingContext::OK) { 280 OnFailure(); 281 return; 282 } 283 284 // Actual printing. 285 document_->RenderPrintedPage(page, printing_context_->context()); 286 287 // Postprocess. 288 if (printing_context_->PageDone() != PrintingContext::OK) { 289 OnFailure(); 290 return; 291 } 292 293 // Signal everyone that the page is printed. 294 task = new NotificationTask(); 295 task->Init(owner_, 296 JobEventDetails::PAGE_DONE, 297 document_.get(), 298 &page); 299 owner_->message_loop()->PostTask(FROM_HERE, task); 300} 301 302void PrintJobWorker::OnFailure() { 303 DCHECK_EQ(message_loop(), MessageLoop::current()); 304 305 // We may loose our last reference by broadcasting the FAILED event. 306 scoped_refptr<PrintJobWorkerOwner> handle(owner_); 307 308 NotificationTask* task = new NotificationTask(); 309 task->Init(owner_, 310 JobEventDetails::FAILED, 311 document_.get(), 312 NULL); 313 owner_->message_loop()->PostTask(FROM_HERE, task); 314 Cancel(); 315 316 // Makes sure the variables are reinitialized. 317 document_ = NULL; 318 page_number_ = PageNumber::npos(); 319} 320 321} // namespace printing 322 323void RunnableMethodTraits<printing::PrintJobWorker>::RetainCallee( 324 printing::PrintJobWorker* obj) { 325 DCHECK(!owner_.get()); 326 owner_ = obj->owner_; 327} 328 329void RunnableMethodTraits<printing::PrintJobWorker>::ReleaseCallee( 330 printing::PrintJobWorker* obj) { 331 DCHECK_EQ(owner_, obj->owner_); 332 owner_ = NULL; 333} 334