print_preview_handler.cc revision e5d81f57cb97b3b6b7fccc9c5610d21eb81db09d
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_view_manager.h" 36#include "chrome/browser/printing/printer_manager_dialog.h" 37#include "chrome/browser/profiles/profile.h" 38#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 39#include "chrome/browser/signin/signin_manager_factory.h" 40#include "chrome/browser/ui/browser_finder.h" 41#include "chrome/browser/ui/browser_tabstrip.h" 42#include "chrome/browser/ui/chrome_select_file_policy.h" 43#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" 44#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h" 45#include "chrome/browser/ui/webui/print_preview/sticky_settings.h" 46#include "chrome/common/chrome_paths.h" 47#include "chrome/common/chrome_switches.h" 48#include "chrome/common/cloud_print/cloud_print_constants.h" 49#include "chrome/common/crash_keys.h" 50#include "chrome/common/pref_names.h" 51#include "chrome/common/print_messages.h" 52#include "components/signin/core/browser/profile_oauth2_token_service.h" 53#include "components/signin/core/browser/signin_manager.h" 54#include "components/signin/core/browser/signin_manager_base.h" 55#include "content/public/browser/browser_context.h" 56#include "content/public/browser/browser_thread.h" 57#include "content/public/browser/navigation_controller.h" 58#include "content/public/browser/navigation_entry.h" 59#include "content/public/browser/render_view_host.h" 60#include "content/public/browser/web_contents.h" 61#include "content/public/browser/web_contents_view.h" 62#include "content/public/browser/web_ui.h" 63#include "google_apis/gaia/oauth2_token_service.h" 64#include "printing/backend/print_backend.h" 65#include "printing/backend/print_backend_consts.h" 66#include "printing/metafile.h" 67#include "printing/metafile_impl.h" 68#include "printing/pdf_render_settings.h" 69#include "printing/print_settings.h" 70#include "printing/units.h" 71#include "third_party/icu/source/i18n/unicode/ulocdata.h" 72 73#if defined(OS_CHROMEOS) 74#include "chrome/browser/chromeos/settings/device_oauth2_token_service.h" 75#include "chrome/browser/chromeos/settings/device_oauth2_token_service_factory.h" 76#endif 77 78#if defined(ENABLE_SERVICE_DISCOVERY) 79#include "chrome/browser/local_discovery/privet_constants.h" 80#endif 81 82using content::BrowserThread; 83using content::RenderViewHost; 84using content::WebContents; 85 86namespace { 87 88enum 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 PRINT_WITH_PRIVET, 99 USERACTION_BUCKET_BOUNDARY 100}; 101 102enum PrintSettingsBuckets { 103 LANDSCAPE = 0, 104 PORTRAIT, 105 COLOR, 106 BLACK_AND_WHITE, 107 COLLATE, 108 SIMPLEX, 109 DUPLEX, 110 TOTAL, 111 HEADERS_AND_FOOTERS, 112 CSS_BACKGROUND, 113 SELECTION_ONLY, 114 PRINT_SETTINGS_BUCKET_BOUNDARY 115}; 116 117enum UiBucketGroups { 118 DESTINATION_SEARCH, 119 GCP_PROMO, 120 UI_BUCKET_GROUP_BOUNDARY 121}; 122 123enum PrintDestinationBuckets { 124 DESTINATION_SHOWN, 125 DESTINATION_CLOSED_CHANGED, 126 DESTINATION_CLOSED_UNCHANGED, 127 SIGNIN_PROMPT, 128 SIGNIN_TRIGGERED, 129 PRIVET_DUPLICATE_SELECTED, 130 CLOUD_DUPLICATE_SELECTED, 131 REGISTER_PROMO_SHOWN, 132 REGISTER_PROMO_SELECTED, 133 PRINT_DESTINATION_BUCKET_BOUNDARY 134}; 135 136enum GcpPromoBuckets { 137 PROMO_SHOWN, 138 PROMO_CLOSED, 139 PROMO_CLICKED, 140 GCP_PROMO_BUCKET_BOUNDARY 141}; 142 143void ReportUserActionHistogram(enum UserActionBuckets event) { 144 UMA_HISTOGRAM_ENUMERATION("PrintPreview.UserAction", event, 145 USERACTION_BUCKET_BOUNDARY); 146} 147 148void ReportPrintSettingHistogram(enum PrintSettingsBuckets setting) { 149 UMA_HISTOGRAM_ENUMERATION("PrintPreview.PrintSettings", setting, 150 PRINT_SETTINGS_BUCKET_BOUNDARY); 151} 152 153void ReportPrintDestinationHistogram(enum PrintDestinationBuckets event) { 154 UMA_HISTOGRAM_ENUMERATION("PrintPreview.DestinationAction", event, 155 PRINT_DESTINATION_BUCKET_BOUNDARY); 156} 157 158void ReportGcpPromoHistogram(enum GcpPromoBuckets event) { 159 UMA_HISTOGRAM_ENUMERATION("PrintPreview.GcpPromo", event, 160 GCP_PROMO_BUCKET_BOUNDARY); 161} 162 163// Name of a dictionary field holding cloud print related data; 164const char kAppState[] = "appState"; 165// Name of a dictionary field holding the initiator title. 166const char kInitiatorTitle[] = "initiatorTitle"; 167// Name of a dictionary field holding the measurement system according to the 168// locale. 169const char kMeasurementSystem[] = "measurementSystem"; 170// Name of a dictionary field holding the number format according to the locale. 171const char kNumberFormat[] = "numberFormat"; 172// Name of a dictionary field specifying whether to print automatically in 173// kiosk mode. See http://crbug.com/31395. 174const char kPrintAutomaticallyInKioskMode[] = "printAutomaticallyInKioskMode"; 175#if defined(OS_WIN) 176const char kHidePrintWithSystemDialogLink[] = "hidePrintWithSystemDialogLink"; 177#endif 178// Name of a dictionary field holding the state of selection for document. 179const char kDocumentHasSelection[] = "documentHasSelection"; 180 181// Additional printer capability setting keys. 182const char kPrinterId[] = "printerId"; 183const char kDisableColorOption[] = "disableColorOption"; 184const char kSetDuplexAsDefault[] = "setDuplexAsDefault"; 185const char kPrinterDefaultDuplexValue[] = "printerDefaultDuplexValue"; 186#if defined(USE_CUPS) 187const char kCUPSsColorModel[] = "cupsColorModel"; 188const char kCUPSsBWModel[] = "cupsBWModel"; 189#endif 190 191// Get the print job settings dictionary from |args|. The caller takes 192// ownership of the returned DictionaryValue. Returns NULL on failure. 193base::DictionaryValue* GetSettingsDictionary(const base::ListValue* args) { 194 std::string json_str; 195 if (!args->GetString(0, &json_str)) { 196 NOTREACHED() << "Could not read JSON argument"; 197 return NULL; 198 } 199 if (json_str.empty()) { 200 NOTREACHED() << "Empty print job settings"; 201 return NULL; 202 } 203 scoped_ptr<base::DictionaryValue> settings( 204 static_cast<base::DictionaryValue*>( 205 base::JSONReader::Read(json_str))); 206 if (!settings.get() || !settings->IsType(base::Value::TYPE_DICTIONARY)) { 207 NOTREACHED() << "Print job settings must be a dictionary."; 208 return NULL; 209 } 210 211 if (settings->empty()) { 212 NOTREACHED() << "Print job settings dictionary is empty"; 213 return NULL; 214 } 215 216 return settings.release(); 217} 218 219// Track the popularity of print settings and report the stats. 220void ReportPrintSettingsStats(const base::DictionaryValue& settings) { 221 ReportPrintSettingHistogram(TOTAL); 222 223 bool landscape = false; 224 if (settings.GetBoolean(printing::kSettingLandscape, &landscape)) 225 ReportPrintSettingHistogram(landscape ? LANDSCAPE : PORTRAIT); 226 227 bool collate = false; 228 if (settings.GetBoolean(printing::kSettingCollate, &collate) && collate) 229 ReportPrintSettingHistogram(COLLATE); 230 231 int duplex_mode = 0; 232 if (settings.GetInteger(printing::kSettingDuplexMode, &duplex_mode)) 233 ReportPrintSettingHistogram(duplex_mode ? DUPLEX : SIMPLEX); 234 235 int color_mode = 0; 236 if (settings.GetInteger(printing::kSettingColor, &color_mode)) { 237 ReportPrintSettingHistogram( 238 printing::IsColorModelSelected(color_mode) ? COLOR : BLACK_AND_WHITE); 239 } 240 241 bool headers = false; 242 if (settings.GetBoolean(printing::kSettingHeaderFooterEnabled, &headers) && 243 headers) { 244 ReportPrintSettingHistogram(HEADERS_AND_FOOTERS); 245 } 246 247 bool css_background = false; 248 if (settings.GetBoolean(printing::kSettingShouldPrintBackgrounds, 249 &css_background) && css_background) { 250 ReportPrintSettingHistogram(CSS_BACKGROUND); 251 } 252 253 bool selection_only = false; 254 if (settings.GetBoolean(printing::kSettingShouldPrintSelectionOnly, 255 &selection_only) && selection_only) { 256 ReportPrintSettingHistogram(SELECTION_ONLY); 257 } 258} 259 260// Callback that stores a PDF file on disk. 261void PrintToPdfCallback(printing::Metafile* metafile, 262 const base::FilePath& path) { 263 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 264 metafile->SaveTo(path); 265 // |metafile| must be deleted on the UI thread. 266 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, metafile); 267} 268 269std::string GetDefaultPrinterOnFileThread( 270 scoped_refptr<printing::PrintBackend> print_backend) { 271 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 272 273 std::string default_printer = print_backend->GetDefaultPrinterName(); 274 VLOG(1) << "Default Printer: " << default_printer; 275 return default_printer; 276} 277 278void EnumeratePrintersOnFileThread( 279 scoped_refptr<printing::PrintBackend> print_backend, 280 base::ListValue* printers) { 281 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 282 283 VLOG(1) << "Enumerate printers start"; 284 printing::PrinterList printer_list; 285 print_backend->EnumeratePrinters(&printer_list); 286 287 for (printing::PrinterList::iterator it = printer_list.begin(); 288 it != printer_list.end(); ++it) { 289 base::DictionaryValue* printer_info = new base::DictionaryValue; 290 printers->Append(printer_info); 291 std::string printer_name; 292 std::string printer_description; 293#if defined(OS_MACOSX) 294 // On Mac, |it->printer_description| specifies the printer name and 295 // |it->printer_name| specifies the device name / printer queue name. 296 printer_name = it->printer_description; 297 if (!it->options[kDriverNameTagName].empty()) 298 printer_description = it->options[kDriverNameTagName]; 299#else 300 printer_name = it->printer_name; 301 printer_description = it->printer_description; 302#endif 303 printer_info->SetString(printing::kSettingDeviceName, it->printer_name); 304 printer_info->SetString(printing::kSettingPrinterDescription, 305 printer_description); 306 printer_info->SetString(printing::kSettingPrinterName, printer_name); 307 VLOG(1) << "Found printer " << printer_name 308 << " with device name " << it->printer_name; 309 310 base::DictionaryValue* options = new base::DictionaryValue; 311 printer_info->Set(printing::kSettingPrinterOptions, options); 312 for (std::map<std::string, std::string>::iterator opt = it->options.begin(); 313 opt != it->options.end(); 314 ++opt) { 315 options->SetString(opt->first, opt->second); 316 } 317 318 VLOG(1) << "Found printer " << printer_name << " with device name " 319 << it->printer_name; 320 } 321 VLOG(1) << "Enumerate printers finished, found " << printers->GetSize() 322 << " printers"; 323} 324 325typedef base::Callback<void(const base::DictionaryValue*)> 326 GetPrinterCapabilitiesSuccessCallback; 327typedef base::Callback<void(const std::string&)> 328 GetPrinterCapabilitiesFailureCallback; 329 330void GetPrinterCapabilitiesOnFileThread( 331 scoped_refptr<printing::PrintBackend> print_backend, 332 const std::string& printer_name, 333 const GetPrinterCapabilitiesSuccessCallback& success_cb, 334 const GetPrinterCapabilitiesFailureCallback& failure_cb) { 335 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 336 DCHECK(!printer_name.empty()); 337 338 VLOG(1) << "Get printer capabilities start for " << printer_name; 339 crash_keys::ScopedPrinterInfo crash_key( 340 print_backend->GetPrinterDriverInfo(printer_name)); 341 342 if (!print_backend->IsValidPrinter(printer_name)) { 343 // TODO(gene): Notify explicitly if printer is not valid, instead of 344 // failed to get capabilities. 345 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 346 base::Bind(failure_cb, printer_name)); 347 return; 348 } 349 350 printing::PrinterSemanticCapsAndDefaults info; 351 if (!print_backend->GetPrinterSemanticCapsAndDefaults(printer_name, &info)) { 352 LOG(WARNING) << "Failed to get capabilities for " << printer_name; 353 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 354 base::Bind(failure_cb, printer_name)); 355 return; 356 } 357 358 scoped_ptr<base::DictionaryValue> settings_info(new base::DictionaryValue); 359 settings_info->SetString(kPrinterId, printer_name); 360 settings_info->SetBoolean(kDisableColorOption, !info.color_changeable); 361 settings_info->SetBoolean(printing::kSettingSetColorAsDefault, 362 info.color_default); 363#if defined(USE_CUPS) 364 settings_info->SetInteger(kCUPSsColorModel, info.color_model); 365 settings_info->SetInteger(kCUPSsBWModel, info.bw_model); 366#endif 367 368 // TODO(gene): Make new capabilities format for Print Preview 369 // that will suit semantic capabiltities better. 370 // Refactor pld API code below 371 bool default_duplex = info.duplex_capable ? 372 (info.duplex_default != printing::SIMPLEX) : false; 373 int duplex_value = info.duplex_capable ? 374 printing::LONG_EDGE : printing::UNKNOWN_DUPLEX_MODE; 375 settings_info->SetBoolean(kSetDuplexAsDefault, default_duplex); 376 settings_info->SetInteger(kPrinterDefaultDuplexValue, duplex_value); 377 378 BrowserThread::PostTask( 379 BrowserThread::UI, FROM_HERE, 380 base::Bind(success_cb, base::Owned(settings_info.release()))); 381} 382 383base::LazyInstance<printing::StickySettings> g_sticky_settings = 384 LAZY_INSTANCE_INITIALIZER; 385 386printing::StickySettings* GetStickySettings() { 387 return g_sticky_settings.Pointer(); 388} 389 390} // namespace 391 392#if defined(USE_CUPS) 393struct PrintPreviewHandler::CUPSPrinterColorModels { 394 std::string printer_name; 395 printing::ColorModel color_model; 396 printing::ColorModel bw_model; 397}; 398#endif 399 400class PrintPreviewHandler::AccessTokenService 401 : public OAuth2TokenService::Consumer { 402 public: 403 explicit AccessTokenService(PrintPreviewHandler* handler) 404 : OAuth2TokenService::Consumer("print_preview"), 405 handler_(handler) { 406 } 407 408 void RequestToken(const std::string& type) { 409 if (requests_.find(type) != requests_.end()) 410 return; // Already in progress. 411 412 OAuth2TokenService* service = NULL; 413 std::string account_id; 414 if (type == "profile") { 415 Profile* profile = Profile::FromWebUI(handler_->web_ui()); 416 if (profile) { 417 ProfileOAuth2TokenService* token_service = 418 ProfileOAuth2TokenServiceFactory::GetForProfile(profile); 419 SigninManagerBase* signin_manager = 420 SigninManagerFactory::GetInstance()->GetForProfile(profile); 421 account_id = signin_manager->GetAuthenticatedAccountId(); 422 service = token_service; 423 } 424 } else if (type == "device") { 425#if defined(OS_CHROMEOS) 426 chromeos::DeviceOAuth2TokenService* token_service = 427 chromeos::DeviceOAuth2TokenServiceFactory::Get(); 428 account_id = token_service->GetRobotAccountId(); 429 service = token_service; 430#endif 431 } 432 433 if (service) { 434 OAuth2TokenService::ScopeSet oauth_scopes; 435 oauth_scopes.insert(cloud_print::kCloudPrintAuth); 436 scoped_ptr<OAuth2TokenService::Request> request( 437 service->StartRequest(account_id, oauth_scopes, this)); 438 requests_[type].reset(request.release()); 439 } else { 440 handler_->SendAccessToken(type, std::string()); // Unknown type. 441 } 442 } 443 444 virtual void OnGetTokenSuccess(const OAuth2TokenService::Request* request, 445 const std::string& access_token, 446 const base::Time& expiration_time) OVERRIDE { 447 OnServiceResponce(request, access_token); 448 } 449 450 virtual void OnGetTokenFailure(const OAuth2TokenService::Request* request, 451 const GoogleServiceAuthError& error) OVERRIDE { 452 OnServiceResponce(request, std::string()); 453 } 454 455 private: 456 void OnServiceResponce(const OAuth2TokenService::Request* request, 457 const std::string& access_token) { 458 for (Requests::iterator i = requests_.begin(); i != requests_.end(); ++i) { 459 if (i->second == request) { 460 handler_->SendAccessToken(i->first, access_token); 461 requests_.erase(i); 462 return; 463 } 464 } 465 NOTREACHED(); 466 } 467 468 typedef std::map<std::string, 469 linked_ptr<OAuth2TokenService::Request> > Requests; 470 Requests requests_; 471 PrintPreviewHandler* handler_; 472 473 DISALLOW_COPY_AND_ASSIGN(AccessTokenService); 474}; 475 476PrintPreviewHandler::PrintPreviewHandler() 477 : print_backend_(printing::PrintBackend::CreateInstance(NULL)), 478 regenerate_preview_request_count_(0), 479 manage_printers_dialog_request_count_(0), 480 manage_cloud_printers_dialog_request_count_(0), 481 reported_failed_preview_(false), 482 has_logged_printers_count_(false), 483 weak_factory_(this) { 484 ReportUserActionHistogram(PREVIEW_STARTED); 485} 486 487PrintPreviewHandler::~PrintPreviewHandler() { 488 if (select_file_dialog_.get()) 489 select_file_dialog_->ListenerDestroyed(); 490} 491 492void PrintPreviewHandler::RegisterMessages() { 493 web_ui()->RegisterMessageCallback("getPrinters", 494 base::Bind(&PrintPreviewHandler::HandleGetPrinters, 495 base::Unretained(this))); 496 web_ui()->RegisterMessageCallback("getPreview", 497 base::Bind(&PrintPreviewHandler::HandleGetPreview, 498 base::Unretained(this))); 499 web_ui()->RegisterMessageCallback("print", 500 base::Bind(&PrintPreviewHandler::HandlePrint, 501 base::Unretained(this))); 502 web_ui()->RegisterMessageCallback("getPrinterCapabilities", 503 base::Bind(&PrintPreviewHandler::HandleGetPrinterCapabilities, 504 base::Unretained(this))); 505 web_ui()->RegisterMessageCallback("showSystemDialog", 506 base::Bind(&PrintPreviewHandler::HandleShowSystemDialog, 507 base::Unretained(this))); 508 web_ui()->RegisterMessageCallback("signIn", 509 base::Bind(&PrintPreviewHandler::HandleSignin, 510 base::Unretained(this))); 511 web_ui()->RegisterMessageCallback("getAccessToken", 512 base::Bind(&PrintPreviewHandler::HandleGetAccessToken, 513 base::Unretained(this))); 514 web_ui()->RegisterMessageCallback("manageCloudPrinters", 515 base::Bind(&PrintPreviewHandler::HandleManageCloudPrint, 516 base::Unretained(this))); 517 web_ui()->RegisterMessageCallback("manageLocalPrinters", 518 base::Bind(&PrintPreviewHandler::HandleManagePrinters, 519 base::Unretained(this))); 520 web_ui()->RegisterMessageCallback("closePrintPreviewDialog", 521 base::Bind(&PrintPreviewHandler::HandleClosePreviewDialog, 522 base::Unretained(this))); 523 web_ui()->RegisterMessageCallback("hidePreview", 524 base::Bind(&PrintPreviewHandler::HandleHidePreview, 525 base::Unretained(this))); 526 web_ui()->RegisterMessageCallback("cancelPendingPrintRequest", 527 base::Bind(&PrintPreviewHandler::HandleCancelPendingPrintRequest, 528 base::Unretained(this))); 529 web_ui()->RegisterMessageCallback("saveAppState", 530 base::Bind(&PrintPreviewHandler::HandleSaveAppState, 531 base::Unretained(this))); 532 web_ui()->RegisterMessageCallback("getInitialSettings", 533 base::Bind(&PrintPreviewHandler::HandleGetInitialSettings, 534 base::Unretained(this))); 535 web_ui()->RegisterMessageCallback("reportUiEvent", 536 base::Bind(&PrintPreviewHandler::HandleReportUiEvent, 537 base::Unretained(this))); 538 web_ui()->RegisterMessageCallback("printWithCloudPrintDialog", 539 base::Bind(&PrintPreviewHandler::HandlePrintWithCloudPrintDialog, 540 base::Unretained(this))); 541 web_ui()->RegisterMessageCallback("forceOpenNewTab", 542 base::Bind(&PrintPreviewHandler::HandleForceOpenNewTab, 543 base::Unretained(this))); 544 web_ui()->RegisterMessageCallback("getPrivetPrinters", 545 base::Bind(&PrintPreviewHandler::HandleGetPrivetPrinters, 546 base::Unretained(this))); 547 web_ui()->RegisterMessageCallback("stopGetPrivetPrinters", 548 base::Bind(&PrintPreviewHandler::HandleStopGetPrivetPrinters, 549 base::Unretained(this))); 550 web_ui()->RegisterMessageCallback("getPrivetPrinterCapabilities", 551 base::Bind(&PrintPreviewHandler::HandleGetPrivetPrinterCapabilities, 552 base::Unretained(this))); 553} 554 555bool PrintPreviewHandler::PrivetPrintingEnabled() { 556#if defined(ENABLE_SERVICE_DISCOVERY) 557 return !CommandLine::ForCurrentProcess()->HasSwitch( 558 switches::kDisableDeviceDiscovery); 559#else 560 return false; 561#endif 562} 563 564WebContents* PrintPreviewHandler::preview_web_contents() const { 565 return web_ui()->GetWebContents(); 566} 567 568void PrintPreviewHandler::HandleGetPrinters(const base::ListValue* /*args*/) { 569 base::ListValue* results = new base::ListValue; 570 BrowserThread::PostTaskAndReply( 571 BrowserThread::FILE, FROM_HERE, 572 base::Bind(&EnumeratePrintersOnFileThread, print_backend_, 573 base::Unretained(results)), 574 base::Bind(&PrintPreviewHandler::SetupPrinterList, 575 weak_factory_.GetWeakPtr(), 576 base::Owned(results))); 577} 578 579void PrintPreviewHandler::HandleGetPrivetPrinters(const base::ListValue* args) { 580#if defined(ENABLE_SERVICE_DISCOVERY) 581 if (PrivetPrintingEnabled()) { 582 Profile* profile = Profile::FromWebUI(web_ui()); 583 service_discovery_client_ = 584 local_discovery::ServiceDiscoverySharedClient::GetInstance(); 585 printer_lister_.reset(new local_discovery::PrivetLocalPrinterLister( 586 service_discovery_client_.get(), 587 profile->GetRequestContext(), 588 this)); 589 printer_lister_->Start(); 590 } 591#endif 592 593 if (!PrivetPrintingEnabled()) { 594 web_ui()->CallJavascriptFunction("onPrivetPrinterSearchDone"); 595 } 596} 597 598void PrintPreviewHandler::HandleStopGetPrivetPrinters( 599 const base::ListValue* args) { 600#if defined(ENABLE_SERVICE_DISCOVERY) 601 if (PrivetPrintingEnabled()) { 602 printer_lister_->Stop(); 603 } 604#endif 605} 606 607void PrintPreviewHandler::HandleGetPrivetPrinterCapabilities( 608 const base::ListValue* args) { 609#if defined(ENABLE_SERVICE_DISCOVERY) 610 std::string name; 611 bool success = args->GetString(0, &name); 612 DCHECK(success); 613 614 CreatePrivetHTTP( 615 name, 616 base::Bind(&PrintPreviewHandler::PrivetCapabilitiesUpdateClient, 617 base::Unretained(this))); 618#endif 619} 620 621void PrintPreviewHandler::HandleGetPreview(const base::ListValue* args) { 622 DCHECK_EQ(3U, args->GetSize()); 623 scoped_ptr<base::DictionaryValue> settings(GetSettingsDictionary(args)); 624 if (!settings.get()) 625 return; 626 int request_id = -1; 627 if (!settings->GetInteger(printing::kPreviewRequestID, &request_id)) 628 return; 629 630 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>( 631 web_ui()->GetController()); 632 print_preview_ui->OnPrintPreviewRequest(request_id); 633 // Add an additional key in order to identify |print_preview_ui| later on 634 // when calling PrintPreviewUI::GetCurrentPrintPreviewStatus() on the IO 635 // thread. 636 settings->SetInteger(printing::kPreviewUIID, 637 print_preview_ui->GetIDForPrintPreviewUI()); 638 639 // Increment request count. 640 ++regenerate_preview_request_count_; 641 642 WebContents* initiator = GetInitiator(); 643 if (!initiator) { 644 ReportUserActionHistogram(INITIATOR_CLOSED); 645 print_preview_ui->OnClosePrintPreviewDialog(); 646 return; 647 } 648 649 // Retrieve the page title and url and send it to the renderer process if 650 // headers and footers are to be displayed. 651 bool display_header_footer = false; 652 if (!settings->GetBoolean(printing::kSettingHeaderFooterEnabled, 653 &display_header_footer)) { 654 NOTREACHED(); 655 } 656 if (display_header_footer) { 657 settings->SetString(printing::kSettingHeaderFooterTitle, 658 initiator->GetTitle()); 659 std::string url; 660 content::NavigationEntry* entry = 661 initiator->GetController().GetLastCommittedEntry(); 662 if (entry) 663 url = entry->GetVirtualURL().spec(); 664 settings->SetString(printing::kSettingHeaderFooterURL, url); 665 } 666 667 bool generate_draft_data = false; 668 bool success = settings->GetBoolean(printing::kSettingGenerateDraftData, 669 &generate_draft_data); 670 DCHECK(success); 671 672 if (!generate_draft_data) { 673 double draft_page_count_double = -1; 674 success = args->GetDouble(1, &draft_page_count_double); 675 DCHECK(success); 676 int draft_page_count = static_cast<int>(draft_page_count_double); 677 678 bool preview_modifiable = false; 679 success = args->GetBoolean(2, &preview_modifiable); 680 DCHECK(success); 681 682 if (draft_page_count != -1 && preview_modifiable && 683 print_preview_ui->GetAvailableDraftPageCount() != draft_page_count) { 684 settings->SetBoolean(printing::kSettingGenerateDraftData, true); 685 } 686 } 687 688 VLOG(1) << "Print preview request start"; 689 RenderViewHost* rvh = initiator->GetRenderViewHost(); 690 rvh->Send(new PrintMsg_PrintPreview(rvh->GetRoutingID(), *settings)); 691} 692 693void PrintPreviewHandler::HandlePrint(const base::ListValue* args) { 694 ReportStats(); 695 696 // Record the number of times the user requests to regenerate preview data 697 // before printing. 698 UMA_HISTOGRAM_COUNTS("PrintPreview.RegeneratePreviewRequest.BeforePrint", 699 regenerate_preview_request_count_); 700 701 WebContents* initiator = GetInitiator(); 702 if (initiator) { 703 RenderViewHost* rvh = initiator->GetRenderViewHost(); 704 rvh->Send(new PrintMsg_ResetScriptedPrintCount(rvh->GetRoutingID())); 705 } 706 707 scoped_ptr<base::DictionaryValue> settings(GetSettingsDictionary(args)); 708 if (!settings.get()) 709 return; 710 711 // Never try to add headers/footers here. It's already in the generated PDF. 712 settings->SetBoolean(printing::kSettingHeaderFooterEnabled, false); 713 714 bool print_to_pdf = false; 715 bool is_cloud_printer = false; 716 bool print_with_privet = false; 717 718 bool open_pdf_in_preview = false; 719#if defined(OS_MACOSX) 720 open_pdf_in_preview = settings->HasKey(printing::kSettingOpenPDFInPreview); 721#endif 722 723 if (!open_pdf_in_preview) { 724 settings->GetBoolean(printing::kSettingPrintToPDF, &print_to_pdf); 725 settings->GetBoolean(printing::kSettingPrintWithPrivet, &print_with_privet); 726 is_cloud_printer = settings->HasKey(printing::kSettingCloudPrintId); 727 } 728 729 int page_count = 0; 730 settings->GetInteger(printing::kSettingPreviewPageCount, &page_count); 731 732 if (print_to_pdf) { 733 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToPDF", page_count); 734 ReportUserActionHistogram(PRINT_TO_PDF); 735 PrintToPdf(); 736 return; 737 } 738 739#if defined(ENABLE_SERVICE_DISCOVERY) 740 if (print_with_privet && PrivetPrintingEnabled()) { 741 std::string printer_name; 742 std::string print_ticket; 743 std::string capabilities; 744 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintWithPrivet", page_count); 745 ReportUserActionHistogram(PRINT_WITH_PRIVET); 746 747 int width = 0; 748 int height = 0; 749 if (!settings->GetString(printing::kSettingDeviceName, &printer_name) || 750 !settings->GetString(printing::kSettingTicket, &print_ticket) || 751 !settings->GetString(printing::kSettingCapabilities, &capabilities) || 752 !settings->GetInteger(printing::kSettingPageWidth, &width) || 753 !settings->GetInteger(printing::kSettingPageHeight, &height) || 754 width <= 0 || height <= 0) { 755 NOTREACHED(); 756 base::FundamentalValue http_code_value(-1); 757 web_ui()->CallJavascriptFunction("onPrivetPrintFailed", http_code_value); 758 return; 759 } 760 761 PrintToPrivetPrinter( 762 printer_name, print_ticket, capabilities, gfx::Size(width, height)); 763 return; 764 } 765#endif 766 767 scoped_refptr<base::RefCountedBytes> data; 768 base::string16 title; 769 if (!GetPreviewDataAndTitle(&data, &title)) { 770 // Nothing to print, no preview available. 771 return; 772 } 773 774 if (is_cloud_printer) { 775 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToCloudPrint", 776 page_count); 777 ReportUserActionHistogram(PRINT_WITH_CLOUD_PRINT); 778 SendCloudPrintJob(data.get()); 779 } else { 780 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToPrinter", page_count); 781 ReportUserActionHistogram(PRINT_TO_PRINTER); 782 ReportPrintSettingsStats(*settings); 783 784 // This tries to activate the initiator as well, so do not clear the 785 // association with the initiator yet. 786 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>( 787 web_ui()->GetController()); 788 print_preview_ui->OnHidePreviewDialog(); 789 790 // Do this so the initiator can open a new print preview dialog, while the 791 // current print preview dialog is still handling its print job. 792 ClearInitiatorDetails(); 793 794 // The PDF being printed contains only the pages that the user selected, 795 // so ignore the page range and print all pages. 796 settings->Remove(printing::kSettingPageRange, NULL); 797 // Reset selection only flag for the same reason. 798 settings->SetBoolean(printing::kSettingShouldPrintSelectionOnly, false); 799 800#if defined(USE_CUPS) 801 if (!open_pdf_in_preview) // We can get here even for cloud printers. 802 ConvertColorSettingToCUPSColorModel(settings.get()); 803#endif 804 805 // Set ID to know whether printing is for preview. 806 settings->SetInteger(printing::kPreviewUIID, 807 print_preview_ui->GetIDForPrintPreviewUI()); 808 RenderViewHost* rvh = preview_web_contents()->GetRenderViewHost(); 809 rvh->Send(new PrintMsg_PrintForPrintPreview(rvh->GetRoutingID(), 810 *settings)); 811 812 // For all other cases above, the preview dialog will stay open until the 813 // printing has finished. Then the dialog closes and PrintPreviewDone() gets 814 // called. In the case below, since the preview dialog will be hidden and 815 // not closed, we need to make this call. 816 if (initiator) { 817 printing::PrintViewManager* print_view_manager = 818 printing::PrintViewManager::FromWebContents(initiator); 819 print_view_manager->PrintPreviewDone(); 820 } 821 } 822} 823 824void PrintPreviewHandler::PrintToPdf() { 825 if (!print_to_pdf_path_.empty()) { 826 // User has already selected a path, no need to show the dialog again. 827 PostPrintToPdfTask(); 828 } else if (!select_file_dialog_.get() || 829 !select_file_dialog_->IsRunning(platform_util::GetTopLevel( 830 preview_web_contents()->GetView()->GetNativeView()))) { 831 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>( 832 web_ui()->GetController()); 833 // Pre-populating select file dialog with print job title. 834 base::string16 print_job_title_utf16 = print_preview_ui->initiator_title(); 835 836#if defined(OS_WIN) 837 base::FilePath::StringType print_job_title(print_job_title_utf16); 838#elif defined(OS_POSIX) 839 base::FilePath::StringType print_job_title = 840 base::UTF16ToUTF8(print_job_title_utf16); 841#endif 842 843 file_util::ReplaceIllegalCharactersInPath(&print_job_title, '_'); 844 base::FilePath default_filename(print_job_title); 845 default_filename = 846 default_filename.ReplaceExtension(FILE_PATH_LITERAL("pdf")); 847 848 SelectFile(default_filename); 849 } 850} 851 852void PrintPreviewHandler::HandleHidePreview(const base::ListValue* /*args*/) { 853 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>( 854 web_ui()->GetController()); 855 print_preview_ui->OnHidePreviewDialog(); 856} 857 858void PrintPreviewHandler::HandleCancelPendingPrintRequest( 859 const base::ListValue* /*args*/) { 860 WebContents* initiator = GetInitiator(); 861 if (initiator) 862 ClearInitiatorDetails(); 863 chrome::ShowPrintErrorDialog(); 864} 865 866void PrintPreviewHandler::HandleSaveAppState(const base::ListValue* args) { 867 std::string data_to_save; 868 printing::StickySettings* sticky_settings = GetStickySettings(); 869 if (args->GetString(0, &data_to_save) && !data_to_save.empty()) 870 sticky_settings->StoreAppState(data_to_save); 871 sticky_settings->SaveInPrefs(Profile::FromBrowserContext( 872 preview_web_contents()->GetBrowserContext())->GetPrefs()); 873} 874 875void PrintPreviewHandler::HandleGetPrinterCapabilities( 876 const base::ListValue* args) { 877 std::string printer_name; 878 bool ret = args->GetString(0, &printer_name); 879 if (!ret || printer_name.empty()) 880 return; 881 882 GetPrinterCapabilitiesSuccessCallback success_cb = 883 base::Bind(&PrintPreviewHandler::SendPrinterCapabilities, 884 weak_factory_.GetWeakPtr()); 885 GetPrinterCapabilitiesFailureCallback failure_cb = 886 base::Bind(&PrintPreviewHandler::SendFailedToGetPrinterCapabilities, 887 weak_factory_.GetWeakPtr()); 888 BrowserThread::PostTask( 889 BrowserThread::FILE, FROM_HERE, 890 base::Bind(&GetPrinterCapabilitiesOnFileThread, 891 print_backend_, printer_name, success_cb, failure_cb)); 892} 893 894void PrintPreviewHandler::OnSigninComplete() { 895 PrintPreviewUI* print_preview_ui = 896 static_cast<PrintPreviewUI*>(web_ui()->GetController()); 897 if (print_preview_ui) 898 print_preview_ui->OnReloadPrintersList(); 899} 900 901void PrintPreviewHandler::HandleSignin(const base::ListValue* args) { 902 bool add_account = false; 903 bool success = args->GetBoolean(0, &add_account); 904 DCHECK(success); 905 906 Profile* profile = Profile::FromBrowserContext( 907 preview_web_contents()->GetBrowserContext()); 908 chrome::ScopedTabbedBrowserDisplayer displayer( 909 profile, chrome::GetActiveDesktop()); 910 print_dialog_cloud::CreateCloudPrintSigninTab( 911 displayer.browser(), 912 add_account, 913 base::Bind(&PrintPreviewHandler::OnSigninComplete, 914 weak_factory_.GetWeakPtr())); 915} 916 917void PrintPreviewHandler::HandleGetAccessToken(const base::ListValue* args) { 918 std::string type; 919 if (!args->GetString(0, &type)) 920 return; 921 if (!token_service_) 922 token_service_.reset(new AccessTokenService(this)); 923 token_service_->RequestToken(type); 924} 925 926void PrintPreviewHandler::PrintWithCloudPrintDialog() { 927 // Record the number of times the user asks to print via cloud print 928 // instead of the print preview dialog. 929 ReportStats(); 930 931 scoped_refptr<base::RefCountedBytes> data; 932 base::string16 title; 933 if (!GetPreviewDataAndTitle(&data, &title)) { 934 // Nothing to print, no preview available. 935 return; 936 } 937 938 gfx::NativeWindow modal_parent = platform_util::GetTopLevel( 939 preview_web_contents()->GetView()->GetNativeView()); 940 print_dialog_cloud::CreatePrintDialogForBytes( 941 preview_web_contents()->GetBrowserContext(), 942 modal_parent, 943 data.get(), 944 title, 945 base::string16(), 946 std::string("application/pdf")); 947 948 // Once the cloud print dialog comes up we're no longer in a background 949 // printing situation. Close the print preview. 950 // TODO(abodenha@chromium.org) The flow should be changed as described in 951 // http://code.google.com/p/chromium/issues/detail?id=44093 952 ClosePreviewDialog(); 953} 954 955void PrintPreviewHandler::HandleManageCloudPrint( 956 const base::ListValue* /*args*/) { 957 ++manage_cloud_printers_dialog_request_count_; 958 Profile* profile = Profile::FromBrowserContext( 959 preview_web_contents()->GetBrowserContext()); 960 preview_web_contents()->OpenURL( 961 content::OpenURLParams( 962 CloudPrintURL(profile).GetCloudPrintServiceManageURL(), 963 content::Referrer(), 964 NEW_FOREGROUND_TAB, 965 content::PAGE_TRANSITION_LINK, 966 false)); 967} 968 969void PrintPreviewHandler::HandleShowSystemDialog( 970 const base::ListValue* /*args*/) { 971 ReportStats(); 972 ReportUserActionHistogram(FALLBACK_TO_ADVANCED_SETTINGS_DIALOG); 973 974 WebContents* initiator = GetInitiator(); 975 if (!initiator) 976 return; 977 978 printing::PrintViewManager* print_view_manager = 979 printing::PrintViewManager::FromWebContents(initiator); 980 print_view_manager->set_observer(this); 981 print_view_manager->PrintForSystemDialogNow(); 982 983 // Cancel the pending preview request if exists. 984 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>( 985 web_ui()->GetController()); 986 print_preview_ui->OnCancelPendingPreviewRequest(); 987} 988 989void PrintPreviewHandler::HandleManagePrinters( 990 const base::ListValue* /*args*/) { 991 ++manage_printers_dialog_request_count_; 992 printing::PrinterManagerDialog::ShowPrinterManagerDialog(); 993} 994 995void PrintPreviewHandler::HandlePrintWithCloudPrintDialog( 996 const base::ListValue* args) { 997 int page_count = 0; 998 if (!args || !args->GetInteger(0, &page_count)) 999 return; 1000 UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToCloudPrintWebDialog", 1001 page_count); 1002 1003 PrintWithCloudPrintDialog(); 1004} 1005 1006void PrintPreviewHandler::HandleClosePreviewDialog( 1007 const base::ListValue* /*args*/) { 1008 ReportStats(); 1009 ReportUserActionHistogram(CANCEL); 1010 1011 // Record the number of times the user requests to regenerate preview data 1012 // before cancelling. 1013 UMA_HISTOGRAM_COUNTS("PrintPreview.RegeneratePreviewRequest.BeforeCancel", 1014 regenerate_preview_request_count_); 1015} 1016 1017void PrintPreviewHandler::ReportStats() { 1018 UMA_HISTOGRAM_COUNTS("PrintPreview.ManagePrinters", 1019 manage_printers_dialog_request_count_); 1020 UMA_HISTOGRAM_COUNTS("PrintPreview.ManageCloudPrinters", 1021 manage_cloud_printers_dialog_request_count_); 1022} 1023 1024void PrintPreviewHandler::GetNumberFormatAndMeasurementSystem( 1025 base::DictionaryValue* settings) { 1026 1027 // Getting the measurement system based on the locale. 1028 UErrorCode errorCode = U_ZERO_ERROR; 1029 const char* locale = g_browser_process->GetApplicationLocale().c_str(); 1030 UMeasurementSystem system = ulocdata_getMeasurementSystem(locale, &errorCode); 1031 if (errorCode > U_ZERO_ERROR || system == UMS_LIMIT) 1032 system = UMS_SI; 1033 1034 // Getting the number formatting based on the locale and writing to 1035 // dictionary. 1036 settings->SetString(kNumberFormat, base::FormatDouble(123456.78, 2)); 1037 settings->SetInteger(kMeasurementSystem, system); 1038} 1039 1040void PrintPreviewHandler::HandleGetInitialSettings( 1041 const base::ListValue* /*args*/) { 1042 // Send before SendInitialSettings to allow cloud printer auto select. 1043 SendCloudPrintEnabled(); 1044 BrowserThread::PostTaskAndReplyWithResult( 1045 BrowserThread::FILE, FROM_HERE, 1046 base::Bind(&GetDefaultPrinterOnFileThread, print_backend_), 1047 base::Bind(&PrintPreviewHandler::SendInitialSettings, 1048 weak_factory_.GetWeakPtr())); 1049} 1050 1051void PrintPreviewHandler::HandleReportUiEvent(const base::ListValue* args) { 1052 int event_group, event_number; 1053 if (!args->GetInteger(0, &event_group) || !args->GetInteger(1, &event_number)) 1054 return; 1055 1056 enum UiBucketGroups ui_bucket_group = 1057 static_cast<enum UiBucketGroups>(event_group); 1058 if (ui_bucket_group >= UI_BUCKET_GROUP_BOUNDARY) 1059 return; 1060 1061 switch (ui_bucket_group) { 1062 case DESTINATION_SEARCH: { 1063 enum PrintDestinationBuckets event = 1064 static_cast<enum PrintDestinationBuckets>(event_number); 1065 if (event >= PRINT_DESTINATION_BUCKET_BOUNDARY) 1066 return; 1067 ReportPrintDestinationHistogram(event); 1068 break; 1069 } 1070 case GCP_PROMO: { 1071 enum GcpPromoBuckets event = 1072 static_cast<enum GcpPromoBuckets>(event_number); 1073 if (event >= GCP_PROMO_BUCKET_BOUNDARY) 1074 return; 1075 ReportGcpPromoHistogram(event); 1076 break; 1077 } 1078 default: 1079 break; 1080 } 1081} 1082 1083void PrintPreviewHandler::HandleForceOpenNewTab(const base::ListValue* args) { 1084 std::string url; 1085 if (!args->GetString(0, &url)) 1086 return; 1087 Browser* browser = chrome::FindBrowserWithWebContents(GetInitiator()); 1088 if (!browser) 1089 return; 1090 chrome::AddSelectedTabWithURL(browser, 1091 GURL(url), 1092 content::PAGE_TRANSITION_LINK); 1093} 1094 1095void PrintPreviewHandler::SendInitialSettings( 1096 const std::string& default_printer) { 1097 PrintPreviewUI* print_preview_ui = 1098 static_cast<PrintPreviewUI*>(web_ui()->GetController()); 1099 1100 base::DictionaryValue initial_settings; 1101 initial_settings.SetString(kInitiatorTitle, 1102 print_preview_ui->initiator_title()); 1103 initial_settings.SetBoolean(printing::kSettingPreviewModifiable, 1104 print_preview_ui->source_is_modifiable()); 1105 initial_settings.SetString(printing::kSettingPrinterName, default_printer); 1106 initial_settings.SetBoolean(kDocumentHasSelection, 1107 print_preview_ui->source_has_selection()); 1108 initial_settings.SetBoolean(printing::kSettingShouldPrintSelectionOnly, 1109 print_preview_ui->print_selection_only()); 1110 printing::StickySettings* sticky_settings = GetStickySettings(); 1111 sticky_settings->RestoreFromPrefs(Profile::FromBrowserContext( 1112 preview_web_contents()->GetBrowserContext())->GetPrefs()); 1113 if (sticky_settings->printer_app_state()) 1114 initial_settings.SetString(kAppState, 1115 *sticky_settings->printer_app_state()); 1116 1117 CommandLine* cmdline = CommandLine::ForCurrentProcess(); 1118 initial_settings.SetBoolean(kPrintAutomaticallyInKioskMode, 1119 cmdline->HasSwitch(switches::kKioskModePrinting)); 1120#if defined(OS_WIN) 1121 // In Win8 metro, the system print dialog can only open on the desktop. Doing 1122 // so will cause the browser to appear hung, so we don't show the link in 1123 // metro. 1124 bool is_ash = (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_ASH); 1125 initial_settings.SetBoolean(kHidePrintWithSystemDialogLink, is_ash); 1126#endif 1127 1128 if (print_preview_ui->source_is_modifiable()) 1129 GetNumberFormatAndMeasurementSystem(&initial_settings); 1130 web_ui()->CallJavascriptFunction("setInitialSettings", initial_settings); 1131} 1132 1133void PrintPreviewHandler::ClosePreviewDialog() { 1134 PrintPreviewUI* print_preview_ui = 1135 static_cast<PrintPreviewUI*>(web_ui()->GetController()); 1136 print_preview_ui->OnClosePrintPreviewDialog(); 1137} 1138 1139void PrintPreviewHandler::SendAccessToken(const std::string& type, 1140 const std::string& access_token) { 1141 VLOG(1) << "Get getAccessToken finished"; 1142 web_ui()->CallJavascriptFunction("onDidGetAccessToken", 1143 base::StringValue(type), 1144 base::StringValue(access_token)); 1145} 1146 1147void PrintPreviewHandler::SendPrinterCapabilities( 1148 const base::DictionaryValue* settings_info) { 1149 VLOG(1) << "Get printer capabilities finished"; 1150 1151#if defined(USE_CUPS) 1152 SaveCUPSColorSetting(settings_info); 1153#endif 1154 1155 web_ui()->CallJavascriptFunction("updateWithPrinterCapabilities", 1156 *settings_info); 1157} 1158 1159void PrintPreviewHandler::SendFailedToGetPrinterCapabilities( 1160 const std::string& printer_name) { 1161 VLOG(1) << "Get printer capabilities failed"; 1162 base::StringValue printer_name_value(printer_name); 1163 web_ui()->CallJavascriptFunction("failedToGetPrinterCapabilities", 1164 printer_name_value); 1165} 1166 1167void PrintPreviewHandler::SetupPrinterList(const base::ListValue* printers) { 1168 if (!has_logged_printers_count_) { 1169 UMA_HISTOGRAM_COUNTS("PrintPreview.NumberOfPrinters", printers->GetSize()); 1170 has_logged_printers_count_ = true; 1171 } 1172 1173 web_ui()->CallJavascriptFunction("setPrinters", *printers); 1174} 1175 1176void PrintPreviewHandler::SendCloudPrintEnabled() { 1177 Profile* profile = Profile::FromBrowserContext( 1178 preview_web_contents()->GetBrowserContext()); 1179 PrefService* prefs = profile->GetPrefs(); 1180 if (prefs->GetBoolean(prefs::kCloudPrintSubmitEnabled)) { 1181 GURL gcp_url(CloudPrintURL(profile).GetCloudPrintServiceURL()); 1182 base::StringValue gcp_url_value(gcp_url.spec()); 1183 web_ui()->CallJavascriptFunction("setUseCloudPrint", gcp_url_value); 1184 } 1185} 1186 1187void PrintPreviewHandler::SendCloudPrintJob(const base::RefCountedBytes* data) { 1188 // BASE64 encode the job data. 1189 std::string raw_data(reinterpret_cast<const char*>(data->front()), 1190 data->size()); 1191 std::string base64_data; 1192 base::Base64Encode(raw_data, &base64_data); 1193 base::StringValue data_value(base64_data); 1194 1195 web_ui()->CallJavascriptFunction("printToCloud", data_value); 1196} 1197 1198WebContents* PrintPreviewHandler::GetInitiator() const { 1199 printing::PrintPreviewDialogController* dialog_controller = 1200 printing::PrintPreviewDialogController::GetInstance(); 1201 if (!dialog_controller) 1202 return NULL; 1203 return dialog_controller->GetInitiator(preview_web_contents()); 1204} 1205 1206void PrintPreviewHandler::OnPrintDialogShown() { 1207 ClosePreviewDialog(); 1208} 1209 1210void PrintPreviewHandler::SelectFile(const base::FilePath& default_filename) { 1211 ui::SelectFileDialog::FileTypeInfo file_type_info; 1212 file_type_info.extensions.resize(1); 1213 file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("pdf")); 1214 1215 // Initializing |save_path_| if it is not already initialized. 1216 printing::StickySettings* sticky_settings = GetStickySettings(); 1217 if (!sticky_settings->save_path()) { 1218 // Allowing IO operation temporarily. It is ok to do so here because 1219 // the select file dialog performs IO anyway in order to display the 1220 // folders and also it is modal. 1221 base::ThreadRestrictions::ScopedAllowIO allow_io; 1222 base::FilePath file_path; 1223 PathService::Get(chrome::DIR_USER_DOCUMENTS, &file_path); 1224 sticky_settings->StoreSavePath(file_path); 1225 sticky_settings->SaveInPrefs(Profile::FromBrowserContext( 1226 preview_web_contents()->GetBrowserContext())->GetPrefs()); 1227 } 1228 1229 select_file_dialog_ = ui::SelectFileDialog::Create( 1230 this, new ChromeSelectFilePolicy(preview_web_contents())), 1231 select_file_dialog_->SelectFile( 1232 ui::SelectFileDialog::SELECT_SAVEAS_FILE, 1233 base::string16(), 1234 sticky_settings->save_path()->Append(default_filename), 1235 &file_type_info, 1236 0, 1237 base::FilePath::StringType(), 1238 platform_util::GetTopLevel( 1239 preview_web_contents()->GetView()->GetNativeView()), 1240 NULL); 1241} 1242 1243void PrintPreviewHandler::OnPrintPreviewDialogDestroyed() { 1244 WebContents* initiator = GetInitiator(); 1245 if (!initiator) 1246 return; 1247 1248 printing::PrintViewManager* print_view_manager = 1249 printing::PrintViewManager::FromWebContents(initiator); 1250 print_view_manager->set_observer(NULL); 1251} 1252 1253void PrintPreviewHandler::OnPrintPreviewFailed() { 1254 if (reported_failed_preview_) 1255 return; 1256 reported_failed_preview_ = true; 1257 ReportUserActionHistogram(PREVIEW_FAILED); 1258} 1259 1260void PrintPreviewHandler::ShowSystemDialog() { 1261 HandleShowSystemDialog(NULL); 1262} 1263 1264void PrintPreviewHandler::FileSelected(const base::FilePath& path, 1265 int index, void* params) { 1266 // Updating |save_path_| to the newly selected folder. 1267 printing::StickySettings* sticky_settings = GetStickySettings(); 1268 sticky_settings->StoreSavePath(path.DirName()); 1269 sticky_settings->SaveInPrefs(Profile::FromBrowserContext( 1270 preview_web_contents()->GetBrowserContext())->GetPrefs()); 1271 web_ui()->CallJavascriptFunction("fileSelectionCompleted"); 1272 print_to_pdf_path_ = path; 1273 PostPrintToPdfTask(); 1274} 1275 1276void PrintPreviewHandler::PostPrintToPdfTask() { 1277 scoped_refptr<base::RefCountedBytes> data; 1278 base::string16 title; 1279 if (!GetPreviewDataAndTitle(&data, &title)) { 1280 NOTREACHED() << "Preview data was checked before file dialog."; 1281 return; 1282 } 1283 scoped_ptr<printing::PreviewMetafile> metafile(new printing::PreviewMetafile); 1284 metafile->InitFromData(static_cast<const void*>(data->front()), data->size()); 1285 BrowserThread::PostTask( 1286 BrowserThread::FILE, FROM_HERE, 1287 base::Bind(&PrintToPdfCallback, metafile.release(), print_to_pdf_path_)); 1288 print_to_pdf_path_ = base::FilePath(); 1289 ClosePreviewDialog(); 1290} 1291 1292void PrintPreviewHandler::FileSelectionCanceled(void* params) { 1293 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>( 1294 web_ui()->GetController()); 1295 print_preview_ui->OnFileSelectionCancelled(); 1296} 1297 1298void PrintPreviewHandler::ClearInitiatorDetails() { 1299 WebContents* initiator = GetInitiator(); 1300 if (!initiator) 1301 return; 1302 1303 // We no longer require the initiator details. Remove those details associated 1304 // with the preview dialog to allow the initiator to create another preview 1305 // dialog. 1306 printing::PrintPreviewDialogController* dialog_controller = 1307 printing::PrintPreviewDialogController::GetInstance(); 1308 if (dialog_controller) 1309 dialog_controller->EraseInitiatorInfo(preview_web_contents()); 1310} 1311 1312bool PrintPreviewHandler::GetPreviewDataAndTitle( 1313 scoped_refptr<base::RefCountedBytes>* data, 1314 base::string16* title) const { 1315 PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>( 1316 web_ui()->GetController()); 1317 scoped_refptr<base::RefCountedBytes> tmp_data; 1318 print_preview_ui->GetPrintPreviewDataForIndex( 1319 printing::COMPLETE_PREVIEW_DOCUMENT_INDEX, &tmp_data); 1320 1321 if (!tmp_data.get()) { 1322 // Nothing to print, no preview available. 1323 return false; 1324 } 1325 DCHECK(tmp_data->size() && tmp_data->front()); 1326 1327 *data = tmp_data; 1328 *title = print_preview_ui->initiator_title(); 1329 return true; 1330} 1331 1332#if defined(USE_CUPS) 1333void PrintPreviewHandler::SaveCUPSColorSetting( 1334 const base::DictionaryValue* settings) { 1335 cups_printer_color_models_.reset(new CUPSPrinterColorModels); 1336 settings->GetString(kPrinterId, &cups_printer_color_models_->printer_name); 1337 settings->GetInteger( 1338 kCUPSsColorModel, 1339 reinterpret_cast<int*>(&cups_printer_color_models_->color_model)); 1340 settings->GetInteger( 1341 kCUPSsBWModel, 1342 reinterpret_cast<int*>(&cups_printer_color_models_->bw_model)); 1343} 1344 1345void PrintPreviewHandler::ConvertColorSettingToCUPSColorModel( 1346 base::DictionaryValue* settings) const { 1347 if (!cups_printer_color_models_) 1348 return; 1349 1350 // Sanity check the printer name. 1351 std::string printer_name; 1352 if (!settings->GetString(printing::kSettingDeviceName, &printer_name) || 1353 printer_name != cups_printer_color_models_->printer_name) { 1354 NOTREACHED(); 1355 return; 1356 } 1357 1358 int color; 1359 if (!settings->GetInteger(printing::kSettingColor, &color)) { 1360 NOTREACHED(); 1361 return; 1362 } 1363 1364 if (color == printing::GRAY) { 1365 if (cups_printer_color_models_->bw_model != printing::UNKNOWN_COLOR_MODEL) { 1366 settings->SetInteger(printing::kSettingColor, 1367 cups_printer_color_models_->bw_model); 1368 } 1369 return; 1370 } 1371 1372 printing::ColorModel color_model = cups_printer_color_models_->color_model; 1373 if (color_model != printing::UNKNOWN_COLOR_MODEL) 1374 settings->SetInteger(printing::kSettingColor, color_model); 1375} 1376 1377#endif 1378 1379#if defined(ENABLE_SERVICE_DISCOVERY) 1380void PrintPreviewHandler::LocalPrinterChanged( 1381 bool added, 1382 const std::string& name, 1383 bool has_local_printing, 1384 const local_discovery::DeviceDescription& description) { 1385 CommandLine* command_line = CommandLine::ForCurrentProcess(); 1386 if (has_local_printing || 1387 command_line->HasSwitch(switches::kEnablePrintPreviewRegisterPromos)) { 1388 base::DictionaryValue info; 1389 FillPrinterDescription(name, description, has_local_printing, &info); 1390 web_ui()->CallJavascriptFunction("onPrivetPrinterChanged", info); 1391 } 1392} 1393 1394void PrintPreviewHandler::LocalPrinterRemoved(const std::string& name) { 1395} 1396 1397void PrintPreviewHandler::LocalPrinterCacheFlushed() { 1398} 1399 1400void PrintPreviewHandler::PrivetCapabilitiesUpdateClient( 1401 scoped_ptr<local_discovery::PrivetHTTPClient> http_client) { 1402 if (!PrivetUpdateClient(http_client.Pass())) 1403 return; 1404 1405 privet_capabilities_operation_ = 1406 privet_http_client_->CreateCapabilitiesOperation( 1407 base::Bind(&PrintPreviewHandler::OnPrivetCapabilities, 1408 base::Unretained(this))); 1409 privet_capabilities_operation_->Start(); 1410} 1411 1412bool PrintPreviewHandler::PrivetUpdateClient( 1413 scoped_ptr<local_discovery::PrivetHTTPClient> http_client) { 1414 if (!http_client) { 1415 SendPrivetCapabilitiesError(privet_http_resolution_->GetName()); 1416 privet_http_resolution_.reset(); 1417 return false; 1418 } 1419 1420 privet_local_print_operation_.reset(); 1421 privet_capabilities_operation_.reset(); 1422 privet_http_client_ = http_client.Pass(); 1423 1424 privet_http_resolution_.reset(); 1425 1426 return true; 1427} 1428 1429void PrintPreviewHandler::PrivetLocalPrintUpdateClient( 1430 std::string print_ticket, 1431 std::string capabilities, 1432 gfx::Size page_size, 1433 scoped_ptr<local_discovery::PrivetHTTPClient> http_client) { 1434 if (!PrivetUpdateClient(http_client.Pass())) 1435 return; 1436 1437 StartPrivetLocalPrint(print_ticket, capabilities, page_size); 1438} 1439 1440void PrintPreviewHandler::StartPrivetLocalPrint(const std::string& print_ticket, 1441 const std::string& capabilities, 1442 const gfx::Size& page_size) { 1443 privet_local_print_operation_ = 1444 privet_http_client_->CreateLocalPrintOperation(this); 1445 1446 privet_local_print_operation_->SetTicket(print_ticket); 1447 privet_local_print_operation_->SetCapabilities(capabilities); 1448 1449 scoped_refptr<base::RefCountedBytes> data; 1450 base::string16 title; 1451 1452 if (!GetPreviewDataAndTitle(&data, &title)) { 1453 base::FundamentalValue http_code_value(-1); 1454 web_ui()->CallJavascriptFunction("onPrivetPrintFailed", http_code_value); 1455 return; 1456 } 1457 1458 privet_local_print_operation_->SetJobname(base::UTF16ToUTF8(title)); 1459 privet_local_print_operation_->SetPageSize(page_size); 1460 privet_local_print_operation_->SetData(data); 1461 1462 Profile* profile = Profile::FromWebUI(web_ui()); 1463 SigninManagerBase* signin_manager = 1464 SigninManagerFactory::GetForProfileIfExists(profile); 1465 1466 if (signin_manager) { 1467 privet_local_print_operation_->SetUsername( 1468 signin_manager->GetAuthenticatedUsername()); 1469 } 1470 1471 privet_local_print_operation_->Start(); 1472} 1473 1474 1475void PrintPreviewHandler::OnPrivetCapabilities( 1476 const base::DictionaryValue* capabilities) { 1477 std::string name = privet_capabilities_operation_->GetHTTPClient()->GetName(); 1478 1479 if (!capabilities || capabilities->HasKey(local_discovery::kPrivetKeyError)) { 1480 SendPrivetCapabilitiesError(name); 1481 return; 1482 } 1483 1484 base::DictionaryValue printer_info; 1485 const local_discovery::DeviceDescription* description = 1486 printer_lister_->GetDeviceDescription(name); 1487 1488 if (!description) { 1489 SendPrivetCapabilitiesError(name); 1490 return; 1491 } 1492 1493 FillPrinterDescription(name, *description, true, &printer_info); 1494 1495 web_ui()->CallJavascriptFunction( 1496 "onPrivetCapabilitiesSet", 1497 printer_info, 1498 *capabilities); 1499 1500 privet_capabilities_operation_.reset(); 1501} 1502 1503void PrintPreviewHandler::SendPrivetCapabilitiesError( 1504 const std::string& device_name) { 1505 base::StringValue name_value(device_name); 1506 web_ui()->CallJavascriptFunction( 1507 "failedToGetPrivetPrinterCapabilities", 1508 name_value); 1509} 1510 1511void PrintPreviewHandler::PrintToPrivetPrinter(const std::string& device_name, 1512 const std::string& ticket, 1513 const std::string& capabilities, 1514 const gfx::Size& page_size) { 1515 CreatePrivetHTTP( 1516 device_name, 1517 base::Bind(&PrintPreviewHandler::PrivetLocalPrintUpdateClient, 1518 base::Unretained(this), 1519 ticket, 1520 capabilities, 1521 page_size)); 1522} 1523 1524bool PrintPreviewHandler::CreatePrivetHTTP( 1525 const std::string& name, 1526 const local_discovery::PrivetHTTPAsynchronousFactory::ResultCallback& 1527 callback) { 1528 const local_discovery::DeviceDescription* device_description = 1529 printer_lister_->GetDeviceDescription(name); 1530 1531 if (!device_description) { 1532 SendPrivetCapabilitiesError(name); 1533 return false; 1534 } 1535 1536 privet_http_factory_ = 1537 local_discovery::PrivetHTTPAsynchronousFactory::CreateInstance( 1538 service_discovery_client_, 1539 Profile::FromWebUI(web_ui())->GetRequestContext()); 1540 privet_http_resolution_ = privet_http_factory_->CreatePrivetHTTP( 1541 name, 1542 device_description->address, 1543 callback); 1544 privet_http_resolution_->Start(); 1545 1546 return true; 1547} 1548 1549void PrintPreviewHandler::OnPrivetPrintingDone( 1550 const local_discovery::PrivetLocalPrintOperation* print_operation) { 1551 ClosePreviewDialog(); 1552} 1553 1554void PrintPreviewHandler::OnPrivetPrintingError( 1555 const local_discovery::PrivetLocalPrintOperation* print_operation, 1556 int http_code) { 1557 base::FundamentalValue http_code_value(http_code); 1558 web_ui()->CallJavascriptFunction("onPrivetPrintFailed", http_code_value); 1559} 1560 1561void PrintPreviewHandler::FillPrinterDescription( 1562 const std::string& name, 1563 const local_discovery::DeviceDescription& description, 1564 bool has_local_printing, 1565 base::DictionaryValue* printer_value) { 1566 CommandLine* command_line = CommandLine::ForCurrentProcess(); 1567 1568 printer_value->SetString("serviceName", name); 1569 printer_value->SetString("name", description.name); 1570 printer_value->SetBoolean("hasLocalPrinting", has_local_printing); 1571 printer_value->SetBoolean( 1572 "isUnregistered", 1573 description.id.empty() && 1574 command_line->HasSwitch(switches::kEnablePrintPreviewRegisterPromos)); 1575 printer_value->SetString("cloudID", description.id); 1576} 1577 1578#endif 1579