NaCl: Update revision in DEPS, r12770 -> r12773
[chromium-blink-merge.git] / chrome / browser / printing / print_job_worker.cc
blobf034e79f548b7680bed4e047a2d727a5f2a55c8a
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/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"
26 using content::BrowserThread;
28 namespace {
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();
36 } // namespace
38 namespace printing {
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));
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());
59 printing_context_.reset(PrintingContext::Create(
60 g_browser_process->GetApplicationLocale()));
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();
71 void PrintJobWorker::SetNewOwner(PrintJobWorkerOwner* new_owner) {
72 DCHECK(page_number_ == PageNumber::npos());
73 owner_ = new_owner;
76 void PrintJobWorker::SetPrintDestination(
77 PrintDestinationInterface* destination) {
78 destination_ = destination;
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());
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);
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))));
118 void PrintJobWorker::SetSettings(
119 const base::DictionaryValue* const new_settings) {
120 DCHECK_EQ(message_loop(), base::MessageLoop::current());
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)));
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;
140 PageRange range;
141 if (!dict->GetInteger(kSettingPageRangeFrom, &range.from) ||
142 !dict->GetInteger(kSettingPageRangeTo, &range.to)) {
143 continue;
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);
153 PrintingContext::Result result =
154 printing_context_->UpdatePrintSettings(*new_settings, new_ranges);
155 delete new_settings;
156 GetSettingsDone(result);
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);
167 // We can't use OnFailure() here since owner_ may not support notifications.
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));
177 void PrintJobWorker::GetSettingsWithUI(
178 scoped_ptr<PrintingUIWebContentsObserver> web_contents_observer,
179 int document_page_count,
180 bool has_selection) {
181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
183 gfx::NativeView parent_view = web_contents_observer->GetParentView();
184 if (!parent_view) {
185 GetSettingsWithUIDone(printing::PrintingContext::FAILED);
186 return;
188 printing_context_->AskUserForSettings(
189 parent_view, document_page_count, has_selection,
190 base::Bind(&PrintJobWorker::GetSettingsWithUIDone,
191 base::Unretained(this)));
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)));
202 void PrintJobWorker::UseDefaultSettings() {
203 PrintingContext::Result result = printing_context_->UseDefaultSettings();
204 GetSettingsDone(result);
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());
213 if (!document_.get() || page_number_ != PageNumber::npos() ||
214 document_.get() != new_document) {
215 return;
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));
224 PrintingContext::Result result =
225 printing_context_->NewDocument(document_name);
226 if (result != PrintingContext::OK) {
227 OnFailure();
228 return;
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.
240 void PrintJobWorker::OnDocumentChanged(PrintedDocument* new_document) {
241 DCHECK_EQ(message_loop(), base::MessageLoop::current());
242 DCHECK_EQ(page_number_, PageNumber::npos());
244 if (page_number_ != PageNumber::npos())
245 return;
247 document_ = new_document;
250 void PrintJobWorker::OnNewPage() {
251 if (!document_.get()) // Spurious message.
252 return;
254 // message_loop() could return NULL when the print job is cancelled.
255 DCHECK_EQ(message_loop(), base::MessageLoop::current());
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;
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);
271 DCHECK_NE(page_number_, PageNumber::npos());
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;
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;
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.
302 void PrintJobWorker::OnDocumentDone() {
303 DCHECK_EQ(message_loop(), base::MessageLoop::current());
304 DCHECK_EQ(page_number_, PageNumber::npos());
305 DCHECK(document_.get());
307 if (printing_context_->DocumentDone() != PrintingContext::OK) {
308 OnFailure();
309 return;
312 owner_->message_loop()->PostTask(
313 FROM_HERE, base::Bind(NotificationCallback, make_scoped_refptr(owner_),
314 JobEventDetails::DOC_DONE, document_,
315 scoped_refptr<PrintedPage>()));
317 // Makes sure the variables are reinitialized.
318 document_ = NULL;
321 void PrintJobWorker::SpoolPage(PrintedPage* page) {
322 DCHECK_EQ(message_loop(), base::MessageLoop::current());
323 DCHECK_NE(page_number_, PageNumber::npos());
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)));
331 // Preprocess.
332 if (printing_context_->NewPage() != PrintingContext::OK) {
333 OnFailure();
334 return;
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;
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
356 // Postprocess.
357 if (printing_context_->PageDone() != PrintingContext::OK) {
358 OnFailure();
359 return;
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)));
370 void PrintJobWorker::OnFailure() {
371 DCHECK_EQ(message_loop(), base::MessageLoop::current());
373 // We may loose our last reference by broadcasting the FAILED event.
374 scoped_refptr<PrintJobWorkerOwner> handle(owner_);
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();
382 // Makes sure the variables are reinitialized.
383 document_ = NULL;
384 page_number_ = PageNumber::npos();
387 } // namespace printing