Roll src/third_party/WebKit 8b42d1d:744641d (svn 186770:186771)
[chromium-blink-merge.git] / chrome / browser / printing / print_job_worker.cc
blobee8cddea181a9a759182b4d828a3d4a45ae424da
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/message_loop/message_loop.h"
12 #include "base/values.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/chrome_notification_types.h"
15 #include "chrome/browser/printing/print_job.h"
16 #include "chrome/grit/generated_resources.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/notification_service.h"
19 #include "content/public/browser/render_view_host.h"
20 #include "content/public/browser/web_contents.h"
21 #include "printing/print_job_constants.h"
22 #include "printing/printed_document.h"
23 #include "printing/printed_page.h"
24 #include "printing/printing_utils.h"
25 #include "ui/base/l10n/l10n_util.h"
27 using content::BrowserThread;
29 namespace printing {
31 namespace {
33 // Helper function to ensure |owner| is valid until at least |callback| returns.
34 void HoldRefCallback(const scoped_refptr<printing::PrintJobWorkerOwner>& owner,
35 const base::Closure& callback) {
36 callback.Run();
39 class PrintingContextDelegate : public PrintingContext::Delegate {
40 public:
41 PrintingContextDelegate(int render_process_id, int render_view_id);
42 ~PrintingContextDelegate() override;
44 gfx::NativeView GetParentView() override;
45 std::string GetAppLocale() override;
47 private:
48 int render_process_id_;
49 int render_view_id_;
52 PrintingContextDelegate::PrintingContextDelegate(int render_process_id,
53 int render_view_id)
54 : render_process_id_(render_process_id),
55 render_view_id_(render_view_id) {
58 PrintingContextDelegate::~PrintingContextDelegate() {
61 gfx::NativeView PrintingContextDelegate::GetParentView() {
62 DCHECK_CURRENTLY_ON(BrowserThread::UI);
63 content::RenderViewHost* view =
64 content::RenderViewHost::FromID(render_process_id_, render_view_id_);
65 if (!view)
66 return NULL;
67 content::WebContents* wc = content::WebContents::FromRenderViewHost(view);
68 return wc ? wc->GetNativeView() : NULL;
71 std::string PrintingContextDelegate::GetAppLocale() {
72 return g_browser_process->GetApplicationLocale();
75 void NotificationCallback(PrintJobWorkerOwner* print_job,
76 JobEventDetails::Type detail_type,
77 PrintedDocument* document,
78 PrintedPage* page) {
79 JobEventDetails* details = new JobEventDetails(detail_type, document, page);
80 content::NotificationService::current()->Notify(
81 chrome::NOTIFICATION_PRINT_JOB_EVENT,
82 // We know that is is a PrintJob object in this circumstance.
83 content::Source<PrintJob>(static_cast<PrintJob*>(print_job)),
84 content::Details<JobEventDetails>(details));
87 } // namespace
89 PrintJobWorker::PrintJobWorker(int render_process_id,
90 int render_view_id,
91 PrintJobWorkerOwner* owner)
92 : owner_(owner), thread_("Printing_Worker"), weak_factory_(this) {
93 // The object is created in the IO thread.
94 DCHECK(owner_->RunsTasksOnCurrentThread());
96 printing_context_delegate_.reset(
97 new PrintingContextDelegate(render_process_id, render_view_id));
98 printing_context_ = PrintingContext::Create(printing_context_delegate_.get());
101 PrintJobWorker::~PrintJobWorker() {
102 // The object is normally deleted in the UI thread, but when the user
103 // cancels printing or in the case of print preview, the worker is destroyed
104 // on the I/O thread.
105 DCHECK(owner_->RunsTasksOnCurrentThread());
106 Stop();
109 void PrintJobWorker::SetNewOwner(PrintJobWorkerOwner* new_owner) {
110 DCHECK(page_number_ == PageNumber::npos());
111 owner_ = new_owner;
114 void PrintJobWorker::GetSettings(
115 bool ask_user_for_settings,
116 int document_page_count,
117 bool has_selection,
118 MarginType margin_type) {
119 DCHECK(task_runner_->RunsTasksOnCurrentThread());
120 DCHECK_EQ(page_number_, PageNumber::npos());
122 // Recursive task processing is needed for the dialog in case it needs to be
123 // destroyed by a task.
124 // TODO(thestig): This code is wrong. SetNestableTasksAllowed(true) is needed
125 // on the thread where the PrintDlgEx is called, and definitely both calls
126 // should happen on the same thread. See http://crbug.com/73466
127 // MessageLoop::current()->SetNestableTasksAllowed(true);
128 printing_context_->set_margin_type(margin_type);
130 // When we delegate to a destination, we don't ask the user for settings.
131 // TODO(mad): Ask the destination for settings.
132 if (ask_user_for_settings) {
133 BrowserThread::PostTask(
134 BrowserThread::UI, FROM_HERE,
135 base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
136 base::Bind(&PrintJobWorker::GetSettingsWithUI,
137 base::Unretained(this),
138 document_page_count,
139 has_selection)));
140 } else {
141 BrowserThread::PostTask(
142 BrowserThread::UI, FROM_HERE,
143 base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
144 base::Bind(&PrintJobWorker::UseDefaultSettings,
145 base::Unretained(this))));
149 void PrintJobWorker::SetSettings(
150 scoped_ptr<base::DictionaryValue> new_settings) {
151 DCHECK(task_runner_->RunsTasksOnCurrentThread());
153 BrowserThread::PostTask(
154 BrowserThread::UI,
155 FROM_HERE,
156 base::Bind(&HoldRefCallback,
157 make_scoped_refptr(owner_),
158 base::Bind(&PrintJobWorker::UpdatePrintSettings,
159 base::Unretained(this),
160 base::Passed(&new_settings))));
163 void PrintJobWorker::UpdatePrintSettings(
164 scoped_ptr<base::DictionaryValue> new_settings) {
165 DCHECK_CURRENTLY_ON(BrowserThread::UI);
166 PrintingContext::Result result =
167 printing_context_->UpdatePrintSettings(*new_settings);
168 GetSettingsDone(result);
171 void PrintJobWorker::GetSettingsDone(PrintingContext::Result result) {
172 // Most PrintingContext functions may start a message loop and process
173 // message recursively, so disable recursive task processing.
174 // TODO(thestig): See above comment. SetNestableTasksAllowed(false) needs to
175 // be called on the same thread as the previous call. See
176 // http://crbug.com/73466
177 // MessageLoop::current()->SetNestableTasksAllowed(false);
179 // We can't use OnFailure() here since owner_ may not support notifications.
181 // PrintJob will create the new PrintedDocument.
182 owner_->PostTask(FROM_HERE,
183 base::Bind(&PrintJobWorkerOwner::GetSettingsDone,
184 make_scoped_refptr(owner_),
185 printing_context_->settings(),
186 result));
189 void PrintJobWorker::GetSettingsWithUI(
190 int document_page_count,
191 bool has_selection) {
192 DCHECK_CURRENTLY_ON(BrowserThread::UI);
193 printing_context_->AskUserForSettings(
194 document_page_count,
195 has_selection,
196 base::Bind(&PrintJobWorker::GetSettingsWithUIDone,
197 base::Unretained(this)));
200 void PrintJobWorker::GetSettingsWithUIDone(PrintingContext::Result result) {
201 PostTask(FROM_HERE,
202 base::Bind(&HoldRefCallback,
203 make_scoped_refptr(owner_),
204 base::Bind(&PrintJobWorker::GetSettingsDone,
205 base::Unretained(this),
206 result)));
209 void PrintJobWorker::UseDefaultSettings() {
210 PrintingContext::Result result = printing_context_->UseDefaultSettings();
211 GetSettingsDone(result);
214 void PrintJobWorker::StartPrinting(PrintedDocument* new_document) {
215 DCHECK(task_runner_->RunsTasksOnCurrentThread());
216 DCHECK_EQ(page_number_, PageNumber::npos());
217 DCHECK_EQ(document_.get(), new_document);
218 DCHECK(document_.get());
220 if (!document_.get() || page_number_ != PageNumber::npos() ||
221 document_.get() != new_document) {
222 return;
225 base::string16 document_name =
226 printing::SimplifyDocumentTitle(document_->name());
227 if (document_name.empty()) {
228 document_name = printing::SimplifyDocumentTitle(
229 l10n_util::GetStringUTF16(IDS_DEFAULT_PRINT_DOCUMENT_TITLE));
231 PrintingContext::Result result =
232 printing_context_->NewDocument(document_name);
233 if (result != PrintingContext::OK) {
234 OnFailure();
235 return;
238 // Try to print already cached data. It may already have been generated for
239 // the print preview.
240 OnNewPage();
241 // Don't touch this anymore since the instance could be destroyed. It happens
242 // if all the pages are printed a one sweep and the client doesn't have a
243 // handle to us anymore. There's a timing issue involved between the worker
244 // thread and the UI thread. Take no chance.
247 void PrintJobWorker::OnDocumentChanged(PrintedDocument* new_document) {
248 DCHECK(task_runner_->RunsTasksOnCurrentThread());
249 DCHECK_EQ(page_number_, PageNumber::npos());
251 if (page_number_ != PageNumber::npos())
252 return;
254 document_ = new_document;
257 void PrintJobWorker::OnNewPage() {
258 if (!document_.get()) // Spurious message.
259 return;
261 // message_loop() could return NULL when the print job is cancelled.
262 DCHECK(task_runner_->RunsTasksOnCurrentThread());
264 if (page_number_ == PageNumber::npos()) {
265 // Find first page to print.
266 int page_count = document_->page_count();
267 if (!page_count) {
268 // We still don't know how many pages the document contains. We can't
269 // start to print the document yet since the header/footer may refer to
270 // the document's page count.
271 return;
273 // We have enough information to initialize page_number_.
274 page_number_.Init(document_->settings(), page_count);
276 DCHECK_NE(page_number_, PageNumber::npos());
278 while (true) {
279 // Is the page available?
280 scoped_refptr<PrintedPage> page = document_->GetPage(page_number_.ToInt());
281 if (!page.get()) {
282 // We need to wait for the page to be available.
283 base::MessageLoop::current()->PostDelayedTask(
284 FROM_HERE,
285 base::Bind(&PrintJobWorker::OnNewPage, weak_factory_.GetWeakPtr()),
286 base::TimeDelta::FromMilliseconds(500));
287 break;
289 // The page is there, print it.
290 SpoolPage(page.get());
291 ++page_number_;
292 if (page_number_ == PageNumber::npos()) {
293 OnDocumentDone();
294 // Don't touch this anymore since the instance could be destroyed.
295 break;
300 void PrintJobWorker::Cancel() {
301 // This is the only function that can be called from any thread.
302 printing_context_->Cancel();
303 // Cannot touch any member variable since we don't know in which thread
304 // context we run.
307 bool PrintJobWorker::IsRunning() const {
308 return thread_.IsRunning();
311 bool PrintJobWorker::PostTask(const tracked_objects::Location& from_here,
312 const base::Closure& task) {
313 if (task_runner_.get())
314 return task_runner_->PostTask(from_here, task);
315 return false;
318 void PrintJobWorker::StopSoon() {
319 thread_.StopSoon();
322 void PrintJobWorker::Stop() {
323 thread_.Stop();
326 bool PrintJobWorker::Start() {
327 bool result = thread_.Start();
328 task_runner_ = thread_.task_runner();
329 return result;
332 void PrintJobWorker::OnDocumentDone() {
333 DCHECK(task_runner_->RunsTasksOnCurrentThread());
334 DCHECK_EQ(page_number_, PageNumber::npos());
335 DCHECK(document_.get());
337 if (printing_context_->DocumentDone() != PrintingContext::OK) {
338 OnFailure();
339 return;
342 owner_->PostTask(FROM_HERE,
343 base::Bind(&NotificationCallback,
344 make_scoped_refptr(owner_),
345 JobEventDetails::DOC_DONE,
346 document_,
347 scoped_refptr<PrintedPage>()));
349 // Makes sure the variables are reinitialized.
350 document_ = NULL;
353 void PrintJobWorker::SpoolPage(PrintedPage* page) {
354 DCHECK(task_runner_->RunsTasksOnCurrentThread());
355 DCHECK_NE(page_number_, PageNumber::npos());
357 // Signal everyone that the page is about to be printed.
358 owner_->PostTask(FROM_HERE,
359 base::Bind(&NotificationCallback,
360 make_scoped_refptr(owner_),
361 JobEventDetails::NEW_PAGE,
362 document_,
363 make_scoped_refptr(page)));
365 // Preprocess.
366 if (printing_context_->NewPage() != PrintingContext::OK) {
367 OnFailure();
368 return;
371 // Actual printing.
372 #if defined(OS_WIN) || defined(OS_MACOSX)
373 document_->RenderPrintedPage(*page, printing_context_->context());
374 #elif defined(OS_POSIX)
375 document_->RenderPrintedPage(*page, printing_context_.get());
376 #endif
378 // Postprocess.
379 if (printing_context_->PageDone() != PrintingContext::OK) {
380 OnFailure();
381 return;
384 // Signal everyone that the page is printed.
385 owner_->PostTask(FROM_HERE,
386 base::Bind(&NotificationCallback,
387 make_scoped_refptr(owner_),
388 JobEventDetails::PAGE_DONE,
389 document_,
390 make_scoped_refptr(page)));
393 void PrintJobWorker::OnFailure() {
394 DCHECK(task_runner_->RunsTasksOnCurrentThread());
396 // We may loose our last reference by broadcasting the FAILED event.
397 scoped_refptr<PrintJobWorkerOwner> handle(owner_);
399 owner_->PostTask(FROM_HERE,
400 base::Bind(&NotificationCallback,
401 make_scoped_refptr(owner_),
402 JobEventDetails::FAILED,
403 document_,
404 scoped_refptr<PrintedPage>()));
405 Cancel();
407 // Makes sure the variables are reinitialized.
408 document_ = NULL;
409 page_number_ = PageNumber::npos();
412 } // namespace printing