OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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/ui/webui/print_preview_handler.h" | |
6 | |
7 #include <ctype.h> | |
8 | |
9 #include <string> | |
10 #include <vector> | |
11 | |
12 #include "base/base64.h" | |
13 #include "base/bind.h" | |
14 #include "base/bind_helpers.h" | |
15 #include "base/i18n/file_util_icu.h" | |
16 #include "base/i18n/number_formatting.h" | |
17 #include "base/json/json_reader.h" | |
18 #include "base/memory/ref_counted.h" | |
19 #include "base/metrics/histogram.h" | |
20 #include "base/path_service.h" | |
21 #include "base/threading/thread.h" | |
22 #include "base/threading/thread_restrictions.h" | |
23 #include "base/utf_string_conversions.h" | |
24 #include "base/values.h" | |
25 #include "chrome/browser/browser_process.h" | |
26 #include "chrome/browser/platform_util.h" | |
27 #include "chrome/browser/prefs/pref_service.h" | |
28 #include "chrome/browser/printing/cloud_print/cloud_print_url.h" | |
29 #include "chrome/browser/printing/print_dialog_cloud.h" | |
30 #include "chrome/browser/printing/print_job_manager.h" | |
31 #include "chrome/browser/printing/print_preview_tab_controller.h" | |
32 #include "chrome/browser/printing/print_system_task_proxy.h" | |
33 #include "chrome/browser/printing/print_view_manager.h" | |
34 #include "chrome/browser/printing/printer_manager_dialog.h" | |
35 #include "chrome/browser/profiles/profile.h" | |
36 #include "chrome/browser/ui/browser_list.h" | |
37 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" | |
38 #include "chrome/browser/ui/webui/cloud_print_signin_dialog.h" | |
39 #include "chrome/browser/ui/webui/print_preview_ui.h" | |
40 #include "chrome/common/chrome_paths.h" | |
41 #include "chrome/common/pref_names.h" | |
42 #include "chrome/common/print_messages.h" | |
43 #include "content/browser/renderer_host/render_view_host.h" | |
44 #include "content/browser/renderer_host/render_view_host_delegate.h" | |
45 #include "content/browser/webui/web_ui.h" | |
46 #include "content/public/browser/browser_thread.h" | |
47 #include "content/public/browser/navigation_controller.h" | |
48 #include "content/public/browser/navigation_entry.h" | |
49 #include "content/public/browser/web_contents.h" | |
50 #include "printing/backend/print_backend.h" | |
51 #include "printing/metafile.h" | |
52 #include "printing/metafile_impl.h" | |
53 #include "printing/page_range.h" | |
54 #include "printing/page_size_margins.h" | |
55 #include "printing/print_settings.h" | |
56 #include "unicode/ulocdata.h" | |
57 | |
58 #if !defined(OS_MACOSX) | |
59 #include "base/command_line.h" | |
60 #include "chrome/common/chrome_switches.h" | |
61 #endif | |
62 | |
63 using content::BrowserThread; | |
64 using content::NavigationEntry; | |
65 using content::OpenURLParams; | |
66 using content::Referrer; | |
67 using content::WebContents; | |
68 using printing::Metafile; | |
69 | |
70 namespace { | |
71 | |
72 enum UserActionBuckets { | |
73 PRINT_TO_PRINTER, | |
74 PRINT_TO_PDF, | |
75 CANCEL, | |
76 FALLBACK_TO_ADVANCED_SETTINGS_DIALOG, | |
77 PREVIEW_FAILED, | |
78 PREVIEW_STARTED, | |
79 INITIATOR_TAB_CRASHED, // UNUSED | |
80 INITIATOR_TAB_CLOSED, | |
81 PRINT_WITH_CLOUD_PRINT, | |
82 USERACTION_BUCKET_BOUNDARY | |
83 }; | |
84 | |
85 enum PrintSettingsBuckets { | |
86 LANDSCAPE, | |
87 PORTRAIT, | |
88 COLOR, | |
89 BLACK_AND_WHITE, | |
90 COLLATE, | |
91 SIMPLEX, | |
92 DUPLEX, | |
93 PRINT_SETTINGS_BUCKET_BOUNDARY | |
94 }; | |
95 | |
96 void ReportUserActionHistogram(enum UserActionBuckets event) { | |
97 UMA_HISTOGRAM_ENUMERATION("PrintPreview.UserAction", event, | |
98 USERACTION_BUCKET_BOUNDARY); | |
99 } | |
100 | |
101 void ReportPrintSettingHistogram(enum PrintSettingsBuckets setting) { | |
102 UMA_HISTOGRAM_ENUMERATION("PrintPreview.PrintSettings", setting, | |
103 PRINT_SETTINGS_BUCKET_BOUNDARY); | |
104 } | |
105 | |
106 // Name of a dictionary fielad holdong cloud print related data; | |
107 const char kCloudPrintData[] = "cloudPrintData"; | |
108 // Name of a dictionary field holding the initiator tab title. | |
109 const char kInitiatorTabTitle[] = "initiatorTabTitle"; | |
110 // Name of a dictionary field holding the measurement system according to the | |
111 // locale. | |
112 const char kMeasurementSystem[] = "measurementSystem"; | |
113 // Name of a dictionary field holding the number format according to the locale. | |
114 const char kNumberFormat[] = "numberFormat"; | |
115 // Name of a dictionary field specifying whether to print automatically in | |
116 // kiosk mode. See http://crbug.com/31395. | |
117 const char kPrintAutomaticallyInKioskMode[] = "printAutomaticallyInKioskMode"; | |
118 | |
119 | |
120 // Get the print job settings dictionary from |args|. The caller takes | |
121 // ownership of the returned DictionaryValue. Returns NULL on failure. | |
122 DictionaryValue* GetSettingsDictionary(const ListValue* args) { | |
123 std::string json_str; | |
124 if (!args->GetString(0, &json_str)) { | |
125 NOTREACHED() << "Could not read JSON argument"; | |
126 return NULL; | |
127 } | |
128 if (json_str.empty()) { | |
129 NOTREACHED() << "Empty print job settings"; | |
130 return NULL; | |
131 } | |
132 scoped_ptr<DictionaryValue> settings(static_cast<DictionaryValue*>( | |
133 base::JSONReader::Read(json_str, false))); | |
134 if (!settings.get() || !settings->IsType(Value::TYPE_DICTIONARY)) { | |
135 NOTREACHED() << "Print job settings must be a dictionary."; | |
136 return NULL; | |
137 } | |
138 | |
139 if (settings->empty()) { | |
140 NOTREACHED() << "Print job settings dictionary is empty"; | |
141 return NULL; | |
142 } | |
143 | |
144 return settings.release(); | |
145 } | |
146 | |
147 int GetPageCountFromSettingsDictionary(const DictionaryValue& settings) { | |
148 int count = 0; | |
149 ListValue* page_range_array; | |
150 if (settings.GetList(printing::kSettingPageRange, &page_range_array)) { | |
151 for (size_t index = 0; index < page_range_array->GetSize(); ++index) { | |
152 DictionaryValue* dict; | |
153 if (!page_range_array->GetDictionary(index, &dict)) | |
154 continue; | |
155 | |
156 printing::PageRange range; | |
157 if (!dict->GetInteger(printing::kSettingPageRangeFrom, &range.from) || | |
158 !dict->GetInteger(printing::kSettingPageRangeTo, &range.to)) { | |
159 continue; | |
160 } | |
161 count += (range.to - range.from) + 1; | |
162 } | |
163 } | |
164 return count; | |
165 } | |
166 | |
167 // Track the popularity of print settings and report the stats. | |
168 void ReportPrintSettingsStats(const DictionaryValue& settings) { | |
169 bool landscape; | |
170 if (settings.GetBoolean(printing::kSettingLandscape, &landscape)) | |
171 ReportPrintSettingHistogram(landscape ? LANDSCAPE : PORTRAIT); | |
172 | |
173 bool collate; | |
174 if (settings.GetBoolean(printing::kSettingCollate, &collate) && collate) | |
175 ReportPrintSettingHistogram(COLLATE); | |
176 | |
177 int duplex_mode; | |
178 if (settings.GetInteger(printing::kSettingDuplexMode, &duplex_mode)) | |
179 ReportPrintSettingHistogram(duplex_mode ? DUPLEX : SIMPLEX); | |
180 | |
181 int color_mode; | |
182 if (settings.GetInteger(printing::kSettingColor, &color_mode)) { | |
183 ReportPrintSettingHistogram( | |
184 printing::isColorModelSelected(color_mode) ? COLOR : BLACK_AND_WHITE); | |
185 } | |
186 } | |
187 | |
188 // Callback that stores a PDF file on disk. | |
189 void PrintToPdfCallback(Metafile* metafile, const FilePath& path) { | |
190 metafile->SaveTo(path); | |
191 // |metafile| must be deleted on the UI thread. | |
192 BrowserThread::PostTask( | |
193 BrowserThread::UI, FROM_HERE, | |
194 base::Bind(&base::DeletePointer<Metafile>, metafile)); | |
195 } | |
196 | |
197 } // namespace | |
198 | |
199 // static | |
200 FilePath* PrintPreviewHandler::last_saved_path_ = NULL; | |
201 std::string* PrintPreviewHandler::last_used_printer_cloud_print_data_ = NULL; | |
202 std::string* PrintPreviewHandler::last_used_printer_name_ = NULL; | |
203 printing::ColorModels PrintPreviewHandler::last_used_color_model_ = | |
204 printing::UNKNOWN_COLOR_MODEL; | |
205 printing::MarginType PrintPreviewHandler::last_used_margins_type_ = | |
206 printing::DEFAULT_MARGINS; | |
207 printing::PageSizeMargins* | |
208 PrintPreviewHandler::last_used_page_size_margins_ = NULL; | |
209 | |
210 PrintPreviewHandler::PrintPreviewHandler() | |
211 : print_backend_(printing::PrintBackend::CreateInstance(NULL)), | |
212 regenerate_preview_request_count_(0), | |
213 manage_printers_dialog_request_count_(0), | |
214 reported_failed_preview_(false), | |
215 has_logged_printers_count_(false) { | |
216 ReportUserActionHistogram(PREVIEW_STARTED); | |
217 } | |
218 | |
219 PrintPreviewHandler::~PrintPreviewHandler() { | |
220 if (select_file_dialog_.get()) | |
221 select_file_dialog_->ListenerDestroyed(); | |
222 } | |
223 | |
224 void PrintPreviewHandler::RegisterMessages() { | |
225 web_ui()->RegisterMessageCallback("getPrinters", | |
226 base::Bind(&PrintPreviewHandler::HandleGetPrinters, | |
227 base::Unretained(this))); | |
228 web_ui()->RegisterMessageCallback("getPreview", | |
229 base::Bind(&PrintPreviewHandler::HandleGetPreview, | |
230 base::Unretained(this))); | |
231 web_ui()->RegisterMessageCallback("print", | |
232 base::Bind(&PrintPreviewHandler::HandlePrint, | |
233 base::Unretained(this))); | |
234 web_ui()->RegisterMessageCallback("getPrinterCapabilities", | |
235 base::Bind(&PrintPreviewHandler::HandleGetPrinterCapabilities, | |
236 base::Unretained(this))); | |
237 web_ui()->RegisterMessageCallback("showSystemDialog", | |
238 base::Bind(&PrintPreviewHandler::HandleShowSystemDialog, | |
239 base::Unretained(this))); | |
240 web_ui()->RegisterMessageCallback("signIn", | |
241 base::Bind(&PrintPreviewHandler::HandleSignin, | |
242 base::Unretained(this))); | |
243 web_ui()->RegisterMessageCallback("manageCloudPrinters", | |
244 base::Bind(&PrintPreviewHandler::HandleManageCloudPrint, | |
245 base::Unretained(this))); | |
246 web_ui()->RegisterMessageCallback("manageLocalPrinters", | |
247 base::Bind(&PrintPreviewHandler::HandleManagePrinters, | |
248 base::Unretained(this))); | |
249 web_ui()->RegisterMessageCallback("closePrintPreviewTab", | |
250 base::Bind(&PrintPreviewHandler::HandleClosePreviewTab, | |
251 base::Unretained(this))); | |
252 web_ui()->RegisterMessageCallback("hidePreview", | |
253 base::Bind(&PrintPreviewHandler::HandleHidePreview, | |
254 base::Unretained(this))); | |
255 web_ui()->RegisterMessageCallback("cancelPendingPrintRequest", | |
256 base::Bind(&PrintPreviewHandler::HandleCancelPendingPrintRequest, | |
257 base::Unretained(this))); | |
258 web_ui()->RegisterMessageCallback("saveLastPrinter", | |
259 base::Bind(&PrintPreviewHandler::HandleSaveLastPrinter, | |
260 base::Unretained(this))); | |
261 web_ui()->RegisterMessageCallback("getInitialSettings", | |
262 base::Bind(&PrintPreviewHandler::HandleGetInitialSettings, | |
263 base::Unretained(this))); | |
264 } | |
265 | |
266 TabContentsWrapper* PrintPreviewHandler::preview_tab_wrapper() const { | |
267 return TabContentsWrapper::GetCurrentWrapperForContents(preview_tab()); | |
268 } | |
269 | |
270 WebContents* PrintPreviewHandler::preview_tab() const { | |
271 return web_ui()->web_contents(); | |
272 } | |
273 | |
274 void PrintPreviewHandler::HandleGetPrinters(const ListValue* /*args*/) { | |
275 scoped_refptr<PrintSystemTaskProxy> task = | |
276 new PrintSystemTaskProxy(AsWeakPtr(), | |
277 print_backend_.get(), | |
278 has_logged_printers_count_); | |
279 has_logged_printers_count_ = true; | |
280 | |
281 BrowserThread::PostTask( | |
282 BrowserThread::FILE, FROM_HERE, | |
283 base::Bind(&PrintSystemTaskProxy::EnumeratePrinters, task.get())); | |
284 } | |
285 | |
286 void PrintPreviewHandler::HandleGetPreview(const ListValue* args) { | |
287 DCHECK_EQ(3U, args->GetSize()); | |
288 scoped_ptr<DictionaryValue> settings(GetSettingsDictionary(args)); | |
289 if (!settings.get()) | |
290 return; | |
291 int request_id = -1; | |
292 if (!settings->GetInteger(printing::kPreviewRequestID, &request_id)) | |
293 return; | |
294 | |
295 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(web_ui()); | |
296 print_preview_ui->OnPrintPreviewRequest(request_id); | |
297 // Add an additional key in order to identify |print_preview_ui| later on | |
298 // when calling PrintPreviewUI::GetCurrentPrintPreviewStatus() on the IO | |
299 // thread. | |
300 settings->SetString(printing::kPreviewUIAddr, | |
301 print_preview_ui->GetPrintPreviewUIAddress()); | |
302 | |
303 // Increment request count. | |
304 ++regenerate_preview_request_count_; | |
305 | |
306 TabContentsWrapper* initiator_tab = GetInitiatorTab(); | |
307 if (!initiator_tab) { | |
308 ReportUserActionHistogram(INITIATOR_TAB_CLOSED); | |
309 print_preview_ui->OnClosePrintPreviewTab(); | |
310 return; | |
311 } | |
312 | |
313 // Retrieve the page title and url and send it to the renderer process if | |
314 // headers and footers are to be displayed. | |
315 bool display_header_footer = false; | |
316 if (!settings->GetBoolean(printing::kSettingHeaderFooterEnabled, | |
317 &display_header_footer)) { | |
318 NOTREACHED(); | |
319 } | |
320 if (display_header_footer) { | |
321 settings->SetString(printing::kSettingHeaderFooterTitle, | |
322 initiator_tab->web_contents()->GetTitle()); | |
323 std::string url; | |
324 NavigationEntry* entry = | |
325 initiator_tab->web_contents()->GetController().GetActiveEntry(); | |
326 if (entry) | |
327 url = entry->GetVirtualURL().spec(); | |
328 settings->SetString(printing::kSettingHeaderFooterURL, url); | |
329 } | |
330 | |
331 bool generate_draft_data = false; | |
332 bool success = settings->GetBoolean(printing::kSettingGenerateDraftData, | |
333 &generate_draft_data); | |
334 DCHECK(success); | |
335 | |
336 if (!generate_draft_data) { | |
337 double draft_page_count_double = -1; | |
338 success = args->GetDouble(1, &draft_page_count_double); | |
339 DCHECK(success); | |
340 int draft_page_count = static_cast<int>(draft_page_count_double); | |
341 | |
342 bool preview_modifiable = false; | |
343 success = args->GetBoolean(2, &preview_modifiable); | |
344 DCHECK(success); | |
345 | |
346 if (draft_page_count != -1 && preview_modifiable && | |
347 print_preview_ui->GetAvailableDraftPageCount() != draft_page_count) { | |
348 settings->SetBoolean(printing::kSettingGenerateDraftData, true); | |
349 } | |
350 } | |
351 | |
352 VLOG(1) << "Print preview request start"; | |
353 RenderViewHost* rvh = initiator_tab->web_contents()->GetRenderViewHost(); | |
354 rvh->Send(new PrintMsg_PrintPreview(rvh->routing_id(), *settings)); | |
355 } | |
356 | |
357 void PrintPreviewHandler::HandlePrint(const ListValue* args) { | |
358 ReportStats(); | |
359 | |
360 // Record the number of times the user requests to regenerate preview data | |
361 // before printing. | |
362 UMA_HISTOGRAM_COUNTS("PrintPreview.RegeneratePreviewRequest.BeforePrint", | |
363 regenerate_preview_request_count_); | |
364 | |
365 TabContentsWrapper* initiator_tab = GetInitiatorTab(); | |
366 CHECK(initiator_tab); | |
367 | |
368 RenderViewHost* init_rvh = initiator_tab->web_contents()->GetRenderViewHost(); | |
369 init_rvh->Send(new PrintMsg_ResetScriptedPrintCount(init_rvh->routing_id())); | |
370 | |
371 scoped_ptr<DictionaryValue> settings(GetSettingsDictionary(args)); | |
372 if (!settings.get()) | |
373 return; | |
374 | |
375 // Storing last used color model. | |
376 int color_model; | |
377 if (!settings->GetInteger(printing::kSettingColor, &color_model)) | |
378 color_model = printing::GRAY; | |
379 last_used_color_model_ = static_cast<printing::ColorModels>(color_model); | |
380 | |
381 // Storing last used margin settings. | |
382 bool is_modifiable; | |
383 settings->GetBoolean(printing::kSettingPreviewModifiable, &is_modifiable); | |
384 if (is_modifiable) { | |
385 int margin_type; | |
386 if (!settings->GetInteger(printing::kSettingMarginsType, &margin_type)) | |
387 margin_type = printing::DEFAULT_MARGINS; | |
388 last_used_margins_type_ = static_cast<printing::MarginType>(margin_type); | |
389 if (last_used_margins_type_ == printing::CUSTOM_MARGINS) { | |
390 if (!last_used_page_size_margins_) | |
391 last_used_page_size_margins_ = new printing::PageSizeMargins(); | |
392 GetCustomMarginsFromJobSettings(*settings, last_used_page_size_margins_); | |
393 } | |
394 } | |
395 | |
396 bool print_to_pdf = false; | |
397 settings->GetBoolean(printing::kSettingPrintToPDF, &print_to_pdf); | |
398 | |
399 bool open_pdf_in_preview = false; | |
400 #if defined(OS_MACOSX) | |
401 open_pdf_in_preview = settings->HasKey(printing::kSettingOpenPDFInPreview); | |
402 #endif | |
403 | |
404 settings->SetBoolean(printing::kSettingHeaderFooterEnabled, false); | |
405 | |
406 bool is_cloud_printer = settings->HasKey(printing::kSettingCloudPrintId); | |
407 bool is_cloud_dialog = false; | |
408 settings->GetBoolean(printing::kSettingCloudPrintDialog, &is_cloud_dialog); | |
409 if (is_cloud_printer && !open_pdf_in_preview) { | |
410 std::string print_ticket; | |
411 bool res = args->GetString(1, &print_ticket); | |
412 DCHECK(res); | |
413 SendCloudPrintJob(*settings, print_ticket); | |
414 } else if (print_to_pdf && !open_pdf_in_preview) { | |
415 HandlePrintToPdf(*settings); | |
416 } else if (is_cloud_dialog && !open_pdf_in_preview) { | |
417 HandlePrintWithCloudPrint(); | |
418 } else { | |
419 ReportPrintSettingsStats(*settings); | |
420 ReportUserActionHistogram(PRINT_TO_PRINTER); | |
421 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToPrinter", | |
422 GetPageCountFromSettingsDictionary(*settings)); | |
423 | |
424 // This tries to activate the initiator tab as well, so do not clear the | |
425 // association with the initiator tab yet. | |
426 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(web_ui()); | |
427 print_preview_ui->OnHidePreviewTab(); | |
428 | |
429 // Do this so the initiator tab can open a new print preview tab. | |
430 ClearInitiatorTabDetails(); | |
431 | |
432 // The PDF being printed contains only the pages that the user selected, | |
433 // so ignore the page range and print all pages. | |
434 settings->Remove(printing::kSettingPageRange, NULL); | |
435 RenderViewHost* rvh = web_ui()->web_contents()->GetRenderViewHost(); | |
436 rvh->Send(new PrintMsg_PrintForPrintPreview(rvh->routing_id(), *settings)); | |
437 } | |
438 initiator_tab->print_view_manager()->PrintPreviewDone(); | |
439 } | |
440 | |
441 void PrintPreviewHandler::HandlePrintToPdf( | |
442 const base::DictionaryValue& settings) { | |
443 if (print_to_pdf_path_.get()) { | |
444 // User has already selected a path, no need to show the dialog again. | |
445 PostPrintToPdfTask(); | |
446 } else if (!select_file_dialog_.get() || !select_file_dialog_->IsRunning( | |
447 platform_util::GetTopLevel(preview_tab()->GetNativeView()))) { | |
448 ReportUserActionHistogram(PRINT_TO_PDF); | |
449 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToPDF", | |
450 GetPageCountFromSettingsDictionary(settings)); | |
451 | |
452 // Pre-populating select file dialog with print job title. | |
453 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(web_ui()); | |
454 string16 print_job_title_utf16 = print_preview_ui->initiator_tab_title(); | |
455 | |
456 #if defined(OS_WIN) | |
457 FilePath::StringType print_job_title(print_job_title_utf16); | |
458 #elif defined(OS_POSIX) | |
459 FilePath::StringType print_job_title = UTF16ToUTF8(print_job_title_utf16); | |
460 #endif | |
461 | |
462 file_util::ReplaceIllegalCharactersInPath(&print_job_title, '_'); | |
463 FilePath default_filename(print_job_title); | |
464 default_filename = | |
465 default_filename.ReplaceExtension(FILE_PATH_LITERAL("pdf")); | |
466 | |
467 SelectFile(default_filename); | |
468 } | |
469 } | |
470 | |
471 void PrintPreviewHandler::HandleHidePreview(const ListValue* /*args*/) { | |
472 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(web_ui()); | |
473 print_preview_ui->OnHidePreviewTab(); | |
474 } | |
475 | |
476 void PrintPreviewHandler::HandleCancelPendingPrintRequest( | |
477 const ListValue* /*args*/) { | |
478 TabContentsWrapper* initiator_tab = GetInitiatorTab(); | |
479 if (initiator_tab) { | |
480 ClearInitiatorTabDetails(); | |
481 } else { | |
482 // Initiator tab does not exists. Get the wrapper contents of current tab. | |
483 Browser* browser = BrowserList::GetLastActive(); | |
484 if (browser) | |
485 initiator_tab = browser->GetSelectedTabContentsWrapper(); | |
486 } | |
487 | |
488 if (initiator_tab) | |
489 initiator_tab->print_view_manager()->PreviewPrintingRequestCancelled(); | |
490 delete preview_tab_wrapper(); | |
491 } | |
492 | |
493 void PrintPreviewHandler::HandleSaveLastPrinter(const ListValue* args) { | |
494 std::string data_to_save; | |
495 if (args->GetString(0, &data_to_save) && !data_to_save.empty()) { | |
496 if (last_used_printer_name_ == NULL) | |
497 last_used_printer_name_ = new std::string(); | |
498 *last_used_printer_name_ = data_to_save; | |
499 } | |
500 if (args->GetString(1, &data_to_save) && !data_to_save.empty()) { | |
501 if (last_used_printer_cloud_print_data_ == NULL) | |
502 last_used_printer_cloud_print_data_ = new std::string(); | |
503 *last_used_printer_cloud_print_data_ = data_to_save; | |
504 } | |
505 } | |
506 | |
507 void PrintPreviewHandler::HandleGetPrinterCapabilities(const ListValue* args) { | |
508 std::string printer_name; | |
509 bool ret = args->GetString(0, &printer_name); | |
510 if (!ret || printer_name.empty()) | |
511 return; | |
512 | |
513 scoped_refptr<PrintSystemTaskProxy> task = | |
514 new PrintSystemTaskProxy(AsWeakPtr(), | |
515 print_backend_.get(), | |
516 has_logged_printers_count_); | |
517 | |
518 BrowserThread::PostTask( | |
519 BrowserThread::FILE, FROM_HERE, | |
520 base::Bind(&PrintSystemTaskProxy::GetPrinterCapabilities, task.get(), | |
521 printer_name)); | |
522 } | |
523 | |
524 void PrintPreviewHandler::HandleSignin(const ListValue* /*args*/) { | |
525 cloud_print_signin_dialog::CreateCloudPrintSigninDialog(preview_tab()); | |
526 } | |
527 | |
528 void PrintPreviewHandler::HandlePrintWithCloudPrint() { | |
529 // Record the number of times the user asks to print via cloud print | |
530 // instead of the print preview dialog. | |
531 ReportStats(); | |
532 ReportUserActionHistogram(PRINT_WITH_CLOUD_PRINT); | |
533 | |
534 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(web_ui()); | |
535 scoped_refptr<RefCountedBytes> data; | |
536 print_preview_ui->GetPrintPreviewDataForIndex( | |
537 printing::COMPLETE_PREVIEW_DOCUMENT_INDEX, &data); | |
538 CHECK(data.get()); | |
539 DCHECK_GT(data->size(), 0U); | |
540 print_dialog_cloud::CreatePrintDialogForBytes(data, | |
541 string16(print_preview_ui->initiator_tab_title()), | |
542 string16(), | |
543 std::string("application/pdf"), | |
544 true); | |
545 | |
546 // Once the cloud print dialog comes up we're no longer in a background | |
547 // printing situation. Close the print preview. | |
548 // TODO(abodenha@chromium.org) The flow should be changed as described in | |
549 // http://code.google.com/p/chromium/issues/detail?id=44093 | |
550 ActivateInitiatorTabAndClosePreviewTab(); | |
551 } | |
552 | |
553 void PrintPreviewHandler::HandleManageCloudPrint(const ListValue* /*args*/) { | |
554 Browser* browser = BrowserList::GetLastActive(); | |
555 browser->OpenURL(OpenURLParams( | |
556 CloudPrintURL(browser->profile()).GetCloudPrintServiceManageURL(), | |
557 Referrer(), | |
558 NEW_FOREGROUND_TAB, | |
559 content::PAGE_TRANSITION_LINK, | |
560 false)); | |
561 } | |
562 | |
563 void PrintPreviewHandler::HandleShowSystemDialog(const ListValue* /*args*/) { | |
564 ReportStats(); | |
565 ReportUserActionHistogram(FALLBACK_TO_ADVANCED_SETTINGS_DIALOG); | |
566 | |
567 TabContentsWrapper* initiator_tab = GetInitiatorTab(); | |
568 if (!initiator_tab) | |
569 return; | |
570 | |
571 printing::PrintViewManager* manager = initiator_tab->print_view_manager(); | |
572 manager->set_observer(this); | |
573 manager->PrintForSystemDialogNow(); | |
574 | |
575 // Cancel the pending preview request if exists. | |
576 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(web_ui()); | |
577 print_preview_ui->OnCancelPendingPreviewRequest(); | |
578 } | |
579 | |
580 void PrintPreviewHandler::HandleManagePrinters(const ListValue* /*args*/) { | |
581 ++manage_printers_dialog_request_count_; | |
582 printing::PrinterManagerDialog::ShowPrinterManagerDialog(); | |
583 } | |
584 | |
585 void PrintPreviewHandler::HandleClosePreviewTab(const ListValue* /*args*/) { | |
586 ReportStats(); | |
587 ReportUserActionHistogram(CANCEL); | |
588 | |
589 // Record the number of times the user requests to regenerate preview data | |
590 // before cancelling. | |
591 UMA_HISTOGRAM_COUNTS("PrintPreview.RegeneratePreviewRequest.BeforeCancel", | |
592 regenerate_preview_request_count_); | |
593 } | |
594 | |
595 void PrintPreviewHandler::ReportStats() { | |
596 UMA_HISTOGRAM_COUNTS("PrintPreview.ManagePrinters", | |
597 manage_printers_dialog_request_count_); | |
598 } | |
599 | |
600 void PrintPreviewHandler::GetNumberFormatAndMeasurementSystem( | |
601 base::DictionaryValue* settings) { | |
602 | |
603 // Getting the measurement system based on the locale. | |
604 UErrorCode errorCode = U_ZERO_ERROR; | |
605 const char* locale = g_browser_process->GetApplicationLocale().c_str(); | |
606 UMeasurementSystem system = ulocdata_getMeasurementSystem(locale, &errorCode); | |
607 if (errorCode > U_ZERO_ERROR || system == UMS_LIMIT) | |
608 system = UMS_SI; | |
609 | |
610 // Getting the number formatting based on the locale and writing to | |
611 // dictionary. | |
612 settings->SetString(kNumberFormat, base::FormatDouble(123456.78, 2)); | |
613 settings->SetInteger(kMeasurementSystem, system); | |
614 } | |
615 | |
616 void PrintPreviewHandler::GetLastUsedMarginSettings( | |
617 base::DictionaryValue* custom_margins) { | |
618 custom_margins->SetInteger(printing::kSettingMarginsType, | |
619 PrintPreviewHandler::last_used_margins_type_); | |
620 if (last_used_page_size_margins_) { | |
621 custom_margins->SetDouble(printing::kSettingMarginTop, | |
622 last_used_page_size_margins_->margin_top); | |
623 custom_margins->SetDouble(printing::kSettingMarginBottom, | |
624 last_used_page_size_margins_->margin_bottom); | |
625 custom_margins->SetDouble(printing::kSettingMarginLeft, | |
626 last_used_page_size_margins_->margin_left); | |
627 custom_margins->SetDouble(printing::kSettingMarginRight, | |
628 last_used_page_size_margins_->margin_right); | |
629 } | |
630 } | |
631 | |
632 void PrintPreviewHandler::HandleGetInitialSettings(const ListValue* /*args*/) { | |
633 scoped_refptr<PrintSystemTaskProxy> task = | |
634 new PrintSystemTaskProxy(AsWeakPtr(), | |
635 print_backend_.get(), | |
636 has_logged_printers_count_); | |
637 BrowserThread::PostTask( | |
638 BrowserThread::FILE, FROM_HERE, | |
639 base::Bind(&PrintSystemTaskProxy::GetDefaultPrinter, task.get())); | |
640 } | |
641 | |
642 void PrintPreviewHandler::SendInitialSettings( | |
643 const std::string& default_printer, | |
644 const std::string& cloud_print_data) { | |
645 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(web_ui()); | |
646 | |
647 base::DictionaryValue initial_settings; | |
648 initial_settings.SetString(kInitiatorTabTitle, | |
649 print_preview_ui->initiator_tab_title()); | |
650 initial_settings.SetBoolean(printing::kSettingPreviewModifiable, | |
651 print_preview_ui->source_is_modifiable()); | |
652 initial_settings.SetString(printing::kSettingPrinterName, | |
653 default_printer); | |
654 initial_settings.SetString(kCloudPrintData, cloud_print_data); | |
655 | |
656 #if defined(OS_MACOSX) | |
657 bool kiosk_mode = false; // No kiosk mode on Mac yet. | |
658 #else | |
659 CommandLine* cmdline = CommandLine::ForCurrentProcess(); | |
660 bool kiosk_mode = (cmdline->HasSwitch(switches::kKioskMode) && | |
661 cmdline->HasSwitch(switches::kKioskModePrinting)); | |
662 #endif | |
663 initial_settings.SetBoolean(kPrintAutomaticallyInKioskMode, kiosk_mode); | |
664 | |
665 if (print_preview_ui->source_is_modifiable()) { | |
666 GetLastUsedMarginSettings(&initial_settings); | |
667 GetNumberFormatAndMeasurementSystem(&initial_settings); | |
668 } | |
669 web_ui()->CallJavascriptFunction("setInitialSettings", initial_settings); | |
670 } | |
671 | |
672 void PrintPreviewHandler::ActivateInitiatorTabAndClosePreviewTab() { | |
673 TabContentsWrapper* initiator_tab = GetInitiatorTab(); | |
674 if (initiator_tab) | |
675 initiator_tab->web_contents()->GetRenderViewHost()->delegate()->Activate(); | |
676 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(web_ui()); | |
677 print_preview_ui->OnClosePrintPreviewTab(); | |
678 } | |
679 | |
680 void PrintPreviewHandler::SendPrinterCapabilities( | |
681 const DictionaryValue& settings_info) { | |
682 VLOG(1) << "Get printer capabilities finished"; | |
683 web_ui()->CallJavascriptFunction("updateWithPrinterCapabilities", | |
684 settings_info); | |
685 } | |
686 | |
687 void PrintPreviewHandler::SetupPrinterList(const ListValue& printers) { | |
688 SendCloudPrintEnabled(); | |
689 web_ui()->CallJavascriptFunction("setPrinters", printers); | |
690 } | |
691 | |
692 void PrintPreviewHandler::SendCloudPrintEnabled() { | |
693 Profile* profile = BrowserList::GetLastActive()->profile(); | |
694 PrefService* prefs = profile->GetPrefs(); | |
695 if (prefs->GetBoolean(prefs::kCloudPrintSubmitEnabled)) { | |
696 GURL gcp_url(CloudPrintURL(profile).GetCloudPrintServiceURL()); | |
697 base::StringValue gcp_url_value(gcp_url.spec()); | |
698 web_ui()->CallJavascriptFunction("setUseCloudPrint", gcp_url_value); | |
699 } | |
700 } | |
701 | |
702 void PrintPreviewHandler::SendCloudPrintJob(const DictionaryValue& settings, | |
703 std::string print_ticket) { | |
704 scoped_refptr<RefCountedBytes> data; | |
705 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(web_ui()); | |
706 print_preview_ui->GetPrintPreviewDataForIndex( | |
707 printing::COMPLETE_PREVIEW_DOCUMENT_INDEX, &data); | |
708 CHECK(data.get()); | |
709 DCHECK_GT(data->size(), 0U); | |
710 | |
711 string16 print_job_title_utf16 = | |
712 preview_tab_wrapper()->print_view_manager()->RenderSourceName(); | |
713 std::string print_job_title = UTF16ToUTF8(print_job_title_utf16); | |
714 std::string printer_id; | |
715 settings.GetString(printing::kSettingCloudPrintId, &printer_id); | |
716 // BASE64 encode the job data. | |
717 std::string raw_data(reinterpret_cast<const char*>(data->front()), | |
718 data->size()); | |
719 std::string base64_data; | |
720 if (!base::Base64Encode(raw_data, &base64_data)) { | |
721 NOTREACHED() << "Base64 encoding PDF data."; | |
722 } | |
723 | |
724 const char boundary[] = "----CloudPrintFormBoundaryjc9wuprokl8i"; | |
725 const char prolog[] = "--%s\r\n" | |
726 "Content-Disposition: form-data; name=\"capabilities\"\r\n\r\n%s\r\n" | |
727 "--%s\r\n" | |
728 "Content-Disposition: form-data; name=\"contentType\"\r\n\r\ndataUrl\r\n" | |
729 "--%s\r\n" | |
730 "Content-Disposition: form-data; name=\"title\"\r\n\r\n%s\r\n" | |
731 "--%s\r\n" | |
732 "Content-Disposition: form-data; name=\"printerid\"\r\n\r\n%s\r\n" | |
733 "--%s\r\n" | |
734 "Content-Disposition: form-data; name=\"content\"\r\n\r\n" | |
735 "data:application/pdf;base64,%s\r\n" | |
736 "--%s\r\n"; | |
737 | |
738 // TODO(abodenha@chromium.org) This implies a large copy operation. | |
739 // Profile this and optimize if necessary. | |
740 std::string final_data; | |
741 base::SStringPrintf(&final_data, | |
742 prolog, | |
743 boundary, | |
744 print_ticket.c_str(), | |
745 boundary, | |
746 boundary, | |
747 print_job_title.c_str(), | |
748 boundary, | |
749 printer_id.c_str(), | |
750 boundary, | |
751 base64_data.c_str(), | |
752 boundary); | |
753 | |
754 StringValue data_value(final_data); | |
755 | |
756 web_ui()->CallJavascriptFunction("printToCloud", data_value); | |
757 } | |
758 | |
759 TabContentsWrapper* PrintPreviewHandler::GetInitiatorTab() const { | |
760 printing::PrintPreviewTabController* tab_controller = | |
761 printing::PrintPreviewTabController::GetInstance(); | |
762 if (!tab_controller) | |
763 return NULL; | |
764 return tab_controller->GetInitiatorTab(preview_tab_wrapper()); | |
765 } | |
766 | |
767 void PrintPreviewHandler::OnPrintDialogShown() { | |
768 ActivateInitiatorTabAndClosePreviewTab(); | |
769 } | |
770 | |
771 void PrintPreviewHandler::SelectFile(const FilePath& default_filename) { | |
772 SelectFileDialog::FileTypeInfo file_type_info; | |
773 file_type_info.extensions.resize(1); | |
774 file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("pdf")); | |
775 | |
776 // Initializing last_saved_path_ if it is not already initialized. | |
777 if (!last_saved_path_) { | |
778 last_saved_path_ = new FilePath(); | |
779 // Allowing IO operation temporarily. It is ok to do so here because | |
780 // the select file dialog performs IO anyway in order to display the | |
781 // folders and also it is modal. | |
782 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
783 PathService::Get(chrome::DIR_USER_DOCUMENTS, last_saved_path_); | |
784 } | |
785 | |
786 if (!select_file_dialog_.get()) | |
787 select_file_dialog_ = SelectFileDialog::Create(this); | |
788 | |
789 select_file_dialog_->SelectFile( | |
790 SelectFileDialog::SELECT_SAVEAS_FILE, | |
791 string16(), | |
792 last_saved_path_->Append(default_filename), | |
793 &file_type_info, | |
794 0, | |
795 FILE_PATH_LITERAL(""), | |
796 preview_tab(), | |
797 platform_util::GetTopLevel(preview_tab()->GetNativeView()), | |
798 NULL); | |
799 } | |
800 | |
801 void PrintPreviewHandler::OnTabDestroyed() { | |
802 TabContentsWrapper* initiator_tab = GetInitiatorTab(); | |
803 if (!initiator_tab) | |
804 return; | |
805 | |
806 initiator_tab->print_view_manager()->set_observer(NULL); | |
807 } | |
808 | |
809 void PrintPreviewHandler::OnPrintPreviewFailed() { | |
810 if (reported_failed_preview_) | |
811 return; | |
812 reported_failed_preview_ = true; | |
813 ReportUserActionHistogram(PREVIEW_FAILED); | |
814 } | |
815 | |
816 void PrintPreviewHandler::ShowSystemDialog() { | |
817 HandleShowSystemDialog(NULL); | |
818 } | |
819 | |
820 void PrintPreviewHandler::FileSelected(const FilePath& path, | |
821 int index, void* params) { | |
822 // Updating last_saved_path_ to the newly selected folder. | |
823 *last_saved_path_ = path.DirName(); | |
824 | |
825 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(web_ui()); | |
826 print_preview_ui->CallJavascriptFunction("fileSelectionCompleted"); | |
827 scoped_refptr<RefCountedBytes> data; | |
828 print_preview_ui->GetPrintPreviewDataForIndex( | |
829 printing::COMPLETE_PREVIEW_DOCUMENT_INDEX, &data); | |
830 print_to_pdf_path_.reset(new FilePath(path)); | |
831 if (data.get()) | |
832 PostPrintToPdfTask(); | |
833 } | |
834 | |
835 void PrintPreviewHandler::PostPrintToPdfTask() { | |
836 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(web_ui()); | |
837 scoped_refptr<RefCountedBytes> data; | |
838 print_preview_ui->GetPrintPreviewDataForIndex( | |
839 printing::COMPLETE_PREVIEW_DOCUMENT_INDEX, &data); | |
840 DCHECK(data.get()); | |
841 printing::PreviewMetafile* metafile = new printing::PreviewMetafile; | |
842 metafile->InitFromData(static_cast<const void*>(data->front()), data->size()); | |
843 // PrintToPdfCallback takes ownership of |metafile|. | |
844 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | |
845 base::Bind(&PrintToPdfCallback, metafile, | |
846 *print_to_pdf_path_)); | |
847 print_to_pdf_path_.reset(); | |
848 ActivateInitiatorTabAndClosePreviewTab(); | |
849 } | |
850 | |
851 void PrintPreviewHandler::FileSelectionCanceled(void* params) { | |
852 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(web_ui()); | |
853 print_preview_ui->OnFileSelectionCancelled(); | |
854 } | |
855 | |
856 void PrintPreviewHandler::ClearInitiatorTabDetails() { | |
857 TabContentsWrapper* initiator_tab = GetInitiatorTab(); | |
858 if (!initiator_tab) | |
859 return; | |
860 | |
861 // We no longer require the initiator tab details. Remove those details | |
862 // associated with the preview tab to allow the initiator tab to create | |
863 // another preview tab. | |
864 printing::PrintPreviewTabController* tab_controller = | |
865 printing::PrintPreviewTabController::GetInstance(); | |
866 if (tab_controller) | |
867 tab_controller->EraseInitiatorTabInfo(preview_tab_wrapper()); | |
868 } | |
OLD | NEW |