Upstream version 5.34.104.0
[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       quit_factory_(this) {
41   DCHECK(ui_message_loop_);
42   // This is normally a UI message loop, but in unit tests, the message loop is
43   // of the 'default' type.
44   DCHECK(base::MessageLoopForUI::IsCurrent() ||
45          ui_message_loop_->type() == base::MessageLoop::TYPE_DEFAULT);
46   ui_message_loop_->AddDestructionObserver(this);
47 }
48
49 PrintJob::~PrintJob() {
50   ui_message_loop_->RemoveDestructionObserver(this);
51   // The job should be finished (or at least canceled) when it is destroyed.
52   DCHECK(!is_job_pending_);
53   DCHECK(!is_canceling_);
54   if (worker_.get())
55     DCHECK(worker_->message_loop() == NULL);
56   DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
57 }
58
59 void PrintJob::Initialize(PrintJobWorkerOwner* job,
60                           PrintedPagesSource* source,
61                           int page_count) {
62   DCHECK(!source_);
63   DCHECK(!worker_.get());
64   DCHECK(!is_job_pending_);
65   DCHECK(!is_canceling_);
66   DCHECK(!document_.get());
67   source_ = source;
68   worker_.reset(job->DetachWorker(this));
69   settings_ = job->settings();
70
71   PrintedDocument* new_doc =
72       new PrintedDocument(settings_, source_, job->cookie());
73   new_doc->set_page_count(page_count);
74   UpdatePrintedDocument(new_doc);
75
76   // Don't forget to register to our own messages.
77   registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT,
78                  content::Source<PrintJob>(this));
79 }
80
81 void PrintJob::Observe(int type,
82                        const content::NotificationSource& source,
83                        const content::NotificationDetails& details) {
84   DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
85   switch (type) {
86     case chrome::NOTIFICATION_PRINT_JOB_EVENT: {
87       OnNotifyPrintJobEvent(*content::Details<JobEventDetails>(details).ptr());
88       break;
89     }
90     default: {
91       break;
92     }
93   }
94 }
95
96 void PrintJob::GetSettingsDone(const PrintSettings& new_settings,
97                                PrintingContext::Result result) {
98   NOTREACHED();
99 }
100
101 PrintJobWorker* PrintJob::DetachWorker(PrintJobWorkerOwner* new_owner) {
102   NOTREACHED();
103   return NULL;
104 }
105
106 base::MessageLoop* PrintJob::message_loop() {
107   return ui_message_loop_;
108 }
109
110 const PrintSettings& PrintJob::settings() const {
111   return settings_;
112 }
113
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();
119 }
120
121 void PrintJob::WillDestroyCurrentMessageLoop() {
122   NOTREACHED();
123 }
124
125 void PrintJob::StartPrinting() {
126   DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
127   DCHECK(worker_->message_loop());
128   DCHECK(!is_job_pending_);
129   if (!worker_->message_loop() || is_job_pending_)
130     return;
131
132   // Real work is done in PrintJobWorker::StartPrinting().
133   worker_->message_loop()->PostTask(
134       FROM_HERE,
135       base::Bind(&HoldRefCallback, make_scoped_refptr(this),
136                  base::Bind(&PrintJobWorker::StartPrinting,
137                             base::Unretained(worker_.get()), document_)));
138   // Set the flag right now.
139   is_job_pending_ = true;
140
141   // Tell everyone!
142   scoped_refptr<JobEventDetails> details(
143       new JobEventDetails(JobEventDetails::NEW_DOC, document_.get(), NULL));
144   content::NotificationService::current()->Notify(
145       chrome::NOTIFICATION_PRINT_JOB_EVENT,
146       content::Source<PrintJob>(this),
147       content::Details<JobEventDetails>(details.get()));
148 }
149
150 void PrintJob::Stop() {
151   DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
152
153   if (quit_factory_.HasWeakPtrs()) {
154     // In case we're running a nested message loop to wait for a job to finish,
155     // and we finished before the timeout, quit the nested loop right away.
156     Quit();
157     quit_factory_.InvalidateWeakPtrs();
158   }
159
160   // Be sure to live long enough.
161   scoped_refptr<PrintJob> handle(this);
162
163   if (worker_->message_loop()) {
164     ControlledWorkerShutdown();
165   } else {
166     // Flush the cached document.
167     UpdatePrintedDocument(NULL);
168   }
169 }
170
171 void PrintJob::Cancel() {
172   if (is_canceling_)
173     return;
174   is_canceling_ = true;
175
176   // Be sure to live long enough.
177   scoped_refptr<PrintJob> handle(this);
178
179   DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
180   base::MessageLoop* worker_loop =
181       worker_.get() ? worker_->message_loop() : NULL;
182   if (worker_loop) {
183     // Call this right now so it renders the context invalid. Do not use
184     // InvokeLater since it would take too much time.
185     worker_->Cancel();
186   }
187   // Make sure a Cancel() is broadcast.
188   scoped_refptr<JobEventDetails> details(
189       new JobEventDetails(JobEventDetails::FAILED, NULL, NULL));
190   content::NotificationService::current()->Notify(
191       chrome::NOTIFICATION_PRINT_JOB_EVENT,
192       content::Source<PrintJob>(this),
193       content::Details<JobEventDetails>(details.get()));
194   Stop();
195   is_canceling_ = false;
196 }
197
198 bool PrintJob::FlushJob(base::TimeDelta timeout) {
199   // Make sure the object outlive this message loop.
200   scoped_refptr<PrintJob> handle(this);
201
202   base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
203       base::Bind(&PrintJob::Quit, quit_factory_.GetWeakPtr()), timeout);
204
205   base::MessageLoop::ScopedNestableTaskAllower allow(
206       base::MessageLoop::current());
207   base::MessageLoop::current()->Run();
208
209   return true;
210 }
211
212 void PrintJob::DisconnectSource() {
213   source_ = NULL;
214   if (document_.get())
215     document_->DisconnectSource();
216 }
217
218 bool PrintJob::is_job_pending() const {
219   return is_job_pending_;
220 }
221
222 PrintedDocument* PrintJob::document() const {
223   return document_.get();
224 }
225
226 void PrintJob::UpdatePrintedDocument(PrintedDocument* new_document) {
227   if (document_.get() == new_document)
228     return;
229
230   document_ = new_document;
231
232   if (document_.get()) {
233     settings_ = document_->settings();
234   }
235
236   if (worker_.get() && worker_->message_loop()) {
237     DCHECK(!is_job_pending_);
238     // Sync the document with the worker.
239     worker_->message_loop()->PostTask(
240         FROM_HERE,
241         base::Bind(&HoldRefCallback, make_scoped_refptr(this),
242                    base::Bind(&PrintJobWorker::OnDocumentChanged,
243                               base::Unretained(worker_.get()), document_)));
244   }
245 }
246
247 void PrintJob::OnNotifyPrintJobEvent(const JobEventDetails& event_details) {
248   switch (event_details.type()) {
249     case JobEventDetails::FAILED: {
250       settings_.Clear();
251       // No need to cancel since the worker already canceled itself.
252       Stop();
253       break;
254     }
255     case JobEventDetails::USER_INIT_DONE:
256     case JobEventDetails::DEFAULT_INIT_DONE:
257     case JobEventDetails::USER_INIT_CANCELED: {
258       DCHECK_EQ(event_details.document(), document_.get());
259       break;
260     }
261     case JobEventDetails::NEW_DOC:
262     case JobEventDetails::NEW_PAGE:
263     case JobEventDetails::PAGE_DONE:
264     case JobEventDetails::JOB_DONE:
265     case JobEventDetails::ALL_PAGES_REQUESTED: {
266       // Don't care.
267       break;
268     }
269     case JobEventDetails::DOC_DONE: {
270       // This will call Stop() and broadcast a JOB_DONE message.
271       base::MessageLoop::current()->PostTask(
272           FROM_HERE, base::Bind(&PrintJob::OnDocumentDone, this));
273       break;
274     }
275     default: {
276       NOTREACHED();
277       break;
278     }
279   }
280 }
281
282 void PrintJob::OnDocumentDone() {
283   // Be sure to live long enough. The instance could be destroyed by the
284   // JOB_DONE broadcast.
285   scoped_refptr<PrintJob> handle(this);
286
287   // Stop the worker thread.
288   Stop();
289
290   scoped_refptr<JobEventDetails> details(
291       new JobEventDetails(JobEventDetails::JOB_DONE, document_.get(), NULL));
292   content::NotificationService::current()->Notify(
293       chrome::NOTIFICATION_PRINT_JOB_EVENT,
294       content::Source<PrintJob>(this),
295       content::Details<JobEventDetails>(details.get()));
296 }
297
298 void PrintJob::ControlledWorkerShutdown() {
299   DCHECK_EQ(ui_message_loop_, base::MessageLoop::current());
300
301   // The deadlock this code works around is specific to window messaging on
302   // Windows, so we aren't likely to need it on any other platforms.
303 #if defined(OS_WIN)
304   // We could easily get into a deadlock case if worker_->Stop() is used; the
305   // printer driver created a window as a child of the browser window. By
306   // canceling the job, the printer driver initiated dialog box is destroyed,
307   // which sends a blocking message to its parent window. If the browser window
308   // thread is not processing messages, a deadlock occurs.
309   //
310   // This function ensures that the dialog box will be destroyed in a timely
311   // manner by the mere fact that the thread will terminate. So the potential
312   // deadlock is eliminated.
313   worker_->StopSoon();
314
315   // Delay shutdown until the worker terminates.  We want this code path
316   // to wait on the thread to quit before continuing.
317   if (worker_->IsRunning()) {
318     base::MessageLoop::current()->PostDelayedTask(
319         FROM_HERE,
320         base::Bind(&PrintJob::ControlledWorkerShutdown, this),
321         base::TimeDelta::FromMilliseconds(100));
322     return;
323   }
324 #endif
325
326
327   // Now make sure the thread object is cleaned up. Do this on a worker
328   // thread because it may block.
329   base::WorkerPool::PostTaskAndReply(
330       FROM_HERE,
331       base::Bind(&PrintJobWorker::Stop, base::Unretained(worker_.get())),
332       base::Bind(&PrintJob::HoldUntilStopIsCalled, this),
333       false);
334
335   is_job_pending_ = false;
336   registrar_.RemoveAll();
337   UpdatePrintedDocument(NULL);
338 }
339
340 void PrintJob::HoldUntilStopIsCalled() {
341 }
342
343 void PrintJob::Quit() {
344   base::MessageLoop::current()->Quit();
345 }
346
347 // Takes settings_ ownership and will be deleted in the receiving thread.
348 JobEventDetails::JobEventDetails(Type type,
349                                  PrintedDocument* document,
350                                  PrintedPage* page)
351     : document_(document),
352       page_(page),
353       type_(type) {
354 }
355
356 JobEventDetails::~JobEventDetails() {
357 }
358
359 PrintedDocument* JobEventDetails::document() const { return document_.get(); }
360
361 PrintedPage* JobEventDetails::page() const { return page_.get(); }
362
363 }  // namespace printing