Home | History | Annotate | Download | only in printing
      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 
     27 using base::TimeDelta;
     28 
     29 namespace {
     30 
     31 string16 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 
     40 namespace printing {
     41 
     42 PrintViewManager::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 
     54 PrintViewManager::~PrintViewManager() {
     55   DisconnectFromCurrentPrintJob();
     56 }
     57 
     58 bool 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 
     66 void PrintViewManager::StopNavigation() {
     67   // Cancel the current job, wait for the worker to finish.
     68   TerminatePrintJob(true);
     69 }
     70 
     71 void 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 
     84 void PrintViewManager::OverrideTitle(TabContents* tab_contents) {
     85   is_title_overridden_ = true;
     86   overridden_title_ = GenerateRenderSourceName(tab_contents);
     87 }
     88 
     89 string16 PrintViewManager::RenderSourceName() {
     90   if (is_title_overridden_)
     91     return overridden_title_;
     92   return GenerateRenderSourceName(tab_contents());
     93 }
     94 
     95 GURL PrintViewManager::RenderSourceUrl() {
     96   NavigationEntry* entry = tab_contents()->controller().GetActiveEntry();
     97   if (entry)
     98     return entry->virtual_url();
     99   return GURL();
    100 }
    101 
    102 void 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 
    117 void 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 
    177 bool 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 
    188 void 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 
    203 void 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 
    245 bool 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 
    285 void 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 
    299 bool 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 
    331 void 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 
    352 void 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 
    361 void 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 
    383 void 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 
    397 bool 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 
    433 bool 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