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. 4 5 #include "chrome/browser/ui/webui/print_preview/print_preview_handler.h" 6 7 #include <ctype.h> 8 9 #include <string> 10 11 #include "base/base64.h" 12 #include "base/bind.h" 13 #include "base/bind_helpers.h" 14 #include "base/command_line.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/lazy_instance.h" 19 #include "base/memory/linked_ptr.h" 20 #include "base/memory/ref_counted_memory.h" 21 #include "base/metrics/histogram.h" 22 #include "base/path_service.h" 23 #include "base/prefs/pref_service.h" 24 #include "base/strings/utf_string_conversions.h" 25 #include "base/threading/thread.h" 26 #include "base/threading/thread_restrictions.h" 27 #include "base/values.h" 28 #include "chrome/browser/browser_process.h" 29 #include "chrome/browser/platform_util.h" 30 #include "chrome/browser/printing/cloud_print/cloud_print_url.h" 31 #include "chrome/browser/printing/print_dialog_cloud.h" 32 #include "chrome/browser/printing/print_error_dialog.h" 33 #include "chrome/browser/printing/print_job_manager.h" 34 #include "chrome/browser/printing/print_preview_dialog_controller.h" 35 #include "chrome/browser/printing/print_system_task_proxy.h" 36 #include "chrome/browser/printing/print_view_manager.h" 37 #include "chrome/browser/printing/printer_manager_dialog.h" 38 #include "chrome/browser/profiles/profile.h" 39 #include "chrome/browser/signin/oauth2_token_service.h" 40 #include "chrome/browser/signin/profile_oauth2_token_service.h" 41 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 42 #include "chrome/browser/ui/browser_finder.h" 43 #include "chrome/browser/ui/browser_tabstrip.h" 44 #include "chrome/browser/ui/chrome_select_file_policy.h" 45 #include "chrome/browser/ui/webui/print_preview/print_preview_ui.h" 46 #include "chrome/browser/ui/webui/print_preview/sticky_settings.h" 47 #include "chrome/common/chrome_paths.h" 48 #include "chrome/common/chrome_switches.h" 49 #include "chrome/common/pref_names.h" 50 #include "chrome/common/print_messages.h" 51 #include "content/public/browser/browser_context.h" 52 #include "content/public/browser/browser_thread.h" 53 #include "content/public/browser/navigation_controller.h" 54 #include "content/public/browser/navigation_entry.h" 55 #include "content/public/browser/render_view_host.h" 56 #include "content/public/browser/web_contents.h" 57 #include "content/public/browser/web_contents_view.h" 58 #include "content/public/browser/web_ui.h" 59 #include "printing/backend/print_backend.h" 60 #include "printing/metafile.h" 61 #include "printing/metafile_impl.h" 62 #include "printing/page_range.h" 63 #include "printing/page_size_margins.h" 64 #include "printing/print_settings.h" 65 #include "third_party/icu/source/i18n/unicode/ulocdata.h" 66 67 #if defined(OS_CHROMEOS) 68 // TODO(kinaba): provide more non-intrusive way for handling local/remote 69 // distinction and remove these ugly #ifdef's. http://crbug.com/140425 70 #include "chrome/browser/chromeos/drive/file_system_util.h" 71 #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h" 72 #include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h" 73 #endif 74 75 using content::BrowserThread; 76 using content::NavigationEntry; 77 using content::OpenURLParams; 78 using content::RenderViewHost; 79 using content::Referrer; 80 using content::WebContents; 81 using printing::Metafile; 82 83 namespace { 84 85 // The cloud print OAuth2 scope. 86 const char kCloudPrintAuth[] = "https://www.googleapis.com/auth/cloudprint"; 87 88 enum UserActionBuckets { 89 PRINT_TO_PRINTER, 90 PRINT_TO_PDF, 91 CANCEL, 92 FALLBACK_TO_ADVANCED_SETTINGS_DIALOG, 93 PREVIEW_FAILED, 94 PREVIEW_STARTED, 95 INITIATOR_CRASHED, // UNUSED 96 INITIATOR_CLOSED, 97 PRINT_WITH_CLOUD_PRINT, 98 USERACTION_BUCKET_BOUNDARY 99 }; 100 101 enum PrintSettingsBuckets { 102 LANDSCAPE = 0, 103 PORTRAIT, 104 COLOR, 105 BLACK_AND_WHITE, 106 COLLATE, 107 SIMPLEX, 108 DUPLEX, 109 TOTAL, 110 HEADERS_AND_FOOTERS, 111 CSS_BACKGROUND, 112 SELECTION_ONLY, 113 PRINT_SETTINGS_BUCKET_BOUNDARY 114 }; 115 116 enum UiBucketGroups { 117 DESTINATION_SEARCH, 118 GCP_PROMO, 119 UI_BUCKET_GROUP_BOUNDARY 120 }; 121 122 enum PrintDestinationBuckets { 123 DESTINATION_SHOWN, 124 DESTINATION_CLOSED_CHANGED, 125 DESTINATION_CLOSED_UNCHANGED, 126 SIGNIN_PROMPT, 127 SIGNIN_TRIGGERED, 128 PRINT_DESTINATION_BUCKET_BOUNDARY 129 }; 130 131 enum GcpPromoBuckets { 132 PROMO_SHOWN, 133 PROMO_CLOSED, 134 PROMO_CLICKED, 135 GCP_PROMO_BUCKET_BOUNDARY 136 }; 137 138 void ReportUserActionHistogram(enum UserActionBuckets event) { 139 UMA_HISTOGRAM_ENUMERATION("PrintPreview.UserAction", event, 140 USERACTION_BUCKET_BOUNDARY); 141 } 142 143 void ReportPrintSettingHistogram(enum PrintSettingsBuckets setting) { 144 UMA_HISTOGRAM_ENUMERATION("PrintPreview.PrintSettings", setting, 145 PRINT_SETTINGS_BUCKET_BOUNDARY); 146 } 147 148 void ReportPrintDestinationHistogram(enum PrintDestinationBuckets event) { 149 UMA_HISTOGRAM_ENUMERATION("PrintPreview.DestinationAction", event, 150 PRINT_DESTINATION_BUCKET_BOUNDARY); 151 } 152 153 void ReportGcpPromoHistogram(enum GcpPromoBuckets event) { 154 UMA_HISTOGRAM_ENUMERATION("PrintPreview.GcpPromo", event, 155 GCP_PROMO_BUCKET_BOUNDARY); 156 } 157 158 // Name of a dictionary field holding cloud print related data; 159 const char kAppState[] = "appState"; 160 // Name of a dictionary field holding the initiator title. 161 const char kInitiatorTitle[] = "initiatorTitle"; 162 // Name of a dictionary field holding the measurement system according to the 163 // locale. 164 const char kMeasurementSystem[] = "measurementSystem"; 165 // Name of a dictionary field holding the number format according to the locale. 166 const char kNumberFormat[] = "numberFormat"; 167 // Name of a dictionary field specifying whether to print automatically in 168 // kiosk mode. See http://crbug.com/31395. 169 const char kPrintAutomaticallyInKioskMode[] = "printAutomaticallyInKioskMode"; 170 // Name of a dictionary field holding the state of selection for document. 171 const char kDocumentHasSelection[] = "documentHasSelection"; 172 173 174 // Get the print job settings dictionary from |args|. The caller takes 175 // ownership of the returned DictionaryValue. Returns NULL on failure. 176 DictionaryValue* GetSettingsDictionary(const ListValue* args) { 177 std::string json_str; 178 if (!args->GetString(0, &json_str)) { 179 NOTREACHED() << "Could not read JSON argument"; 180 return NULL; 181 } 182 if (json_str.empty()) { 183 NOTREACHED() << "Empty print job settings"; 184 return NULL; 185 } 186 scoped_ptr<DictionaryValue> settings(static_cast<DictionaryValue*>( 187 base::JSONReader::Read(json_str))); 188 if (!settings.get() || !settings->IsType(Value::TYPE_DICTIONARY)) { 189 NOTREACHED() << "Print job settings must be a dictionary."; 190 return NULL; 191 } 192 193 if (settings->empty()) { 194 NOTREACHED() << "Print job settings dictionary is empty"; 195 return NULL; 196 } 197 198 return settings.release(); 199 } 200 201 // Track the popularity of print settings and report the stats. 202 void ReportPrintSettingsStats(const DictionaryValue& settings) { 203 ReportPrintSettingHistogram(TOTAL); 204 205 bool landscape = false; 206 if (settings.GetBoolean(printing::kSettingLandscape, &landscape)) 207 ReportPrintSettingHistogram(landscape ? LANDSCAPE : PORTRAIT); 208 209 bool collate = false; 210 if (settings.GetBoolean(printing::kSettingCollate, &collate) && collate) 211 ReportPrintSettingHistogram(COLLATE); 212 213 int duplex_mode = 0; 214 if (settings.GetInteger(printing::kSettingDuplexMode, &duplex_mode)) 215 ReportPrintSettingHistogram(duplex_mode ? DUPLEX : SIMPLEX); 216 217 int color_mode = 0; 218 if (settings.GetInteger(printing::kSettingColor, &color_mode)) { 219 ReportPrintSettingHistogram( 220 printing::isColorModelSelected(color_mode) ? COLOR : BLACK_AND_WHITE); 221 } 222 223 bool headers = false; 224 if (settings.GetBoolean(printing::kSettingHeaderFooterEnabled, &headers) && 225 headers) { 226 ReportPrintSettingHistogram(HEADERS_AND_FOOTERS); 227 } 228 229 bool css_background = false; 230 if (settings.GetBoolean(printing::kSettingShouldPrintBackgrounds, 231 &css_background) && css_background) { 232 ReportPrintSettingHistogram(CSS_BACKGROUND); 233 } 234 235 bool selection_only = false; 236 if (settings.GetBoolean(printing::kSettingShouldPrintSelectionOnly, 237 &selection_only) && selection_only) { 238 ReportPrintSettingHistogram(SELECTION_ONLY); 239 } 240 } 241 242 // Callback that stores a PDF file on disk. 243 void PrintToPdfCallback(Metafile* metafile, const base::FilePath& path) { 244 metafile->SaveTo(path); 245 // |metafile| must be deleted on the UI thread. 246 BrowserThread::PostTask( 247 BrowserThread::UI, FROM_HERE, 248 base::Bind(&base::DeletePointer<Metafile>, metafile)); 249 } 250 251 #if defined(OS_CHROMEOS) 252 void PrintToPdfCallbackWithCheck(Metafile* metafile, 253 drive::FileError error, 254 const base::FilePath& path) { 255 if (error != drive::FILE_ERROR_OK) { 256 LOG(ERROR) << "Save to pdf failed to write: " << error; 257 } else { 258 metafile->SaveTo(path); 259 } 260 // |metafile| must be deleted on the UI thread. 261 BrowserThread::PostTask( 262 BrowserThread::UI, FROM_HERE, 263 base::Bind(&base::DeletePointer<Metafile>, metafile)); 264 } 265 #endif 266 267 static base::LazyInstance<printing::StickySettings> sticky_settings = 268 LAZY_INSTANCE_INITIALIZER; 269 270 } // namespace 271 272 class PrintPreviewHandler::AccessTokenService 273 : public OAuth2TokenService::Consumer { 274 public: 275 explicit AccessTokenService(PrintPreviewHandler* handler) 276 : handler_(handler) { 277 } 278 279 void RequestToken(const std::string& type) { 280 if (requests_.find(type) != requests_.end()) 281 return; // Already in progress. 282 283 OAuth2TokenService* service = NULL; 284 if (type == "profile") { 285 Profile* profile = Profile::FromWebUI(handler_->web_ui()); 286 if (profile) 287 service = ProfileOAuth2TokenServiceFactory::GetForProfile(profile); 288 } else if (type == "device") { 289 #if defined(OS_CHROMEOS) 290 service = chromeos::DeviceOAuth2TokenServiceFactory::Get(); 291 #endif 292 } 293 294 if (service) { 295 OAuth2TokenService::ScopeSet oauth_scopes; 296 oauth_scopes.insert(kCloudPrintAuth); 297 scoped_ptr<OAuth2TokenService::Request> request( 298 service->StartRequest(oauth_scopes, this)); 299 requests_[type].reset(request.release()); 300 } else { 301 handler_->SendAccessToken(type, std::string()); // Unknown type. 302 } 303 } 304 305 virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request, 306 const std::string& access_token, 307 const base::Time& expiration_time) OVERRIDE { 308 OnServiceResponce(request, access_token); 309 } 310 311 virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request, 312 const GoogleServiceAuthError& error) OVERRIDE { 313 OnServiceResponce(request, std::string()); 314 } 315 316 private: 317 void OnServiceResponce(const OAuth2TokenService::Request* request, 318 const std::string& access_token) { 319 for (Requests::iterator i = requests_.begin(); i != requests_.end(); ++i) { 320 if (i->second == request) { 321 handler_->SendAccessToken(i->first, access_token); 322 requests_.erase(i); 323 return; 324 } 325 } 326 NOTREACHED(); 327 } 328 329 typedef std::map<std::string, 330 linked_ptr<OAuth2TokenService::Request> > Requests; 331 Requests requests_; 332 PrintPreviewHandler* handler_; 333 334 DISALLOW_COPY_AND_ASSIGN(AccessTokenService); 335 }; 336 337 // static 338 printing::StickySettings* PrintPreviewHandler::GetStickySettings() { 339 return sticky_settings.Pointer(); 340 } 341 342 PrintPreviewHandler::PrintPreviewHandler() 343 : print_backend_(printing::PrintBackend::CreateInstance(NULL)), 344 regenerate_preview_request_count_(0), 345 manage_printers_dialog_request_count_(0), 346 manage_cloud_printers_dialog_request_count_(0), 347 reported_failed_preview_(false), 348 has_logged_printers_count_(false) { 349 ReportUserActionHistogram(PREVIEW_STARTED); 350 } 351 352 PrintPreviewHandler::~PrintPreviewHandler() { 353 if (select_file_dialog_.get()) 354 select_file_dialog_->ListenerDestroyed(); 355 } 356 357 void PrintPreviewHandler::RegisterMessages() { 358 web_ui()->RegisterMessageCallback("getPrinters", 359 base::Bind(&PrintPreviewHandler::HandleGetPrinters, 360 base::Unretained(this))); 361 web_ui()->RegisterMessageCallback("getPreview", 362 base::Bind(&PrintPreviewHandler::HandleGetPreview, 363 base::Unretained(this))); 364 web_ui()->RegisterMessageCallback("print", 365 base::Bind(&PrintPreviewHandler::HandlePrint, 366 base::Unretained(this))); 367 web_ui()->RegisterMessageCallback("getPrinterCapabilities", 368 base::Bind(&PrintPreviewHandler::HandleGetPrinterCapabilities, 369 base::Unretained(this))); 370 web_ui()->RegisterMessageCallback("showSystemDialog", 371 base::Bind(&PrintPreviewHandler::HandleShowSystemDialog, 372 base::Unretained(this))); 373 web_ui()->RegisterMessageCallback("signIn", 374 base::Bind(&PrintPreviewHandler::HandleSignin, 375 base::Unretained(this))); 376 web_ui()->RegisterMessageCallback("getAccessToken", 377 base::Bind(&PrintPreviewHandler::HandleGetAccessToken, 378 base::Unretained(this))); 379 web_ui()->RegisterMessageCallback("manageCloudPrinters", 380 base::Bind(&PrintPreviewHandler::HandleManageCloudPrint, 381 base::Unretained(this))); 382 web_ui()->RegisterMessageCallback("manageLocalPrinters", 383 base::Bind(&PrintPreviewHandler::HandleManagePrinters, 384 base::Unretained(this))); 385 web_ui()->RegisterMessageCallback("closePrintPreviewDialog", 386 base::Bind(&PrintPreviewHandler::HandleClosePreviewDialog, 387 base::Unretained(this))); 388 web_ui()->RegisterMessageCallback("hidePreview", 389 base::Bind(&PrintPreviewHandler::HandleHidePreview, 390 base::Unretained(this))); 391 web_ui()->RegisterMessageCallback("cancelPendingPrintRequest", 392 base::Bind(&PrintPreviewHandler::HandleCancelPendingPrintRequest, 393 base::Unretained(this))); 394 web_ui()->RegisterMessageCallback("saveAppState", 395 base::Bind(&PrintPreviewHandler::HandleSaveAppState, 396 base::Unretained(this))); 397 web_ui()->RegisterMessageCallback("getInitialSettings", 398 base::Bind(&PrintPreviewHandler::HandleGetInitialSettings, 399 base::Unretained(this))); 400 web_ui()->RegisterMessageCallback("reportUiEvent", 401 base::Bind(&PrintPreviewHandler::HandleReportUiEvent, 402 base::Unretained(this))); 403 web_ui()->RegisterMessageCallback("printWithCloudPrintDialog", 404 base::Bind(&PrintPreviewHandler::HandlePrintWithCloudPrintDialog, 405 base::Unretained(this))); 406 web_ui()->RegisterMessageCallback("forceOpenNewTab", 407 base::Bind(&PrintPreviewHandler::HandleForceOpenNewTab, 408 base::Unretained(this))); 409 } 410 411 WebContents* PrintPreviewHandler::preview_web_contents() const { 412 return web_ui()->GetWebContents(); 413 } 414 415 void PrintPreviewHandler::HandleGetPrinters(const ListValue* /*args*/) { 416 scoped_refptr<PrintSystemTaskProxy> task = 417 new PrintSystemTaskProxy(AsWeakPtr(), 418 print_backend_.get(), 419 has_logged_printers_count_); 420 has_logged_printers_count_ = true; 421 422 BrowserThread::PostTask( 423 BrowserThread::FILE, FROM_HERE, 424 base::Bind(&PrintSystemTaskProxy::EnumeratePrinters, task.get())); 425 } 426 427 void PrintPreviewHandler::HandleGetPreview(const ListValue* args) { 428 DCHECK_EQ(3U, args->GetSize()); 429 scoped_ptr<DictionaryValue> settings(GetSettingsDictionary(args)); 430 if (!settings.get()) 431 return; 432 int request_id = -1; 433 if (!settings->GetInteger(printing::kPreviewRequestID, &request_id)) 434 return; 435 436 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>( 437 web_ui()->GetController()); 438 print_preview_ui->OnPrintPreviewRequest(request_id); 439 // Add an additional key in order to identify |print_preview_ui| later on 440 // when calling PrintPreviewUI::GetCurrentPrintPreviewStatus() on the IO 441 // thread. 442 settings->SetInteger(printing::kPreviewUIID, 443 print_preview_ui->GetIDForPrintPreviewUI()); 444 445 // Increment request count. 446 ++regenerate_preview_request_count_; 447 448 WebContents* initiator = GetInitiator(); 449 if (!initiator) { 450 ReportUserActionHistogram(INITIATOR_CLOSED); 451 print_preview_ui->OnClosePrintPreviewDialog(); 452 return; 453 } 454 455 // Retrieve the page title and url and send it to the renderer process if 456 // headers and footers are to be displayed. 457 bool display_header_footer = false; 458 if (!settings->GetBoolean(printing::kSettingHeaderFooterEnabled, 459 &display_header_footer)) { 460 NOTREACHED(); 461 } 462 if (display_header_footer) { 463 settings->SetString(printing::kSettingHeaderFooterTitle, 464 initiator->GetTitle()); 465 std::string url; 466 NavigationEntry* entry = initiator->GetController().GetActiveEntry(); 467 if (entry) 468 url = entry->GetVirtualURL().spec(); 469 settings->SetString(printing::kSettingHeaderFooterURL, url); 470 } 471 472 bool generate_draft_data = false; 473 bool success = settings->GetBoolean(printing::kSettingGenerateDraftData, 474 &generate_draft_data); 475 DCHECK(success); 476 477 if (!generate_draft_data) { 478 double draft_page_count_double = -1; 479 success = args->GetDouble(1, &draft_page_count_double); 480 DCHECK(success); 481 int draft_page_count = static_cast<int>(draft_page_count_double); 482 483 bool preview_modifiable = false; 484 success = args->GetBoolean(2, &preview_modifiable); 485 DCHECK(success); 486 487 if (draft_page_count != -1 && preview_modifiable && 488 print_preview_ui->GetAvailableDraftPageCount() != draft_page_count) { 489 settings->SetBoolean(printing::kSettingGenerateDraftData, true); 490 } 491 } 492 493 VLOG(1) << "Print preview request start"; 494 RenderViewHost* rvh = initiator->GetRenderViewHost(); 495 rvh->Send(new PrintMsg_PrintPreview(rvh->GetRoutingID(), *settings)); 496 } 497 498 void PrintPreviewHandler::HandlePrint(const ListValue* args) { 499 ReportStats(); 500 501 // Record the number of times the user requests to regenerate preview data 502 // before printing. 503 UMA_HISTOGRAM_COUNTS("PrintPreview.RegeneratePreviewRequest.BeforePrint", 504 regenerate_preview_request_count_); 505 506 WebContents* initiator = GetInitiator(); 507 if (initiator) { 508 RenderViewHost* rvh = initiator->GetRenderViewHost(); 509 rvh->Send(new PrintMsg_ResetScriptedPrintCount(rvh->GetRoutingID())); 510 } 511 512 scoped_ptr<DictionaryValue> settings(GetSettingsDictionary(args)); 513 if (!settings.get()) 514 return; 515 516 // Never try to add headers/footers here. It's already in the generated PDF. 517 settings->SetBoolean(printing::kSettingHeaderFooterEnabled, false); 518 519 bool print_to_pdf = false; 520 bool is_cloud_printer = false; 521 522 bool open_pdf_in_preview = false; 523 #if defined(OS_MACOSX) 524 open_pdf_in_preview = settings->HasKey(printing::kSettingOpenPDFInPreview); 525 #endif 526 527 if (!open_pdf_in_preview) { 528 settings->GetBoolean(printing::kSettingPrintToPDF, &print_to_pdf); 529 is_cloud_printer = settings->HasKey(printing::kSettingCloudPrintId); 530 } 531 532 int page_count = 0; 533 settings->GetInteger(printing::kSettingPreviewPageCount, &page_count); 534 535 if (print_to_pdf) { 536 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToPDF", page_count); 537 ReportUserActionHistogram(PRINT_TO_PDF); 538 PrintToPdf(); 539 return; 540 } 541 542 scoped_refptr<base::RefCountedBytes> data; 543 string16 title; 544 if (!GetPreviewDataAndTitle(&data, &title)) { 545 // Nothing to print, no preview available. 546 return; 547 } 548 549 if (is_cloud_printer) { 550 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToCloudPrint", 551 page_count); 552 ReportUserActionHistogram(PRINT_WITH_CLOUD_PRINT); 553 SendCloudPrintJob(data.get()); 554 } else { 555 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToPrinter", page_count); 556 ReportUserActionHistogram(PRINT_TO_PRINTER); 557 ReportPrintSettingsStats(*settings); 558 559 // This tries to activate the initiator as well, so do not clear the 560 // association with the initiator yet. 561 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>( 562 web_ui()->GetController()); 563 print_preview_ui->OnHidePreviewDialog(); 564 565 // Do this so the initiator can open a new print preview dialog, while the 566 // current print preview dialog is still handling its print job. 567 ClearInitiatorDetails(); 568 569 // The PDF being printed contains only the pages that the user selected, 570 // so ignore the page range and print all pages. 571 settings->Remove(printing::kSettingPageRange, NULL); 572 // Remove selection only flag for the same reason. 573 settings->Remove(printing::kSettingShouldPrintSelectionOnly, NULL); 574 575 // Set ID to know whether printing is for preview. 576 settings->SetInteger(printing::kPreviewUIID, 577 print_preview_ui->GetIDForPrintPreviewUI()); 578 RenderViewHost* rvh = preview_web_contents()->GetRenderViewHost(); 579 rvh->Send(new PrintMsg_PrintForPrintPreview(rvh->GetRoutingID(), 580 *settings)); 581 582 // For all other cases above, the preview dialog will stay open until the 583 // printing has finished. Then the dialog closes and PrintPreviewDone() gets 584 // called. In the case below, since the preview dialog will be hidden and 585 // not closed, we need to make this call. 586 if (initiator) { 587 printing::PrintViewManager* print_view_manager = 588 printing::PrintViewManager::FromWebContents(initiator); 589 print_view_manager->PrintPreviewDone(); 590 } 591 } 592 } 593 594 void PrintPreviewHandler::PrintToPdf() { 595 if (print_to_pdf_path_.get()) { 596 // User has already selected a path, no need to show the dialog again. 597 PostPrintToPdfTask(); 598 } else if (!select_file_dialog_.get() || 599 !select_file_dialog_->IsRunning(platform_util::GetTopLevel( 600 preview_web_contents()->GetView()->GetNativeView()))) { 601 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>( 602 web_ui()->GetController()); 603 // Pre-populating select file dialog with print job title. 604 string16 print_job_title_utf16 = print_preview_ui->initiator_title(); 605 606 #if defined(OS_WIN) 607 base::FilePath::StringType print_job_title(print_job_title_utf16); 608 #elif defined(OS_POSIX) 609 base::FilePath::StringType print_job_title = 610 UTF16ToUTF8(print_job_title_utf16); 611 #endif 612 613 file_util::ReplaceIllegalCharactersInPath(&print_job_title, '_'); 614 base::FilePath default_filename(print_job_title); 615 default_filename = 616 default_filename.ReplaceExtension(FILE_PATH_LITERAL("pdf")); 617 618 SelectFile(default_filename); 619 } 620 } 621 622 void PrintPreviewHandler::HandleHidePreview(const ListValue* /*args*/) { 623 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>( 624 web_ui()->GetController()); 625 print_preview_ui->OnHidePreviewDialog(); 626 } 627 628 void PrintPreviewHandler::HandleCancelPendingPrintRequest( 629 const ListValue* /*args*/) { 630 WebContents* initiator = GetInitiator(); 631 if (initiator) 632 ClearInitiatorDetails(); 633 gfx::NativeWindow parent = initiator ? 634 initiator->GetView()->GetTopLevelNativeWindow() : 635 NULL; 636 chrome::ShowPrintErrorDialog(parent); 637 } 638 639 void PrintPreviewHandler::HandleSaveAppState(const ListValue* args) { 640 std::string data_to_save; 641 printing::StickySettings* sticky_settings = GetStickySettings(); 642 if (args->GetString(0, &data_to_save) && !data_to_save.empty()) 643 sticky_settings->StoreAppState(data_to_save); 644 sticky_settings->SaveInPrefs(Profile::FromBrowserContext( 645 preview_web_contents()->GetBrowserContext())->GetPrefs()); 646 } 647 648 void PrintPreviewHandler::HandleGetPrinterCapabilities(const ListValue* args) { 649 std::string printer_name; 650 bool ret = args->GetString(0, &printer_name); 651 if (!ret || printer_name.empty()) 652 return; 653 654 scoped_refptr<PrintSystemTaskProxy> task = 655 new PrintSystemTaskProxy(AsWeakPtr(), 656 print_backend_.get(), 657 has_logged_printers_count_); 658 659 BrowserThread::PostTask( 660 BrowserThread::FILE, FROM_HERE, 661 base::Bind(&PrintSystemTaskProxy::GetPrinterCapabilities, task.get(), 662 printer_name)); 663 } 664 665 // static 666 void PrintPreviewHandler::OnSigninComplete( 667 const base::WeakPtr<PrintPreviewHandler>& handler) { 668 if (handler.get()) { 669 PrintPreviewUI* print_preview_ui = 670 static_cast<PrintPreviewUI*>(handler->web_ui()->GetController()); 671 if (print_preview_ui) 672 print_preview_ui->OnReloadPrintersList(); 673 } 674 } 675 676 void PrintPreviewHandler::HandleSignin(const ListValue* /*args*/) { 677 gfx::NativeWindow modal_parent = platform_util::GetTopLevel( 678 preview_web_contents()->GetView()->GetNativeView()); 679 print_dialog_cloud::CreateCloudPrintSigninDialog( 680 preview_web_contents()->GetBrowserContext(), 681 modal_parent, 682 base::Bind(&PrintPreviewHandler::OnSigninComplete, AsWeakPtr())); 683 } 684 685 void PrintPreviewHandler::HandleGetAccessToken(const base::ListValue* args) { 686 std::string type; 687 if (!args->GetString(0, &type)) 688 return; 689 if (!token_service_) 690 token_service_.reset(new AccessTokenService(this)); 691 token_service_->RequestToken(type); 692 } 693 694 void PrintPreviewHandler::PrintWithCloudPrintDialog() { 695 // Record the number of times the user asks to print via cloud print 696 // instead of the print preview dialog. 697 ReportStats(); 698 699 scoped_refptr<base::RefCountedBytes> data; 700 string16 title; 701 if (!GetPreviewDataAndTitle(&data, &title)) { 702 // Nothing to print, no preview available. 703 return; 704 } 705 706 gfx::NativeWindow modal_parent = platform_util::GetTopLevel( 707 preview_web_contents()->GetView()->GetNativeView()); 708 print_dialog_cloud::CreatePrintDialogForBytes( 709 preview_web_contents()->GetBrowserContext(), 710 modal_parent, 711 data.get(), 712 title, 713 string16(), 714 std::string("application/pdf")); 715 716 // Once the cloud print dialog comes up we're no longer in a background 717 // printing situation. Close the print preview. 718 // TODO(abodenha (at) chromium.org) The flow should be changed as described in 719 // http://code.google.com/p/chromium/issues/detail?id=44093 720 ClosePreviewDialog(); 721 } 722 723 void PrintPreviewHandler::HandleManageCloudPrint(const ListValue* /*args*/) { 724 ++manage_cloud_printers_dialog_request_count_; 725 Profile* profile = Profile::FromBrowserContext( 726 preview_web_contents()->GetBrowserContext()); 727 preview_web_contents()->OpenURL( 728 OpenURLParams( 729 CloudPrintURL(profile).GetCloudPrintServiceManageURL(), 730 Referrer(), 731 NEW_FOREGROUND_TAB, 732 content::PAGE_TRANSITION_LINK, 733 false)); 734 } 735 736 void PrintPreviewHandler::HandleShowSystemDialog(const ListValue* /*args*/) { 737 ReportStats(); 738 ReportUserActionHistogram(FALLBACK_TO_ADVANCED_SETTINGS_DIALOG); 739 740 WebContents* initiator = GetInitiator(); 741 if (!initiator) 742 return; 743 744 printing::PrintViewManager* print_view_manager = 745 printing::PrintViewManager::FromWebContents(initiator); 746 print_view_manager->set_observer(this); 747 print_view_manager->PrintForSystemDialogNow(); 748 749 // Cancel the pending preview request if exists. 750 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>( 751 web_ui()->GetController()); 752 print_preview_ui->OnCancelPendingPreviewRequest(); 753 } 754 755 void PrintPreviewHandler::HandleManagePrinters(const ListValue* /*args*/) { 756 ++manage_printers_dialog_request_count_; 757 printing::PrinterManagerDialog::ShowPrinterManagerDialog(); 758 } 759 760 void PrintPreviewHandler::HandlePrintWithCloudPrintDialog( 761 const base::ListValue* args) { 762 int page_count = 0; 763 if (!args || !args->GetInteger(0, &page_count)) 764 return; 765 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToCloudPrintWebDialog", 766 page_count); 767 768 PrintWithCloudPrintDialog(); 769 } 770 771 void PrintPreviewHandler::HandleClosePreviewDialog(const ListValue* /*args*/) { 772 ReportStats(); 773 ReportUserActionHistogram(CANCEL); 774 775 // Record the number of times the user requests to regenerate preview data 776 // before cancelling. 777 UMA_HISTOGRAM_COUNTS("PrintPreview.RegeneratePreviewRequest.BeforeCancel", 778 regenerate_preview_request_count_); 779 } 780 781 void PrintPreviewHandler::ReportStats() { 782 UMA_HISTOGRAM_COUNTS("PrintPreview.ManagePrinters", 783 manage_printers_dialog_request_count_); 784 UMA_HISTOGRAM_COUNTS("PrintPreview.ManageCloudPrinters", 785 manage_cloud_printers_dialog_request_count_); 786 } 787 788 void PrintPreviewHandler::GetNumberFormatAndMeasurementSystem( 789 base::DictionaryValue* settings) { 790 791 // Getting the measurement system based on the locale. 792 UErrorCode errorCode = U_ZERO_ERROR; 793 const char* locale = g_browser_process->GetApplicationLocale().c_str(); 794 UMeasurementSystem system = ulocdata_getMeasurementSystem(locale, &errorCode); 795 if (errorCode > U_ZERO_ERROR || system == UMS_LIMIT) 796 system = UMS_SI; 797 798 // Getting the number formatting based on the locale and writing to 799 // dictionary. 800 settings->SetString(kNumberFormat, base::FormatDouble(123456.78, 2)); 801 settings->SetInteger(kMeasurementSystem, system); 802 } 803 804 void PrintPreviewHandler::HandleGetInitialSettings(const ListValue* /*args*/) { 805 scoped_refptr<PrintSystemTaskProxy> task = 806 new PrintSystemTaskProxy(AsWeakPtr(), 807 print_backend_.get(), 808 has_logged_printers_count_); 809 BrowserThread::PostTask( 810 BrowserThread::FILE, FROM_HERE, 811 base::Bind(&PrintSystemTaskProxy::GetDefaultPrinter, task.get())); 812 SendCloudPrintEnabled(); 813 } 814 815 void PrintPreviewHandler::HandleReportUiEvent(const ListValue* args) { 816 int event_group, event_number; 817 if (!args->GetInteger(0, &event_group) || !args->GetInteger(1, &event_number)) 818 return; 819 820 enum UiBucketGroups ui_bucket_group = 821 static_cast<enum UiBucketGroups>(event_group); 822 if (ui_bucket_group >= UI_BUCKET_GROUP_BOUNDARY) 823 return; 824 825 switch (ui_bucket_group) { 826 case DESTINATION_SEARCH: { 827 enum PrintDestinationBuckets event = 828 static_cast<enum PrintDestinationBuckets>(event_number); 829 if (event >= PRINT_DESTINATION_BUCKET_BOUNDARY) 830 return; 831 ReportPrintDestinationHistogram(event); 832 break; 833 } 834 case GCP_PROMO: { 835 enum GcpPromoBuckets event = 836 static_cast<enum GcpPromoBuckets>(event_number); 837 if (event >= GCP_PROMO_BUCKET_BOUNDARY) 838 return; 839 ReportGcpPromoHistogram(event); 840 break; 841 } 842 default: 843 break; 844 } 845 } 846 847 void PrintPreviewHandler::HandleForceOpenNewTab(const ListValue* args) { 848 std::string url; 849 if (!args->GetString(0, &url)) 850 return; 851 Browser* browser = chrome::FindBrowserWithWebContents(GetInitiator()); 852 if (!browser) 853 return; 854 chrome::AddSelectedTabWithURL(browser, 855 GURL(url), 856 content::PAGE_TRANSITION_LINK); 857 } 858 859 void PrintPreviewHandler::SendInitialSettings( 860 const std::string& default_printer, 861 const std::string& cloud_print_data) { 862 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>( 863 web_ui()->GetController()); 864 865 base::DictionaryValue initial_settings; 866 initial_settings.SetString(kInitiatorTitle, 867 print_preview_ui->initiator_title()); 868 initial_settings.SetBoolean(printing::kSettingPreviewModifiable, 869 print_preview_ui->source_is_modifiable()); 870 initial_settings.SetString(printing::kSettingPrinterName, default_printer); 871 initial_settings.SetBoolean(kDocumentHasSelection, 872 print_preview_ui->source_has_selection()); 873 initial_settings.SetBoolean(printing::kSettingShouldPrintSelectionOnly, 874 print_preview_ui->print_selection_only()); 875 printing::StickySettings* sticky_settings = GetStickySettings(); 876 sticky_settings->RestoreFromPrefs(Profile::FromBrowserContext( 877 preview_web_contents()->GetBrowserContext())->GetPrefs()); 878 if (sticky_settings->printer_app_state()) 879 initial_settings.SetString(kAppState, 880 *sticky_settings->printer_app_state()); 881 882 CommandLine* cmdline = CommandLine::ForCurrentProcess(); 883 initial_settings.SetBoolean(kPrintAutomaticallyInKioskMode, 884 cmdline->HasSwitch(switches::kKioskModePrinting)); 885 886 if (print_preview_ui->source_is_modifiable()) 887 GetNumberFormatAndMeasurementSystem(&initial_settings); 888 web_ui()->CallJavascriptFunction("setInitialSettings", initial_settings); 889 } 890 891 void PrintPreviewHandler::ClosePreviewDialog() { 892 PrintPreviewUI* print_preview_ui = 893 static_cast<PrintPreviewUI*>(web_ui()->GetController()); 894 print_preview_ui->OnClosePrintPreviewDialog(); 895 } 896 897 void PrintPreviewHandler::SendAccessToken(const std::string& type, 898 const std::string& access_token) { 899 VLOG(1) << "Get getAccessToken finished"; 900 web_ui()->CallJavascriptFunction("onDidGetAccessToken", StringValue(type), 901 StringValue(access_token)); 902 } 903 904 void PrintPreviewHandler::SendPrinterCapabilities( 905 const DictionaryValue& settings_info) { 906 VLOG(1) << "Get printer capabilities finished"; 907 web_ui()->CallJavascriptFunction("updateWithPrinterCapabilities", 908 settings_info); 909 } 910 911 void PrintPreviewHandler::SendFailedToGetPrinterCapabilities( 912 const std::string& printer_name) { 913 VLOG(1) << "Get printer capabilities failed"; 914 base::StringValue printer_name_value(printer_name); 915 web_ui()->CallJavascriptFunction("failedToGetPrinterCapabilities", 916 printer_name_value); 917 } 918 919 void PrintPreviewHandler::SetupPrinterList(const ListValue& printers) { 920 web_ui()->CallJavascriptFunction("setPrinters", printers); 921 } 922 923 void PrintPreviewHandler::SendCloudPrintEnabled() { 924 Profile* profile = Profile::FromBrowserContext( 925 preview_web_contents()->GetBrowserContext()); 926 PrefService* prefs = profile->GetPrefs(); 927 if (prefs->GetBoolean(prefs::kCloudPrintSubmitEnabled)) { 928 GURL gcp_url(CloudPrintURL(profile).GetCloudPrintServiceURL()); 929 base::StringValue gcp_url_value(gcp_url.spec()); 930 web_ui()->CallJavascriptFunction("setUseCloudPrint", gcp_url_value); 931 } 932 } 933 934 void PrintPreviewHandler::SendCloudPrintJob(const base::RefCountedBytes* data) { 935 // BASE64 encode the job data. 936 std::string raw_data(reinterpret_cast<const char*>(data->front()), 937 data->size()); 938 std::string base64_data; 939 if (!base::Base64Encode(raw_data, &base64_data)) { 940 NOTREACHED() << "Base64 encoding PDF data."; 941 } 942 StringValue data_value(base64_data); 943 944 web_ui()->CallJavascriptFunction("printToCloud", data_value); 945 } 946 947 WebContents* PrintPreviewHandler::GetInitiator() const { 948 printing::PrintPreviewDialogController* dialog_controller = 949 printing::PrintPreviewDialogController::GetInstance(); 950 if (!dialog_controller) 951 return NULL; 952 return dialog_controller->GetInitiator(preview_web_contents()); 953 } 954 955 void PrintPreviewHandler::OnPrintDialogShown() { 956 ClosePreviewDialog(); 957 } 958 959 void PrintPreviewHandler::SelectFile(const base::FilePath& default_filename) { 960 ui::SelectFileDialog::FileTypeInfo file_type_info; 961 file_type_info.extensions.resize(1); 962 file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("pdf")); 963 file_type_info.support_drive = true; 964 965 // Initializing save_path_ if it is not already initialized. 966 printing::StickySettings* sticky_settings = GetStickySettings(); 967 if (!sticky_settings->save_path()) { 968 // Allowing IO operation temporarily. It is ok to do so here because 969 // the select file dialog performs IO anyway in order to display the 970 // folders and also it is modal. 971 base::ThreadRestrictions::ScopedAllowIO allow_io; 972 base::FilePath file_path; 973 PathService::Get(chrome::DIR_USER_DOCUMENTS, &file_path); 974 sticky_settings->StoreSavePath(file_path); 975 sticky_settings->SaveInPrefs(Profile::FromBrowserContext( 976 preview_web_contents()->GetBrowserContext())->GetPrefs()); 977 } 978 979 select_file_dialog_ = ui::SelectFileDialog::Create( 980 this, new ChromeSelectFilePolicy(preview_web_contents())), 981 select_file_dialog_->SelectFile( 982 ui::SelectFileDialog::SELECT_SAVEAS_FILE, 983 string16(), 984 sticky_settings->save_path()->Append(default_filename), 985 &file_type_info, 986 0, 987 base::FilePath::StringType(), 988 platform_util::GetTopLevel( 989 preview_web_contents()->GetView()->GetNativeView()), 990 NULL); 991 } 992 993 void PrintPreviewHandler::OnPrintPreviewDialogDestroyed() { 994 WebContents* initiator = GetInitiator(); 995 if (!initiator) 996 return; 997 998 printing::PrintViewManager* print_view_manager = 999 printing::PrintViewManager::FromWebContents(initiator); 1000 print_view_manager->set_observer(NULL); 1001 } 1002 1003 void PrintPreviewHandler::OnPrintPreviewFailed() { 1004 if (reported_failed_preview_) 1005 return; 1006 reported_failed_preview_ = true; 1007 ReportUserActionHistogram(PREVIEW_FAILED); 1008 } 1009 1010 void PrintPreviewHandler::ShowSystemDialog() { 1011 HandleShowSystemDialog(NULL); 1012 } 1013 1014 void PrintPreviewHandler::FileSelected(const base::FilePath& path, 1015 int index, void* params) { 1016 // Updating |save_path_| to the newly selected folder. 1017 printing::StickySettings* sticky_settings = GetStickySettings(); 1018 sticky_settings->StoreSavePath(path.DirName()); 1019 sticky_settings->SaveInPrefs(Profile::FromBrowserContext( 1020 preview_web_contents()->GetBrowserContext())->GetPrefs()); 1021 web_ui()->CallJavascriptFunction("fileSelectionCompleted"); 1022 print_to_pdf_path_.reset(new base::FilePath(path)); 1023 PostPrintToPdfTask(); 1024 } 1025 1026 void PrintPreviewHandler::PostPrintToPdfTask() { 1027 scoped_refptr<base::RefCountedBytes> data; 1028 string16 title; 1029 if (!GetPreviewDataAndTitle(&data, &title)) { 1030 NOTREACHED() << "Preview data was checked before file dialog."; 1031 return; 1032 } 1033 printing::PreviewMetafile* metafile = new printing::PreviewMetafile; 1034 metafile->InitFromData(static_cast<const void*>(data->front()), data->size()); 1035 // PrintToPdfCallback takes ownership of |metafile|. 1036 #if defined(OS_CHROMEOS) 1037 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1038 drive::util::PrepareWritableFileAndRun( 1039 Profile::FromBrowserContext(preview_web_contents()->GetBrowserContext()), 1040 *print_to_pdf_path_, 1041 base::Bind(&PrintToPdfCallbackWithCheck, metafile)); 1042 #else 1043 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 1044 base::Bind(&PrintToPdfCallback, metafile, 1045 *print_to_pdf_path_)); 1046 #endif 1047 1048 print_to_pdf_path_.reset(); 1049 ClosePreviewDialog(); 1050 } 1051 1052 void PrintPreviewHandler::FileSelectionCanceled(void* params) { 1053 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>( 1054 web_ui()->GetController()); 1055 print_preview_ui->OnFileSelectionCancelled(); 1056 } 1057 1058 void PrintPreviewHandler::ClearInitiatorDetails() { 1059 WebContents* initiator = GetInitiator(); 1060 if (!initiator) 1061 return; 1062 1063 // We no longer require the initiator details. Remove those details associated 1064 // with the preview dialog to allow the initiator to create another preview 1065 // dialog. 1066 printing::PrintPreviewDialogController* dialog_controller = 1067 printing::PrintPreviewDialogController::GetInstance(); 1068 if (dialog_controller) 1069 dialog_controller->EraseInitiatorInfo(preview_web_contents()); 1070 } 1071 1072 bool PrintPreviewHandler::GetPreviewDataAndTitle( 1073 scoped_refptr<base::RefCountedBytes>* data, 1074 string16* title) const { 1075 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>( 1076 web_ui()->GetController()); 1077 scoped_refptr<base::RefCountedBytes> tmp_data; 1078 print_preview_ui->GetPrintPreviewDataForIndex( 1079 printing::COMPLETE_PREVIEW_DOCUMENT_INDEX, &tmp_data); 1080 1081 if (!tmp_data.get()) { 1082 // Nothing to print, no preview available. 1083 return false; 1084 } 1085 DCHECK(tmp_data->size() && tmp_data->front()); 1086 1087 *data = tmp_data; 1088 *title = print_preview_ui->initiator_title(); 1089 return true; 1090 } 1091 1092