local_discovery_ui_handler.cc revision e5d81f57cb97b3b6b7fccc9c5610d21eb81db09d
1// Copyright 2013 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/local_discovery/local_discovery_ui_handler.h" 6 7#include <set> 8 9#include "base/bind.h" 10#include "base/command_line.h" 11#include "base/message_loop/message_loop.h" 12#include "base/prefs/pref_service.h" 13#include "base/strings/stringprintf.h" 14#include "base/strings/utf_string_conversions.h" 15#include "base/values.h" 16#include "chrome/browser/local_discovery/privet_confirm_api_flow.h" 17#include "chrome/browser/local_discovery/privet_constants.h" 18#include "chrome/browser/local_discovery/privet_device_lister_impl.h" 19#include "chrome/browser/local_discovery/privet_http_asynchronous_factory.h" 20#include "chrome/browser/local_discovery/privet_http_impl.h" 21#include "chrome/browser/local_discovery/service_discovery_shared_client.h" 22#include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h" 23#include "chrome/browser/printing/cloud_print/cloud_print_proxy_service_factory.h" 24#include "chrome/browser/printing/cloud_print/cloud_print_url.h" 25#include "chrome/browser/profiles/profile.h" 26#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 27#include "chrome/browser/signin/signin_manager_factory.h" 28#include "chrome/browser/signin/signin_promo.h" 29#include "chrome/browser/ui/browser_finder.h" 30#include "chrome/browser/ui/browser_tabstrip.h" 31#include "chrome/common/chrome_switches.h" 32#include "chrome/common/pref_names.h" 33#include "components/signin/core/browser/profile_oauth2_token_service.h" 34#include "components/signin/core/browser/signin_manager_base.h" 35#include "content/public/browser/user_metrics.h" 36#include "content/public/browser/user_metrics.h" 37#include "content/public/browser/web_ui.h" 38#include "content/public/common/page_transition_types.h" 39#include "grit/generated_resources.h" 40#include "net/base/host_port_pair.h" 41#include "net/base/net_util.h" 42#include "net/http/http_status_code.h" 43#include "ui/base/l10n/l10n_util.h" 44 45#if defined(ENABLE_FULL_PRINTING) && !defined(OS_CHROMEOS) 46#define CLOUD_PRINT_CONNECTOR_UI_AVAILABLE 47#endif 48 49namespace local_discovery { 50 51namespace { 52const char kPrivetAutomatedClaimURLFormat[] = "%s/confirm?token=%s"; 53 54int g_num_visible = 0; 55} // namespace 56 57LocalDiscoveryUIHandler::LocalDiscoveryUIHandler() : is_visible_(false) { 58#if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE) 59#if !defined(GOOGLE_CHROME_BUILD) && defined(OS_WIN) 60 // On Windows, we need the PDF plugin which is only guaranteed to exist on 61 // Google Chrome builds. Use a command-line switch for Windows non-Google 62 // Chrome builds. 63 cloud_print_connector_ui_enabled_ = 64 CommandLine::ForCurrentProcess()->HasSwitch( 65 switches::kEnableCloudPrintProxy); 66#else 67 // Always enabled for Linux and Google Chrome Windows builds. 68 // Never enabled for Chrome OS, we don't even need to indicate it. 69 cloud_print_connector_ui_enabled_ = true; 70#endif 71#endif // defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE) 72} 73 74LocalDiscoveryUIHandler::~LocalDiscoveryUIHandler() { 75 Profile* profile = Profile::FromWebUI(web_ui()); 76 SigninManagerBase* signin_manager = 77 SigninManagerFactory::GetInstance()->GetForProfile(profile); 78 if (signin_manager) 79 signin_manager->RemoveObserver(this); 80 ResetCurrentRegistration(); 81 SetIsVisible(false); 82} 83 84// static 85bool LocalDiscoveryUIHandler::GetHasVisible() { 86 return g_num_visible != 0; 87} 88 89void LocalDiscoveryUIHandler::RegisterMessages() { 90 web_ui()->RegisterMessageCallback("start", base::Bind( 91 &LocalDiscoveryUIHandler::HandleStart, 92 base::Unretained(this))); 93 web_ui()->RegisterMessageCallback("isVisible", base::Bind( 94 &LocalDiscoveryUIHandler::HandleIsVisible, 95 base::Unretained(this))); 96 web_ui()->RegisterMessageCallback("registerDevice", base::Bind( 97 &LocalDiscoveryUIHandler::HandleRegisterDevice, 98 base::Unretained(this))); 99 web_ui()->RegisterMessageCallback("cancelRegistration", base::Bind( 100 &LocalDiscoveryUIHandler::HandleCancelRegistration, 101 base::Unretained(this))); 102 web_ui()->RegisterMessageCallback("requestPrinterList", base::Bind( 103 &LocalDiscoveryUIHandler::HandleRequestPrinterList, 104 base::Unretained(this))); 105 web_ui()->RegisterMessageCallback("openCloudPrintURL", base::Bind( 106 &LocalDiscoveryUIHandler::HandleOpenCloudPrintURL, 107 base::Unretained(this))); 108 web_ui()->RegisterMessageCallback("showSyncUI", base::Bind( 109 &LocalDiscoveryUIHandler::HandleShowSyncUI, 110 base::Unretained(this))); 111 112 // Cloud print connector related messages 113#if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE) 114 if (cloud_print_connector_ui_enabled_) { 115 web_ui()->RegisterMessageCallback( 116 "showCloudPrintSetupDialog", 117 base::Bind(&LocalDiscoveryUIHandler::ShowCloudPrintSetupDialog, 118 base::Unretained(this))); 119 web_ui()->RegisterMessageCallback( 120 "disableCloudPrintConnector", 121 base::Bind(&LocalDiscoveryUIHandler::HandleDisableCloudPrintConnector, 122 base::Unretained(this))); 123 } 124#endif // defined(ENABLE_FULL_PRINTING) 125} 126 127void LocalDiscoveryUIHandler::HandleStart(const base::ListValue* args) { 128 Profile* profile = Profile::FromWebUI(web_ui()); 129 130 // If privet_lister_ is already set, it is a mock used for tests or the result 131 // of a reload. 132 if (!privet_lister_) { 133 service_discovery_client_ = ServiceDiscoverySharedClient::GetInstance(); 134 privet_lister_.reset(new PrivetDeviceListerImpl( 135 service_discovery_client_.get(), this)); 136 privet_http_factory_ = 137 PrivetHTTPAsynchronousFactory::CreateInstance( 138 service_discovery_client_.get(), profile->GetRequestContext()); 139 140 SigninManagerBase* signin_manager = 141 SigninManagerFactory::GetInstance()->GetForProfile(profile); 142 if (signin_manager) 143 signin_manager->AddObserver(this); 144 } 145 146 privet_lister_->Start(); 147 privet_lister_->DiscoverNewDevices(false); 148 149#if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE) 150 StartCloudPrintConnector(); 151#endif 152 153 CheckUserLoggedIn(); 154} 155 156void LocalDiscoveryUIHandler::HandleIsVisible(const base::ListValue* args) { 157 bool is_visible = false; 158 bool rv = args->GetBoolean(0, &is_visible); 159 DCHECK(rv); 160 SetIsVisible(is_visible); 161} 162 163void LocalDiscoveryUIHandler::HandleRegisterDevice( 164 const base::ListValue* args) { 165 std::string device; 166 167 bool rv = args->GetString(0, &device); 168 DCHECK(rv); 169 170 privet_resolution_ = privet_http_factory_->CreatePrivetHTTP( 171 device, 172 device_descriptions_[device].address, 173 base::Bind(&LocalDiscoveryUIHandler::StartRegisterHTTP, 174 base::Unretained(this))); 175 privet_resolution_->Start(); 176} 177 178void LocalDiscoveryUIHandler::HandleCancelRegistration( 179 const base::ListValue* args) { 180 ResetCurrentRegistration(); 181} 182 183void LocalDiscoveryUIHandler::HandleRequestPrinterList( 184 const base::ListValue* args) { 185 Profile* profile = Profile::FromWebUI(web_ui()); 186 ProfileOAuth2TokenService* token_service = 187 ProfileOAuth2TokenServiceFactory::GetForProfile(profile); 188 189 SigninManagerBase* signin_manager = 190 SigninManagerFactory::GetInstance()->GetForProfile(profile); 191 192 cloud_print_printer_list_.reset(new CloudPrintPrinterList( 193 profile->GetRequestContext(), 194 GetCloudPrintBaseUrl(), 195 token_service, 196 signin_manager->GetAuthenticatedAccountId(), 197 this)); 198 cloud_print_printer_list_->Start(); 199} 200 201void LocalDiscoveryUIHandler::HandleOpenCloudPrintURL( 202 const base::ListValue* args) { 203 std::string url; 204 bool rv = args->GetString(0, &url); 205 DCHECK(rv); 206 207 GURL url_full(GetCloudPrintBaseUrl() + url); 208 209 Browser* browser = chrome::FindBrowserWithWebContents( 210 web_ui()->GetWebContents()); 211 DCHECK(browser); 212 213 chrome::AddSelectedTabWithURL(browser, 214 url_full, 215 content::PAGE_TRANSITION_FROM_API); 216} 217 218void LocalDiscoveryUIHandler::HandleShowSyncUI( 219 const base::ListValue* args) { 220 Browser* browser = chrome::FindBrowserWithWebContents( 221 web_ui()->GetWebContents()); 222 DCHECK(browser); 223 224 GURL url(signin::GetPromoURL(signin::SOURCE_DEVICES_PAGE, 225 true)); // auto close after success. 226 227 browser->OpenURL( 228 content::OpenURLParams(url, content::Referrer(), SINGLETON_TAB, 229 content::PAGE_TRANSITION_AUTO_BOOKMARK, false)); 230} 231 232void LocalDiscoveryUIHandler::StartRegisterHTTP( 233 scoped_ptr<PrivetHTTPClient> http_client) { 234 current_http_client_.swap(http_client); 235 236 std::string user = GetSyncAccount(); 237 238 if (!current_http_client_) { 239 SendRegisterError(); 240 return; 241 } 242 243 current_register_operation_ = 244 current_http_client_->CreateRegisterOperation(user, this); 245 current_register_operation_->Start(); 246} 247 248void LocalDiscoveryUIHandler::OnPrivetRegisterClaimToken( 249 PrivetRegisterOperation* operation, 250 const std::string& token, 251 const GURL& url) { 252 web_ui()->CallJavascriptFunction( 253 "local_discovery.onRegistrationConfirmedOnPrinter"); 254 if (device_descriptions_.count(current_http_client_->GetName()) == 0) { 255 SendRegisterError(); 256 return; 257 } 258 259 std::string base_url = GetCloudPrintBaseUrl(); 260 261 GURL automated_claim_url(base::StringPrintf( 262 kPrivetAutomatedClaimURLFormat, 263 base_url.c_str(), 264 token.c_str())); 265 266 Profile* profile = Profile::FromWebUI(web_ui()); 267 268 ProfileOAuth2TokenService* token_service = 269 ProfileOAuth2TokenServiceFactory::GetForProfile(profile); 270 271 if (!token_service) { 272 SendRegisterError(); 273 return; 274 } 275 276 SigninManagerBase* signin_manager = 277 SigninManagerFactory::GetInstance()->GetForProfile(profile); 278 if (!signin_manager) { 279 SendRegisterError(); 280 return; 281 } 282 283 confirm_api_call_flow_.reset(new PrivetConfirmApiCallFlow( 284 profile->GetRequestContext(), 285 token_service, 286 signin_manager->GetAuthenticatedAccountId(), 287 automated_claim_url, 288 base::Bind(&LocalDiscoveryUIHandler::OnConfirmDone, 289 base::Unretained(this)))); 290 confirm_api_call_flow_->Start(); 291} 292 293void LocalDiscoveryUIHandler::OnPrivetRegisterError( 294 PrivetRegisterOperation* operation, 295 const std::string& action, 296 PrivetRegisterOperation::FailureReason reason, 297 int printer_http_code, 298 const base::DictionaryValue* json) { 299 std::string error; 300 301 if (reason == PrivetRegisterOperation::FAILURE_JSON_ERROR && 302 json->GetString(kPrivetKeyError, &error)) { 303 if (error == kPrivetErrorTimeout) { 304 web_ui()->CallJavascriptFunction( 305 "local_discovery.onRegistrationTimeout"); 306 return; 307 } else if (error == kPrivetErrorCancel) { 308 web_ui()->CallJavascriptFunction( 309 "local_discovery.onRegistrationCanceledPrinter"); 310 return; 311 } 312 } 313 314 SendRegisterError(); 315} 316 317void LocalDiscoveryUIHandler::OnPrivetRegisterDone( 318 PrivetRegisterOperation* operation, 319 const std::string& device_id) { 320 std::string name = operation->GetHTTPClient()->GetName(); 321 322 current_register_operation_.reset(); 323 current_http_client_.reset(); 324 325 // HACK(noamsml): Generate network traffic so the Windows firewall doesn't 326 // block the printer's announcement. 327 privet_lister_->DiscoverNewDevices(false); 328 329 DeviceDescriptionMap::iterator found = device_descriptions_.find(name); 330 331 if (found == device_descriptions_.end()) { 332 // TODO(noamsml): Handle the case where a printer's record is not present at 333 // the end of registration. 334 SendRegisterError(); 335 return; 336 } 337 338 SendRegisterDone(found->first, found->second); 339} 340 341void LocalDiscoveryUIHandler::OnConfirmDone( 342 CloudPrintBaseApiFlow::Status status) { 343 if (status == CloudPrintBaseApiFlow::SUCCESS) { 344 confirm_api_call_flow_.reset(); 345 current_register_operation_->CompleteRegistration(); 346 } else { 347 SendRegisterError(); 348 } 349} 350 351void LocalDiscoveryUIHandler::DeviceChanged( 352 bool added, 353 const std::string& name, 354 const DeviceDescription& description) { 355 device_descriptions_[name] = description; 356 357 base::DictionaryValue info; 358 359 base::StringValue service_name(name); 360 scoped_ptr<base::Value> null_value(base::Value::CreateNullValue()); 361 362 if (description.id.empty()) { 363 info.SetString("service_name", name); 364 info.SetString("human_readable_name", description.name); 365 info.SetString("description", description.description); 366 367 web_ui()->CallJavascriptFunction( 368 "local_discovery.onUnregisteredDeviceUpdate", 369 service_name, info); 370 } else { 371 web_ui()->CallJavascriptFunction( 372 "local_discovery.onUnregisteredDeviceUpdate", 373 service_name, *null_value); 374 } 375} 376 377void LocalDiscoveryUIHandler::DeviceRemoved(const std::string& name) { 378 device_descriptions_.erase(name); 379 scoped_ptr<base::Value> null_value(base::Value::CreateNullValue()); 380 base::StringValue name_value(name); 381 382 web_ui()->CallJavascriptFunction("local_discovery.onUnregisteredDeviceUpdate", 383 name_value, *null_value); 384} 385 386void LocalDiscoveryUIHandler::DeviceCacheFlushed() { 387 web_ui()->CallJavascriptFunction("local_discovery.onDeviceCacheFlushed"); 388 privet_lister_->DiscoverNewDevices(false); 389} 390 391void LocalDiscoveryUIHandler::OnCloudPrintPrinterListReady() { 392 base::ListValue printer_object_list; 393 std::set<std::string> local_ids; 394 395 for (DeviceDescriptionMap::iterator i = device_descriptions_.begin(); 396 i != device_descriptions_.end(); 397 i++) { 398 std::string device_id = i->second.id; 399 if (!device_id.empty()) { 400 const CloudPrintPrinterList::PrinterDetails* details = 401 cloud_print_printer_list_->GetDetailsFor(device_id); 402 403 if (details) { 404 local_ids.insert(device_id); 405 printer_object_list.Append(CreatePrinterInfo(*details).release()); 406 } 407 } 408 } 409 410 for (CloudPrintPrinterList::iterator i = cloud_print_printer_list_->begin(); 411 i != cloud_print_printer_list_->end(); i++) { 412 if (local_ids.count(i->id) == 0) { 413 printer_object_list.Append(CreatePrinterInfo(*i).release()); 414 } 415 } 416 417 web_ui()->CallJavascriptFunction( 418 "local_discovery.onCloudDeviceListAvailable", printer_object_list); 419} 420 421void LocalDiscoveryUIHandler::OnCloudPrintPrinterListUnavailable() { 422 web_ui()->CallJavascriptFunction( 423 "local_discovery.onCloudDeviceListUnavailable"); 424} 425 426void LocalDiscoveryUIHandler::GoogleSigninSucceeded( 427 const std::string& username, 428 const std::string& password) { 429 CheckUserLoggedIn(); 430} 431 432void LocalDiscoveryUIHandler::GoogleSignedOut(const std::string& username) { 433 CheckUserLoggedIn(); 434} 435 436void LocalDiscoveryUIHandler::SendRegisterError() { 437 web_ui()->CallJavascriptFunction("local_discovery.onRegistrationFailed"); 438} 439 440void LocalDiscoveryUIHandler::SendRegisterDone( 441 const std::string& service_name, const DeviceDescription& device) { 442 base::DictionaryValue printer_value; 443 444 printer_value.SetString("id", device.id); 445 printer_value.SetString("display_name", device.name); 446 printer_value.SetString("description", device.description); 447 printer_value.SetString("service_name", service_name); 448 449 web_ui()->CallJavascriptFunction("local_discovery.onRegistrationSuccess", 450 printer_value); 451} 452 453void LocalDiscoveryUIHandler::SetIsVisible(bool visible) { 454 if (visible != is_visible_) { 455 g_num_visible += visible ? 1 : -1; 456 is_visible_ = visible; 457 } 458} 459 460std::string LocalDiscoveryUIHandler::GetSyncAccount() { 461 Profile* profile = Profile::FromWebUI(web_ui()); 462 SigninManagerBase* signin_manager = 463 SigninManagerFactory::GetForProfileIfExists(profile); 464 465 if (!signin_manager) { 466 return ""; 467 } 468 469 return signin_manager->GetAuthenticatedUsername(); 470} 471 472std::string LocalDiscoveryUIHandler::GetCloudPrintBaseUrl() { 473 CloudPrintURL cloud_print_url(Profile::FromWebUI(web_ui())); 474 475 return cloud_print_url.GetCloudPrintServiceURL().spec(); 476} 477 478// TODO(noamsml): Create master object for registration flow. 479void LocalDiscoveryUIHandler::ResetCurrentRegistration() { 480 if (current_register_operation_.get()) { 481 current_register_operation_->Cancel(); 482 current_register_operation_.reset(); 483 } 484 485 confirm_api_call_flow_.reset(); 486 privet_resolution_.reset(); 487 current_http_client_.reset(); 488} 489 490scoped_ptr<base::DictionaryValue> LocalDiscoveryUIHandler::CreatePrinterInfo( 491 const CloudPrintPrinterList::PrinterDetails& description) { 492 scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue); 493 494 return_value->SetString("id", description.id); 495 return_value->SetString("display_name", description.display_name); 496 return_value->SetString("description", description.description); 497 498 return return_value.Pass(); 499} 500 501void LocalDiscoveryUIHandler::CheckUserLoggedIn() { 502 base::FundamentalValue logged_in_value(!GetSyncAccount().empty()); 503 web_ui()->CallJavascriptFunction("local_discovery.setUserLoggedIn", 504 logged_in_value); 505} 506 507#if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE) 508void LocalDiscoveryUIHandler::StartCloudPrintConnector() { 509 Profile* profile = Profile::FromWebUI(web_ui()); 510 511 base::Closure cloud_print_callback = base::Bind( 512 &LocalDiscoveryUIHandler::OnCloudPrintPrefsChanged, 513 base::Unretained(this)); 514 515 if (cloud_print_connector_email_.GetPrefName().empty()) { 516 cloud_print_connector_email_.Init( 517 prefs::kCloudPrintEmail, profile->GetPrefs(), cloud_print_callback); 518 } 519 520 if (cloud_print_connector_enabled_.GetPrefName().empty()) { 521 cloud_print_connector_enabled_.Init( 522 prefs::kCloudPrintProxyEnabled, profile->GetPrefs(), 523 cloud_print_callback); 524 } 525 526 if (cloud_print_connector_ui_enabled_) { 527 SetupCloudPrintConnectorSection(); 528 RefreshCloudPrintStatusFromService(); 529 } else { 530 RemoveCloudPrintConnectorSection(); 531 } 532} 533 534void LocalDiscoveryUIHandler::OnCloudPrintPrefsChanged() { 535 if (cloud_print_connector_ui_enabled_) 536 SetupCloudPrintConnectorSection(); 537} 538 539void LocalDiscoveryUIHandler::ShowCloudPrintSetupDialog( 540 const base::ListValue* args) { 541 content::RecordAction( 542 base::UserMetricsAction("Options_EnableCloudPrintProxy")); 543 // Open the connector enable page in the current tab. 544 Profile* profile = Profile::FromWebUI(web_ui()); 545 content::OpenURLParams params( 546 CloudPrintURL(profile).GetCloudPrintServiceEnableURL( 547 CloudPrintProxyServiceFactory::GetForProfile(profile)->proxy_id()), 548 content::Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_LINK, false); 549 web_ui()->GetWebContents()->OpenURL(params); 550} 551 552void LocalDiscoveryUIHandler::HandleDisableCloudPrintConnector( 553 const base::ListValue* args) { 554 content::RecordAction( 555 base::UserMetricsAction("Options_DisableCloudPrintProxy")); 556 CloudPrintProxyServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()))-> 557 DisableForUser(); 558} 559 560void LocalDiscoveryUIHandler::SetupCloudPrintConnectorSection() { 561 Profile* profile = Profile::FromWebUI(web_ui()); 562 563 if (!CloudPrintProxyServiceFactory::GetForProfile(profile)) { 564 cloud_print_connector_ui_enabled_ = false; 565 RemoveCloudPrintConnectorSection(); 566 return; 567 } 568 569 bool cloud_print_connector_allowed = 570 !cloud_print_connector_enabled_.IsManaged() || 571 cloud_print_connector_enabled_.GetValue(); 572 base::FundamentalValue allowed(cloud_print_connector_allowed); 573 574 std::string email; 575 if (profile->GetPrefs()->HasPrefPath(prefs::kCloudPrintEmail) && 576 cloud_print_connector_allowed) { 577 email = profile->GetPrefs()->GetString(prefs::kCloudPrintEmail); 578 } 579 base::FundamentalValue disabled(email.empty()); 580 581 base::string16 label_str; 582 if (email.empty()) { 583 label_str = l10n_util::GetStringFUTF16( 584 IDS_LOCAL_DISCOVERY_CLOUD_PRINT_CONNECTOR_DISABLED_LABEL, 585 l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT)); 586 } else { 587 label_str = l10n_util::GetStringFUTF16( 588 IDS_OPTIONS_CLOUD_PRINT_CONNECTOR_ENABLED_LABEL, 589 l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT), 590 base::UTF8ToUTF16(email)); 591 } 592 base::StringValue label(label_str); 593 594 web_ui()->CallJavascriptFunction( 595 "local_discovery.setupCloudPrintConnectorSection", disabled, label, 596 allowed); 597} 598 599void LocalDiscoveryUIHandler::RemoveCloudPrintConnectorSection() { 600 web_ui()->CallJavascriptFunction( 601 "local_discovery.removeCloudPrintConnectorSection"); 602} 603 604void LocalDiscoveryUIHandler::RefreshCloudPrintStatusFromService() { 605 if (cloud_print_connector_ui_enabled_) 606 CloudPrintProxyServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()))-> 607 RefreshStatusFromService(); 608} 609#endif // cloud print connector option stuff 610 611} // namespace local_discovery 612