local_discovery_ui_handler.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/chrome_notification_types.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/notification_source.h" 38#include "content/public/browser/user_metrics.h" 39#include "content/public/browser/user_metrics.h" 40#include "content/public/browser/web_ui.h" 41#include "content/public/common/page_transition_types.h" 42#include "grit/generated_resources.h" 43#include "net/base/host_port_pair.h" 44#include "net/base/net_util.h" 45#include "net/http/http_status_code.h" 46#include "ui/base/l10n/l10n_util.h" 47 48#if defined(ENABLE_FULL_PRINTING) && !defined(OS_CHROMEOS) && \ 49 !defined(OS_MACOSX) 50#define CLOUD_PRINT_CONNECTOR_UI_AVAILABLE 51#endif 52 53namespace local_discovery { 54 55namespace { 56const char kPrivetAutomatedClaimURLFormat[] = "%s/confirm?token=%s"; 57 58int g_num_visible = 0; 59} // namespace 60 61LocalDiscoveryUIHandler::LocalDiscoveryUIHandler() : is_visible_(false) { 62#if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE) 63#if !defined(GOOGLE_CHROME_BUILD) && defined(OS_WIN) 64 // On Windows, we need the PDF plugin which is only guaranteed to exist on 65 // Google Chrome builds. Use a command-line switch for Windows non-Google 66 // Chrome builds. 67 cloud_print_connector_ui_enabled_ = 68 CommandLine::ForCurrentProcess()->HasSwitch( 69 switches::kEnableCloudPrintProxy); 70#elif !defined(OS_CHROMEOS) 71 // Always enabled for Linux and Google Chrome Windows builds. 72 // Never enabled for Chrome OS, we don't even need to indicate it. 73 cloud_print_connector_ui_enabled_ = true; 74#endif 75#endif // !defined(OS_MACOSX) 76} 77 78LocalDiscoveryUIHandler::~LocalDiscoveryUIHandler() { 79 ResetCurrentRegistration(); 80 SetIsVisible(false); 81} 82 83// static 84bool LocalDiscoveryUIHandler::GetHasVisible() { 85 return g_num_visible != 0; 86} 87 88void LocalDiscoveryUIHandler::RegisterMessages() { 89 web_ui()->RegisterMessageCallback("start", base::Bind( 90 &LocalDiscoveryUIHandler::HandleStart, 91 base::Unretained(this))); 92 web_ui()->RegisterMessageCallback("isVisible", base::Bind( 93 &LocalDiscoveryUIHandler::HandleIsVisible, 94 base::Unretained(this))); 95 web_ui()->RegisterMessageCallback("registerDevice", base::Bind( 96 &LocalDiscoveryUIHandler::HandleRegisterDevice, 97 base::Unretained(this))); 98 web_ui()->RegisterMessageCallback("cancelRegistration", base::Bind( 99 &LocalDiscoveryUIHandler::HandleCancelRegistration, 100 base::Unretained(this))); 101 web_ui()->RegisterMessageCallback("requestPrinterList", base::Bind( 102 &LocalDiscoveryUIHandler::HandleRequestPrinterList, 103 base::Unretained(this))); 104 web_ui()->RegisterMessageCallback("openCloudPrintURL", base::Bind( 105 &LocalDiscoveryUIHandler::HandleOpenCloudPrintURL, 106 base::Unretained(this))); 107 web_ui()->RegisterMessageCallback("showSyncUI", base::Bind( 108 &LocalDiscoveryUIHandler::HandleShowSyncUI, 109 base::Unretained(this))); 110 111 // Cloud print connector related messages 112#if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE) 113 if (cloud_print_connector_ui_enabled_) { 114 web_ui()->RegisterMessageCallback( 115 "showCloudPrintSetupDialog", 116 base::Bind(&LocalDiscoveryUIHandler::ShowCloudPrintSetupDialog, 117 base::Unretained(this))); 118 web_ui()->RegisterMessageCallback( 119 "disableCloudPrintConnector", 120 base::Bind(&LocalDiscoveryUIHandler::HandleDisableCloudPrintConnector, 121 base::Unretained(this))); 122 } 123#endif // defined(ENABLE_FULL_PRINTING) 124} 125 126void LocalDiscoveryUIHandler::HandleStart(const base::ListValue* args) { 127 Profile* profile = Profile::FromWebUI(web_ui()); 128 129 // If privet_lister_ is already set, it is a mock used for tests or the result 130 // of a reload. 131 if (!privet_lister_) { 132 service_discovery_client_ = ServiceDiscoverySharedClient::GetInstance(); 133 privet_lister_.reset(new PrivetDeviceListerImpl( 134 service_discovery_client_.get(), this)); 135 privet_http_factory_ = 136 PrivetHTTPAsynchronousFactory::CreateInstance( 137 service_discovery_client_.get(), profile->GetRequestContext()); 138 } 139 140 privet_lister_->Start(); 141 privet_lister_->DiscoverNewDevices(false); 142 143#if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE) 144 StartCloudPrintConnector(); 145#endif 146 147 CheckUserLoggedIn(); 148 149 notification_registrar_.RemoveAll(); 150 notification_registrar_.Add(this, 151 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, 152 content::Source<Profile>(profile)); 153 notification_registrar_.Add(this, chrome::NOTIFICATION_GOOGLE_SIGNED_OUT, 154 content::Source<Profile>(profile)); 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 // We use SOURCE_SETTINGS because the URL for SOURCE_SETTINGS is detected on 226 // redirect. 227 GURL url(signin::GetPromoURL(signin::SOURCE_SETTINGS, 228 true)); // auto close after success. 229 230 browser->OpenURL( 231 content::OpenURLParams(url, content::Referrer(), SINGLETON_TAB, 232 content::PAGE_TRANSITION_AUTO_BOOKMARK, false)); 233} 234 235void LocalDiscoveryUIHandler::StartRegisterHTTP( 236 scoped_ptr<PrivetHTTPClient> http_client) { 237 current_http_client_.swap(http_client); 238 239 std::string user = GetSyncAccount(); 240 241 if (!current_http_client_) { 242 SendRegisterError(); 243 return; 244 } 245 246 current_register_operation_ = 247 current_http_client_->CreateRegisterOperation(user, this); 248 current_register_operation_->Start(); 249} 250 251void LocalDiscoveryUIHandler::OnPrivetRegisterClaimToken( 252 PrivetRegisterOperation* operation, 253 const std::string& token, 254 const GURL& url) { 255 web_ui()->CallJavascriptFunction( 256 "local_discovery.onRegistrationConfirmedOnPrinter"); 257 if (device_descriptions_.count(current_http_client_->GetName()) == 0) { 258 SendRegisterError(); 259 return; 260 } 261 262 std::string base_url = GetCloudPrintBaseUrl(); 263 264 GURL automated_claim_url(base::StringPrintf( 265 kPrivetAutomatedClaimURLFormat, 266 base_url.c_str(), 267 token.c_str())); 268 269 Profile* profile = Profile::FromWebUI(web_ui()); 270 271 ProfileOAuth2TokenService* token_service = 272 ProfileOAuth2TokenServiceFactory::GetForProfile(profile); 273 274 if (!token_service) { 275 SendRegisterError(); 276 return; 277 } 278 279 SigninManagerBase* signin_manager = 280 SigninManagerFactory::GetInstance()->GetForProfile(profile); 281 if (!signin_manager) { 282 SendRegisterError(); 283 return; 284 } 285 286 confirm_api_call_flow_.reset(new PrivetConfirmApiCallFlow( 287 profile->GetRequestContext(), 288 token_service, 289 signin_manager->GetAuthenticatedAccountId(), 290 automated_claim_url, 291 base::Bind(&LocalDiscoveryUIHandler::OnConfirmDone, 292 base::Unretained(this)))); 293 confirm_api_call_flow_->Start(); 294} 295 296void LocalDiscoveryUIHandler::OnPrivetRegisterError( 297 PrivetRegisterOperation* operation, 298 const std::string& action, 299 PrivetRegisterOperation::FailureReason reason, 300 int printer_http_code, 301 const base::DictionaryValue* json) { 302 std::string error; 303 304 if (reason == PrivetRegisterOperation::FAILURE_JSON_ERROR && 305 json->GetString(kPrivetKeyError, &error)) { 306 if (error == kPrivetErrorTimeout) { 307 web_ui()->CallJavascriptFunction( 308 "local_discovery.onRegistrationTimeout"); 309 return; 310 } else if (error == kPrivetErrorCancel) { 311 web_ui()->CallJavascriptFunction( 312 "local_discovery.onRegistrationCanceledPrinter"); 313 return; 314 } 315 } 316 317 SendRegisterError(); 318} 319 320void LocalDiscoveryUIHandler::OnPrivetRegisterDone( 321 PrivetRegisterOperation* operation, 322 const std::string& device_id) { 323 std::string name = operation->GetHTTPClient()->GetName(); 324 325 current_register_operation_.reset(); 326 current_http_client_.reset(); 327 328 // HACK(noamsml): Generate network traffic so the Windows firewall doesn't 329 // block the printer's announcement. 330 privet_lister_->DiscoverNewDevices(false); 331 332 DeviceDescriptionMap::iterator found = device_descriptions_.find(name); 333 334 if (found == device_descriptions_.end()) { 335 // TODO(noamsml): Handle the case where a printer's record is not present at 336 // the end of registration. 337 SendRegisterError(); 338 return; 339 } 340 341 SendRegisterDone(found->first, found->second); 342} 343 344void LocalDiscoveryUIHandler::OnConfirmDone( 345 CloudPrintBaseApiFlow::Status status) { 346 if (status == CloudPrintBaseApiFlow::SUCCESS) { 347 confirm_api_call_flow_.reset(); 348 current_register_operation_->CompleteRegistration(); 349 } else { 350 SendRegisterError(); 351 } 352} 353 354void LocalDiscoveryUIHandler::DeviceChanged( 355 bool added, 356 const std::string& name, 357 const DeviceDescription& description) { 358 device_descriptions_[name] = description; 359 360 base::DictionaryValue info; 361 362 base::StringValue service_name(name); 363 scoped_ptr<base::Value> null_value(base::Value::CreateNullValue()); 364 365 if (description.id.empty()) { 366 info.SetString("service_name", name); 367 info.SetString("human_readable_name", description.name); 368 info.SetString("description", description.description); 369 370 web_ui()->CallJavascriptFunction( 371 "local_discovery.onUnregisteredDeviceUpdate", 372 service_name, info); 373 } else { 374 web_ui()->CallJavascriptFunction( 375 "local_discovery.onUnregisteredDeviceUpdate", 376 service_name, *null_value); 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 429void LocalDiscoveryUIHandler::Observe( 430 int type, 431 const content::NotificationSource& source, 432 const content::NotificationDetails& details) { 433 switch (type) { 434 case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL: 435 case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT: 436 CheckUserLoggedIn(); 437 break; 438 default: 439 NOTREACHED(); 440 } 441} 442 443void LocalDiscoveryUIHandler::SendRegisterError() { 444 web_ui()->CallJavascriptFunction("local_discovery.onRegistrationFailed"); 445} 446 447void LocalDiscoveryUIHandler::SendRegisterDone( 448 const std::string& service_name, const DeviceDescription& device) { 449 base::DictionaryValue printer_value; 450 451 printer_value.SetString("id", device.id); 452 printer_value.SetString("display_name", device.name); 453 printer_value.SetString("description", device.description); 454 printer_value.SetString("service_name", service_name); 455 456 web_ui()->CallJavascriptFunction("local_discovery.onRegistrationSuccess", 457 printer_value); 458} 459 460void LocalDiscoveryUIHandler::SetIsVisible(bool visible) { 461 if (visible != is_visible_) { 462 g_num_visible += visible ? 1 : -1; 463 is_visible_ = visible; 464 } 465} 466 467std::string LocalDiscoveryUIHandler::GetSyncAccount() { 468 Profile* profile = Profile::FromWebUI(web_ui()); 469 SigninManagerBase* signin_manager = 470 SigninManagerFactory::GetForProfileIfExists(profile); 471 472 if (!signin_manager) { 473 return ""; 474 } 475 476 return signin_manager->GetAuthenticatedUsername(); 477} 478 479std::string LocalDiscoveryUIHandler::GetCloudPrintBaseUrl() { 480 CloudPrintURL cloud_print_url(Profile::FromWebUI(web_ui())); 481 482 return cloud_print_url.GetCloudPrintServiceURL().spec(); 483} 484 485// TODO(noamsml): Create master object for registration flow. 486void LocalDiscoveryUIHandler::ResetCurrentRegistration() { 487 if (current_register_operation_.get()) { 488 current_register_operation_->Cancel(); 489 current_register_operation_.reset(); 490 } 491 492 confirm_api_call_flow_.reset(); 493 privet_resolution_.reset(); 494 current_http_client_.reset(); 495} 496 497scoped_ptr<base::DictionaryValue> LocalDiscoveryUIHandler::CreatePrinterInfo( 498 const CloudPrintPrinterList::PrinterDetails& description) { 499 scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue); 500 501 return_value->SetString("id", description.id); 502 return_value->SetString("display_name", description.display_name); 503 return_value->SetString("description", description.description); 504 505 return return_value.Pass(); 506} 507 508void LocalDiscoveryUIHandler::CheckUserLoggedIn() { 509 base::FundamentalValue logged_in_value(!GetSyncAccount().empty()); 510 web_ui()->CallJavascriptFunction("local_discovery.setUserLoggedIn", 511 logged_in_value); 512} 513 514#if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE) 515void LocalDiscoveryUIHandler::StartCloudPrintConnector() { 516 Profile* profile = Profile::FromWebUI(web_ui()); 517 518 base::Closure cloud_print_callback = base::Bind( 519 &LocalDiscoveryUIHandler::OnCloudPrintPrefsChanged, 520 base::Unretained(this)); 521 522 if (cloud_print_connector_email_.GetPrefName().empty()) { 523 cloud_print_connector_email_.Init( 524 prefs::kCloudPrintEmail, profile->GetPrefs(), cloud_print_callback); 525 } 526 527 if (cloud_print_connector_enabled_.GetPrefName().empty()) { 528 cloud_print_connector_enabled_.Init( 529 prefs::kCloudPrintProxyEnabled, profile->GetPrefs(), 530 cloud_print_callback); 531 } 532 533 if (cloud_print_connector_ui_enabled_) { 534 SetupCloudPrintConnectorSection(); 535 RefreshCloudPrintStatusFromService(); 536 } else { 537 RemoveCloudPrintConnectorSection(); 538 } 539} 540 541void LocalDiscoveryUIHandler::OnCloudPrintPrefsChanged() { 542 if (cloud_print_connector_ui_enabled_) 543 SetupCloudPrintConnectorSection(); 544} 545 546void LocalDiscoveryUIHandler::ShowCloudPrintSetupDialog( 547 const base::ListValue* args) { 548 content::RecordAction( 549 base::UserMetricsAction("Options_EnableCloudPrintProxy")); 550 // Open the connector enable page in the current tab. 551 Profile* profile = Profile::FromWebUI(web_ui()); 552 content::OpenURLParams params( 553 CloudPrintURL(profile).GetCloudPrintServiceEnableURL( 554 CloudPrintProxyServiceFactory::GetForProfile(profile)->proxy_id()), 555 content::Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_LINK, false); 556 web_ui()->GetWebContents()->OpenURL(params); 557} 558 559void LocalDiscoveryUIHandler::HandleDisableCloudPrintConnector( 560 const base::ListValue* args) { 561 content::RecordAction( 562 base::UserMetricsAction("Options_DisableCloudPrintProxy")); 563 CloudPrintProxyServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()))-> 564 DisableForUser(); 565} 566 567void LocalDiscoveryUIHandler::SetupCloudPrintConnectorSection() { 568 Profile* profile = Profile::FromWebUI(web_ui()); 569 570 if (!CloudPrintProxyServiceFactory::GetForProfile(profile)) { 571 cloud_print_connector_ui_enabled_ = false; 572 RemoveCloudPrintConnectorSection(); 573 return; 574 } 575 576 bool cloud_print_connector_allowed = 577 !cloud_print_connector_enabled_.IsManaged() || 578 cloud_print_connector_enabled_.GetValue(); 579 base::FundamentalValue allowed(cloud_print_connector_allowed); 580 581 std::string email; 582 if (profile->GetPrefs()->HasPrefPath(prefs::kCloudPrintEmail) && 583 cloud_print_connector_allowed) { 584 email = profile->GetPrefs()->GetString(prefs::kCloudPrintEmail); 585 } 586 base::FundamentalValue disabled(email.empty()); 587 588 base::string16 label_str; 589 if (email.empty()) { 590 label_str = l10n_util::GetStringFUTF16( 591 IDS_LOCAL_DISCOVERY_CLOUD_PRINT_CONNECTOR_DISABLED_LABEL, 592 l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT)); 593 } else { 594 label_str = l10n_util::GetStringFUTF16( 595 IDS_OPTIONS_CLOUD_PRINT_CONNECTOR_ENABLED_LABEL, 596 l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT), 597 base::UTF8ToUTF16(email)); 598 } 599 base::StringValue label(label_str); 600 601 web_ui()->CallJavascriptFunction( 602 "local_discovery.setupCloudPrintConnectorSection", disabled, label, 603 allowed); 604} 605 606void LocalDiscoveryUIHandler::RemoveCloudPrintConnectorSection() { 607 web_ui()->CallJavascriptFunction( 608 "local_discovery.removeCloudPrintConnectorSection"); 609} 610 611void LocalDiscoveryUIHandler::RefreshCloudPrintStatusFromService() { 612 if (cloud_print_connector_ui_enabled_) 613 CloudPrintProxyServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()))-> 614 RefreshStatusFromService(); 615} 616#endif // cloud print connector option stuff 617 618} // namespace local_discovery 619