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