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