Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / printing / print_view_manager_base.cc
1 // Copyright 2013 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_view_manager_base.h"
6
7 #include "base/bind.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/timer/timer.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/printing/print_job.h"
15 #include "chrome/browser/printing/print_job_manager.h"
16 #include "chrome/browser/printing/printer_query.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/ui/simple_message_box.h"
19 #include "chrome/common/pref_names.h"
20 #include "chrome/common/print_messages.h"
21 #include "chrome/grit/generated_resources.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/notification_details.h"
24 #include "content/public/browser/notification_service.h"
25 #include "content/public/browser/notification_source.h"
26 #include "content/public/browser/render_view_host.h"
27 #include "content/public/browser/web_contents.h"
28 #include "printing/pdf_metafile_skia.h"
29 #include "printing/printed_document.h"
30 #include "ui/base/l10n/l10n_util.h"
31
32 #if defined(ENABLE_FULL_PRINTING)
33 #include "chrome/browser/printing/print_error_dialog.h"
34 #endif
35
36 using base::TimeDelta;
37 using content::BrowserThread;
38
39 namespace printing {
40
41 namespace {
42
43 }  // namespace
44
45 PrintViewManagerBase::PrintViewManagerBase(content::WebContents* web_contents)
46     : content::WebContentsObserver(web_contents),
47       number_pages_(0),
48       printing_succeeded_(false),
49       inside_inner_message_loop_(false),
50       cookie_(0),
51       queue_(g_browser_process->print_job_manager()->queue()) {
52   DCHECK(queue_.get());
53 #if !defined(OS_MACOSX)
54   expecting_first_page_ = true;
55 #endif  // OS_MACOSX
56   Profile* profile =
57       Profile::FromBrowserContext(web_contents->GetBrowserContext());
58   printing_enabled_.Init(
59       prefs::kPrintingEnabled,
60       profile->GetPrefs(),
61       base::Bind(&PrintViewManagerBase::UpdateScriptedPrintingBlocked,
62                  base::Unretained(this)));
63 }
64
65 PrintViewManagerBase::~PrintViewManagerBase() {
66   ReleasePrinterQuery();
67   DisconnectFromCurrentPrintJob();
68 }
69
70 #if !defined(DISABLE_BASIC_PRINTING)
71 bool PrintViewManagerBase::PrintNow() {
72   return PrintNowInternal(new PrintMsg_PrintPages(routing_id()));
73 }
74 #endif  // !DISABLE_BASIC_PRINTING
75
76 void PrintViewManagerBase::UpdateScriptedPrintingBlocked() {
77   Send(new PrintMsg_SetScriptedPrintingBlocked(
78        routing_id(),
79        !printing_enabled_.GetValue()));
80 }
81
82 void PrintViewManagerBase::NavigationStopped() {
83   // Cancel the current job, wait for the worker to finish.
84   TerminatePrintJob(true);
85 }
86
87 void PrintViewManagerBase::RenderProcessGone(base::TerminationStatus status) {
88   ReleasePrinterQuery();
89
90   if (!print_job_.get())
91     return;
92
93   scoped_refptr<PrintedDocument> document(print_job_->document());
94   if (document.get()) {
95     // If IsComplete() returns false, the document isn't completely rendered.
96     // Since our renderer is gone, there's nothing to do, cancel it. Otherwise,
97     // the print job may finish without problem.
98     TerminatePrintJob(!document->IsComplete());
99   }
100 }
101
102 base::string16 PrintViewManagerBase::RenderSourceName() {
103   base::string16 name(web_contents()->GetTitle());
104   if (name.empty())
105     name = l10n_util::GetStringUTF16(IDS_DEFAULT_PRINT_DOCUMENT_TITLE);
106   return name;
107 }
108
109 void PrintViewManagerBase::OnDidGetPrintedPagesCount(int cookie,
110                                                      int number_pages) {
111   DCHECK_GT(cookie, 0);
112   DCHECK_GT(number_pages, 0);
113   number_pages_ = number_pages;
114   OpportunisticallyCreatePrintJob(cookie);
115 }
116
117 void PrintViewManagerBase::OnDidGetDocumentCookie(int cookie) {
118   cookie_ = cookie;
119 }
120
121 void PrintViewManagerBase::OnDidPrintPage(
122   const PrintHostMsg_DidPrintPage_Params& params) {
123   if (!OpportunisticallyCreatePrintJob(params.document_cookie))
124     return;
125
126   PrintedDocument* document = print_job_->document();
127   if (!document || params.document_cookie != document->cookie()) {
128     // Out of sync. It may happen since we are completely asynchronous. Old
129     // spurious messages can be received if one of the processes is overloaded.
130     return;
131   }
132
133 #if defined(OS_MACOSX)
134   const bool metafile_must_be_valid = true;
135 #else
136   const bool metafile_must_be_valid = expecting_first_page_;
137   expecting_first_page_ = false;
138 #endif  // OS_MACOSX
139
140   base::SharedMemory shared_buf(params.metafile_data_handle, true);
141   if (metafile_must_be_valid) {
142     if (!shared_buf.Map(params.data_size)) {
143       NOTREACHED() << "couldn't map";
144       web_contents()->Stop();
145       return;
146     }
147   }
148
149   scoped_ptr<PdfMetafileSkia> metafile(new PdfMetafileSkia);
150   if (metafile_must_be_valid) {
151     if (!metafile->InitFromData(shared_buf.memory(), params.data_size)) {
152       NOTREACHED() << "Invalid metafile header";
153       web_contents()->Stop();
154       return;
155     }
156   }
157
158 #if !defined(OS_WIN)
159   // Update the rendered document. It will send notifications to the listener.
160   document->SetPage(params.page_number,
161                     metafile.PassAs<MetafilePlayer>(),
162                     params.page_size,
163                     params.content_area);
164
165   ShouldQuitFromInnerMessageLoop();
166 #else
167   if (metafile_must_be_valid) {
168     scoped_refptr<base::RefCountedBytes> bytes = new base::RefCountedBytes(
169         reinterpret_cast<const unsigned char*>(shared_buf.memory()),
170         params.data_size);
171
172     document->DebugDumpData(bytes, FILE_PATH_LITERAL(".pdf"));
173     print_job_->StartPdfToEmfConversion(
174         bytes, params.page_size, params.content_area);
175   }
176 #endif  // !OS_WIN
177 }
178
179 void PrintViewManagerBase::OnPrintingFailed(int cookie) {
180   if (cookie != cookie_) {
181     NOTREACHED();
182     return;
183   }
184
185 #if defined(ENABLE_FULL_PRINTING)
186   chrome::ShowPrintErrorDialog();
187 #endif
188
189   ReleasePrinterQuery();
190
191   content::NotificationService::current()->Notify(
192       chrome::NOTIFICATION_PRINT_JOB_RELEASED,
193       content::Source<content::WebContents>(web_contents()),
194       content::NotificationService::NoDetails());
195 }
196
197 void PrintViewManagerBase::OnShowInvalidPrinterSettingsError() {
198   chrome::ShowMessageBox(NULL,
199                          base::string16(),
200                          l10n_util::GetStringUTF16(
201                              IDS_PRINT_INVALID_PRINTER_SETTINGS),
202                          chrome::MESSAGE_BOX_TYPE_WARNING);
203 }
204
205 void PrintViewManagerBase::DidStartLoading(
206     content::RenderViewHost* render_view_host) {
207   UpdateScriptedPrintingBlocked();
208 }
209
210 bool PrintViewManagerBase::OnMessageReceived(const IPC::Message& message) {
211   bool handled = true;
212   IPC_BEGIN_MESSAGE_MAP(PrintViewManagerBase, message)
213     IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetPrintedPagesCount,
214                         OnDidGetPrintedPagesCount)
215     IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetDocumentCookie,
216                         OnDidGetDocumentCookie)
217     IPC_MESSAGE_HANDLER(PrintHostMsg_DidPrintPage, OnDidPrintPage)
218     IPC_MESSAGE_HANDLER(PrintHostMsg_PrintingFailed, OnPrintingFailed)
219     IPC_MESSAGE_HANDLER(PrintHostMsg_ShowInvalidPrinterSettingsError,
220                         OnShowInvalidPrinterSettingsError);
221     IPC_MESSAGE_UNHANDLED(handled = false)
222   IPC_END_MESSAGE_MAP()
223   return handled;
224 }
225
226 void PrintViewManagerBase::Observe(
227     int type,
228     const content::NotificationSource& source,
229     const content::NotificationDetails& details) {
230   switch (type) {
231     case chrome::NOTIFICATION_PRINT_JOB_EVENT: {
232       OnNotifyPrintJobEvent(*content::Details<JobEventDetails>(details).ptr());
233       break;
234     }
235     default: {
236       NOTREACHED();
237       break;
238     }
239   }
240 }
241
242 void PrintViewManagerBase::OnNotifyPrintJobEvent(
243     const JobEventDetails& event_details) {
244   switch (event_details.type()) {
245     case JobEventDetails::FAILED: {
246       TerminatePrintJob(true);
247
248       content::NotificationService::current()->Notify(
249           chrome::NOTIFICATION_PRINT_JOB_RELEASED,
250           content::Source<content::WebContents>(web_contents()),
251           content::NotificationService::NoDetails());
252       break;
253     }
254     case JobEventDetails::USER_INIT_DONE:
255     case JobEventDetails::DEFAULT_INIT_DONE:
256     case JobEventDetails::USER_INIT_CANCELED: {
257       NOTREACHED();
258       break;
259     }
260     case JobEventDetails::ALL_PAGES_REQUESTED: {
261       ShouldQuitFromInnerMessageLoop();
262       break;
263     }
264     case JobEventDetails::NEW_DOC:
265     case JobEventDetails::NEW_PAGE:
266     case JobEventDetails::PAGE_DONE:
267     case JobEventDetails::DOC_DONE: {
268       // Don't care about the actual printing process.
269       break;
270     }
271     case JobEventDetails::JOB_DONE: {
272       // Printing is done, we don't need it anymore.
273       // print_job_->is_job_pending() may still be true, depending on the order
274       // of object registration.
275       printing_succeeded_ = true;
276       ReleasePrintJob();
277
278       content::NotificationService::current()->Notify(
279           chrome::NOTIFICATION_PRINT_JOB_RELEASED,
280           content::Source<content::WebContents>(web_contents()),
281           content::NotificationService::NoDetails());
282       break;
283     }
284     default: {
285       NOTREACHED();
286       break;
287     }
288   }
289 }
290
291 bool PrintViewManagerBase::RenderAllMissingPagesNow() {
292   if (!print_job_.get() || !print_job_->is_job_pending())
293     return false;
294
295   // We can't print if there is no renderer.
296   if (!web_contents() ||
297       !web_contents()->GetRenderViewHost() ||
298       !web_contents()->GetRenderViewHost()->IsRenderViewLive()) {
299     return false;
300   }
301
302   // Is the document already complete?
303   if (print_job_->document() && print_job_->document()->IsComplete()) {
304     printing_succeeded_ = true;
305     return true;
306   }
307
308   // WebContents is either dying or a second consecutive request to print
309   // happened before the first had time to finish. We need to render all the
310   // pages in an hurry if a print_job_ is still pending. No need to wait for it
311   // to actually spool the pages, only to have the renderer generate them. Run
312   // a message loop until we get our signal that the print job is satisfied.
313   // PrintJob will send a ALL_PAGES_REQUESTED after having received all the
314   // pages it needs. MessageLoop::current()->Quit() will be called as soon as
315   // print_job_->document()->IsComplete() is true on either ALL_PAGES_REQUESTED
316   // or in DidPrintPage(). The check is done in
317   // ShouldQuitFromInnerMessageLoop().
318   // BLOCKS until all the pages are received. (Need to enable recursive task)
319   if (!RunInnerMessageLoop()) {
320     // This function is always called from DisconnectFromCurrentPrintJob() so we
321     // know that the job will be stopped/canceled in any case.
322     return false;
323   }
324   return true;
325 }
326
327 void PrintViewManagerBase::ShouldQuitFromInnerMessageLoop() {
328   // Look at the reason.
329   DCHECK(print_job_->document());
330   if (print_job_->document() &&
331       print_job_->document()->IsComplete() &&
332       inside_inner_message_loop_) {
333     // We are in a message loop created by RenderAllMissingPagesNow. Quit from
334     // it.
335     base::MessageLoop::current()->Quit();
336     inside_inner_message_loop_ = false;
337   }
338 }
339
340 bool PrintViewManagerBase::CreateNewPrintJob(PrintJobWorkerOwner* job) {
341   DCHECK(!inside_inner_message_loop_);
342
343   // Disconnect the current print_job_.
344   DisconnectFromCurrentPrintJob();
345
346   // We can't print if there is no renderer.
347   if (!web_contents()->GetRenderViewHost() ||
348       !web_contents()->GetRenderViewHost()->IsRenderViewLive()) {
349     return false;
350   }
351
352   // Ask the renderer to generate the print preview, create the print preview
353   // view and switch to it, initialize the printer and show the print dialog.
354   DCHECK(!print_job_.get());
355   DCHECK(job);
356   if (!job)
357     return false;
358
359   print_job_ = new PrintJob();
360   print_job_->Initialize(job, this, number_pages_);
361   registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT,
362                  content::Source<PrintJob>(print_job_.get()));
363   printing_succeeded_ = false;
364   return true;
365 }
366
367 void PrintViewManagerBase::DisconnectFromCurrentPrintJob() {
368   // Make sure all the necessary rendered page are done. Don't bother with the
369   // return value.
370   bool result = RenderAllMissingPagesNow();
371
372   // Verify that assertion.
373   if (print_job_.get() &&
374       print_job_->document() &&
375       !print_job_->document()->IsComplete()) {
376     DCHECK(!result);
377     // That failed.
378     TerminatePrintJob(true);
379   } else {
380     // DO NOT wait for the job to finish.
381     ReleasePrintJob();
382   }
383 #if !defined(OS_MACOSX)
384   expecting_first_page_ = true;
385 #endif  // OS_MACOSX
386 }
387
388 void PrintViewManagerBase::PrintingDone(bool success) {
389   if (!print_job_.get())
390     return;
391   Send(new PrintMsg_PrintingDone(routing_id(), success));
392 }
393
394 void PrintViewManagerBase::TerminatePrintJob(bool cancel) {
395   if (!print_job_.get())
396     return;
397
398   if (cancel) {
399     // We don't need the metafile data anymore because the printing is canceled.
400     print_job_->Cancel();
401     inside_inner_message_loop_ = false;
402   } else {
403     DCHECK(!inside_inner_message_loop_);
404     DCHECK(!print_job_->document() || print_job_->document()->IsComplete());
405
406     // WebContents is either dying or navigating elsewhere. We need to render
407     // all the pages in an hurry if a print job is still pending. This does the
408     // trick since it runs a blocking message loop:
409     print_job_->Stop();
410   }
411   ReleasePrintJob();
412 }
413
414 void PrintViewManagerBase::ReleasePrintJob() {
415   if (!print_job_.get())
416     return;
417
418   PrintingDone(printing_succeeded_);
419
420   registrar_.Remove(this, chrome::NOTIFICATION_PRINT_JOB_EVENT,
421                     content::Source<PrintJob>(print_job_.get()));
422   print_job_->DisconnectSource();
423   // Don't close the worker thread.
424   print_job_ = NULL;
425 }
426
427 bool PrintViewManagerBase::RunInnerMessageLoop() {
428   // This value may actually be too low:
429   //
430   // - If we're looping because of printer settings initialization, the premise
431   // here is that some poor users have their print server away on a VPN over a
432   // slow connection. In this situation, the simple fact of opening the printer
433   // can be dead slow. On the other side, we don't want to die infinitely for a
434   // real network error. Give the printer 60 seconds to comply.
435   //
436   // - If we're looping because of renderer page generation, the renderer could
437   // be CPU bound, the page overly complex/large or the system just
438   // memory-bound.
439   static const int kPrinterSettingsTimeout = 60000;
440   base::OneShotTimer<base::MessageLoop> quit_timer;
441   quit_timer.Start(FROM_HERE,
442                    TimeDelta::FromMilliseconds(kPrinterSettingsTimeout),
443                    base::MessageLoop::current(), &base::MessageLoop::Quit);
444
445   inside_inner_message_loop_ = true;
446
447   // Need to enable recursive task.
448   {
449     base::MessageLoop::ScopedNestableTaskAllower allow(
450         base::MessageLoop::current());
451     base::MessageLoop::current()->Run();
452   }
453
454   bool success = true;
455   if (inside_inner_message_loop_) {
456     // Ok we timed out. That's sad.
457     inside_inner_message_loop_ = false;
458     success = false;
459   }
460
461   return success;
462 }
463
464 bool PrintViewManagerBase::OpportunisticallyCreatePrintJob(int cookie) {
465   if (print_job_.get())
466     return true;
467
468   if (!cookie) {
469     // Out of sync. It may happens since we are completely asynchronous. Old
470     // spurious message can happen if one of the processes is overloaded.
471     return false;
472   }
473
474   // The job was initiated by a script. Time to get the corresponding worker
475   // thread.
476   scoped_refptr<PrinterQuery> queued_query = queue_->PopPrinterQuery(cookie);
477   if (!queued_query.get()) {
478     NOTREACHED();
479     return false;
480   }
481
482   if (!CreateNewPrintJob(queued_query.get())) {
483     // Don't kill anything.
484     return false;
485   }
486
487   // Settings are already loaded. Go ahead. This will set
488   // print_job_->is_job_pending() to true.
489   print_job_->StartPrinting();
490   return true;
491 }
492
493 bool PrintViewManagerBase::PrintNowInternal(IPC::Message* message) {
494   // Don't print / print preview interstitials.
495   if (web_contents()->ShowingInterstitialPage()) {
496     delete message;
497     return false;
498   }
499   return Send(message);
500 }
501
502 void PrintViewManagerBase::ReleasePrinterQuery() {
503   if (!cookie_)
504     return;
505
506   int cookie = cookie_;
507   cookie_ = 0;
508
509   printing::PrintJobManager* print_job_manager =
510       g_browser_process->print_job_manager();
511   // May be NULL in tests.
512   if (!print_job_manager)
513     return;
514
515   scoped_refptr<printing::PrinterQuery> printer_query;
516   printer_query = queue_->PopPrinterQuery(cookie);
517   if (!printer_query.get())
518     return;
519   BrowserThread::PostTask(
520       BrowserThread::IO, FROM_HERE,
521       base::Bind(&PrinterQuery::StopWorker, printer_query.get()));
522 }
523
524 }  // namespace printing