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