1// Copyright (c) 2011 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.h"
6
7#include "base/memory/scoped_ptr.h"
8#include "base/utf_string_conversions.h"
9#include "chrome/browser/browser_process.h"
10#include "chrome/browser/printing/print_job.h"
11#include "chrome/browser/printing/print_job_manager.h"
12#include "chrome/browser/printing/print_preview_tab_controller.h"
13#include "chrome/browser/printing/printer_query.h"
14#include "chrome/browser/ui/webui/print_preview_ui.h"
15#include "chrome/common/print_messages.h"
16#include "content/browser/renderer_host/render_view_host.h"
17#include "content/browser/tab_contents/navigation_entry.h"
18#include "content/browser/tab_contents/tab_contents.h"
19#include "content/common/notification_details.h"
20#include "content/common/notification_source.h"
21#include "grit/generated_resources.h"
22#include "printing/metafile.h"
23#include "printing/metafile_impl.h"
24#include "printing/printed_document.h"
25#include "ui/base/l10n/l10n_util.h"
26
27using base::TimeDelta;
28
29namespace {
30
31string16 GenerateRenderSourceName(TabContents* tab_contents) {
32  string16 name(tab_contents->GetTitle());
33  if (name.empty())
34    name = l10n_util::GetStringUTF16(IDS_DEFAULT_PRINT_DOCUMENT_TITLE);
35  return name;
36}
37
38}  // namespace
39
40namespace printing {
41
42PrintViewManager::PrintViewManager(TabContents* tab_contents)
43    : TabContentsObserver(tab_contents),
44      number_pages_(0),
45      waiting_to_print_(false),
46      printing_succeeded_(false),
47      inside_inner_message_loop_(false),
48      is_title_overridden_(false) {
49#if defined(OS_POSIX) && !defined(OS_MACOSX)
50  expecting_first_page_ = true;
51#endif
52}
53
54PrintViewManager::~PrintViewManager() {
55  DisconnectFromCurrentPrintJob();
56}
57
58bool PrintViewManager::PrintNow() {
59  // Don't print interstitials.
60  if (tab_contents()->showing_interstitial_page())
61    return false;
62
63  return Send(new PrintMsg_PrintPages(routing_id()));
64}
65
66void PrintViewManager::StopNavigation() {
67  // Cancel the current job, wait for the worker to finish.
68  TerminatePrintJob(true);
69}
70
71void PrintViewManager::RenderViewGone() {
72  if (!print_job_.get())
73    return;
74
75  scoped_refptr<PrintedDocument> document(print_job_->document());
76  if (document) {
77    // If IsComplete() returns false, the document isn't completely rendered.
78    // Since our renderer is gone, there's nothing to do, cancel it. Otherwise,
79    // the print job may finish without problem.
80    TerminatePrintJob(!document->IsComplete());
81  }
82}
83
84void PrintViewManager::OverrideTitle(TabContents* tab_contents) {
85  is_title_overridden_ = true;
86  overridden_title_ = GenerateRenderSourceName(tab_contents);
87}
88
89string16 PrintViewManager::RenderSourceName() {
90  if (is_title_overridden_)
91    return overridden_title_;
92  return GenerateRenderSourceName(tab_contents());
93}
94
95GURL PrintViewManager::RenderSourceUrl() {
96  NavigationEntry* entry = tab_contents()->controller().GetActiveEntry();
97  if (entry)
98    return entry->virtual_url();
99  return GURL();
100}
101
102void PrintViewManager::OnDidGetPrintedPagesCount(int cookie, int number_pages) {
103  DCHECK_GT(cookie, 0);
104  DCHECK_GT(number_pages, 0);
105  number_pages_ = number_pages;
106  if (!OpportunisticallyCreatePrintJob(cookie))
107    return;
108
109  PrintedDocument* document = print_job_->document();
110  if (!document || cookie != document->cookie()) {
111    // Out of sync. It may happens since we are completely asynchronous. Old
112    // spurious message can happen if one of the processes is overloaded.
113    return;
114  }
115}
116
117void PrintViewManager::OnDidPrintPage(
118    const PrintHostMsg_DidPrintPage_Params& params) {
119  if (!OpportunisticallyCreatePrintJob(params.document_cookie))
120    return;
121
122  PrintedDocument* document = print_job_->document();
123  if (!document || params.document_cookie != document->cookie()) {
124    // Out of sync. It may happen since we are completely asynchronous. Old
125    // spurious messages can be received if one of the processes is overloaded.
126    return;
127  }
128
129#if defined(OS_WIN)
130  // http://msdn2.microsoft.com/en-us/library/ms535522.aspx
131  // Windows 2000/XP: When a page in a spooled file exceeds approximately 350
132  // MB, it can fail to print and not send an error message.
133  if (params.data_size && params.data_size >= 350*1024*1024) {
134    NOTREACHED() << "size:" << params.data_size;
135    TerminatePrintJob(true);
136    tab_contents()->Stop();
137    return;
138  }
139#endif
140
141#if defined(OS_WIN) || defined(OS_MACOSX)
142  const bool metafile_must_be_valid = true;
143#elif defined(OS_POSIX)
144  const bool metafile_must_be_valid = expecting_first_page_;
145  expecting_first_page_ = false;
146#endif
147
148  base::SharedMemory shared_buf(params.metafile_data_handle, true);
149  if (metafile_must_be_valid) {
150    if (!shared_buf.Map(params.data_size)) {
151      NOTREACHED() << "couldn't map";
152      tab_contents()->Stop();
153      return;
154    }
155  }
156
157  scoped_ptr<Metafile> metafile(new NativeMetafile);
158  if (metafile_must_be_valid) {
159    if (!metafile->InitFromData(shared_buf.memory(), params.data_size)) {
160      NOTREACHED() << "Invalid metafile header";
161      tab_contents()->Stop();
162      return;
163    }
164  }
165
166  // Update the rendered document. It will send notifications to the listener.
167  document->SetPage(params.page_number,
168                    metafile.release(),
169                    params.actual_shrink,
170                    params.page_size,
171                    params.content_area,
172                    params.has_visible_overlays);
173
174  ShouldQuitFromInnerMessageLoop();
175}
176
177bool PrintViewManager::OnMessageReceived(const IPC::Message& message) {
178  bool handled = true;
179  IPC_BEGIN_MESSAGE_MAP(PrintViewManager, message)
180    IPC_MESSAGE_HANDLER(PrintHostMsg_DidGetPrintedPagesCount,
181                        OnDidGetPrintedPagesCount)
182    IPC_MESSAGE_HANDLER(PrintHostMsg_DidPrintPage, OnDidPrintPage)
183    IPC_MESSAGE_UNHANDLED(handled = false)
184  IPC_END_MESSAGE_MAP()
185  return handled;
186}
187
188void PrintViewManager::Observe(NotificationType type,
189                               const NotificationSource& source,
190                               const NotificationDetails& details) {
191  switch (type.value) {
192    case NotificationType::PRINT_JOB_EVENT: {
193      OnNotifyPrintJobEvent(*Details<JobEventDetails>(details).ptr());
194      break;
195    }
196    default: {
197      NOTREACHED();
198      break;
199    }
200  }
201}
202
203void PrintViewManager::OnNotifyPrintJobEvent(
204    const JobEventDetails& event_details) {
205  switch (event_details.type()) {
206    case JobEventDetails::FAILED: {
207      TerminatePrintJob(true);
208      break;
209    }
210    case JobEventDetails::USER_INIT_DONE:
211    case JobEventDetails::DEFAULT_INIT_DONE:
212    case JobEventDetails::USER_INIT_CANCELED: {
213      NOTREACHED();
214      break;
215    }
216    case JobEventDetails::ALL_PAGES_REQUESTED: {
217      ShouldQuitFromInnerMessageLoop();
218      break;
219    }
220    case JobEventDetails::NEW_DOC:
221    case JobEventDetails::NEW_PAGE:
222    case JobEventDetails::PAGE_DONE: {
223      // Don't care about the actual printing process.
224      break;
225    }
226    case JobEventDetails::DOC_DONE: {
227      waiting_to_print_ = false;
228      break;
229    }
230    case JobEventDetails::JOB_DONE: {
231      // Printing is done, we don't need it anymore.
232      // print_job_->is_job_pending() may still be true, depending on the order
233      // of object registration.
234      printing_succeeded_ = true;
235      ReleasePrintJob();
236      break;
237    }
238    default: {
239      NOTREACHED();
240      break;
241    }
242  }
243}
244
245bool PrintViewManager::RenderAllMissingPagesNow() {
246  if (!print_job_.get() || !print_job_->is_job_pending()) {
247    DCHECK_EQ(waiting_to_print_, false);
248    return false;
249  }
250
251  // We can't print if there is no renderer.
252  if (!tab_contents() ||
253      !tab_contents()->render_view_host() ||
254      !tab_contents()->render_view_host()->IsRenderViewLive()) {
255    waiting_to_print_ = false;
256    return false;
257  }
258
259  // Is the document already complete?
260  if (print_job_->document() && print_job_->document()->IsComplete()) {
261    waiting_to_print_ = false;
262    printing_succeeded_ = true;
263    return true;
264  }
265
266  // TabContents is either dying or a second consecutive request to print
267  // happened before the first had time to finish. We need to render all the
268  // pages in an hurry if a print_job_ is still pending. No need to wait for it
269  // to actually spool the pages, only to have the renderer generate them. Run
270  // a message loop until we get our signal that the print job is satisfied.
271  // PrintJob will send a ALL_PAGES_REQUESTED after having received all the
272  // pages it needs. MessageLoop::current()->Quit() will be called as soon as
273  // print_job_->document()->IsComplete() is true on either ALL_PAGES_REQUESTED
274  // or in DidPrintPage(). The check is done in
275  // ShouldQuitFromInnerMessageLoop().
276  // BLOCKS until all the pages are received. (Need to enable recursive task)
277  if (!RunInnerMessageLoop()) {
278    // This function is always called from DisconnectFromCurrentPrintJob() so we
279    // know that the job will be stopped/canceled in any case.
280    return false;
281  }
282  return true;
283}
284
285void PrintViewManager::ShouldQuitFromInnerMessageLoop() {
286  // Look at the reason.
287  DCHECK(print_job_->document());
288  if (print_job_->document() &&
289      print_job_->document()->IsComplete() &&
290      inside_inner_message_loop_) {
291    // We are in a message loop created by RenderAllMissingPagesNow. Quit from
292    // it.
293    MessageLoop::current()->Quit();
294    inside_inner_message_loop_ = false;
295    waiting_to_print_ = false;
296  }
297}
298
299bool PrintViewManager::CreateNewPrintJob(PrintJobWorkerOwner* job) {
300  DCHECK(!inside_inner_message_loop_);
301  if (waiting_to_print_) {
302    // We can't help; we are waiting for a print job initialization. The user is
303    // button bashing. The only thing we could do is to batch up the requests.
304    return false;
305  }
306
307  // Disconnect the current print_job_.
308  DisconnectFromCurrentPrintJob();
309
310  // We can't print if there is no renderer.
311  if (!tab_contents()->render_view_host() ||
312      !tab_contents()->render_view_host()->IsRenderViewLive()) {
313    return false;
314  }
315
316  // Ask the renderer to generate the print preview, create the print preview
317  // view and switch to it, initialize the printer and show the print dialog.
318  DCHECK(!print_job_.get());
319  DCHECK(job);
320  if (!job)
321    return false;
322
323  print_job_ = new PrintJob();
324  print_job_->Initialize(job, this, number_pages_);
325  registrar_.Add(this, NotificationType::PRINT_JOB_EVENT,
326                 Source<PrintJob>(print_job_.get()));
327  printing_succeeded_ = false;
328  return true;
329}
330
331void PrintViewManager::DisconnectFromCurrentPrintJob() {
332  // Make sure all the necessary rendered page are done. Don't bother with the
333  // return value.
334  bool result = RenderAllMissingPagesNow();
335
336  // Verify that assertion.
337  if (print_job_.get() &&
338      print_job_->document() &&
339      !print_job_->document()->IsComplete()) {
340    DCHECK(!result);
341    // That failed.
342    TerminatePrintJob(true);
343  } else {
344    // DO NOT wait for the job to finish.
345    ReleasePrintJob();
346  }
347#if defined(OS_POSIX) && !defined(OS_MACOSX)
348  expecting_first_page_ = true;
349#endif
350}
351
352void PrintViewManager::PrintingDone(bool success) {
353  if (!print_job_.get() || !tab_contents())
354    return;
355  RenderViewHost* rvh = tab_contents()->render_view_host();
356  rvh->Send(new PrintMsg_PrintingDone(rvh->routing_id(),
357                                      print_job_->cookie(),
358                                      success));
359}
360
361void PrintViewManager::TerminatePrintJob(bool cancel) {
362  if (!print_job_.get())
363    return;
364
365  if (cancel) {
366    // We don't need the metafile data anymore because the printing is canceled.
367    print_job_->Cancel();
368    waiting_to_print_ = false;
369    inside_inner_message_loop_ = false;
370  } else {
371    DCHECK(!inside_inner_message_loop_);
372    DCHECK(!print_job_->document() || print_job_->document()->IsComplete() ||
373           !waiting_to_print_);
374
375    // TabContents is either dying or navigating elsewhere. We need to render
376    // all the pages in an hurry if a print job is still pending. This does the
377    // trick since it runs a blocking message loop:
378    print_job_->Stop();
379  }
380  ReleasePrintJob();
381}
382
383void PrintViewManager::ReleasePrintJob() {
384  DCHECK_EQ(waiting_to_print_, false);
385  if (!print_job_.get())
386    return;
387
388  PrintingDone(printing_succeeded_);
389
390  registrar_.Remove(this, NotificationType::PRINT_JOB_EVENT,
391                    Source<PrintJob>(print_job_.get()));
392  print_job_->DisconnectSource();
393  // Don't close the worker thread.
394  print_job_ = NULL;
395}
396
397bool PrintViewManager::RunInnerMessageLoop() {
398  // This value may actually be too low:
399  //
400  // - If we're looping because of printer settings initializaton, the premise
401  // here is that some poor users have their print server away on a VPN over
402  // dialup. In this situation, the simple fact of opening the printer can be
403  // dead slow. On the other side, we don't want to die infinitely for a real
404  // network error. Give the printer 60 seconds to comply.
405  //
406  // - If we're looping because of renderer page generation, the renderer could
407  // be cpu bound, the page overly complex/large or the system just
408  // memory-bound.
409  static const int kPrinterSettingsTimeout = 60000;
410  base::OneShotTimer<MessageLoop> quit_timer;
411  quit_timer.Start(TimeDelta::FromMilliseconds(kPrinterSettingsTimeout),
412                   MessageLoop::current(), &MessageLoop::Quit);
413
414  inside_inner_message_loop_ = true;
415
416  // Need to enable recursive task.
417  bool old_state = MessageLoop::current()->NestableTasksAllowed();
418  MessageLoop::current()->SetNestableTasksAllowed(true);
419  MessageLoop::current()->Run();
420  // Restore task state.
421  MessageLoop::current()->SetNestableTasksAllowed(old_state);
422
423  bool success = true;
424  if (inside_inner_message_loop_) {
425    // Ok we timed out. That's sad.
426    inside_inner_message_loop_ = false;
427    success = false;
428  }
429
430  return success;
431}
432
433bool PrintViewManager::OpportunisticallyCreatePrintJob(int cookie) {
434  if (print_job_.get())
435    return true;
436
437  if (!cookie) {
438    // Out of sync. It may happens since we are completely asynchronous. Old
439    // spurious message can happen if one of the processes is overloaded.
440    return false;
441  }
442
443  // The job was initiated by a script. Time to get the corresponding worker
444  // thread.
445  scoped_refptr<PrinterQuery> queued_query;
446  g_browser_process->print_job_manager()->PopPrinterQuery(cookie,
447                                                          &queued_query);
448  DCHECK(queued_query.get());
449  if (!queued_query.get())
450    return false;
451
452  if (!CreateNewPrintJob(queued_query.get())) {
453    // Don't kill anything.
454    return false;
455  }
456
457  // Settings are already loaded. Go ahead. This will set
458  // print_job_->is_job_pending() to true.
459  print_job_->StartPrinting();
460  return true;
461}
462
463}  // namespace printing
464