Upstream version 7.36.149.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/browser/printing/printing_ui_web_contents_observer.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/notification_service.h"
19 #include "grit/generated_resources.h"
20 #include "printing/print_job_constants.h"
21 #include "printing/printed_document.h"
22 #include "printing/printed_page.h"
23 #include "printing/printing_utils.h"
24 #include "ui/base/l10n/l10n_util.h"
25
26 using content::BrowserThread;
27
28 namespace {
29
30 // Helper function to ensure |owner| is valid until at least |callback| returns.
31 void HoldRefCallback(const scoped_refptr<printing::PrintJobWorkerOwner>& owner,
32                      const base::Closure& callback) {
33   callback.Run();
34 }
35
36 }  // namespace
37
38 namespace printing {
39
40 void NotificationCallback(PrintJobWorkerOwner* print_job,
41                           JobEventDetails::Type detail_type,
42                           PrintedDocument* document,
43                           PrintedPage* page) {
44   JobEventDetails* details = new JobEventDetails(detail_type, document, page);
45   content::NotificationService::current()->Notify(
46       chrome::NOTIFICATION_PRINT_JOB_EVENT,
47       // We know that is is a PrintJob object in this circumstance.
48       content::Source<PrintJob>(static_cast<PrintJob*>(print_job)),
49       content::Details<JobEventDetails>(details));
50 }
51
52 PrintJobWorker::PrintJobWorker(PrintJobWorkerOwner* owner)
53     : Thread("Printing_Worker"),
54       owner_(owner),
55       weak_factory_(this) {
56   // The object is created in the IO thread.
57   DCHECK_EQ(owner_->message_loop(), base::MessageLoop::current());
58
59   printing_context_.reset(PrintingContext::Create(
60       g_browser_process->GetApplicationLocale()));
61 }
62
63 PrintJobWorker::~PrintJobWorker() {
64   // The object is normally deleted in the UI thread, but when the user
65   // cancels printing or in the case of print preview, the worker is destroyed
66   // on the I/O thread.
67   DCHECK_EQ(owner_->message_loop(), base::MessageLoop::current());
68   Stop();
69 }
70
71 void PrintJobWorker::SetNewOwner(PrintJobWorkerOwner* new_owner) {
72   DCHECK(page_number_ == PageNumber::npos());
73   owner_ = new_owner;
74 }
75
76 void PrintJobWorker::SetPrintDestination(
77     PrintDestinationInterface* destination) {
78   destination_ = destination;
79 }
80
81 void PrintJobWorker::GetSettings(
82     bool ask_user_for_settings,
83     scoped_ptr<PrintingUIWebContentsObserver> web_contents_observer,
84     int document_page_count,
85     bool has_selection,
86     MarginType margin_type) {
87   DCHECK_EQ(message_loop(), base::MessageLoop::current());
88   DCHECK_EQ(page_number_, PageNumber::npos());
89
90   // Recursive task processing is needed for the dialog in case it needs to be
91   // destroyed by a task.
92   // TODO(thestig): This code is wrong. SetNestableTasksAllowed(true) is needed
93   // on the thread where the PrintDlgEx is called, and definitely both calls
94   // should happen on the same thread. See http://crbug.com/73466
95   // MessageLoop::current()->SetNestableTasksAllowed(true);
96   printing_context_->set_margin_type(margin_type);
97
98   // When we delegate to a destination, we don't ask the user for settings.
99   // TODO(mad): Ask the destination for settings.
100   if (ask_user_for_settings && destination_.get() == NULL) {
101     BrowserThread::PostTask(
102         BrowserThread::UI, FROM_HERE,
103         base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
104                    base::Bind(&PrintJobWorker::GetSettingsWithUI,
105                               base::Unretained(this),
106                               base::Passed(&web_contents_observer),
107                               document_page_count,
108                               has_selection)));
109   } else {
110     BrowserThread::PostTask(
111         BrowserThread::UI, FROM_HERE,
112         base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
113                    base::Bind(&PrintJobWorker::UseDefaultSettings,
114                               base::Unretained(this))));
115   }
116 }
117
118 void PrintJobWorker::SetSettings(
119     const base::DictionaryValue* const new_settings) {
120   DCHECK_EQ(message_loop(), base::MessageLoop::current());
121
122   BrowserThread::PostTask(
123       BrowserThread::UI, FROM_HERE,
124       base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
125                  base::Bind(&PrintJobWorker::UpdatePrintSettings,
126                             base::Unretained(this), new_settings)));
127 }
128
129 void PrintJobWorker::UpdatePrintSettings(
130     const base::DictionaryValue* const new_settings) {
131   // Create new PageRanges based on |new_settings|.
132   PageRanges new_ranges;
133   const base::ListValue* page_range_array;
134   if (new_settings->GetList(kSettingPageRange, &page_range_array)) {
135     for (size_t index = 0; index < page_range_array->GetSize(); ++index) {
136       const base::DictionaryValue* dict;
137       if (!page_range_array->GetDictionary(index, &dict))
138         continue;
139
140       PageRange range;
141       if (!dict->GetInteger(kSettingPageRangeFrom, &range.from) ||
142           !dict->GetInteger(kSettingPageRangeTo, &range.to)) {
143         continue;
144       }
145
146       // Page numbers are 1-based in the dictionary.
147       // Page numbers are 0-based for the printing context.
148       range.from--;
149       range.to--;
150       new_ranges.push_back(range);
151     }
152   }
153   PrintingContext::Result result =
154       printing_context_->UpdatePrintSettings(*new_settings, new_ranges);
155   delete new_settings;
156   GetSettingsDone(result);
157 }
158
159 void PrintJobWorker::GetSettingsDone(PrintingContext::Result result) {
160   // Most PrintingContext functions may start a message loop and process
161   // message recursively, so disable recursive task processing.
162   // TODO(thestig): See above comment. SetNestableTasksAllowed(false) needs to
163   // be called on the same thread as the previous call.  See
164   // http://crbug.com/73466
165   // MessageLoop::current()->SetNestableTasksAllowed(false);
166
167   // We can't use OnFailure() here since owner_ may not support notifications.
168
169   // PrintJob will create the new PrintedDocument.
170   owner_->message_loop()->PostTask(
171       FROM_HERE,
172       base::Bind(&PrintJobWorkerOwner::GetSettingsDone,
173                  make_scoped_refptr(owner_), printing_context_->settings(),
174                  result));
175 }
176
177 void PrintJobWorker::GetSettingsWithUI(
178     scoped_ptr<PrintingUIWebContentsObserver> web_contents_observer,
179     int document_page_count,
180     bool has_selection) {
181   DCHECK_CURRENTLY_ON(BrowserThread::UI);
182
183   gfx::NativeView parent_view = web_contents_observer->GetParentView();
184   if (!parent_view) {
185     GetSettingsWithUIDone(printing::PrintingContext::FAILED);
186     return;
187   }
188   printing_context_->AskUserForSettings(
189       parent_view, document_page_count, has_selection,
190       base::Bind(&PrintJobWorker::GetSettingsWithUIDone,
191                  base::Unretained(this)));
192 }
193
194 void PrintJobWorker::GetSettingsWithUIDone(PrintingContext::Result result) {
195   message_loop()->PostTask(
196       FROM_HERE,
197       base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
198                  base::Bind(&PrintJobWorker::GetSettingsDone,
199                             base::Unretained(this), result)));
200 }
201
202 void PrintJobWorker::UseDefaultSettings() {
203   PrintingContext::Result result = printing_context_->UseDefaultSettings();
204   GetSettingsDone(result);
205 }
206
207 void PrintJobWorker::StartPrinting(PrintedDocument* new_document) {
208   DCHECK_EQ(message_loop(), base::MessageLoop::current());
209   DCHECK_EQ(page_number_, PageNumber::npos());
210   DCHECK_EQ(document_, new_document);
211   DCHECK(document_.get());
212
213   if (!document_.get() || page_number_ != PageNumber::npos() ||
214       document_.get() != new_document) {
215     return;
216   }
217
218   base::string16 document_name =
219       printing::SimplifyDocumentTitle(document_->name());
220   if (document_name.empty()) {
221     document_name = printing::SimplifyDocumentTitle(
222         l10n_util::GetStringUTF16(IDS_DEFAULT_PRINT_DOCUMENT_TITLE));
223   }
224   PrintingContext::Result result =
225       printing_context_->NewDocument(document_name);
226   if (result != PrintingContext::OK) {
227     OnFailure();
228     return;
229   }
230
231   // Try to print already cached data. It may already have been generated for
232   // the print preview.
233   OnNewPage();
234   // Don't touch this anymore since the instance could be destroyed. It happens
235   // if all the pages are printed a one sweep and the client doesn't have a
236   // handle to us anymore. There's a timing issue involved between the worker
237   // thread and the UI thread. Take no chance.
238 }
239
240 void PrintJobWorker::OnDocumentChanged(PrintedDocument* new_document) {
241   DCHECK_EQ(message_loop(), base::MessageLoop::current());
242   DCHECK_EQ(page_number_, PageNumber::npos());
243
244   if (page_number_ != PageNumber::npos())
245     return;
246
247   document_ = new_document;
248 }
249
250 void PrintJobWorker::OnNewPage() {
251   if (!document_.get())  // Spurious message.
252     return;
253
254   // message_loop() could return NULL when the print job is cancelled.
255   DCHECK_EQ(message_loop(), base::MessageLoop::current());
256
257   if (page_number_ == PageNumber::npos()) {
258     // Find first page to print.
259     int page_count = document_->page_count();
260     if (!page_count) {
261       // We still don't know how many pages the document contains. We can't
262       // start to print the document yet since the header/footer may refer to
263       // the document's page count.
264       return;
265     }
266     // We have enough information to initialize page_number_.
267     page_number_.Init(document_->settings(), page_count);
268     if (destination_.get() != NULL)
269       destination_->SetPageCount(page_count);
270   }
271   DCHECK_NE(page_number_, PageNumber::npos());
272
273   while (true) {
274     // Is the page available?
275     scoped_refptr<PrintedPage> page;
276     if (!document_->GetPage(page_number_.ToInt(), &page)) {
277       // We need to wait for the page to be available.
278       base::MessageLoop::current()->PostDelayedTask(
279           FROM_HERE,
280           base::Bind(&PrintJobWorker::OnNewPage, weak_factory_.GetWeakPtr()),
281           base::TimeDelta::FromMilliseconds(500));
282       break;
283     }
284     // The page is there, print it.
285     SpoolPage(page.get());
286     ++page_number_;
287     if (page_number_ == PageNumber::npos()) {
288       OnDocumentDone();
289       // Don't touch this anymore since the instance could be destroyed.
290       break;
291     }
292   }
293 }
294
295 void PrintJobWorker::Cancel() {
296   // This is the only function that can be called from any thread.
297   printing_context_->Cancel();
298   // Cannot touch any member variable since we don't know in which thread
299   // context we run.
300 }
301
302 void PrintJobWorker::OnDocumentDone() {
303   DCHECK_EQ(message_loop(), base::MessageLoop::current());
304   DCHECK_EQ(page_number_, PageNumber::npos());
305   DCHECK(document_.get());
306
307   if (printing_context_->DocumentDone() != PrintingContext::OK) {
308     OnFailure();
309     return;
310   }
311
312   owner_->message_loop()->PostTask(
313       FROM_HERE, base::Bind(NotificationCallback, make_scoped_refptr(owner_),
314                             JobEventDetails::DOC_DONE, document_,
315                             scoped_refptr<PrintedPage>()));
316
317   // Makes sure the variables are reinitialized.
318   document_ = NULL;
319 }
320
321 void PrintJobWorker::SpoolPage(PrintedPage* page) {
322   DCHECK_EQ(message_loop(), base::MessageLoop::current());
323   DCHECK_NE(page_number_, PageNumber::npos());
324
325   // Signal everyone that the page is about to be printed.
326   owner_->message_loop()->PostTask(
327       FROM_HERE, base::Bind(NotificationCallback, make_scoped_refptr(owner_),
328                             JobEventDetails::NEW_PAGE, document_,
329                             make_scoped_refptr(page)));
330
331   // Preprocess.
332   if (printing_context_->NewPage() != PrintingContext::OK) {
333     OnFailure();
334     return;
335   }
336
337   if (destination_.get() != NULL) {
338     std::vector<uint8> metabytes(page->metafile()->GetDataSize());
339     bool success = page->metafile()->GetData(
340         reinterpret_cast<void*>(&metabytes[0]), metabytes.size());
341     DCHECK(success) << "Failed to get metafile data.";
342     destination_->SetPageContent(
343         page->page_number(),
344         reinterpret_cast<void*>(&metabytes[0]),
345         metabytes.size());
346     return;
347   }
348
349   // Actual printing.
350 #if defined(OS_WIN) || defined(OS_MACOSX)
351   document_->RenderPrintedPage(*page, printing_context_->context());
352 #elif defined(OS_POSIX)
353   document_->RenderPrintedPage(*page, printing_context_.get());
354 #endif
355
356   // Postprocess.
357   if (printing_context_->PageDone() != PrintingContext::OK) {
358     OnFailure();
359     return;
360   }
361
362   // Signal everyone that the page is printed.
363   owner_->message_loop()->PostTask(
364       FROM_HERE,
365       base::Bind(NotificationCallback, make_scoped_refptr(owner_),
366                  JobEventDetails::PAGE_DONE, document_,
367                  make_scoped_refptr(page)));
368 }
369
370 void PrintJobWorker::OnFailure() {
371   DCHECK_EQ(message_loop(), base::MessageLoop::current());
372
373   // We may loose our last reference by broadcasting the FAILED event.
374   scoped_refptr<PrintJobWorkerOwner> handle(owner_);
375
376   owner_->message_loop()->PostTask(
377       FROM_HERE, base::Bind(NotificationCallback, make_scoped_refptr(owner_),
378                             JobEventDetails::FAILED, document_,
379                             scoped_refptr<PrintedPage>()));
380   Cancel();
381
382   // Makes sure the variables are reinitialized.
383   document_ = NULL;
384   page_number_ = PageNumber::npos();
385 }
386
387 }  // namespace printing