Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / printing / print_job_worker.cc
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
5 #include "chrome/browser/printing/print_job_worker.h"
6
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"
26
27 using content::BrowserThread;
28
29 namespace printing {
30
31 namespace {
32
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();
37 }
38
39 class PrintingContextDelegate : public PrintingContext::Delegate {
40  public:
41   PrintingContextDelegate(int render_process_id, int render_view_id);
42   ~PrintingContextDelegate() override;
43
44   gfx::NativeView GetParentView() override;
45   std::string GetAppLocale() override;
46
47  private:
48   int render_process_id_;
49   int render_view_id_;
50 };
51
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) {
56 }
57
58 PrintingContextDelegate::~PrintingContextDelegate() {
59 }
60
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;
69 }
70
71 std::string PrintingContextDelegate::GetAppLocale() {
72   return g_browser_process->GetApplicationLocale();
73 }
74
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));
85 }
86
87 }  // namespace
88
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());
95
96   printing_context_delegate_.reset(
97       new PrintingContextDelegate(render_process_id, render_view_id));
98   printing_context_ = PrintingContext::Create(printing_context_delegate_.get());
99 }
100
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();
107 }
108
109 void PrintJobWorker::SetNewOwner(PrintJobWorkerOwner* new_owner) {
110   DCHECK(page_number_ == PageNumber::npos());
111   owner_ = new_owner;
112 }
113
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());
121
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);
129
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))));
146   }
147 }
148
149 void PrintJobWorker::SetSettings(
150     scoped_ptr<base::DictionaryValue> new_settings) {
151   DCHECK(task_runner_->RunsTasksOnCurrentThread());
152
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))));
161 }
162
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);
169 }
170
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);
178
179   // We can't use OnFailure() here since owner_ may not support notifications.
180
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));
187 }
188
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)));
198 }
199
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)));
207 }
208
209 void PrintJobWorker::UseDefaultSettings() {
210   PrintingContext::Result result = printing_context_->UseDefaultSettings();
211   GetSettingsDone(result);
212 }
213
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());
219
220   if (!document_.get() || page_number_ != PageNumber::npos() ||
221       document_.get() != new_document) {
222     return;
223   }
224
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));
230   }
231   PrintingContext::Result result =
232       printing_context_->NewDocument(document_name);
233   if (result != PrintingContext::OK) {
234     OnFailure();
235     return;
236   }
237
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.
245 }
246
247 void PrintJobWorker::OnDocumentChanged(PrintedDocument* new_document) {
248   DCHECK(task_runner_->RunsTasksOnCurrentThread());
249   DCHECK_EQ(page_number_, PageNumber::npos());
250
251   if (page_number_ != PageNumber::npos())
252     return;
253
254   document_ = new_document;
255 }
256
257 void PrintJobWorker::OnNewPage() {
258   if (!document_.get())  // Spurious message.
259     return;
260
261   // message_loop() could return NULL when the print job is cancelled.
262   DCHECK(task_runner_->RunsTasksOnCurrentThread());
263
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;
272     }
273     // We have enough information to initialize page_number_.
274     page_number_.Init(document_->settings(), page_count);
275   }
276   DCHECK_NE(page_number_, PageNumber::npos());
277
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;
288     }
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;
296     }
297   }
298 }
299
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.
305 }
306
307 bool PrintJobWorker::IsRunning() const {
308   return thread_.IsRunning();
309 }
310
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;
316 }
317
318 void PrintJobWorker::StopSoon() {
319   thread_.StopSoon();
320 }
321
322 void PrintJobWorker::Stop() {
323   thread_.Stop();
324 }
325
326 bool PrintJobWorker::Start() {
327   bool result = thread_.Start();
328   task_runner_ = thread_.task_runner();
329   return result;
330 }
331
332 void PrintJobWorker::OnDocumentDone() {
333   DCHECK(task_runner_->RunsTasksOnCurrentThread());
334   DCHECK_EQ(page_number_, PageNumber::npos());
335   DCHECK(document_.get());
336
337   if (printing_context_->DocumentDone() != PrintingContext::OK) {
338     OnFailure();
339     return;
340   }
341
342   owner_->PostTask(FROM_HERE,
343                    base::Bind(&NotificationCallback,
344                               make_scoped_refptr(owner_),
345                               JobEventDetails::DOC_DONE,
346                               document_,
347                               scoped_refptr<PrintedPage>()));
348
349   // Makes sure the variables are reinitialized.
350   document_ = NULL;
351 }
352
353 void PrintJobWorker::SpoolPage(PrintedPage* page) {
354   DCHECK(task_runner_->RunsTasksOnCurrentThread());
355   DCHECK_NE(page_number_, PageNumber::npos());
356
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)));
364
365   // Preprocess.
366   if (printing_context_->NewPage() != PrintingContext::OK) {
367     OnFailure();
368     return;
369   }
370
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
377
378   // Postprocess.
379   if (printing_context_->PageDone() != PrintingContext::OK) {
380     OnFailure();
381     return;
382   }
383
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)));
391 }
392
393 void PrintJobWorker::OnFailure() {
394   DCHECK(task_runner_->RunsTasksOnCurrentThread());
395
396   // We may loose our last reference by broadcasting the FAILED event.
397   scoped_refptr<PrintJobWorkerOwner> handle(owner_);
398
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();
406
407   // Makes sure the variables are reinitialized.
408   document_ = NULL;
409   page_number_ = PageNumber::npos();
410 }
411
412 }  // namespace printing