print_view_manager_base.cc 16 KB

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