Give names to all utility processes.
[chromium-blink-merge.git] / chrome / browser / printing / print_job.cc
blobb6d59d7eacf51bf5efafafc86570995dbc113131
1 // Copyright (c) 2012 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.
5 #include "chrome/browser/printing/print_job.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/threading/thread_restrictions.h"
11 #include "base/threading/worker_pool.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/printing/print_job_worker.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/browser/notification_service.h"
16 #include "printing/printed_document.h"
17 #include "printing/printed_page.h"
19 #if defined(OS_WIN)
20 #include "chrome/browser/printing/pdf_to_emf_converter.h"
21 #include "printing/pdf_render_settings.h"
22 #endif
24 using base::TimeDelta;
26 namespace {
28 // Helper function to ensure |owner| is valid until at least |callback| returns.
29 void HoldRefCallback(const scoped_refptr<printing::PrintJobWorkerOwner>& owner,
30 const base::Closure& callback) {
31 callback.Run();
34 } // namespace
36 namespace printing {
38 PrintJob::PrintJob()
39 : source_(NULL),
40 worker_(),
41 settings_(),
42 is_job_pending_(false),
43 is_canceling_(false),
44 quit_factory_(this) {
45 // This is normally a UI message loop, but in unit tests, the message loop is
46 // of the 'default' type.
47 DCHECK(base::MessageLoopForUI::IsCurrent() ||
48 base::MessageLoop::current()->type() ==
49 base::MessageLoop::TYPE_DEFAULT);
52 PrintJob::~PrintJob() {
53 // The job should be finished (or at least canceled) when it is destroyed.
54 DCHECK(!is_job_pending_);
55 DCHECK(!is_canceling_);
56 DCHECK(!worker_ || !worker_->IsRunning());
57 DCHECK(RunsTasksOnCurrentThread());
60 void PrintJob::Initialize(PrintJobWorkerOwner* job,
61 PrintedPagesSource* source,
62 int page_count) {
63 DCHECK(!source_);
64 DCHECK(!worker_.get());
65 DCHECK(!is_job_pending_);
66 DCHECK(!is_canceling_);
67 DCHECK(!document_.get());
68 source_ = source;
69 worker_.reset(job->DetachWorker(this));
70 settings_ = job->settings();
72 PrintedDocument* new_doc =
73 new PrintedDocument(settings_,
74 source_,
75 job->cookie(),
76 content::BrowserThread::GetBlockingPool());
77 new_doc->set_page_count(page_count);
78 UpdatePrintedDocument(new_doc);
80 // Don't forget to register to our own messages.
81 registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT,
82 content::Source<PrintJob>(this));
85 void PrintJob::Observe(int type,
86 const content::NotificationSource& source,
87 const content::NotificationDetails& details) {
88 DCHECK(RunsTasksOnCurrentThread());
89 switch (type) {
90 case chrome::NOTIFICATION_PRINT_JOB_EVENT: {
91 OnNotifyPrintJobEvent(*content::Details<JobEventDetails>(details).ptr());
92 break;
94 default: {
95 break;
100 void PrintJob::GetSettingsDone(const PrintSettings& new_settings,
101 PrintingContext::Result result) {
102 NOTREACHED();
105 PrintJobWorker* PrintJob::DetachWorker(PrintJobWorkerOwner* new_owner) {
106 NOTREACHED();
107 return NULL;
110 const PrintSettings& PrintJob::settings() const {
111 return settings_;
114 int PrintJob::cookie() const {
115 if (!document_.get())
116 // Always use an invalid cookie in this case.
117 return 0;
118 return document_->cookie();
121 void PrintJob::StartPrinting() {
122 DCHECK(RunsTasksOnCurrentThread());
123 DCHECK(worker_->IsRunning());
124 DCHECK(!is_job_pending_);
125 if (!worker_->IsRunning() || is_job_pending_)
126 return;
128 // Real work is done in PrintJobWorker::StartPrinting().
129 worker_->PostTask(FROM_HERE,
130 base::Bind(&HoldRefCallback,
131 make_scoped_refptr(this),
132 base::Bind(&PrintJobWorker::StartPrinting,
133 base::Unretained(worker_.get()),
134 document_)));
135 // Set the flag right now.
136 is_job_pending_ = true;
138 // Tell everyone!
139 scoped_refptr<JobEventDetails> details(
140 new JobEventDetails(JobEventDetails::NEW_DOC, document_.get(), NULL));
141 content::NotificationService::current()->Notify(
142 chrome::NOTIFICATION_PRINT_JOB_EVENT,
143 content::Source<PrintJob>(this),
144 content::Details<JobEventDetails>(details.get()));
147 void PrintJob::Stop() {
148 DCHECK(RunsTasksOnCurrentThread());
150 if (quit_factory_.HasWeakPtrs()) {
151 // In case we're running a nested message loop to wait for a job to finish,
152 // and we finished before the timeout, quit the nested loop right away.
153 Quit();
154 quit_factory_.InvalidateWeakPtrs();
157 // Be sure to live long enough.
158 scoped_refptr<PrintJob> handle(this);
160 if (worker_->IsRunning()) {
161 ControlledWorkerShutdown();
162 } else {
163 // Flush the cached document.
164 UpdatePrintedDocument(NULL);
168 void PrintJob::Cancel() {
169 if (is_canceling_)
170 return;
171 is_canceling_ = true;
173 // Be sure to live long enough.
174 scoped_refptr<PrintJob> handle(this);
176 DCHECK(RunsTasksOnCurrentThread());
177 if (worker_ && worker_->IsRunning()) {
178 // Call this right now so it renders the context invalid. Do not use
179 // InvokeLater since it would take too much time.
180 worker_->Cancel();
182 // Make sure a Cancel() is broadcast.
183 scoped_refptr<JobEventDetails> details(
184 new JobEventDetails(JobEventDetails::FAILED, NULL, NULL));
185 content::NotificationService::current()->Notify(
186 chrome::NOTIFICATION_PRINT_JOB_EVENT,
187 content::Source<PrintJob>(this),
188 content::Details<JobEventDetails>(details.get()));
189 Stop();
190 is_canceling_ = false;
193 bool PrintJob::FlushJob(base::TimeDelta timeout) {
194 // Make sure the object outlive this message loop.
195 scoped_refptr<PrintJob> handle(this);
197 base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
198 base::Bind(&PrintJob::Quit, quit_factory_.GetWeakPtr()), timeout);
200 base::MessageLoop::ScopedNestableTaskAllower allow(
201 base::MessageLoop::current());
202 base::MessageLoop::current()->Run();
204 return true;
207 void PrintJob::DisconnectSource() {
208 source_ = NULL;
209 if (document_.get())
210 document_->DisconnectSource();
213 bool PrintJob::is_job_pending() const {
214 return is_job_pending_;
217 PrintedDocument* PrintJob::document() const {
218 return document_.get();
221 #if defined(OS_WIN)
223 class PrintJob::PdfToEmfState {
224 public:
225 PdfToEmfState(const gfx::Size& page_size, const gfx::Rect& content_area)
226 : page_count_(0),
227 current_page_(0),
228 pages_in_progress_(0),
229 page_size_(page_size),
230 content_area_(content_area),
231 converter_(PdfToEmfConverter::CreateDefault()) {}
233 void Start(const scoped_refptr<base::RefCountedMemory>& data,
234 const PdfRenderSettings& conversion_settings,
235 const PdfToEmfConverter::StartCallback& start_callback) {
236 converter_->Start(data, conversion_settings, start_callback);
239 void GetMorePages(
240 const PdfToEmfConverter::GetPageCallback& get_page_callback) {
241 const int kMaxNumberOfTempFilesPerDocument = 3;
242 while (pages_in_progress_ < kMaxNumberOfTempFilesPerDocument &&
243 current_page_ < page_count_) {
244 ++pages_in_progress_;
245 converter_->GetPage(current_page_++, get_page_callback);
249 void OnPageProcessed(
250 const PdfToEmfConverter::GetPageCallback& get_page_callback) {
251 --pages_in_progress_;
252 GetMorePages(get_page_callback);
253 // Release converter if we don't need this any more.
254 if (!pages_in_progress_ && current_page_ >= page_count_)
255 converter_.reset();
258 void set_page_count(int page_count) { page_count_ = page_count; }
259 gfx::Size page_size() const { return page_size_; }
260 gfx::Rect content_area() const { return content_area_; }
262 private:
263 int page_count_;
264 int current_page_;
265 int pages_in_progress_;
266 gfx::Size page_size_;
267 gfx::Rect content_area_;
268 scoped_ptr<PdfToEmfConverter> converter_;
271 void PrintJob::StartPdfToEmfConversion(
272 const scoped_refptr<base::RefCountedMemory>& bytes,
273 const gfx::Size& page_size,
274 const gfx::Rect& content_area) {
275 DCHECK(!ptd_to_emf_state_.get());
276 ptd_to_emf_state_.reset(new PdfToEmfState(page_size, content_area));
277 const int kPrinterDpi = settings().dpi();
278 ptd_to_emf_state_->Start(
279 bytes,
280 printing::PdfRenderSettings(content_area, kPrinterDpi, true),
281 base::Bind(&PrintJob::OnPdfToEmfStarted, this));
284 void PrintJob::OnPdfToEmfStarted(int page_count) {
285 if (page_count <= 0) {
286 ptd_to_emf_state_.reset();
287 Cancel();
288 return;
290 ptd_to_emf_state_->set_page_count(page_count);
291 ptd_to_emf_state_->GetMorePages(
292 base::Bind(&PrintJob::OnPdfToEmfPageConverted, this));
295 void PrintJob::OnPdfToEmfPageConverted(int page_number,
296 float scale_factor,
297 scoped_ptr<MetafilePlayer> emf) {
298 DCHECK(ptd_to_emf_state_);
299 if (!document_.get() || !emf) {
300 ptd_to_emf_state_.reset();
301 Cancel();
302 return;
305 // Update the rendered document. It will send notifications to the listener.
306 document_->SetPage(page_number,
307 emf.Pass(),
308 scale_factor,
309 ptd_to_emf_state_->page_size(),
310 ptd_to_emf_state_->content_area());
312 ptd_to_emf_state_->GetMorePages(
313 base::Bind(&PrintJob::OnPdfToEmfPageConverted, this));
316 #endif // OS_WIN
318 void PrintJob::UpdatePrintedDocument(PrintedDocument* new_document) {
319 if (document_.get() == new_document)
320 return;
322 document_ = new_document;
324 if (document_.get()) {
325 settings_ = document_->settings();
328 if (worker_) {
329 DCHECK(!is_job_pending_);
330 // Sync the document with the worker.
331 worker_->PostTask(FROM_HERE,
332 base::Bind(&HoldRefCallback,
333 make_scoped_refptr(this),
334 base::Bind(&PrintJobWorker::OnDocumentChanged,
335 base::Unretained(worker_.get()),
336 document_)));
340 void PrintJob::OnNotifyPrintJobEvent(const JobEventDetails& event_details) {
341 switch (event_details.type()) {
342 case JobEventDetails::FAILED: {
343 settings_.Clear();
344 // No need to cancel since the worker already canceled itself.
345 Stop();
346 break;
348 case JobEventDetails::USER_INIT_DONE:
349 case JobEventDetails::DEFAULT_INIT_DONE:
350 case JobEventDetails::USER_INIT_CANCELED: {
351 DCHECK_EQ(event_details.document(), document_.get());
352 break;
354 case JobEventDetails::NEW_DOC:
355 case JobEventDetails::NEW_PAGE:
356 case JobEventDetails::JOB_DONE:
357 case JobEventDetails::ALL_PAGES_REQUESTED: {
358 // Don't care.
359 break;
361 case JobEventDetails::DOC_DONE: {
362 // This will call Stop() and broadcast a JOB_DONE message.
363 base::MessageLoop::current()->PostTask(
364 FROM_HERE, base::Bind(&PrintJob::OnDocumentDone, this));
365 break;
367 case JobEventDetails::PAGE_DONE:
368 #if defined(OS_WIN)
369 ptd_to_emf_state_->OnPageProcessed(
370 base::Bind(&PrintJob::OnPdfToEmfPageConverted, this));
371 #endif // OS_WIN
372 break;
373 default: {
374 NOTREACHED();
375 break;
380 void PrintJob::OnDocumentDone() {
381 // Be sure to live long enough. The instance could be destroyed by the
382 // JOB_DONE broadcast.
383 scoped_refptr<PrintJob> handle(this);
385 // Stop the worker thread.
386 Stop();
388 scoped_refptr<JobEventDetails> details(
389 new JobEventDetails(JobEventDetails::JOB_DONE, document_.get(), NULL));
390 content::NotificationService::current()->Notify(
391 chrome::NOTIFICATION_PRINT_JOB_EVENT,
392 content::Source<PrintJob>(this),
393 content::Details<JobEventDetails>(details.get()));
396 void PrintJob::ControlledWorkerShutdown() {
397 DCHECK(RunsTasksOnCurrentThread());
399 // The deadlock this code works around is specific to window messaging on
400 // Windows, so we aren't likely to need it on any other platforms.
401 #if defined(OS_WIN)
402 // We could easily get into a deadlock case if worker_->Stop() is used; the
403 // printer driver created a window as a child of the browser window. By
404 // canceling the job, the printer driver initiated dialog box is destroyed,
405 // which sends a blocking message to its parent window. If the browser window
406 // thread is not processing messages, a deadlock occurs.
408 // This function ensures that the dialog box will be destroyed in a timely
409 // manner by the mere fact that the thread will terminate. So the potential
410 // deadlock is eliminated.
411 worker_->StopSoon();
413 // Delay shutdown until the worker terminates. We want this code path
414 // to wait on the thread to quit before continuing.
415 if (worker_->IsRunning()) {
416 base::MessageLoop::current()->PostDelayedTask(
417 FROM_HERE,
418 base::Bind(&PrintJob::ControlledWorkerShutdown, this),
419 base::TimeDelta::FromMilliseconds(100));
420 return;
422 #endif
425 // Now make sure the thread object is cleaned up. Do this on a worker
426 // thread because it may block.
427 base::WorkerPool::PostTaskAndReply(
428 FROM_HERE,
429 base::Bind(&PrintJobWorker::Stop, base::Unretained(worker_.get())),
430 base::Bind(&PrintJob::HoldUntilStopIsCalled, this),
431 false);
433 is_job_pending_ = false;
434 registrar_.RemoveAll();
435 UpdatePrintedDocument(NULL);
438 void PrintJob::HoldUntilStopIsCalled() {
441 void PrintJob::Quit() {
442 base::MessageLoop::current()->Quit();
445 // Takes settings_ ownership and will be deleted in the receiving thread.
446 JobEventDetails::JobEventDetails(Type type,
447 PrintedDocument* document,
448 PrintedPage* page)
449 : document_(document),
450 page_(page),
451 type_(type) {
454 JobEventDetails::~JobEventDetails() {
457 PrintedDocument* JobEventDetails::document() const { return document_.get(); }
459 PrintedPage* JobEventDetails::page() const { return page_.get(); }
461 } // namespace printing