| 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 |