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