Upstream version 5.34.104.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/common/pref_names.h"
21 #include "chrome/common/print_messages.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 "content/public/browser/web_contents_view.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   const CommandLine* cmdline = CommandLine::ForCurrentProcess();
167   int raster_size = std::min(params.page_size.GetArea(),
168                              kMaxRasterSizeInPixels);
169   if (big_emf || (cmdline && cmdline->HasSwitch(switches::kPrintRaster))) {
170     scoped_ptr<NativeMetafile> raster_metafile(
171         metafile->RasterizeMetafile(raster_size));
172     if (raster_metafile.get()) {
173       metafile.swap(raster_metafile);
174     } else if (big_emf) {
175       // Don't fall back to emf here.
176       NOTREACHED() << "size:" << params.data_size;
177       TerminatePrintJob(true);
178       web_contents()->Stop();
179       return;
180     }
181   }
182 #endif
183
184   // Update the rendered document. It will send notifications to the listener.
185   document->SetPage(params.page_number,
186                     metafile.release(),
187                     params.actual_shrink,
188                     params.page_size,
189                     params.content_area);
190
191   ShouldQuitFromInnerMessageLoop();
192 }
193
194 void PrintViewManagerBase::OnPrintingFailed(int cookie) {
195   if (cookie != cookie_) {
196     NOTREACHED();
197     return;
198   }
199
200 #if defined(ENABLE_FULL_PRINTING)
201   chrome::ShowPrintErrorDialog(
202       web_contents()->GetView()->GetTopLevelNativeWindow());
203 #endif
204
205   ReleasePrinterQuery();
206
207   content::NotificationService::current()->Notify(
208       chrome::NOTIFICATION_PRINT_JOB_RELEASED,
209       content::Source<content::WebContents>(web_contents()),
210       content::NotificationService::NoDetails());
211 }
212
213 void PrintViewManagerBase::DidStartLoading(
214     content::RenderViewHost* render_view_host) {
215   UpdateScriptedPrintingBlocked();
216 }
217
218 bool PrintViewManagerBase::OnMessageReceived(const IPC::Message& message) {
219   bool handled = true;
220   IPC_BEGIN_MESSAGE_MAP(PrintViewManagerBase, message)
221     IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetPrintedPagesCount,
222                         OnDidGetPrintedPagesCount)
223     IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetDocumentCookie,
224                         OnDidGetDocumentCookie)
225     IPC_MESSAGE_HANDLER(PrintHostMsg_DidPrintPage, OnDidPrintPage)
226     IPC_MESSAGE_HANDLER(PrintHostMsg_PrintingFailed, OnPrintingFailed)
227     IPC_MESSAGE_UNHANDLED(handled = false)
228   IPC_END_MESSAGE_MAP()
229   return handled;
230 }
231
232 void PrintViewManagerBase::Observe(
233     int type,
234     const content::NotificationSource& source,
235     const content::NotificationDetails& details) {
236   switch (type) {
237     case chrome::NOTIFICATION_PRINT_JOB_EVENT: {
238       OnNotifyPrintJobEvent(*content::Details<JobEventDetails>(details).ptr());
239       break;
240     }
241     default: {
242       NOTREACHED();
243       break;
244     }
245   }
246 }
247
248 void PrintViewManagerBase::OnNotifyPrintJobEvent(
249     const JobEventDetails& event_details) {
250   switch (event_details.type()) {
251     case JobEventDetails::FAILED: {
252       TerminatePrintJob(true);
253
254       content::NotificationService::current()->Notify(
255           chrome::NOTIFICATION_PRINT_JOB_RELEASED,
256           content::Source<content::WebContents>(web_contents()),
257           content::NotificationService::NoDetails());
258       break;
259     }
260     case JobEventDetails::USER_INIT_DONE:
261     case JobEventDetails::DEFAULT_INIT_DONE:
262     case JobEventDetails::USER_INIT_CANCELED: {
263       NOTREACHED();
264       break;
265     }
266     case JobEventDetails::ALL_PAGES_REQUESTED: {
267       ShouldQuitFromInnerMessageLoop();
268       break;
269     }
270     case JobEventDetails::NEW_DOC:
271     case JobEventDetails::NEW_PAGE:
272     case JobEventDetails::PAGE_DONE:
273     case JobEventDetails::DOC_DONE: {
274       // Don't care about the actual printing process.
275       break;
276     }
277     case JobEventDetails::JOB_DONE: {
278       // Printing is done, we don't need it anymore.
279       // print_job_->is_job_pending() may still be true, depending on the order
280       // of object registration.
281       printing_succeeded_ = true;
282       ReleasePrintJob();
283
284       content::NotificationService::current()->Notify(
285           chrome::NOTIFICATION_PRINT_JOB_RELEASED,
286           content::Source<content::WebContents>(web_contents()),
287           content::NotificationService::NoDetails());
288       break;
289     }
290     default: {
291       NOTREACHED();
292       break;
293     }
294   }
295 }
296
297 bool PrintViewManagerBase::RenderAllMissingPagesNow() {
298   if (!print_job_.get() || !print_job_->is_job_pending())
299     return false;
300
301   // We can't print if there is no renderer.
302   if (!web_contents() ||
303       !web_contents()->GetRenderViewHost() ||
304       !web_contents()->GetRenderViewHost()->IsRenderViewLive()) {
305     return false;
306   }
307
308   // Is the document already complete?
309   if (print_job_->document() && print_job_->document()->IsComplete()) {
310     printing_succeeded_ = true;
311     return true;
312   }
313
314   // WebContents is either dying or a second consecutive request to print
315   // happened before the first had time to finish. We need to render all the
316   // pages in an hurry if a print_job_ is still pending. No need to wait for it
317   // to actually spool the pages, only to have the renderer generate them. Run
318   // a message loop until we get our signal that the print job is satisfied.
319   // PrintJob will send a ALL_PAGES_REQUESTED after having received all the
320   // pages it needs. MessageLoop::current()->Quit() will be called as soon as
321   // print_job_->document()->IsComplete() is true on either ALL_PAGES_REQUESTED
322   // or in DidPrintPage(). The check is done in
323   // ShouldQuitFromInnerMessageLoop().
324   // BLOCKS until all the pages are received. (Need to enable recursive task)
325   if (!RunInnerMessageLoop()) {
326     // This function is always called from DisconnectFromCurrentPrintJob() so we
327     // know that the job will be stopped/canceled in any case.
328     return false;
329   }
330   return true;
331 }
332
333 void PrintViewManagerBase::ShouldQuitFromInnerMessageLoop() {
334   // Look at the reason.
335   DCHECK(print_job_->document());
336   if (print_job_->document() &&
337       print_job_->document()->IsComplete() &&
338       inside_inner_message_loop_) {
339     // We are in a message loop created by RenderAllMissingPagesNow. Quit from
340     // it.
341     base::MessageLoop::current()->Quit();
342     inside_inner_message_loop_ = false;
343   }
344 }
345
346 bool PrintViewManagerBase::CreateNewPrintJob(PrintJobWorkerOwner* job) {
347   DCHECK(!inside_inner_message_loop_);
348
349   // Disconnect the current print_job_.
350   DisconnectFromCurrentPrintJob();
351
352   // We can't print if there is no renderer.
353   if (!web_contents()->GetRenderViewHost() ||
354       !web_contents()->GetRenderViewHost()->IsRenderViewLive()) {
355     return false;
356   }
357
358   // Ask the renderer to generate the print preview, create the print preview
359   // view and switch to it, initialize the printer and show the print dialog.
360   DCHECK(!print_job_.get());
361   DCHECK(job);
362   if (!job)
363     return false;
364
365   print_job_ = new PrintJob();
366   print_job_->Initialize(job, this, number_pages_);
367   registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT,
368                  content::Source<PrintJob>(print_job_.get()));
369   printing_succeeded_ = false;
370   return true;
371 }
372
373 void PrintViewManagerBase::DisconnectFromCurrentPrintJob() {
374   // Make sure all the necessary rendered page are done. Don't bother with the
375   // return value.
376   bool result = RenderAllMissingPagesNow();
377
378   // Verify that assertion.
379   if (print_job_.get() &&
380       print_job_->document() &&
381       !print_job_->document()->IsComplete()) {
382     DCHECK(!result);
383     // That failed.
384     TerminatePrintJob(true);
385   } else {
386     // DO NOT wait for the job to finish.
387     ReleasePrintJob();
388   }
389 #if defined(OS_POSIX) && !defined(OS_MACOSX)
390   expecting_first_page_ = true;
391 #endif
392 }
393
394 void PrintViewManagerBase::PrintingDone(bool success) {
395   if (!print_job_.get())
396     return;
397   Send(new PrintMsg_PrintingDone(routing_id(), success));
398 }
399
400 void PrintViewManagerBase::TerminatePrintJob(bool cancel) {
401   if (!print_job_.get())
402     return;
403
404   if (cancel) {
405     // We don't need the metafile data anymore because the printing is canceled.
406     print_job_->Cancel();
407     inside_inner_message_loop_ = false;
408   } else {
409     DCHECK(!inside_inner_message_loop_);
410     DCHECK(!print_job_->document() || print_job_->document()->IsComplete());
411
412     // WebContents is either dying or navigating elsewhere. We need to render
413     // all the pages in an hurry if a print job is still pending. This does the
414     // trick since it runs a blocking message loop:
415     print_job_->Stop();
416   }
417   ReleasePrintJob();
418 }
419
420 void PrintViewManagerBase::ReleasePrintJob() {
421   if (!print_job_.get())
422     return;
423
424   PrintingDone(printing_succeeded_);
425
426   registrar_.Remove(this, chrome::NOTIFICATION_PRINT_JOB_EVENT,
427                     content::Source<PrintJob>(print_job_.get()));
428   print_job_->DisconnectSource();
429   // Don't close the worker thread.
430   print_job_ = NULL;
431 }
432
433 bool PrintViewManagerBase::RunInnerMessageLoop() {
434   // This value may actually be too low:
435   //
436   // - If we're looping because of printer settings initialization, the premise
437   // here is that some poor users have their print server away on a VPN over a
438   // slow connection. In this situation, the simple fact of opening the printer
439   // can be dead slow. On the other side, we don't want to die infinitely for a
440   // real network error. Give the printer 60 seconds to comply.
441   //
442   // - If we're looping because of renderer page generation, the renderer could
443   // be CPU bound, the page overly complex/large or the system just
444   // memory-bound.
445   static const int kPrinterSettingsTimeout = 60000;
446   base::OneShotTimer<base::MessageLoop> quit_timer;
447   quit_timer.Start(FROM_HERE,
448                    TimeDelta::FromMilliseconds(kPrinterSettingsTimeout),
449                    base::MessageLoop::current(), &base::MessageLoop::Quit);
450
451   inside_inner_message_loop_ = true;
452
453   // Need to enable recursive task.
454   {
455     base::MessageLoop::ScopedNestableTaskAllower allow(
456         base::MessageLoop::current());
457     base::MessageLoop::current()->Run();
458   }
459
460   bool success = true;
461   if (inside_inner_message_loop_) {
462     // Ok we timed out. That's sad.
463     inside_inner_message_loop_ = false;
464     success = false;
465   }
466
467   return success;
468 }
469
470 bool PrintViewManagerBase::OpportunisticallyCreatePrintJob(int cookie) {
471   if (print_job_.get())
472     return true;
473
474   if (!cookie) {
475     // Out of sync. It may happens since we are completely asynchronous. Old
476     // spurious message can happen if one of the processes is overloaded.
477     return false;
478   }
479
480   // The job was initiated by a script. Time to get the corresponding worker
481   // thread.
482   scoped_refptr<PrinterQuery> queued_query = queue_->PopPrinterQuery(cookie);
483   if (!queued_query) {
484     NOTREACHED();
485     return false;
486   }
487
488   if (!CreateNewPrintJob(queued_query)) {
489     // Don't kill anything.
490     return false;
491   }
492
493   // Settings are already loaded. Go ahead. This will set
494   // print_job_->is_job_pending() to true.
495   print_job_->StartPrinting();
496   return true;
497 }
498
499 bool PrintViewManagerBase::PrintNowInternal(IPC::Message* message) {
500   // Don't print / print preview interstitials.
501   if (web_contents()->ShowingInterstitialPage()) {
502     delete message;
503     return false;
504   }
505   return Send(message);
506 }
507
508 void PrintViewManagerBase::ReleasePrinterQuery() {
509   if (!cookie_)
510     return;
511
512   int cookie = cookie_;
513   cookie_ = 0;
514   queue_->SetDestination(NULL);
515
516
517   printing::PrintJobManager* print_job_manager =
518       g_browser_process->print_job_manager();
519   // May be NULL in tests.
520   if (!print_job_manager)
521     return;
522
523   scoped_refptr<printing::PrinterQuery> printer_query;
524   printer_query = queue_->PopPrinterQuery(cookie);
525   if (!printer_query)
526     return;
527   BrowserThread::PostTask(
528       BrowserThread::IO, FROM_HERE,
529       base::Bind(&PrinterQuery::StopWorker, printer_query.get()));
530 }
531
532 }  // namespace printing