print_job.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. // Copyright (c) 2012 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_job.h"
  5. #include <memory>
  6. #include <utility>
  7. #include "base/bind.h"
  8. #include "base/bind_helpers.h"
  9. #include "base/location.h"
  10. #include "base/message_loop/message_loop.h"
  11. #include "base/run_loop.h"
  12. #include "base/single_thread_task_runner.h"
  13. #include "base/task_scheduler/post_task.h"
  14. #include "base/threading/sequenced_worker_pool.h"
  15. #include "base/threading/thread_restrictions.h"
  16. #include "base/threading/thread_task_runner_handle.h"
  17. #include "build/build_config.h"
  18. #include "chrome/browser/chrome_notification_types.h"
  19. #include "chrome/browser/printing/print_job_worker.h"
  20. #include "content/public/browser/notification_service.h"
  21. #include "printing/printed_document.h"
  22. #include "printing/printed_page.h"
  23. #if defined(OS_WIN)
  24. #include "chrome/browser/printing/pdf_to_emf_converter.h"
  25. #include "printing/pdf_render_settings.h"
  26. #endif
  27. using base::TimeDelta;
  28. namespace printing {
  29. namespace {
  30. // Helper function to ensure |owner| is valid until at least |callback| returns.
  31. void HoldRefCallback(const scoped_refptr<PrintJobWorkerOwner>& owner,
  32. const base::Closure& callback) {
  33. callback.Run();
  34. }
  35. } // namespace
  36. PrintJob::PrintJob()
  37. : is_job_pending_(false), is_canceling_(false), quit_factory_(this) {
  38. // This is normally a UI message loop, but in unit tests, the message loop is
  39. // of the 'default' type.
  40. DCHECK(base::MessageLoopForUI::IsCurrent() ||
  41. base::MessageLoop::current()->type() ==
  42. base::MessageLoop::TYPE_DEFAULT);
  43. }
  44. PrintJob::~PrintJob() {
  45. // The job should be finished (or at least canceled) when it is destroyed.
  46. DCHECK(!is_job_pending_);
  47. DCHECK(!is_canceling_);
  48. DCHECK(!worker_ || !worker_->IsRunning());
  49. DCHECK(RunsTasksInCurrentSequence());
  50. }
  51. void PrintJob::Initialize(PrintJobWorkerOwner* job,
  52. const base::string16& name,
  53. int page_count) {
  54. DCHECK(!worker_);
  55. DCHECK(!is_job_pending_);
  56. DCHECK(!is_canceling_);
  57. DCHECK(!document_.get());
  58. worker_ = job->DetachWorker(this);
  59. settings_ = job->settings();
  60. PrintedDocument* new_doc =
  61. new PrintedDocument(settings_, name, job->cookie());
  62. new_doc->set_page_count(page_count);
  63. UpdatePrintedDocument(new_doc);
  64. // Don't forget to register to our own messages.
  65. registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT,
  66. content::Source<PrintJob>(this));
  67. }
  68. void PrintJob::Observe(int type,
  69. const content::NotificationSource& source,
  70. const content::NotificationDetails& details) {
  71. DCHECK(RunsTasksInCurrentSequence());
  72. DCHECK_EQ(chrome::NOTIFICATION_PRINT_JOB_EVENT, type);
  73. OnNotifyPrintJobEvent(*content::Details<JobEventDetails>(details).ptr());
  74. }
  75. void PrintJob::GetSettingsDone(const PrintSettings& new_settings,
  76. PrintingContext::Result result) {
  77. NOTREACHED();
  78. }
  79. std::unique_ptr<PrintJobWorker> PrintJob::DetachWorker(
  80. PrintJobWorkerOwner* new_owner) {
  81. NOTREACHED();
  82. return nullptr;
  83. }
  84. const PrintSettings& PrintJob::settings() const {
  85. return settings_;
  86. }
  87. int PrintJob::cookie() const {
  88. // Always use an invalid cookie in this case.
  89. if (!document_.get())
  90. return 0;
  91. return document_->cookie();
  92. }
  93. void PrintJob::StartPrinting() {
  94. DCHECK(RunsTasksInCurrentSequence());
  95. if (!worker_->IsRunning() || is_job_pending_) {
  96. NOTREACHED();
  97. return;
  98. }
  99. // Real work is done in PrintJobWorker::StartPrinting().
  100. worker_->PostTask(FROM_HERE,
  101. base::Bind(&HoldRefCallback, WrapRefCounted(this),
  102. base::Bind(&PrintJobWorker::StartPrinting,
  103. base::Unretained(worker_.get()),
  104. base::RetainedRef(document_))));
  105. // Set the flag right now.
  106. is_job_pending_ = true;
  107. // Tell everyone!
  108. scoped_refptr<JobEventDetails> details(
  109. new JobEventDetails(JobEventDetails::NEW_DOC, document_.get(), nullptr));
  110. content::NotificationService::current()->Notify(
  111. chrome::NOTIFICATION_PRINT_JOB_EVENT, content::Source<PrintJob>(this),
  112. content::Details<JobEventDetails>(details.get()));
  113. }
  114. void PrintJob::Stop() {
  115. DCHECK(RunsTasksInCurrentSequence());
  116. if (quit_factory_.HasWeakPtrs()) {
  117. // In case we're running a nested message loop to wait for a job to finish,
  118. // and we finished before the timeout, quit the nested loop right away.
  119. Quit();
  120. quit_factory_.InvalidateWeakPtrs();
  121. }
  122. // Be sure to live long enough.
  123. scoped_refptr<PrintJob> handle(this);
  124. if (worker_->IsRunning()) {
  125. ControlledWorkerShutdown();
  126. } else {
  127. // Flush the cached document.
  128. is_job_pending_ = false;
  129. UpdatePrintedDocument(nullptr);
  130. }
  131. }
  132. void PrintJob::Cancel() {
  133. if (is_canceling_)
  134. return;
  135. is_canceling_ = true;
  136. // Be sure to live long enough.
  137. scoped_refptr<PrintJob> handle(this);
  138. DCHECK(RunsTasksInCurrentSequence());
  139. if (worker_ && worker_->IsRunning()) {
  140. // Call this right now so it renders the context invalid. Do not use
  141. // InvokeLater since it would take too much time.
  142. worker_->Cancel();
  143. }
  144. // Make sure a Cancel() is broadcast.
  145. scoped_refptr<JobEventDetails> details(
  146. new JobEventDetails(JobEventDetails::FAILED, nullptr, nullptr));
  147. content::NotificationService::current()->Notify(
  148. chrome::NOTIFICATION_PRINT_JOB_EVENT, content::Source<PrintJob>(this),
  149. content::Details<JobEventDetails>(details.get()));
  150. Stop();
  151. is_canceling_ = false;
  152. }
  153. bool PrintJob::FlushJob(base::TimeDelta timeout) {
  154. // Make sure the object outlive this message loop.
  155. scoped_refptr<PrintJob> handle(this);
  156. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
  157. FROM_HERE, base::Bind(&PrintJob::Quit, quit_factory_.GetWeakPtr()),
  158. timeout);
  159. base::MessageLoop::ScopedNestableTaskAllower allow(
  160. base::MessageLoop::current());
  161. base::RunLoop().Run();
  162. return true;
  163. }
  164. bool PrintJob::is_job_pending() const {
  165. return is_job_pending_;
  166. }
  167. PrintedDocument* PrintJob::document() const {
  168. return document_.get();
  169. }
  170. #if defined(OS_WIN)
  171. class PrintJob::PdfConversionState {
  172. public:
  173. PdfConversionState(gfx::Size page_size, gfx::Rect content_area)
  174. : page_count_(0),
  175. current_page_(0),
  176. pages_in_progress_(0),
  177. page_size_(page_size),
  178. content_area_(content_area) {}
  179. void Start(const scoped_refptr<base::RefCountedMemory>& data,
  180. const PdfRenderSettings& conversion_settings,
  181. const PdfConverter::StartCallback& start_callback) {
  182. converter_ = PdfConverter::StartPdfConverter(data, conversion_settings,
  183. start_callback);
  184. }
  185. void GetMorePages(const PdfConverter::GetPageCallback& get_page_callback) {
  186. const int kMaxNumberOfTempFilesPerDocument = 3;
  187. while (pages_in_progress_ < kMaxNumberOfTempFilesPerDocument &&
  188. current_page_ < page_count_) {
  189. ++pages_in_progress_;
  190. converter_->GetPage(current_page_++, get_page_callback);
  191. }
  192. }
  193. void OnPageProcessed(const PdfConverter::GetPageCallback& get_page_callback) {
  194. --pages_in_progress_;
  195. GetMorePages(get_page_callback);
  196. // Release converter if we don't need this any more.
  197. if (!pages_in_progress_ && current_page_ >= page_count_)
  198. converter_.reset();
  199. }
  200. void set_page_count(int page_count) { page_count_ = page_count; }
  201. gfx::Size page_size() const { return page_size_; }
  202. gfx::Rect content_area() const { return content_area_; }
  203. private:
  204. int page_count_;
  205. int current_page_;
  206. int pages_in_progress_;
  207. gfx::Size page_size_;
  208. gfx::Rect content_area_;
  209. std::unique_ptr<PdfConverter> converter_;
  210. };
  211. void PrintJob::AppendPrintedPage(int page_number) {
  212. pdf_page_mapping_.push_back(page_number);
  213. }
  214. void PrintJob::StartPdfToEmfConversion(
  215. const scoped_refptr<base::RefCountedMemory>& bytes,
  216. const gfx::Size& page_size,
  217. const gfx::Rect& content_area,
  218. bool print_text_with_gdi) {
  219. DCHECK(!pdf_conversion_state_);
  220. pdf_conversion_state_ =
  221. std::make_unique<PdfConversionState>(page_size, content_area);
  222. const int kPrinterDpi = settings().dpi();
  223. PdfRenderSettings settings(
  224. content_area, gfx::Point(0, 0), kPrinterDpi, /*autorotate=*/true,
  225. print_text_with_gdi ? PdfRenderSettings::Mode::GDI_TEXT
  226. : PdfRenderSettings::Mode::NORMAL);
  227. pdf_conversion_state_->Start(
  228. bytes, settings, base::Bind(&PrintJob::OnPdfConversionStarted, this));
  229. }
  230. void PrintJob::OnPdfConversionStarted(int page_count) {
  231. if (page_count <= 0) {
  232. pdf_conversion_state_.reset();
  233. Cancel();
  234. return;
  235. }
  236. pdf_conversion_state_->set_page_count(page_count);
  237. pdf_conversion_state_->GetMorePages(
  238. base::Bind(&PrintJob::OnPdfPageConverted, this));
  239. }
  240. void PrintJob::OnPdfPageConverted(int page_number,
  241. float scale_factor,
  242. std::unique_ptr<MetafilePlayer> metafile) {
  243. DCHECK(pdf_conversion_state_);
  244. if (!document_.get() || !metafile || page_number < 0 ||
  245. static_cast<size_t>(page_number) >= pdf_page_mapping_.size()) {
  246. pdf_conversion_state_.reset();
  247. Cancel();
  248. return;
  249. }
  250. // Update the rendered document. It will send notifications to the listener.
  251. document_->SetPage(pdf_page_mapping_[page_number], std::move(metafile),
  252. scale_factor, pdf_conversion_state_->page_size(),
  253. pdf_conversion_state_->content_area());
  254. pdf_conversion_state_->GetMorePages(
  255. base::Bind(&PrintJob::OnPdfPageConverted, this));
  256. }
  257. void PrintJob::StartPdfToPostScriptConversion(
  258. const scoped_refptr<base::RefCountedMemory>& bytes,
  259. const gfx::Rect& content_area,
  260. const gfx::Point& physical_offsets,
  261. bool ps_level2) {
  262. DCHECK(!pdf_conversion_state_);
  263. pdf_conversion_state_ =
  264. std::make_unique<PdfConversionState>(gfx::Size(), gfx::Rect());
  265. const int kPrinterDpi = settings().dpi();
  266. PdfRenderSettings settings(
  267. content_area, physical_offsets, kPrinterDpi, true /* autorotate? */,
  268. ps_level2 ? PdfRenderSettings::Mode::POSTSCRIPT_LEVEL2
  269. : PdfRenderSettings::Mode::POSTSCRIPT_LEVEL3);
  270. pdf_conversion_state_->Start(
  271. bytes, settings, base::Bind(&PrintJob::OnPdfConversionStarted, this));
  272. }
  273. #endif // defined(OS_WIN)
  274. void PrintJob::UpdatePrintedDocument(PrintedDocument* new_document) {
  275. if (document_.get() == new_document)
  276. return;
  277. document_ = new_document;
  278. if (document_.get())
  279. settings_ = document_->settings();
  280. if (worker_) {
  281. DCHECK(!is_job_pending_);
  282. // Sync the document with the worker.
  283. worker_->PostTask(FROM_HERE,
  284. base::Bind(&HoldRefCallback, WrapRefCounted(this),
  285. base::Bind(&PrintJobWorker::OnDocumentChanged,
  286. base::Unretained(worker_.get()),
  287. base::RetainedRef(document_))));
  288. }
  289. }
  290. void PrintJob::OnNotifyPrintJobEvent(const JobEventDetails& event_details) {
  291. switch (event_details.type()) {
  292. case JobEventDetails::FAILED: {
  293. settings_.Clear();
  294. // No need to cancel since the worker already canceled itself.
  295. Stop();
  296. break;
  297. }
  298. case JobEventDetails::USER_INIT_DONE:
  299. case JobEventDetails::DEFAULT_INIT_DONE:
  300. case JobEventDetails::USER_INIT_CANCELED: {
  301. DCHECK_EQ(event_details.document(), document_.get());
  302. break;
  303. }
  304. case JobEventDetails::NEW_DOC:
  305. case JobEventDetails::NEW_PAGE:
  306. case JobEventDetails::JOB_DONE:
  307. case JobEventDetails::ALL_PAGES_REQUESTED: {
  308. // Don't care.
  309. break;
  310. }
  311. case JobEventDetails::DOC_DONE: {
  312. // This will call Stop() and broadcast a JOB_DONE message.
  313. base::ThreadTaskRunnerHandle::Get()->PostTask(
  314. FROM_HERE, base::Bind(&PrintJob::OnDocumentDone, this));
  315. break;
  316. }
  317. case JobEventDetails::PAGE_DONE:
  318. #if defined(OS_WIN)
  319. if (pdf_conversion_state_) {
  320. pdf_conversion_state_->OnPageProcessed(
  321. base::Bind(&PrintJob::OnPdfPageConverted, this));
  322. }
  323. #endif // defined(OS_WIN)
  324. break;
  325. default: {
  326. NOTREACHED();
  327. break;
  328. }
  329. }
  330. }
  331. void PrintJob::OnDocumentDone() {
  332. // Be sure to live long enough. The instance could be destroyed by the
  333. // JOB_DONE broadcast.
  334. scoped_refptr<PrintJob> handle(this);
  335. // Stop the worker thread.
  336. Stop();
  337. scoped_refptr<JobEventDetails> details(
  338. new JobEventDetails(JobEventDetails::JOB_DONE, document_.get(), nullptr));
  339. content::NotificationService::current()->Notify(
  340. chrome::NOTIFICATION_PRINT_JOB_EVENT, content::Source<PrintJob>(this),
  341. content::Details<JobEventDetails>(details.get()));
  342. }
  343. void PrintJob::ControlledWorkerShutdown() {
  344. DCHECK(RunsTasksInCurrentSequence());
  345. // The deadlock this code works around is specific to window messaging on
  346. // Windows, so we aren't likely to need it on any other platforms.
  347. #if defined(OS_WIN)
  348. // We could easily get into a deadlock case if worker_->Stop() is used; the
  349. // printer driver created a window as a child of the browser window. By
  350. // canceling the job, the printer driver initiated dialog box is destroyed,
  351. // which sends a blocking message to its parent window. If the browser window
  352. // thread is not processing messages, a deadlock occurs.
  353. //
  354. // This function ensures that the dialog box will be destroyed in a timely
  355. // manner by the mere fact that the thread will terminate. So the potential
  356. // deadlock is eliminated.
  357. worker_->StopSoon();
  358. // Delay shutdown until the worker terminates. We want this code path
  359. // to wait on the thread to quit before continuing.
  360. if (worker_->IsRunning()) {
  361. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
  362. FROM_HERE, base::Bind(&PrintJob::ControlledWorkerShutdown, this),
  363. base::TimeDelta::FromMilliseconds(100));
  364. return;
  365. }
  366. #endif
  367. // Now make sure the thread object is cleaned up. Do this on a worker
  368. // thread because it may block.
  369. base::PostTaskAndReply(
  370. FROM_HERE,
  371. base::Bind(&PrintJobWorker::Stop, base::Unretained(worker_.get())),
  372. base::Bind(&PrintJob::HoldUntilStopIsCalled, this));
  373. is_job_pending_ = false;
  374. registrar_.RemoveAll();
  375. UpdatePrintedDocument(nullptr);
  376. }
  377. void PrintJob::HoldUntilStopIsCalled() {}
  378. void PrintJob::Quit() {
  379. base::RunLoop::QuitCurrentWhenIdleDeprecated();
  380. }
  381. // Takes settings_ ownership and will be deleted in the receiving thread.
  382. JobEventDetails::JobEventDetails(Type type,
  383. PrintedDocument* document,
  384. PrintedPage* page)
  385. : document_(document), page_(page), type_(type) {}
  386. JobEventDetails::~JobEventDetails() {}
  387. PrintedDocument* JobEventDetails::document() const {
  388. return document_.get();
  389. }
  390. PrintedPage* JobEventDetails::page() const {
  391. return page_.get();
  392. }
  393. } // namespace printing