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