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