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