Check USB device path access when prompting users to select a device.
[chromium-blink-merge.git] / chrome / browser / printing / print_job_worker.cc
blob34a3483b5e41ba79c37265728b9b66f9ab14364e
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 #if defined(OS_ANDROID)
28 #include "chrome/browser/android/tab_android.h"
29 #endif
31 using content::BrowserThread;
33 namespace printing {
35 namespace {
37 // Helper function to ensure |owner| is valid until at least |callback| returns.
38 void HoldRefCallback(const scoped_refptr<printing::PrintJobWorkerOwner>& owner,
39 const base::Closure& callback) {
40 callback.Run();
43 class PrintingContextDelegate : public PrintingContext::Delegate {
44 public:
45 PrintingContextDelegate(int render_process_id, int render_view_id);
46 ~PrintingContextDelegate() override;
48 gfx::NativeView GetParentView() override;
49 std::string GetAppLocale() override;
51 // Not exposed to PrintingContext::Delegate because of dependency issues.
52 content::WebContents* GetWebContents();
54 private:
55 int render_process_id_;
56 int render_view_id_;
59 PrintingContextDelegate::PrintingContextDelegate(int render_process_id,
60 int render_view_id)
61 : render_process_id_(render_process_id),
62 render_view_id_(render_view_id) {
65 PrintingContextDelegate::~PrintingContextDelegate() {
68 gfx::NativeView PrintingContextDelegate::GetParentView() {
69 content::WebContents* wc = GetWebContents();
70 return wc ? wc->GetNativeView() : nullptr;
73 content::WebContents* PrintingContextDelegate::GetWebContents() {
74 DCHECK_CURRENTLY_ON(BrowserThread::UI);
75 content::RenderViewHost* view =
76 content::RenderViewHost::FromID(render_process_id_, render_view_id_);
77 return view ? content::WebContents::FromRenderViewHost(view) : nullptr;
80 std::string PrintingContextDelegate::GetAppLocale() {
81 return g_browser_process->GetApplicationLocale();
84 void NotificationCallback(PrintJobWorkerOwner* print_job,
85 JobEventDetails::Type detail_type,
86 PrintedDocument* document,
87 PrintedPage* page) {
88 JobEventDetails* details = new JobEventDetails(detail_type, document, page);
89 content::NotificationService::current()->Notify(
90 chrome::NOTIFICATION_PRINT_JOB_EVENT,
91 // We know that is is a PrintJob object in this circumstance.
92 content::Source<PrintJob>(static_cast<PrintJob*>(print_job)),
93 content::Details<JobEventDetails>(details));
96 } // namespace
98 PrintJobWorker::PrintJobWorker(int render_process_id,
99 int render_view_id,
100 PrintJobWorkerOwner* owner)
101 : owner_(owner), thread_("Printing_Worker"), weak_factory_(this) {
102 // The object is created in the IO thread.
103 DCHECK(owner_->RunsTasksOnCurrentThread());
105 printing_context_delegate_.reset(
106 new PrintingContextDelegate(render_process_id, render_view_id));
107 printing_context_ = PrintingContext::Create(printing_context_delegate_.get());
110 PrintJobWorker::~PrintJobWorker() {
111 // The object is normally deleted in the UI thread, but when the user
112 // cancels printing or in the case of print preview, the worker is destroyed
113 // on the I/O thread.
114 DCHECK(owner_->RunsTasksOnCurrentThread());
115 Stop();
118 void PrintJobWorker::SetNewOwner(PrintJobWorkerOwner* new_owner) {
119 DCHECK(page_number_ == PageNumber::npos());
120 owner_ = new_owner;
123 void PrintJobWorker::GetSettings(
124 bool ask_user_for_settings,
125 int document_page_count,
126 bool has_selection,
127 MarginType margin_type,
128 bool is_scripted) {
129 DCHECK(task_runner_->RunsTasksOnCurrentThread());
130 DCHECK_EQ(page_number_, PageNumber::npos());
132 // Recursive task processing is needed for the dialog in case it needs to be
133 // destroyed by a task.
134 // TODO(thestig): This code is wrong. SetNestableTasksAllowed(true) is needed
135 // on the thread where the PrintDlgEx is called, and definitely both calls
136 // should happen on the same thread. See http://crbug.com/73466
137 // MessageLoop::current()->SetNestableTasksAllowed(true);
138 printing_context_->set_margin_type(margin_type);
140 // When we delegate to a destination, we don't ask the user for settings.
141 // TODO(mad): Ask the destination for settings.
142 if (ask_user_for_settings) {
143 BrowserThread::PostTask(
144 BrowserThread::UI, FROM_HERE,
145 base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
146 base::Bind(&PrintJobWorker::GetSettingsWithUI,
147 base::Unretained(this),
148 document_page_count,
149 has_selection,
150 is_scripted)));
151 } else {
152 BrowserThread::PostTask(
153 BrowserThread::UI, FROM_HERE,
154 base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
155 base::Bind(&PrintJobWorker::UseDefaultSettings,
156 base::Unretained(this))));
160 void PrintJobWorker::SetSettings(
161 scoped_ptr<base::DictionaryValue> new_settings) {
162 DCHECK(task_runner_->RunsTasksOnCurrentThread());
164 BrowserThread::PostTask(
165 BrowserThread::UI,
166 FROM_HERE,
167 base::Bind(&HoldRefCallback,
168 make_scoped_refptr(owner_),
169 base::Bind(&PrintJobWorker::UpdatePrintSettings,
170 base::Unretained(this),
171 base::Passed(&new_settings))));
174 void PrintJobWorker::UpdatePrintSettings(
175 scoped_ptr<base::DictionaryValue> new_settings) {
176 DCHECK_CURRENTLY_ON(BrowserThread::UI);
177 PrintingContext::Result result =
178 printing_context_->UpdatePrintSettings(*new_settings);
179 GetSettingsDone(result);
182 void PrintJobWorker::GetSettingsDone(PrintingContext::Result result) {
183 // Most PrintingContext functions may start a message loop and process
184 // message recursively, so disable recursive task processing.
185 // TODO(thestig): See above comment. SetNestableTasksAllowed(false) needs to
186 // be called on the same thread as the previous call. See
187 // http://crbug.com/73466
188 // MessageLoop::current()->SetNestableTasksAllowed(false);
190 // We can't use OnFailure() here since owner_ may not support notifications.
192 // PrintJob will create the new PrintedDocument.
193 owner_->PostTask(FROM_HERE,
194 base::Bind(&PrintJobWorkerOwner::GetSettingsDone,
195 make_scoped_refptr(owner_),
196 printing_context_->settings(),
197 result));
200 void PrintJobWorker::GetSettingsWithUI(
201 int document_page_count,
202 bool has_selection,
203 bool is_scripted) {
204 DCHECK_CURRENTLY_ON(BrowserThread::UI);
206 #if defined(OS_ANDROID)
207 if (is_scripted) {
208 PrintingContextDelegate* printing_context_delegate =
209 static_cast<PrintingContextDelegate*>(printing_context_delegate_.get());
210 content::WebContents* web_contents =
211 printing_context_delegate->GetWebContents();
212 TabAndroid* tab = TabAndroid::FromWebContents(web_contents);
214 // Regardless of whether the following call fails or not, the javascript
215 // call will return since startPendingPrint will make it return immediately
216 // in case of error.
217 if (tab)
218 tab->SetPendingPrint();
220 #endif
222 printing_context_->AskUserForSettings(
223 document_page_count,
224 has_selection,
225 is_scripted,
226 base::Bind(&PrintJobWorker::GetSettingsWithUIDone,
227 base::Unretained(this)));
230 void PrintJobWorker::GetSettingsWithUIDone(PrintingContext::Result result) {
231 PostTask(FROM_HERE,
232 base::Bind(&HoldRefCallback,
233 make_scoped_refptr(owner_),
234 base::Bind(&PrintJobWorker::GetSettingsDone,
235 base::Unretained(this),
236 result)));
239 void PrintJobWorker::UseDefaultSettings() {
240 PrintingContext::Result result = printing_context_->UseDefaultSettings();
241 GetSettingsDone(result);
244 void PrintJobWorker::StartPrinting(PrintedDocument* new_document) {
245 DCHECK(task_runner_->RunsTasksOnCurrentThread());
246 DCHECK_EQ(page_number_, PageNumber::npos());
247 DCHECK_EQ(document_.get(), new_document);
248 DCHECK(document_.get());
250 if (!document_.get() || page_number_ != PageNumber::npos() ||
251 document_.get() != new_document) {
252 return;
255 base::string16 document_name =
256 printing::SimplifyDocumentTitle(document_->name());
257 if (document_name.empty()) {
258 document_name = printing::SimplifyDocumentTitle(
259 l10n_util::GetStringUTF16(IDS_DEFAULT_PRINT_DOCUMENT_TITLE));
261 PrintingContext::Result result =
262 printing_context_->NewDocument(document_name);
263 if (result != PrintingContext::OK) {
264 OnFailure();
265 return;
268 // Try to print already cached data. It may already have been generated for
269 // the print preview.
270 OnNewPage();
271 // Don't touch this anymore since the instance could be destroyed. It happens
272 // if all the pages are printed a one sweep and the client doesn't have a
273 // handle to us anymore. There's a timing issue involved between the worker
274 // thread and the UI thread. Take no chance.
277 void PrintJobWorker::OnDocumentChanged(PrintedDocument* new_document) {
278 DCHECK(task_runner_->RunsTasksOnCurrentThread());
279 DCHECK_EQ(page_number_, PageNumber::npos());
281 if (page_number_ != PageNumber::npos())
282 return;
284 document_ = new_document;
287 void PrintJobWorker::OnNewPage() {
288 if (!document_.get()) // Spurious message.
289 return;
291 // message_loop() could return NULL when the print job is cancelled.
292 DCHECK(task_runner_->RunsTasksOnCurrentThread());
294 if (page_number_ == PageNumber::npos()) {
295 // Find first page to print.
296 int page_count = document_->page_count();
297 if (!page_count) {
298 // We still don't know how many pages the document contains. We can't
299 // start to print the document yet since the header/footer may refer to
300 // the document's page count.
301 return;
303 // We have enough information to initialize page_number_.
304 page_number_.Init(document_->settings(), page_count);
306 DCHECK_NE(page_number_, PageNumber::npos());
308 while (true) {
309 // Is the page available?
310 scoped_refptr<PrintedPage> page = document_->GetPage(page_number_.ToInt());
311 if (!page.get()) {
312 // We need to wait for the page to be available.
313 base::MessageLoop::current()->PostDelayedTask(
314 FROM_HERE,
315 base::Bind(&PrintJobWorker::OnNewPage, weak_factory_.GetWeakPtr()),
316 base::TimeDelta::FromMilliseconds(500));
317 break;
319 // The page is there, print it.
320 SpoolPage(page.get());
321 ++page_number_;
322 if (page_number_ == PageNumber::npos()) {
323 OnDocumentDone();
324 // Don't touch this anymore since the instance could be destroyed.
325 break;
330 void PrintJobWorker::Cancel() {
331 // This is the only function that can be called from any thread.
332 printing_context_->Cancel();
333 // Cannot touch any member variable since we don't know in which thread
334 // context we run.
337 bool PrintJobWorker::IsRunning() const {
338 return thread_.IsRunning();
341 bool PrintJobWorker::PostTask(const tracked_objects::Location& from_here,
342 const base::Closure& task) {
343 if (task_runner_.get())
344 return task_runner_->PostTask(from_here, task);
345 return false;
348 void PrintJobWorker::StopSoon() {
349 thread_.StopSoon();
352 void PrintJobWorker::Stop() {
353 thread_.Stop();
356 bool PrintJobWorker::Start() {
357 bool result = thread_.Start();
358 task_runner_ = thread_.task_runner();
359 return result;
362 void PrintJobWorker::OnDocumentDone() {
363 DCHECK(task_runner_->RunsTasksOnCurrentThread());
364 DCHECK_EQ(page_number_, PageNumber::npos());
365 DCHECK(document_.get());
367 if (printing_context_->DocumentDone() != PrintingContext::OK) {
368 OnFailure();
369 return;
372 owner_->PostTask(FROM_HERE,
373 base::Bind(&NotificationCallback,
374 make_scoped_refptr(owner_),
375 JobEventDetails::DOC_DONE,
376 document_,
377 scoped_refptr<PrintedPage>()));
379 // Makes sure the variables are reinitialized.
380 document_ = NULL;
383 void PrintJobWorker::SpoolPage(PrintedPage* page) {
384 DCHECK(task_runner_->RunsTasksOnCurrentThread());
385 DCHECK_NE(page_number_, PageNumber::npos());
387 // Signal everyone that the page is about to be printed.
388 owner_->PostTask(FROM_HERE,
389 base::Bind(&NotificationCallback,
390 make_scoped_refptr(owner_),
391 JobEventDetails::NEW_PAGE,
392 document_,
393 make_scoped_refptr(page)));
395 // Preprocess.
396 if (printing_context_->NewPage() != PrintingContext::OK) {
397 OnFailure();
398 return;
401 // Actual printing.
402 #if defined(OS_WIN) || defined(OS_MACOSX)
403 document_->RenderPrintedPage(*page, printing_context_->context());
404 #elif defined(OS_POSIX)
405 document_->RenderPrintedPage(*page, printing_context_.get());
406 #endif
408 // Postprocess.
409 if (printing_context_->PageDone() != PrintingContext::OK) {
410 OnFailure();
411 return;
414 // Signal everyone that the page is printed.
415 owner_->PostTask(FROM_HERE,
416 base::Bind(&NotificationCallback,
417 make_scoped_refptr(owner_),
418 JobEventDetails::PAGE_DONE,
419 document_,
420 make_scoped_refptr(page)));
423 void PrintJobWorker::OnFailure() {
424 DCHECK(task_runner_->RunsTasksOnCurrentThread());
426 // We may loose our last reference by broadcasting the FAILED event.
427 scoped_refptr<PrintJobWorkerOwner> handle(owner_);
429 owner_->PostTask(FROM_HERE,
430 base::Bind(&NotificationCallback,
431 make_scoped_refptr(owner_),
432 JobEventDetails::FAILED,
433 document_,
434 scoped_refptr<PrintedPage>()));
435 Cancel();
437 // Makes sure the variables are reinitialized.
438 document_ = NULL;
439 page_number_ = PageNumber::npos();
442 } // namespace printing