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