- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / printing / print_job.cc
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.
4
5 #include "chrome/browser/printing/print_job.h"
6
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 "base/timer/timer.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/printing/print_job_worker.h"
15 #include "content/public/browser/notification_service.h"
16 #include "printing/printed_document.h"
17 #include "printing/printed_page.h"
18
19 using base::TimeDelta;
20
21 namespace {
22
23 // Helper function to ensure |owner| is valid until at least |callback| returns.
24 void HoldRefCallback(const scoped_refptr<printing::PrintJobWorkerOwner>& owner,
25                      const base::Closure& callback) {
26   callback.Run();
27 }
28
29 }  // namespace
30
31 namespace printing {
32
33 PrintJob::PrintJob()
34     : ui_message_loop_(base::MessageLoop::current()),
35       source_(NULL),
36       worker_(),
37       settings_(),
38       is_job_pending_(false),
39       is_canceling_(false),
40       is_stopping_(false),
41       is_stopped_(false),
42       quit_factory_(this),
43       weak_ptr_factory_(this) {
44   DCHECK(ui_message_loop_);
45   // This is normally a UI message loop, but in unit tests, the message loop is
46   // of the 'default' type.
47   DCHECK(ui_message_loop_->type() == base::MessageLoop::TYPE_UI ||
48          ui_message_loop_->type() == base::MessageLoop::TYPE_DEFAULT);
49   ui_message_loop_->AddDestructionObserver(this);
50 }
51
52 PrintJob::~PrintJob() {
53   ui_message_loop_->RemoveDestructionObserver(this);
54   // The job should be finished (or at least canceled) when it is destroyed.
55   DCHECK(!is_job_pending_);
56   DCHECK(!is_canceling_);
57   if (worker_.get())
58     DCHECK(worker_->message_loop() == NULL);
59   DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
60 }
61
62 void PrintJob::Initialize(PrintJobWorkerOwner* job,
63                           PrintedPagesSource* source,
64                           int page_count) {
65   DCHECK(!source_);
66   DCHECK(!worker_.get());
67   DCHECK(!is_job_pending_);
68   DCHECK(!is_canceling_);
69   DCHECK(!document_.get());
70   source_ = source;
71   worker_.reset(job->DetachWorker(this));
72   settings_ = job->settings();
73
74   PrintedDocument* new_doc =
75       new PrintedDocument(settings_, source_, job->cookie());
76   new_doc->set_page_count(page_count);
77   UpdatePrintedDocument(new_doc);
78
79   // Don't forget to register to our own messages.
80   registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT,
81                  content::Source<PrintJob>(this));
82 }
83
84 void PrintJob::Observe(int type,
85                        const content::NotificationSource& source,
86                        const content::NotificationDetails& details) {
87   DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
88   switch (type) {
89     case chrome::NOTIFICATION_PRINT_JOB_EVENT: {
90       OnNotifyPrintJobEvent(*content::Details<JobEventDetails>(details).ptr());
91       break;
92     }
93     default: {
94       break;
95     }
96   }
97 }
98
99 void PrintJob::GetSettingsDone(const PrintSettings& new_settings,
100                                PrintingContext::Result result) {
101   NOTREACHED();
102 }
103
104 PrintJobWorker* PrintJob::DetachWorker(PrintJobWorkerOwner* new_owner) {
105   NOTREACHED();
106   return NULL;
107 }
108
109 base::MessageLoop* PrintJob::message_loop() {
110   return ui_message_loop_;
111 }
112
113 const PrintSettings& PrintJob::settings() const {
114   return settings_;
115 }
116
117 int PrintJob::cookie() const {
118   if (!document_.get())
119     // Always use an invalid cookie in this case.
120     return 0;
121   return document_->cookie();
122 }
123
124 void PrintJob::WillDestroyCurrentMessageLoop() {
125   NOTREACHED();
126 }
127
128 void PrintJob::StartPrinting() {
129   DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
130   DCHECK(worker_->message_loop());
131   DCHECK(!is_job_pending_);
132   if (!worker_->message_loop() || is_job_pending_)
133     return;
134
135   // Real work is done in PrintJobWorker::StartPrinting().
136   worker_->message_loop()->PostTask(
137       FROM_HERE,
138       base::Bind(&HoldRefCallback, make_scoped_refptr(this),
139                  base::Bind(&PrintJobWorker::StartPrinting,
140                             base::Unretained(worker_.get()), document_)));
141   // Set the flag right now.
142   is_job_pending_ = true;
143
144   // Tell everyone!
145   scoped_refptr<JobEventDetails> details(
146       new JobEventDetails(JobEventDetails::NEW_DOC, document_.get(), NULL));
147   content::NotificationService::current()->Notify(
148       chrome::NOTIFICATION_PRINT_JOB_EVENT,
149       content::Source<PrintJob>(this),
150       content::Details<JobEventDetails>(details.get()));
151 }
152
153 void PrintJob::Stop() {
154   DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
155
156   if (quit_factory_.HasWeakPtrs()) {
157     // In case we're running a nested message loop to wait for a job to finish,
158     // and we finished before the timeout, quit the nested loop right away.
159     Quit();
160     quit_factory_.InvalidateWeakPtrs();
161   }
162
163   // Be sure to live long enough.
164   scoped_refptr<PrintJob> handle(this);
165
166   base::MessageLoop* worker_loop = worker_->message_loop();
167   if (worker_loop) {
168     ControlledWorkerShutdown();
169
170     is_job_pending_ = false;
171     registrar_.Remove(this, chrome::NOTIFICATION_PRINT_JOB_EVENT,
172                       content::Source<PrintJob>(this));
173   }
174   // Flush the cached document.
175   UpdatePrintedDocument(NULL);
176 }
177
178 void PrintJob::Cancel() {
179   if (is_canceling_)
180     return;
181   is_canceling_ = true;
182
183   // Be sure to live long enough.
184   scoped_refptr<PrintJob> handle(this);
185
186   DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
187   base::MessageLoop* worker_loop =
188       worker_.get() ? worker_->message_loop() : NULL;
189   if (worker_loop) {
190     // Call this right now so it renders the context invalid. Do not use
191     // InvokeLater since it would take too much time.
192     worker_->Cancel();
193   }
194   // Make sure a Cancel() is broadcast.
195   scoped_refptr<JobEventDetails> details(
196       new JobEventDetails(JobEventDetails::FAILED, NULL, NULL));
197   content::NotificationService::current()->Notify(
198       chrome::NOTIFICATION_PRINT_JOB_EVENT,
199       content::Source<PrintJob>(this),
200       content::Details<JobEventDetails>(details.get()));
201   Stop();
202   is_canceling_ = false;
203 }
204
205 bool PrintJob::FlushJob(base::TimeDelta timeout) {
206   // Make sure the object outlive this message loop.
207   scoped_refptr<PrintJob> handle(this);
208
209   base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
210       base::Bind(&PrintJob::Quit, quit_factory_.GetWeakPtr()), timeout);
211
212   base::MessageLoop::ScopedNestableTaskAllower allow(
213       base::MessageLoop::current());
214   base::MessageLoop::current()->Run();
215
216   return true;
217 }
218
219 void PrintJob::DisconnectSource() {
220   source_ = NULL;
221   if (document_.get())
222     document_->DisconnectSource();
223 }
224
225 bool PrintJob::is_job_pending() const {
226   return is_job_pending_;
227 }
228
229 bool PrintJob::is_stopping() const {
230   return is_stopping_;
231 }
232
233 bool PrintJob::is_stopped() const {
234   return is_stopped_;
235 }
236
237 PrintedDocument* PrintJob::document() const {
238   return document_.get();
239 }
240
241 void PrintJob::UpdatePrintedDocument(PrintedDocument* new_document) {
242   if (document_.get() == new_document)
243     return;
244
245   document_ = new_document;
246
247   if (document_.get()) {
248     settings_ = document_->settings();
249   }
250
251   if (worker_.get() && worker_->message_loop()) {
252     DCHECK(!is_job_pending_);
253     // Sync the document with the worker.
254     worker_->message_loop()->PostTask(
255         FROM_HERE,
256         base::Bind(&HoldRefCallback, make_scoped_refptr(this),
257                    base::Bind(&PrintJobWorker::OnDocumentChanged,
258                               base::Unretained(worker_.get()), document_)));
259   }
260 }
261
262 void PrintJob::OnNotifyPrintJobEvent(const JobEventDetails& event_details) {
263   switch (event_details.type()) {
264     case JobEventDetails::FAILED: {
265       settings_.Clear();
266       // No need to cancel since the worker already canceled itself.
267       Stop();
268       break;
269     }
270     case JobEventDetails::USER_INIT_DONE:
271     case JobEventDetails::DEFAULT_INIT_DONE:
272     case JobEventDetails::USER_INIT_CANCELED: {
273       DCHECK_EQ(event_details.document(), document_.get());
274       break;
275     }
276     case JobEventDetails::NEW_DOC:
277     case JobEventDetails::NEW_PAGE:
278     case JobEventDetails::PAGE_DONE:
279     case JobEventDetails::JOB_DONE:
280     case JobEventDetails::ALL_PAGES_REQUESTED: {
281       // Don't care.
282       break;
283     }
284     case JobEventDetails::DOC_DONE: {
285       // This will call Stop() and broadcast a JOB_DONE message.
286       base::MessageLoop::current()->PostTask(
287           FROM_HERE, base::Bind(&PrintJob::OnDocumentDone, this));
288       break;
289     }
290     default: {
291       NOTREACHED();
292       break;
293     }
294   }
295 }
296
297 void PrintJob::OnDocumentDone() {
298   // Be sure to live long enough. The instance could be destroyed by the
299   // JOB_DONE broadcast.
300   scoped_refptr<PrintJob> handle(this);
301
302   // Stop the worker thread.
303   Stop();
304
305   scoped_refptr<JobEventDetails> details(
306       new JobEventDetails(JobEventDetails::JOB_DONE, document_.get(), NULL));
307   content::NotificationService::current()->Notify(
308       chrome::NOTIFICATION_PRINT_JOB_EVENT,
309       content::Source<PrintJob>(this),
310       content::Details<JobEventDetails>(details.get()));
311 }
312
313 void PrintJob::ControlledWorkerShutdown() {
314   DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
315
316   // The deadlock this code works around is specific to window messaging on
317   // Windows, so we aren't likely to need it on any other platforms.
318 #if defined(OS_WIN)
319   // We could easily get into a deadlock case if worker_->Stop() is used; the
320   // printer driver created a window as a child of the browser window. By
321   // canceling the job, the printer driver initiated dialog box is destroyed,
322   // which sends a blocking message to its parent window. If the browser window
323   // thread is not processing messages, a deadlock occurs.
324   //
325   // This function ensures that the dialog box will be destroyed in a timely
326   // manner by the mere fact that the thread will terminate. So the potential
327   // deadlock is eliminated.
328   worker_->StopSoon();
329
330   // Run a tight message loop until the worker terminates. It may seems like a
331   // hack but I see no other way to get it to work flawlessly. The issues here
332   // are:
333   // - We don't want to run tasks while the thread is quitting.
334   // - We want this code path to wait on the thread to quit before continuing.
335   MSG msg;
336   HANDLE thread_handle = worker_->thread_handle().platform_handle();
337   for (; thread_handle;) {
338     // Note that we don't do any kind of message prioritization since we don't
339     // execute any pending task or timer.
340     DWORD result = MsgWaitForMultipleObjects(1, &thread_handle,
341                                              FALSE, INFINITE, QS_ALLINPUT);
342     if (result == WAIT_OBJECT_0 + 1) {
343       while (PeekMessage(&msg, NULL, 0, 0, TRUE) > 0) {
344         TranslateMessage(&msg);
345         DispatchMessage(&msg);
346       }
347       // Continue looping.
348     } else if (result == WAIT_OBJECT_0) {
349       // The thread quit.
350       break;
351     } else {
352       // An error occurred. Assume the thread quit.
353       NOTREACHED();
354       break;
355     }
356   }
357 #endif
358
359
360   // Now make sure the thread object is cleaned up. Do this on a worker
361   // thread because it may block.
362   is_stopping_ = true;
363
364   base::WorkerPool::PostTaskAndReply(
365       FROM_HERE,
366       base::Bind(&PrintJobWorker::Stop,
367                  base::Unretained(worker_.get())),
368       base::Bind(&PrintJob::HoldUntilStopIsCalled,
369                  weak_ptr_factory_.GetWeakPtr(),
370                  scoped_refptr<PrintJob>(this)),
371       false);
372 }
373
374 void PrintJob::HoldUntilStopIsCalled(const scoped_refptr<PrintJob>&) {
375   is_stopped_ = true;
376   is_stopping_ = false;
377 }
378
379 void PrintJob::Quit() {
380   base::MessageLoop::current()->Quit();
381 }
382
383 // Takes settings_ ownership and will be deleted in the receiving thread.
384 JobEventDetails::JobEventDetails(Type type,
385                                  PrintedDocument* document,
386                                  PrintedPage* page)
387     : document_(document),
388       page_(page),
389       type_(type) {
390 }
391
392 JobEventDetails::~JobEventDetails() {
393 }
394
395 PrintedDocument* JobEventDetails::document() const { return document_.get(); }
396
397 PrintedPage* JobEventDetails::page() const { return page_.get(); }
398
399 }  // namespace printing