print_job_worker.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  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_worker.h"
  5. #include <memory>
  6. #include "base/bind.h"
  7. #include "base/bind_helpers.h"
  8. #include "base/callback.h"
  9. #include "base/compiler_specific.h"
  10. #include "base/location.h"
  11. #include "base/message_loop/message_loop.h"
  12. #include "base/single_thread_task_runner.h"
  13. #include "base/threading/thread_task_runner_handle.h"
  14. #include "base/values.h"
  15. #include "build/build_config.h"
  16. #include "chrome/browser/browser_process.h"
  17. #include "chrome/browser/chrome_notification_types.h"
  18. #include "chrome/browser/printing/print_job.h"
  19. #include "content/public/browser/browser_thread.h"
  20. #include "content/public/browser/notification_service.h"
  21. #include "content/public/browser/render_frame_host.h"
  22. #include "content/public/browser/web_contents.h"
  23. #include "printing/print_job_constants.h"
  24. #include "printing/printed_document.h"
  25. #include "printing/printed_page.h"
  26. #include "printing/printing_utils.h"
  27. #include "ui/base/l10n/l10n_util.h"
  28. #include <stddef.h>
  29. #include <algorithm>
  30. #include <cmath>
  31. #include <string>
  32. #include <utility>
  33. #include "base/strings/string_number_conversions.h"
  34. #include "base/strings/utf_string_conversions.h"
  35. #include "base/time/time.h"
  36. #include "printing/page_size_margins.h"
  37. #include "printing/print_job_constants.h"
  38. #include "printing/print_settings.h"
  39. #include "printing/units.h"
  40. using content::BrowserThread;
  41. namespace printing {
  42. namespace {
  43. // Helper function to ensure |owner| is valid until at least |callback| returns.
  44. void HoldRefCallback(const scoped_refptr<PrintJobWorkerOwner>& owner,
  45. const base::Closure& callback) {
  46. callback.Run();
  47. }
  48. void SetCustomMarginsToJobSettings(const PageSizeMargins& page_size_margins,
  49. base::DictionaryValue* settings) {
  50. std::unique_ptr<base::DictionaryValue> custom_margins(
  51. new base::DictionaryValue());
  52. custom_margins->SetDouble(kSettingMarginTop, page_size_margins.margin_top);
  53. custom_margins->SetDouble(kSettingMarginBottom,
  54. page_size_margins.margin_bottom);
  55. custom_margins->SetDouble(kSettingMarginLeft, page_size_margins.margin_left);
  56. custom_margins->SetDouble(kSettingMarginRight,
  57. page_size_margins.margin_right);
  58. settings->Set(kSettingMarginsCustom, std::move(custom_margins));
  59. }
  60. void PrintSettingsToJobSettings(const PrintSettings& settings,
  61. base::DictionaryValue* job_settings) {
  62. // header footer
  63. job_settings->SetBoolean(kSettingHeaderFooterEnabled,
  64. settings.display_header_footer());
  65. job_settings->SetString(kSettingHeaderFooterTitle, settings.title());
  66. job_settings->SetString(kSettingHeaderFooterURL, settings.url());
  67. // bg
  68. job_settings->SetBoolean(kSettingShouldPrintBackgrounds,
  69. settings.should_print_backgrounds());
  70. job_settings->SetBoolean(kSettingShouldPrintSelectionOnly,
  71. settings.selection_only());
  72. // margin
  73. auto margin_type = settings.margin_type();
  74. job_settings->SetInteger(kSettingMarginsType, settings.margin_type());
  75. if (margin_type == CUSTOM_MARGINS) {
  76. const auto& margins_in_points =
  77. settings.requested_custom_margins_in_points();
  78. PageSizeMargins page_size_margins;
  79. page_size_margins.margin_top = margins_in_points.top;
  80. page_size_margins.margin_bottom = margins_in_points.bottom;
  81. page_size_margins.margin_left = margins_in_points.left;
  82. page_size_margins.margin_right = margins_in_points.right;
  83. SetCustomMarginsToJobSettings(page_size_margins, job_settings);
  84. }
  85. job_settings->SetInteger(kSettingPreviewPageCount, 1);
  86. // range
  87. if (!settings.ranges().empty()) {
  88. auto page_range_array = std::make_unique<base::ListValue>();
  89. job_settings->Set(kSettingPageRange, std::move(page_range_array));
  90. for (size_t i = 0; i < settings.ranges().size(); ++i) {
  91. std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
  92. dict->SetInteger(kSettingPageRangeFrom, settings.ranges()[i].from + 1);
  93. dict->SetInteger(kSettingPageRangeTo, settings.ranges()[i].to + 1);
  94. page_range_array->Append(std::move(dict));
  95. }
  96. }
  97. job_settings->SetBoolean(kSettingCollate, settings.collate());
  98. job_settings->SetInteger(kSettingCopies, 1);
  99. job_settings->SetInteger(kSettingColor, settings.color());
  100. job_settings->SetInteger(kSettingDuplexMode, settings.duplex_mode());
  101. job_settings->SetBoolean(kSettingLandscape, settings.landscape());
  102. job_settings->SetString(kSettingDeviceName, settings.device_name());
  103. job_settings->SetInteger(kSettingScaleFactor, 100);
  104. job_settings->SetBoolean("rasterizePDF", false);
  105. job_settings->SetInteger("dpi", settings.dpi());
  106. job_settings->SetInteger("dpiHorizontal", 72);
  107. job_settings->SetInteger("dpiVertical", 72);
  108. job_settings->SetBoolean(kSettingPrintToPDF, false);
  109. job_settings->SetBoolean(kSettingCloudPrintDialog, false);
  110. job_settings->SetBoolean(kSettingPrintWithPrivet, false);
  111. job_settings->SetBoolean(kSettingPrintWithExtension, false);
  112. job_settings->SetBoolean(kSettingShowSystemDialog, false);
  113. job_settings->SetInteger(kSettingPreviewPageCount, 1);
  114. }
  115. class PrintingContextDelegate : public PrintingContext::Delegate {
  116. public:
  117. PrintingContextDelegate(int render_process_id, int render_frame_id);
  118. ~PrintingContextDelegate() override;
  119. gfx::NativeView GetParentView() override;
  120. std::string GetAppLocale() override;
  121. // Not exposed to PrintingContext::Delegate because of dependency issues.
  122. content::WebContents* GetWebContents();
  123. private:
  124. const int render_process_id_;
  125. const int render_frame_id_;
  126. };
  127. PrintingContextDelegate::PrintingContextDelegate(int render_process_id,
  128. int render_frame_id)
  129. : render_process_id_(render_process_id),
  130. render_frame_id_(render_frame_id) {}
  131. PrintingContextDelegate::~PrintingContextDelegate() {}
  132. gfx::NativeView PrintingContextDelegate::GetParentView() {
  133. content::WebContents* wc = GetWebContents();
  134. return wc ? wc->GetNativeView() : nullptr;
  135. }
  136. content::WebContents* PrintingContextDelegate::GetWebContents() {
  137. DCHECK_CURRENTLY_ON(BrowserThread::UI);
  138. auto* rfh =
  139. content::RenderFrameHost::FromID(render_process_id_, render_frame_id_);
  140. return rfh ? content::WebContents::FromRenderFrameHost(rfh) : nullptr;
  141. }
  142. std::string PrintingContextDelegate::GetAppLocale() {
  143. return g_browser_process->GetApplicationLocale();
  144. }
  145. void NotificationCallback(PrintJobWorkerOwner* print_job,
  146. JobEventDetails::Type detail_type,
  147. PrintedDocument* document,
  148. PrintedPage* page) {
  149. JobEventDetails* details = new JobEventDetails(detail_type, document, page);
  150. content::NotificationService::current()->Notify(
  151. chrome::NOTIFICATION_PRINT_JOB_EVENT,
  152. // We know that is is a PrintJob object in this circumstance.
  153. content::Source<PrintJob>(static_cast<PrintJob*>(print_job)),
  154. content::Details<JobEventDetails>(details));
  155. }
  156. void PostOnOwnerThread(const scoped_refptr<PrintJobWorkerOwner>& owner,
  157. const PrintingContext::PrintSettingsCallback& callback,
  158. PrintingContext::Result result) {
  159. owner->PostTask(FROM_HERE, base::Bind(&HoldRefCallback, owner,
  160. base::Bind(callback, result)));
  161. }
  162. } // namespace
  163. PrintJobWorker::PrintJobWorker(int render_process_id,
  164. int render_frame_id,
  165. PrintJobWorkerOwner* owner)
  166. : owner_(owner), thread_("Printing_Worker"), weak_factory_(this) {
  167. // The object is created in the IO thread.
  168. DCHECK(owner_->RunsTasksInCurrentSequence());
  169. printing_context_delegate_ = std::make_unique<PrintingContextDelegate>(
  170. render_process_id, render_frame_id);
  171. printing_context_ = PrintingContext::Create(printing_context_delegate_.get());
  172. }
  173. PrintJobWorker::~PrintJobWorker() {
  174. // The object is normally deleted in the UI thread, but when the user
  175. // cancels printing or in the case of print preview, the worker is destroyed
  176. // on the I/O thread.
  177. DCHECK(owner_->RunsTasksInCurrentSequence());
  178. Stop();
  179. }
  180. void PrintJobWorker::SetNewOwner(PrintJobWorkerOwner* new_owner) {
  181. DCHECK(page_number_ == PageNumber::npos());
  182. owner_ = new_owner;
  183. }
  184. void PrintJobWorker::GetSettings(bool ask_user_for_settings,
  185. int document_page_count,
  186. bool has_selection,
  187. MarginType margin_type,
  188. bool is_scripted,
  189. bool is_modifiable,
  190. const base::string16& device_name) {
  191. DCHECK(task_runner_->RunsTasksInCurrentSequence());
  192. DCHECK_EQ(page_number_, PageNumber::npos());
  193. // Recursive task processing is needed for the dialog in case it needs to be
  194. // destroyed by a task.
  195. // TODO(thestig): This code is wrong. SetNestableTasksAllowed(true) is needed
  196. // on the thread where the PrintDlgEx is called, and definitely both calls
  197. // should happen on the same thread. See http://crbug.com/73466
  198. // MessageLoop::current()->SetNestableTasksAllowed(true);
  199. printing_context_->set_margin_type(margin_type);
  200. printing_context_->set_is_modifiable(is_modifiable);
  201. // When we delegate to a destination, we don't ask the user for settings.
  202. // TODO(mad): Ask the destination for settings.
  203. if (ask_user_for_settings) {
  204. BrowserThread::PostTask(
  205. BrowserThread::UI, FROM_HERE,
  206. base::Bind(&HoldRefCallback, WrapRefCounted(owner_),
  207. base::Bind(&PrintJobWorker::GetSettingsWithUI,
  208. base::Unretained(this), document_page_count,
  209. has_selection, is_scripted)));
  210. } else if (!device_name.empty()) {
  211. BrowserThread::PostTask(
  212. BrowserThread::UI, FROM_HERE,
  213. base::Bind(&HoldRefCallback, WrapRefCounted(owner_),
  214. base::Bind(&PrintJobWorker::InitWithDeviceName,
  215. base::Unretained(this), device_name)));
  216. } else {
  217. BrowserThread::PostTask(
  218. BrowserThread::UI, FROM_HERE,
  219. base::Bind(&HoldRefCallback, WrapRefCounted(owner_),
  220. base::Bind(&PrintJobWorker::UseDefaultSettings,
  221. base::Unretained(this))));
  222. }
  223. }
  224. void PrintJobWorker::SetSettings(
  225. std::unique_ptr<base::DictionaryValue> new_settings) {
  226. DCHECK(task_runner_->RunsTasksInCurrentSequence());
  227. BrowserThread::PostTask(
  228. BrowserThread::UI, FROM_HERE,
  229. base::Bind(
  230. &HoldRefCallback, WrapRefCounted(owner_),
  231. base::Bind(&PrintJobWorker::UpdatePrintSettings,
  232. base::Unretained(this), base::Passed(&new_settings))));
  233. }
  234. void PrintJobWorker::UpdatePrintSettings(
  235. std::unique_ptr<base::DictionaryValue> new_settings) {
  236. DCHECK_CURRENTLY_ON(BrowserThread::UI);
  237. PrintingContext::Result result =
  238. printing_context_->UpdatePrintSettings(*new_settings);
  239. GetSettingsDone(result);
  240. }
  241. void PrintJobWorker::GetSettingsDone(PrintingContext::Result result) {
  242. // Most PrintingContext functions may start a message loop and process
  243. // message recursively, so disable recursive task processing.
  244. // TODO(thestig): See above comment. SetNestableTasksAllowed(false) needs to
  245. // be called on the same thread as the previous call. See
  246. // http://crbug.com/73466
  247. // MessageLoop::current()->SetNestableTasksAllowed(false);
  248. // We can't use OnFailure() here since owner_ may not support notifications.
  249. // PrintJob will create the new PrintedDocument.
  250. owner_->PostTask(
  251. FROM_HERE,
  252. base::Bind(&PrintJobWorkerOwner::GetSettingsDone, WrapRefCounted(owner_),
  253. printing_context_->settings(), result));
  254. }
  255. void PrintJobWorker::GetSettingsWithUI(int document_page_count,
  256. bool has_selection,
  257. bool is_scripted) {
  258. DCHECK_CURRENTLY_ON(BrowserThread::UI);
  259. // weak_factory_ creates pointers valid only on owner_ thread.
  260. printing_context_->AskUserForSettings(
  261. document_page_count, has_selection, is_scripted,
  262. base::Bind(&PostOnOwnerThread, WrapRefCounted(owner_),
  263. base::Bind(&PrintJobWorker::GetSettingsDone,
  264. weak_factory_.GetWeakPtr())));
  265. }
  266. void PrintJobWorker::UseDefaultSettings() {
  267. PrintingContext::Result result = printing_context_->UseDefaultSettings();
  268. GetSettingsDone(result);
  269. }
  270. void PrintJobWorker::InitWithDeviceName(const base::string16& device_name) {
  271. const auto& settings = printing_context_->settings();
  272. std::unique_ptr<base::DictionaryValue> dic(new base::DictionaryValue);
  273. PrintSettingsToJobSettings(settings, dic.get());
  274. dic->SetString(kSettingDeviceName, device_name);
  275. UpdatePrintSettings(std::move(dic));
  276. }
  277. void PrintJobWorker::StartPrinting(PrintedDocument* new_document) {
  278. DCHECK(task_runner_->RunsTasksInCurrentSequence());
  279. DCHECK_EQ(page_number_, PageNumber::npos());
  280. DCHECK_EQ(document_.get(), new_document);
  281. DCHECK(document_.get());
  282. if (!document_.get() || page_number_ != PageNumber::npos() ||
  283. document_.get() != new_document) {
  284. return;
  285. }
  286. base::string16 document_name =
  287. printing::SimplifyDocumentTitle(document_->name());
  288. PrintingContext::Result result =
  289. printing_context_->NewDocument(document_name);
  290. if (result != PrintingContext::OK) {
  291. OnFailure();
  292. return;
  293. }
  294. // Try to print already cached data. It may already have been generated for
  295. // the print preview.
  296. OnNewPage();
  297. // Don't touch this anymore since the instance could be destroyed. It happens
  298. // if all the pages are printed a one sweep and the client doesn't have a
  299. // handle to us anymore. There's a timing issue involved between the worker
  300. // thread and the UI thread. Take no chance.
  301. }
  302. void PrintJobWorker::OnDocumentChanged(PrintedDocument* new_document) {
  303. DCHECK(task_runner_->RunsTasksInCurrentSequence());
  304. DCHECK_EQ(page_number_, PageNumber::npos());
  305. if (page_number_ != PageNumber::npos())
  306. return;
  307. document_ = new_document;
  308. }
  309. void PrintJobWorker::OnNewPage() {
  310. if (!document_.get()) // Spurious message.
  311. return;
  312. // message_loop() could return NULL when the print job is cancelled.
  313. DCHECK(task_runner_->RunsTasksInCurrentSequence());
  314. if (page_number_ == PageNumber::npos()) {
  315. // Find first page to print.
  316. int page_count = document_->page_count();
  317. if (!page_count) {
  318. // We still don't know how many pages the document contains. We can't
  319. // start to print the document yet since the header/footer may refer to
  320. // the document's page count.
  321. return;
  322. }
  323. // We have enough information to initialize page_number_.
  324. page_number_.Init(document_->settings(), page_count);
  325. }
  326. DCHECK_NE(page_number_, PageNumber::npos());
  327. while (true) {
  328. // Is the page available?
  329. scoped_refptr<PrintedPage> page = document_->GetPage(page_number_.ToInt());
  330. if (!page.get()) {
  331. // We need to wait for the page to be available.
  332. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
  333. FROM_HERE,
  334. base::Bind(&PrintJobWorker::OnNewPage, weak_factory_.GetWeakPtr()),
  335. base::TimeDelta::FromMilliseconds(500));
  336. break;
  337. }
  338. // The page is there, print it.
  339. SpoolPage(page.get());
  340. ++page_number_;
  341. if (page_number_ == PageNumber::npos()) {
  342. OnDocumentDone();
  343. // Don't touch this anymore since the instance could be destroyed.
  344. break;
  345. }
  346. }
  347. }
  348. void PrintJobWorker::Cancel() {
  349. // This is the only function that can be called from any thread.
  350. printing_context_->Cancel();
  351. // Cannot touch any member variable since we don't know in which thread
  352. // context we run.
  353. }
  354. bool PrintJobWorker::IsRunning() const {
  355. return thread_.IsRunning();
  356. }
  357. bool PrintJobWorker::PostTask(const base::Location& from_here,
  358. const base::Closure& task) {
  359. if (task_runner_.get())
  360. return task_runner_->PostTask(from_here, task);
  361. return false;
  362. }
  363. void PrintJobWorker::StopSoon() {
  364. thread_.StopSoon();
  365. }
  366. void PrintJobWorker::Stop() {
  367. thread_.Stop();
  368. }
  369. bool PrintJobWorker::Start() {
  370. bool result = thread_.Start();
  371. task_runner_ = thread_.task_runner();
  372. return result;
  373. }
  374. void PrintJobWorker::OnDocumentDone() {
  375. DCHECK(task_runner_->RunsTasksInCurrentSequence());
  376. DCHECK_EQ(page_number_, PageNumber::npos());
  377. DCHECK(document_.get());
  378. if (printing_context_->DocumentDone() != PrintingContext::OK) {
  379. OnFailure();
  380. return;
  381. }
  382. owner_->PostTask(FROM_HERE,
  383. base::Bind(&NotificationCallback, base::RetainedRef(owner_),
  384. JobEventDetails::DOC_DONE,
  385. base::RetainedRef(document_), nullptr));
  386. // Makes sure the variables are reinitialized.
  387. document_ = NULL;
  388. }
  389. void PrintJobWorker::SpoolPage(PrintedPage* page) {
  390. DCHECK(task_runner_->RunsTasksInCurrentSequence());
  391. DCHECK_NE(page_number_, PageNumber::npos());
  392. // Signal everyone that the page is about to be printed.
  393. owner_->PostTask(
  394. FROM_HERE,
  395. base::Bind(&NotificationCallback, base::RetainedRef(owner_),
  396. JobEventDetails::NEW_PAGE, base::RetainedRef(document_),
  397. base::RetainedRef(page)));
  398. // Preprocess.
  399. if (printing_context_->NewPage() != PrintingContext::OK) {
  400. OnFailure();
  401. return;
  402. }
  403. // Actual printing.
  404. #if defined(OS_WIN) || defined(OS_MACOSX)
  405. document_->RenderPrintedPage(*page, printing_context_->context());
  406. #elif defined(OS_POSIX)
  407. document_->RenderPrintedPage(*page, printing_context_.get());
  408. #endif
  409. // Postprocess.
  410. if (printing_context_->PageDone() != PrintingContext::OK) {
  411. OnFailure();
  412. return;
  413. }
  414. // Signal everyone that the page is printed.
  415. owner_->PostTask(
  416. FROM_HERE,
  417. base::Bind(&NotificationCallback, base::RetainedRef(owner_),
  418. JobEventDetails::PAGE_DONE, base::RetainedRef(document_),
  419. base::RetainedRef(page)));
  420. }
  421. void PrintJobWorker::OnFailure() {
  422. DCHECK(task_runner_->RunsTasksInCurrentSequence());
  423. // We may loose our last reference by broadcasting the FAILED event.
  424. scoped_refptr<PrintJobWorkerOwner> handle(owner_);
  425. owner_->PostTask(FROM_HERE,
  426. base::Bind(&NotificationCallback, base::RetainedRef(owner_),
  427. JobEventDetails::FAILED,
  428. base::RetainedRef(document_), nullptr));
  429. Cancel();
  430. // Makes sure the variables are reinitialized.
  431. document_ = NULL;
  432. page_number_ = PageNumber::npos();
  433. }
  434. } // namespace printing