Fix build break
[chromium-blink-merge.git] / chrome / browser / printing / print_job.cc
blob3d63d3371cec9021d3d90a6573c87658fccdee7c
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.h"
10 #include "base/threading/thread_restrictions.h"
11 #include "base/timer.h"
12 #include "chrome/browser/printing/print_job_worker.h"
13 #include "chrome/common/chrome_notification_types.h"
14 #include "content/public/browser/notification_service.h"
15 #include "printing/printed_document.h"
16 #include "printing/printed_page.h"
18 using base::TimeDelta;
20 namespace {
22 // Helper function to ensure |owner| is valid until at least |callback| returns.
23 void HoldRefCallback(const scoped_refptr<printing::PrintJobWorkerOwner>& owner,
24 const base::Closure& callback) {
25 callback.Run();
28 } // namespace
30 namespace printing {
32 PrintJob::PrintJob()
33 : ui_message_loop_(MessageLoop::current()),
34 source_(NULL),
35 worker_(),
36 settings_(),
37 is_job_pending_(false),
38 is_canceling_(false),
39 ALLOW_THIS_IN_INITIALIZER_LIST(quit_factory_(this)) {
40 DCHECK(ui_message_loop_);
41 // This is normally a UI message loop, but in unit tests, the message loop is
42 // of the 'default' type.
43 DCHECK(ui_message_loop_->type() == MessageLoop::TYPE_UI ||
44 ui_message_loop_->type() == MessageLoop::TYPE_DEFAULT);
45 ui_message_loop_->AddDestructionObserver(this);
48 PrintJob::~PrintJob() {
49 ui_message_loop_->RemoveDestructionObserver(this);
50 // The job should be finished (or at least canceled) when it is destroyed.
51 DCHECK(!is_job_pending_);
52 DCHECK(!is_canceling_);
53 if (worker_.get())
54 DCHECK(worker_->message_loop() == NULL);
55 DCHECK_EQ(ui_message_loop_, MessageLoop::current());
58 void PrintJob::Initialize(PrintJobWorkerOwner* job,
59 PrintedPagesSource* source,
60 int page_count) {
61 DCHECK(!source_);
62 DCHECK(!worker_.get());
63 DCHECK(!is_job_pending_);
64 DCHECK(!is_canceling_);
65 DCHECK(!document_.get());
66 source_ = source;
67 worker_.reset(job->DetachWorker(this));
68 settings_ = job->settings();
70 PrintedDocument* new_doc =
71 new PrintedDocument(settings_, source_, job->cookie());
72 new_doc->set_page_count(page_count);
73 UpdatePrintedDocument(new_doc);
75 // Don't forget to register to our own messages.
76 registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT,
77 content::Source<PrintJob>(this));
80 void PrintJob::Observe(int type,
81 const content::NotificationSource& source,
82 const content::NotificationDetails& details) {
83 DCHECK_EQ(ui_message_loop_, MessageLoop::current());
84 switch (type) {
85 case chrome::NOTIFICATION_PRINT_JOB_EVENT: {
86 OnNotifyPrintJobEvent(*content::Details<JobEventDetails>(details).ptr());
87 break;
89 default: {
90 break;
95 void PrintJob::GetSettingsDone(const PrintSettings& new_settings,
96 PrintingContext::Result result) {
97 NOTREACHED();
100 PrintJobWorker* PrintJob::DetachWorker(PrintJobWorkerOwner* new_owner) {
101 NOTREACHED();
102 return NULL;
105 MessageLoop* PrintJob::message_loop() {
106 return ui_message_loop_;
109 const PrintSettings& PrintJob::settings() const {
110 return settings_;
113 int PrintJob::cookie() const {
114 if (!document_.get())
115 // Always use an invalid cookie in this case.
116 return 0;
117 return document_->cookie();
120 void PrintJob::WillDestroyCurrentMessageLoop() {
121 NOTREACHED();
124 void PrintJob::StartPrinting() {
125 DCHECK_EQ(ui_message_loop_, MessageLoop::current());
126 DCHECK(worker_->message_loop());
127 DCHECK(!is_job_pending_);
128 if (!worker_->message_loop() || is_job_pending_)
129 return;
131 // Real work is done in PrintJobWorker::StartPrinting().
132 worker_->message_loop()->PostTask(
133 FROM_HERE,
134 base::Bind(&HoldRefCallback, make_scoped_refptr(this),
135 base::Bind(&PrintJobWorker::StartPrinting,
136 base::Unretained(worker_.get()), document_)));
137 // Set the flag right now.
138 is_job_pending_ = true;
140 // Tell everyone!
141 scoped_refptr<JobEventDetails> details(
142 new JobEventDetails(JobEventDetails::NEW_DOC, document_.get(), NULL));
143 content::NotificationService::current()->Notify(
144 chrome::NOTIFICATION_PRINT_JOB_EVENT,
145 content::Source<PrintJob>(this),
146 content::Details<JobEventDetails>(details.get()));
149 void PrintJob::Stop() {
150 DCHECK_EQ(ui_message_loop_, MessageLoop::current());
152 if (quit_factory_.HasWeakPtrs()) {
153 // In case we're running a nested message loop to wait for a job to finish,
154 // and we finished before the timeout, quit the nested loop right away.
155 Quit();
156 quit_factory_.InvalidateWeakPtrs();
159 // Be sure to live long enough.
160 scoped_refptr<PrintJob> handle(this);
162 MessageLoop* worker_loop = worker_->message_loop();
163 if (worker_loop) {
164 ControlledWorkerShutdown();
166 is_job_pending_ = false;
167 registrar_.Remove(this, chrome::NOTIFICATION_PRINT_JOB_EVENT,
168 content::Source<PrintJob>(this));
170 // Flush the cached document.
171 UpdatePrintedDocument(NULL);
174 void PrintJob::Cancel() {
175 if (is_canceling_)
176 return;
177 is_canceling_ = true;
179 // Be sure to live long enough.
180 scoped_refptr<PrintJob> handle(this);
182 DCHECK_EQ(ui_message_loop_, MessageLoop::current());
183 MessageLoop* worker_loop = worker_.get() ? worker_->message_loop() : NULL;
184 if (worker_loop) {
185 // Call this right now so it renders the context invalid. Do not use
186 // InvokeLater since it would take too much time.
187 worker_->Cancel();
189 // Make sure a Cancel() is broadcast.
190 scoped_refptr<JobEventDetails> details(
191 new JobEventDetails(JobEventDetails::FAILED, NULL, NULL));
192 content::NotificationService::current()->Notify(
193 chrome::NOTIFICATION_PRINT_JOB_EVENT,
194 content::Source<PrintJob>(this),
195 content::Details<JobEventDetails>(details.get()));
196 Stop();
197 is_canceling_ = false;
200 bool PrintJob::FlushJob(base::TimeDelta timeout) {
201 // Make sure the object outlive this message loop.
202 scoped_refptr<PrintJob> handle(this);
204 MessageLoop::current()->PostDelayedTask(FROM_HERE,
205 base::Bind(&PrintJob::Quit, quit_factory_.GetWeakPtr()), timeout);
207 MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
208 MessageLoop::current()->Run();
210 return true;
213 void PrintJob::DisconnectSource() {
214 source_ = NULL;
215 if (document_.get())
216 document_->DisconnectSource();
219 bool PrintJob::is_job_pending() const {
220 return is_job_pending_;
223 PrintedDocument* PrintJob::document() const {
224 return document_.get();
227 void PrintJob::UpdatePrintedDocument(PrintedDocument* new_document) {
228 if (document_.get() == new_document)
229 return;
231 document_ = new_document;
233 if (document_.get()) {
234 settings_ = document_->settings();
237 if (worker_.get() && worker_->message_loop()) {
238 DCHECK(!is_job_pending_);
239 // Sync the document with the worker.
240 worker_->message_loop()->PostTask(
241 FROM_HERE,
242 base::Bind(&HoldRefCallback, make_scoped_refptr(this),
243 base::Bind(&PrintJobWorker::OnDocumentChanged,
244 base::Unretained(worker_.get()), document_)));
248 void PrintJob::OnNotifyPrintJobEvent(const JobEventDetails& event_details) {
249 switch (event_details.type()) {
250 case JobEventDetails::FAILED: {
251 settings_.Clear();
252 // No need to cancel since the worker already canceled itself.
253 Stop();
254 break;
256 case JobEventDetails::USER_INIT_DONE:
257 case JobEventDetails::DEFAULT_INIT_DONE:
258 case JobEventDetails::USER_INIT_CANCELED: {
259 DCHECK_EQ(event_details.document(), document_.get());
260 break;
262 case JobEventDetails::NEW_DOC:
263 case JobEventDetails::NEW_PAGE:
264 case JobEventDetails::PAGE_DONE:
265 case JobEventDetails::JOB_DONE:
266 case JobEventDetails::ALL_PAGES_REQUESTED: {
267 // Don't care.
268 break;
270 case JobEventDetails::DOC_DONE: {
271 // This will call Stop() and broadcast a JOB_DONE message.
272 MessageLoop::current()->PostTask(
273 FROM_HERE, base::Bind(&PrintJob::OnDocumentDone, this));
274 break;
276 default: {
277 NOTREACHED();
278 break;
283 void PrintJob::OnDocumentDone() {
284 // Be sure to live long enough. The instance could be destroyed by the
285 // JOB_DONE broadcast.
286 scoped_refptr<PrintJob> handle(this);
288 // Stop the worker thread.
289 Stop();
291 scoped_refptr<JobEventDetails> details(
292 new JobEventDetails(JobEventDetails::JOB_DONE, document_.get(), NULL));
293 content::NotificationService::current()->Notify(
294 chrome::NOTIFICATION_PRINT_JOB_EVENT,
295 content::Source<PrintJob>(this),
296 content::Details<JobEventDetails>(details.get()));
299 void PrintJob::ControlledWorkerShutdown() {
300 DCHECK_EQ(ui_message_loop_, MessageLoop::current());
302 // The deadlock this code works around is specific to window messaging on
303 // Windows, so we aren't likely to need it on any other platforms.
304 #if defined(OS_WIN)
305 // We could easily get into a deadlock case if worker_->Stop() is used; the
306 // printer driver created a window as a child of the browser window. By
307 // canceling the job, the printer driver initiated dialog box is destroyed,
308 // which sends a blocking message to its parent window. If the browser window
309 // thread is not processing messages, a deadlock occurs.
311 // This function ensures that the dialog box will be destroyed in a timely
312 // manner by the mere fact that the thread will terminate. So the potential
313 // deadlock is eliminated.
314 worker_->StopSoon();
316 // Run a tight message loop until the worker terminates. It may seems like a
317 // hack but I see no other way to get it to work flawlessly. The issues here
318 // are:
319 // - We don't want to run tasks while the thread is quitting.
320 // - We want this code path to wait on the thread to quit before continuing.
321 MSG msg;
322 HANDLE thread_handle = worker_->thread_handle();
323 for (; thread_handle;) {
324 // Note that we don't do any kind of message prioritization since we don't
325 // execute any pending task or timer.
326 DWORD result = MsgWaitForMultipleObjects(1, &thread_handle,
327 FALSE, INFINITE, QS_ALLINPUT);
328 if (result == WAIT_OBJECT_0 + 1) {
329 while (PeekMessage(&msg, NULL, 0, 0, TRUE) > 0) {
330 TranslateMessage(&msg);
331 DispatchMessage(&msg);
333 // Continue looping.
334 } else if (result == WAIT_OBJECT_0) {
335 // The thread quit.
336 break;
337 } else {
338 // An error occurred. Assume the thread quit.
339 NOTREACHED();
340 break;
343 #endif
345 // Temporarily allow it until we fix
346 // http://code.google.com/p/chromium/issues/detail?id=67044
347 base::ThreadRestrictions::ScopedAllowIO allow_io;
349 // Now make sure the thread object is cleaned up.
350 worker_->Stop();
353 void PrintJob::Quit() {
354 MessageLoop::current()->Quit();
357 // Takes settings_ ownership and will be deleted in the receiving thread.
358 JobEventDetails::JobEventDetails(Type type,
359 PrintedDocument* document,
360 PrintedPage* page)
361 : document_(document),
362 page_(page),
363 type_(type) {
366 JobEventDetails::~JobEventDetails() {
369 PrintedDocument* JobEventDetails::document() const {
370 return document_;
373 PrintedPage* JobEventDetails::page() const {
374 return page_;
377 } // namespace printing