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