PageRenderTime 26ms CodeModel.GetById 46ms RepoModel.GetById 0ms app.codeStats 0ms

/chrome/browser/printing/print_view_manager.cc

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