print_view_manager_base.cc 16 KB

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