Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / printing / print_job_worker.cc
blob4648e1f594746184437c02166f54e4db0bf39731
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.
5 #include "chrome/browser/printing/print_job_worker.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback.h"
10 #include "base/compiler_specific.h"
11 #include "base/location.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "base/values.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 "chrome/grit/generated_resources.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/notification_service.h"
22 #include "content/public/browser/render_view_host.h"
23 #include "content/public/browser/web_contents.h"
24 #include "printing/print_job_constants.h"
25 #include "printing/printed_document.h"
26 #include "printing/printed_page.h"
27 #include "printing/printing_utils.h"
28 #include "ui/base/l10n/l10n_util.h"
30 #if defined(OS_ANDROID)
31 #include "chrome/browser/android/tab_android.h"
32 #endif
34 using content::BrowserThread;
36 namespace printing {
38 namespace {
40 // Helper function to ensure |owner| is valid until at least |callback| returns.
41 void HoldRefCallback(const scoped_refptr<printing::PrintJobWorkerOwner>& owner,
42 const base::Closure& callback) {
43 callback.Run();
46 class PrintingContextDelegate : public PrintingContext::Delegate {
47 public:
48 PrintingContextDelegate(int render_process_id, int render_view_id);
49 ~PrintingContextDelegate() override;
51 gfx::NativeView GetParentView() override;
52 std::string GetAppLocale() override;
54 // Not exposed to PrintingContext::Delegate because of dependency issues.
55 content::WebContents* GetWebContents();
57 private:
58 int render_process_id_;
59 int render_view_id_;
62 PrintingContextDelegate::PrintingContextDelegate(int render_process_id,
63 int render_view_id)
64 : render_process_id_(render_process_id),
65 render_view_id_(render_view_id) {
68 PrintingContextDelegate::~PrintingContextDelegate() {
71 gfx::NativeView PrintingContextDelegate::GetParentView() {
72 content::WebContents* wc = GetWebContents();
73 return wc ? wc->GetNativeView() : nullptr;
76 content::WebContents* PrintingContextDelegate::GetWebContents() {
77 DCHECK_CURRENTLY_ON(BrowserThread::UI);
78 content::RenderViewHost* view =
79 content::RenderViewHost::FromID(render_process_id_, render_view_id_);
80 return view ? content::WebContents::FromRenderViewHost(view) : nullptr;
83 std::string PrintingContextDelegate::GetAppLocale() {
84 return g_browser_process->GetApplicationLocale();
87 void NotificationCallback(PrintJobWorkerOwner* print_job,
88 JobEventDetails::Type detail_type,
89 PrintedDocument* document,
90 PrintedPage* page) {
91 JobEventDetails* details = new JobEventDetails(detail_type, document, page);
92 content::NotificationService::current()->Notify(
93 chrome::NOTIFICATION_PRINT_JOB_EVENT,
94 // We know that is is a PrintJob object in this circumstance.
95 content::Source<PrintJob>(static_cast<PrintJob*>(print_job)),
96 content::Details<JobEventDetails>(details));
99 void PostOnOwnerThread(const scoped_refptr<PrintJobWorkerOwner>& owner,
100 const PrintingContext::PrintSettingsCallback& callback,
101 PrintingContext::Result result) {
102 owner->PostTask(FROM_HERE, base::Bind(&HoldRefCallback, owner,
103 base::Bind(callback, result)));
106 } // namespace
108 PrintJobWorker::PrintJobWorker(int render_process_id,
109 int render_view_id,
110 PrintJobWorkerOwner* owner)
111 : owner_(owner), thread_("Printing_Worker"), weak_factory_(this) {
112 // The object is created in the IO thread.
113 DCHECK(owner_->RunsTasksOnCurrentThread());
115 printing_context_delegate_.reset(
116 new PrintingContextDelegate(render_process_id, render_view_id));
117 printing_context_ = PrintingContext::Create(printing_context_delegate_.get());
120 PrintJobWorker::~PrintJobWorker() {
121 // The object is normally deleted in the UI thread, but when the user
122 // cancels printing or in the case of print preview, the worker is destroyed
123 // on the I/O thread.
124 DCHECK(owner_->RunsTasksOnCurrentThread());
125 Stop();
128 void PrintJobWorker::SetNewOwner(PrintJobWorkerOwner* new_owner) {
129 DCHECK(page_number_ == PageNumber::npos());
130 owner_ = new_owner;
133 void PrintJobWorker::GetSettings(
134 bool ask_user_for_settings,
135 int document_page_count,
136 bool has_selection,
137 MarginType margin_type,
138 bool is_scripted) {
139 DCHECK(task_runner_->RunsTasksOnCurrentThread());
140 DCHECK_EQ(page_number_, PageNumber::npos());
142 // Recursive task processing is needed for the dialog in case it needs to be
143 // destroyed by a task.
144 // TODO(thestig): This code is wrong. SetNestableTasksAllowed(true) is needed
145 // on the thread where the PrintDlgEx is called, and definitely both calls
146 // should happen on the same thread. See http://crbug.com/73466
147 // MessageLoop::current()->SetNestableTasksAllowed(true);
148 printing_context_->set_margin_type(margin_type);
150 // When we delegate to a destination, we don't ask the user for settings.
151 // TODO(mad): Ask the destination for settings.
152 if (ask_user_for_settings) {
153 BrowserThread::PostTask(
154 BrowserThread::UI, FROM_HERE,
155 base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
156 base::Bind(&PrintJobWorker::GetSettingsWithUI,
157 base::Unretained(this),
158 document_page_count,
159 has_selection,
160 is_scripted)));
161 } else {
162 BrowserThread::PostTask(
163 BrowserThread::UI, FROM_HERE,
164 base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
165 base::Bind(&PrintJobWorker::UseDefaultSettings,
166 base::Unretained(this))));
170 void PrintJobWorker::SetSettings(
171 scoped_ptr<base::DictionaryValue> new_settings) {
172 DCHECK(task_runner_->RunsTasksOnCurrentThread());
174 BrowserThread::PostTask(
175 BrowserThread::UI,
176 FROM_HERE,
177 base::Bind(&HoldRefCallback,
178 make_scoped_refptr(owner_),
179 base::Bind(&PrintJobWorker::UpdatePrintSettings,
180 base::Unretained(this),
181 base::Passed(&new_settings))));
184 void PrintJobWorker::UpdatePrintSettings(
185 scoped_ptr<base::DictionaryValue> new_settings) {
186 DCHECK_CURRENTLY_ON(BrowserThread::UI);
187 PrintingContext::Result result =
188 printing_context_->UpdatePrintSettings(*new_settings);
189 GetSettingsDone(result);
192 void PrintJobWorker::GetSettingsDone(PrintingContext::Result result) {
193 // Most PrintingContext functions may start a message loop and process
194 // message recursively, so disable recursive task processing.
195 // TODO(thestig): See above comment. SetNestableTasksAllowed(false) needs to
196 // be called on the same thread as the previous call. See
197 // http://crbug.com/73466
198 // MessageLoop::current()->SetNestableTasksAllowed(false);
200 // We can't use OnFailure() here since owner_ may not support notifications.
202 // PrintJob will create the new PrintedDocument.
203 owner_->PostTask(FROM_HERE,
204 base::Bind(&PrintJobWorkerOwner::GetSettingsDone,
205 make_scoped_refptr(owner_),
206 printing_context_->settings(),
207 result));
210 void PrintJobWorker::GetSettingsWithUI(
211 int document_page_count,
212 bool has_selection,
213 bool is_scripted) {
214 DCHECK_CURRENTLY_ON(BrowserThread::UI);
216 #if defined(OS_ANDROID)
217 if (is_scripted) {
218 PrintingContextDelegate* printing_context_delegate =
219 static_cast<PrintingContextDelegate*>(printing_context_delegate_.get());
220 content::WebContents* web_contents =
221 printing_context_delegate->GetWebContents();
222 TabAndroid* tab =
223 web_contents ? TabAndroid::FromWebContents(web_contents) : nullptr;
225 // Regardless of whether the following call fails or not, the javascript
226 // call will return since startPendingPrint will make it return immediately
227 // in case of error.
228 if (tab)
229 tab->SetPendingPrint();
231 #endif
233 // weak_factory_ creates pointers valid only on owner_ thread.
234 printing_context_->AskUserForSettings(
235 document_page_count, has_selection, is_scripted,
236 base::Bind(&PostOnOwnerThread, make_scoped_refptr(owner_),
237 base::Bind(&PrintJobWorker::GetSettingsDone,
238 weak_factory_.GetWeakPtr())));
241 void PrintJobWorker::UseDefaultSettings() {
242 PrintingContext::Result result = printing_context_->UseDefaultSettings();
243 GetSettingsDone(result);
246 void PrintJobWorker::StartPrinting(PrintedDocument* new_document) {
247 DCHECK(task_runner_->RunsTasksOnCurrentThread());
248 DCHECK_EQ(page_number_, PageNumber::npos());
249 DCHECK_EQ(document_.get(), new_document);
250 DCHECK(document_.get());
252 if (!document_.get() || page_number_ != PageNumber::npos() ||
253 document_.get() != new_document) {
254 return;
257 base::string16 document_name =
258 printing::SimplifyDocumentTitle(document_->name());
259 if (document_name.empty()) {
260 document_name = printing::SimplifyDocumentTitle(
261 l10n_util::GetStringUTF16(IDS_DEFAULT_PRINT_DOCUMENT_TITLE));
263 PrintingContext::Result result =
264 printing_context_->NewDocument(document_name);
265 if (result != PrintingContext::OK) {
266 OnFailure();
267 return;
270 // Try to print already cached data. It may already have been generated for
271 // the print preview.
272 OnNewPage();
273 // Don't touch this anymore since the instance could be destroyed. It happens
274 // if all the pages are printed a one sweep and the client doesn't have a
275 // handle to us anymore. There's a timing issue involved between the worker
276 // thread and the UI thread. Take no chance.
279 void PrintJobWorker::OnDocumentChanged(PrintedDocument* new_document) {
280 DCHECK(task_runner_->RunsTasksOnCurrentThread());
281 DCHECK_EQ(page_number_, PageNumber::npos());
283 if (page_number_ != PageNumber::npos())
284 return;
286 document_ = new_document;
289 void PrintJobWorker::OnNewPage() {
290 if (!document_.get()) // Spurious message.
291 return;
293 // message_loop() could return NULL when the print job is cancelled.
294 DCHECK(task_runner_->RunsTasksOnCurrentThread());
296 if (page_number_ == PageNumber::npos()) {
297 // Find first page to print.
298 int page_count = document_->page_count();
299 if (!page_count) {
300 // We still don't know how many pages the document contains. We can't
301 // start to print the document yet since the header/footer may refer to
302 // the document's page count.
303 return;
305 // We have enough information to initialize page_number_.
306 page_number_.Init(document_->settings(), page_count);
308 DCHECK_NE(page_number_, PageNumber::npos());
310 while (true) {
311 // Is the page available?
312 scoped_refptr<PrintedPage> page = document_->GetPage(page_number_.ToInt());
313 if (!page.get()) {
314 // We need to wait for the page to be available.
315 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
316 FROM_HERE,
317 base::Bind(&PrintJobWorker::OnNewPage, weak_factory_.GetWeakPtr()),
318 base::TimeDelta::FromMilliseconds(500));
319 break;
321 // The page is there, print it.
322 SpoolPage(page.get());
323 ++page_number_;
324 if (page_number_ == PageNumber::npos()) {
325 OnDocumentDone();
326 // Don't touch this anymore since the instance could be destroyed.
327 break;
332 void PrintJobWorker::Cancel() {
333 // This is the only function that can be called from any thread.
334 printing_context_->Cancel();
335 // Cannot touch any member variable since we don't know in which thread
336 // context we run.
339 bool PrintJobWorker::IsRunning() const {
340 return thread_.IsRunning();
343 bool PrintJobWorker::PostTask(const tracked_objects::Location& from_here,
344 const base::Closure& task) {
345 if (task_runner_.get())
346 return task_runner_->PostTask(from_here, task);
347 return false;
350 void PrintJobWorker::StopSoon() {
351 thread_.StopSoon();
354 void PrintJobWorker::Stop() {
355 thread_.Stop();
358 bool PrintJobWorker::Start() {
359 bool result = thread_.Start();
360 task_runner_ = thread_.task_runner();
361 return result;
364 void PrintJobWorker::OnDocumentDone() {
365 DCHECK(task_runner_->RunsTasksOnCurrentThread());
366 DCHECK_EQ(page_number_, PageNumber::npos());
367 DCHECK(document_.get());
369 if (printing_context_->DocumentDone() != PrintingContext::OK) {
370 OnFailure();
371 return;
374 owner_->PostTask(FROM_HERE,
375 base::Bind(&NotificationCallback,
376 make_scoped_refptr(owner_),
377 JobEventDetails::DOC_DONE,
378 document_,
379 scoped_refptr<PrintedPage>()));
381 // Makes sure the variables are reinitialized.
382 document_ = NULL;
385 void PrintJobWorker::SpoolPage(PrintedPage* page) {
386 DCHECK(task_runner_->RunsTasksOnCurrentThread());
387 DCHECK_NE(page_number_, PageNumber::npos());
389 // Signal everyone that the page is about to be printed.
390 owner_->PostTask(FROM_HERE,
391 base::Bind(&NotificationCallback,
392 make_scoped_refptr(owner_),
393 JobEventDetails::NEW_PAGE,
394 document_,
395 make_scoped_refptr(page)));
397 // Preprocess.
398 if (printing_context_->NewPage() != PrintingContext::OK) {
399 OnFailure();
400 return;
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
410 // Postprocess.
411 if (printing_context_->PageDone() != PrintingContext::OK) {
412 OnFailure();
413 return;
416 // Signal everyone that the page is printed.
417 owner_->PostTask(FROM_HERE,
418 base::Bind(&NotificationCallback,
419 make_scoped_refptr(owner_),
420 JobEventDetails::PAGE_DONE,
421 document_,
422 make_scoped_refptr(page)));
425 void PrintJobWorker::OnFailure() {
426 DCHECK(task_runner_->RunsTasksOnCurrentThread());
428 // We may loose our last reference by broadcasting the FAILED event.
429 scoped_refptr<PrintJobWorkerOwner> handle(owner_);
431 owner_->PostTask(FROM_HERE,
432 base::Bind(&NotificationCallback,
433 make_scoped_refptr(owner_),
434 JobEventDetails::FAILED,
435 document_,
436 scoped_refptr<PrintedPage>()));
437 Cancel();
439 // Makes sure the variables are reinitialized.
440 document_ = NULL;
441 page_number_ = PageNumber::npos();
444 } // namespace printing