Home | History | Annotate | Download | only in local_discovery
      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/profiles/profile.h"
     25 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
     26 #include "chrome/browser/signin/signin_manager_factory.h"
     27 #include "chrome/browser/signin/signin_promo.h"
     28 #include "chrome/browser/ui/browser_finder.h"
     29 #include "chrome/browser/ui/browser_tabstrip.h"
     30 #include "chrome/common/chrome_switches.h"
     31 #include "chrome/common/pref_names.h"
     32 #include "chrome/grit/generated_resources.h"
     33 #include "components/cloud_devices/common/cloud_devices_switches.h"
     34 #include "components/cloud_devices/common/cloud_devices_urls.h"
     35 #include "components/signin/core/browser/profile_oauth2_token_service.h"
     36 #include "components/signin/core/browser/signin_manager_base.h"
     37 #include "content/public/browser/user_metrics.h"
     38 #include "content/public/browser/web_ui.h"
     39 #include "net/base/host_port_pair.h"
     40 #include "net/base/net_util.h"
     41 #include "net/base/url_util.h"
     42 #include "net/http/http_status_code.h"
     43 #include "ui/base/l10n/l10n_util.h"
     44 #include "ui/base/page_transition_types.h"
     45 
     46 #if defined(ENABLE_FULL_PRINTING) && !defined(OS_CHROMEOS)
     47 #define CLOUD_PRINT_CONNECTOR_UI_AVAILABLE
     48 #endif
     49 
     50 namespace local_discovery {
     51 
     52 namespace {
     53 
     54 const char kDictionaryKeyServiceName[] = "service_name";
     55 const char kDictionaryKeyDisplayName[] = "display_name";
     56 const char kDictionaryKeyDescription[] = "description";
     57 const char kDictionaryKeyType[] = "type";
     58 const char kDictionaryKeyIsWifi[] = "is_wifi";
     59 const char kDictionaryKeyID[] = "id";
     60 
     61 const char kKeyPrefixMDns[] = "MDns:";
     62 
     63 #if defined(ENABLE_WIFI_BOOTSTRAPPING)
     64 const char kKeyPrefixWifi[] = "WiFi:";
     65 #endif  // ENABLE_WIFI_BOOTSTRAPPING
     66 
     67 int g_num_visible = 0;
     68 
     69 const int kCloudDevicesPrivetVersion = 3;
     70 
     71 scoped_ptr<base::DictionaryValue> CreateDeviceInfo(
     72     const CloudDeviceListDelegate::Device& description) {
     73   scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue);
     74 
     75   return_value->SetString(kDictionaryKeyID, description.id);
     76   return_value->SetString(kDictionaryKeyDisplayName, description.display_name);
     77   return_value->SetString(kDictionaryKeyDescription, description.description);
     78   return_value->SetString(kDictionaryKeyType, description.type);
     79 
     80   return return_value.Pass();
     81 }
     82 
     83 void ReadDevicesList(
     84     const std::vector<CloudDeviceListDelegate::Device>& devices,
     85     const std::set<std::string>& local_ids,
     86     base::ListValue* devices_list) {
     87   for (CloudDeviceList::iterator i = devices.begin(); i != devices.end(); i++) {
     88     if (local_ids.count(i->id) > 0) {
     89       devices_list->Append(CreateDeviceInfo(*i).release());
     90     }
     91   }
     92 
     93   for (CloudDeviceList::iterator i = devices.begin(); i != devices.end(); i++) {
     94     if (local_ids.count(i->id) == 0) {
     95       devices_list->Append(CreateDeviceInfo(*i).release());
     96     }
     97   }
     98 }
     99 
    100 }  // namespace
    101 
    102 LocalDiscoveryUIHandler::LocalDiscoveryUIHandler()
    103     : is_visible_(false),
    104       failed_list_count_(0),
    105       succeded_list_count_(0) {
    106 #if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
    107 #if !defined(GOOGLE_CHROME_BUILD) && defined(OS_WIN)
    108   // On Windows, we need the PDF plugin which is only guaranteed to exist on
    109   // Google Chrome builds. Use a command-line switch for Windows non-Google
    110   //  Chrome builds.
    111   cloud_print_connector_ui_enabled_ =
    112       CommandLine::ForCurrentProcess()->HasSwitch(
    113       switches::kEnableCloudPrintProxy);
    114 #else
    115   // Always enabled for Linux and Google Chrome Windows builds.
    116   // Never enabled for Chrome OS, we don't even need to indicate it.
    117   cloud_print_connector_ui_enabled_ = true;
    118 #endif
    119 #endif  // defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
    120 }
    121 
    122 LocalDiscoveryUIHandler::~LocalDiscoveryUIHandler() {
    123   Profile* profile = Profile::FromWebUI(web_ui());
    124   SigninManagerBase* signin_manager =
    125       SigninManagerFactory::GetInstance()->GetForProfile(profile);
    126   if (signin_manager)
    127     signin_manager->RemoveObserver(this);
    128   ResetCurrentRegistration();
    129   SetIsVisible(false);
    130 }
    131 
    132 // static
    133 bool LocalDiscoveryUIHandler::GetHasVisible() {
    134   return g_num_visible != 0;
    135 }
    136 
    137 void LocalDiscoveryUIHandler::RegisterMessages() {
    138   web_ui()->RegisterMessageCallback("start", base::Bind(
    139       &LocalDiscoveryUIHandler::HandleStart,
    140       base::Unretained(this)));
    141   web_ui()->RegisterMessageCallback("isVisible", base::Bind(
    142       &LocalDiscoveryUIHandler::HandleIsVisible,
    143       base::Unretained(this)));
    144   web_ui()->RegisterMessageCallback("registerDevice", base::Bind(
    145       &LocalDiscoveryUIHandler::HandleRegisterDevice,
    146       base::Unretained(this)));
    147   web_ui()->RegisterMessageCallback(
    148       "confirmCode",
    149       base::Bind(&LocalDiscoveryUIHandler::HandleConfirmCode,
    150                  base::Unretained(this)));
    151   web_ui()->RegisterMessageCallback("cancelRegistration", base::Bind(
    152       &LocalDiscoveryUIHandler::HandleCancelRegistration,
    153       base::Unretained(this)));
    154   web_ui()->RegisterMessageCallback(
    155       "requestDeviceList",
    156       base::Bind(&LocalDiscoveryUIHandler::HandleRequestDeviceList,
    157                  base::Unretained(this)));
    158   web_ui()->RegisterMessageCallback("openCloudPrintURL", base::Bind(
    159       &LocalDiscoveryUIHandler::HandleOpenCloudPrintURL,
    160       base::Unretained(this)));
    161   web_ui()->RegisterMessageCallback("showSyncUI", base::Bind(
    162       &LocalDiscoveryUIHandler::HandleShowSyncUI,
    163       base::Unretained(this)));
    164 
    165   // Cloud print connector related messages
    166 #if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
    167   if (cloud_print_connector_ui_enabled_) {
    168     web_ui()->RegisterMessageCallback(
    169         "showCloudPrintSetupDialog",
    170         base::Bind(&LocalDiscoveryUIHandler::ShowCloudPrintSetupDialog,
    171                    base::Unretained(this)));
    172     web_ui()->RegisterMessageCallback(
    173         "disableCloudPrintConnector",
    174         base::Bind(&LocalDiscoveryUIHandler::HandleDisableCloudPrintConnector,
    175                    base::Unretained(this)));
    176   }
    177 #endif  // defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
    178 }
    179 
    180 void LocalDiscoveryUIHandler::HandleStart(const base::ListValue* args) {
    181   Profile* profile = Profile::FromWebUI(web_ui());
    182 
    183   // If privet_lister_ is already set, it is a mock used for tests or the result
    184   // of a reload.
    185   if (!privet_lister_) {
    186     service_discovery_client_ = ServiceDiscoverySharedClient::GetInstance();
    187     privet_lister_.reset(
    188         new PrivetDeviceListerImpl(service_discovery_client_.get(), this));
    189     privet_http_factory_ = PrivetHTTPAsynchronousFactory::CreateInstance(
    190         service_discovery_client_.get(), profile->GetRequestContext());
    191 
    192     SigninManagerBase* signin_manager =
    193         SigninManagerFactory::GetInstance()->GetForProfile(profile);
    194     if (signin_manager)
    195       signin_manager->AddObserver(this);
    196   }
    197 
    198   privet_lister_->Start();
    199   privet_lister_->DiscoverNewDevices(false);
    200 
    201 #if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
    202   StartCloudPrintConnector();
    203 #endif
    204 
    205 #if defined(ENABLE_WIFI_BOOTSTRAPPING)
    206   if (CommandLine::ForCurrentProcess()->HasSwitch(
    207           switches::kEnableCloudDevices)) {
    208     StartWifiBootstrapping();
    209   }
    210 #endif
    211 
    212   CheckUserLoggedIn();
    213 }
    214 
    215 void LocalDiscoveryUIHandler::HandleIsVisible(const base::ListValue* args) {
    216   bool is_visible = false;
    217   bool rv = args->GetBoolean(0, &is_visible);
    218   DCHECK(rv);
    219   SetIsVisible(is_visible);
    220 }
    221 
    222 void LocalDiscoveryUIHandler::HandleRegisterDevice(
    223     const base::ListValue* args) {
    224   std::string device;
    225 
    226   bool rv = args->GetString(0, &device);
    227   DCHECK(rv);
    228 
    229   DeviceDescriptionMap::iterator found = device_descriptions_.find(device);
    230   if (found == device_descriptions_.end()) {
    231     OnSetupError();
    232     return;
    233   }
    234 
    235   if (found->second.version == kCloudDevicesPrivetVersion) {
    236     current_setup_operation_.reset(new PrivetV3SetupFlow(this));
    237     current_setup_operation_->Register(device);
    238   } else if (found->second.version <= 2) {
    239     privet_resolution_ = privet_http_factory_->CreatePrivetHTTP(
    240         device,
    241         found->second.address,
    242         base::Bind(&LocalDiscoveryUIHandler::StartRegisterHTTP,
    243                    base::Unretained(this)));
    244     privet_resolution_->Start();
    245   } else {
    246     OnSetupError();
    247   }
    248 }
    249 
    250 void LocalDiscoveryUIHandler::HandleConfirmCode(const base::ListValue* args) {
    251   device_code_callback_.Run(true);
    252 }
    253 
    254 void LocalDiscoveryUIHandler::HandleCancelRegistration(
    255     const base::ListValue* args) {
    256   ResetCurrentRegistration();
    257 }
    258 
    259 void LocalDiscoveryUIHandler::HandleRequestDeviceList(
    260     const base::ListValue* args) {
    261   failed_list_count_ = 0;
    262   succeded_list_count_ = 0;
    263   cloud_devices_.clear();
    264 
    265   cloud_print_printer_list_ = CreateApiFlow();
    266   if (CommandLine::ForCurrentProcess()->HasSwitch(
    267       switches::kEnableCloudDevices)) {
    268     cloud_device_list_ = CreateApiFlow();
    269   }
    270 
    271   if (cloud_print_printer_list_) {
    272     cloud_print_printer_list_->Start(
    273         make_scoped_ptr<GCDApiFlow::Request>(new CloudPrintPrinterList(this)));
    274   }
    275   if (cloud_device_list_) {
    276     cloud_device_list_->Start(
    277         make_scoped_ptr<GCDApiFlow::Request>(new CloudDeviceList(this)));
    278   }
    279   CheckListingDone();
    280 }
    281 
    282 void LocalDiscoveryUIHandler::HandleOpenCloudPrintURL(
    283     const base::ListValue* args) {
    284   std::string id;
    285   bool rv = args->GetString(0, &id);
    286   DCHECK(rv);
    287 
    288   Browser* browser = chrome::FindBrowserWithWebContents(
    289       web_ui()->GetWebContents());
    290   DCHECK(browser);
    291 
    292   chrome::AddSelectedTabWithURL(browser,
    293                                 cloud_devices::GetCloudPrintManageDeviceURL(id),
    294                                 ui::PAGE_TRANSITION_FROM_API);
    295 }
    296 
    297 void LocalDiscoveryUIHandler::HandleShowSyncUI(
    298     const base::ListValue* args) {
    299   Browser* browser = chrome::FindBrowserWithWebContents(
    300       web_ui()->GetWebContents());
    301   DCHECK(browser);
    302 
    303   GURL url(signin::GetPromoURL(signin::SOURCE_DEVICES_PAGE,
    304                                true));  // auto close after success.
    305 
    306   browser->OpenURL(
    307       content::OpenURLParams(url, content::Referrer(), SINGLETON_TAB,
    308                              ui::PAGE_TRANSITION_AUTO_BOOKMARK, false));
    309 }
    310 
    311 void LocalDiscoveryUIHandler::StartRegisterHTTP(
    312     scoped_ptr<PrivetHTTPClient> http_client) {
    313   current_http_client_ = PrivetV1HTTPClient::CreateDefault(http_client.Pass());
    314 
    315   std::string user = GetSyncAccount();
    316 
    317   if (!current_http_client_) {
    318     SendRegisterError();
    319     return;
    320   }
    321 
    322   current_register_operation_ =
    323       current_http_client_->CreateRegisterOperation(user, this);
    324   current_register_operation_->Start();
    325 }
    326 
    327 void LocalDiscoveryUIHandler::OnPrivetRegisterClaimToken(
    328     PrivetRegisterOperation* operation,
    329     const std::string& token,
    330     const GURL& url) {
    331   web_ui()->CallJavascriptFunction(
    332       "local_discovery.onRegistrationConfirmedOnPrinter");
    333   if (device_descriptions_.count(current_http_client_->GetName()) == 0) {
    334     SendRegisterError();
    335     return;
    336   }
    337 
    338   confirm_api_call_flow_ = CreateApiFlow();
    339   if (!confirm_api_call_flow_) {
    340     SendRegisterError();
    341     return;
    342   }
    343   confirm_api_call_flow_->Start(
    344       make_scoped_ptr<GCDApiFlow::Request>(new PrivetConfirmApiCallFlow(
    345           token,
    346           base::Bind(&LocalDiscoveryUIHandler::OnConfirmDone,
    347                      base::Unretained(this)))));
    348 }
    349 
    350 void LocalDiscoveryUIHandler::OnPrivetRegisterError(
    351     PrivetRegisterOperation* operation,
    352     const std::string& action,
    353     PrivetRegisterOperation::FailureReason reason,
    354     int printer_http_code,
    355     const base::DictionaryValue* json) {
    356   std::string error;
    357 
    358   if (reason == PrivetRegisterOperation::FAILURE_JSON_ERROR &&
    359       json->GetString(kPrivetKeyError, &error)) {
    360     if (error == kPrivetErrorTimeout) {
    361         web_ui()->CallJavascriptFunction(
    362             "local_discovery.onRegistrationTimeout");
    363       return;
    364     } else if (error == kPrivetErrorCancel) {
    365       web_ui()->CallJavascriptFunction(
    366             "local_discovery.onRegistrationCanceledPrinter");
    367       return;
    368     }
    369   }
    370 
    371   SendRegisterError();
    372 }
    373 
    374 void LocalDiscoveryUIHandler::OnPrivetRegisterDone(
    375     PrivetRegisterOperation* operation,
    376     const std::string& device_id) {
    377   std::string name = operation->GetHTTPClient()->GetName();
    378   current_setup_operation_.reset();
    379   current_register_operation_.reset();
    380   current_http_client_.reset();
    381   SendRegisterDone(name);
    382 }
    383 
    384 void LocalDiscoveryUIHandler::GetWiFiCredentials(
    385     const CredentialsCallback& callback) {
    386   callback.Run("", "");
    387 }
    388 
    389 void LocalDiscoveryUIHandler::SwitchToSetupWiFi(
    390     const ResultCallback& callback) {
    391   callback.Run(true);
    392 }
    393 
    394 void LocalDiscoveryUIHandler::ConfirmSecurityCode(
    395     const std::string& confirmation_code,
    396     const ResultCallback& callback) {
    397   device_code_callback_ = callback;
    398   web_ui()->CallJavascriptFunction(
    399       "local_discovery.onRegistrationConfirmDeviceCode",
    400       base::StringValue(confirmation_code));
    401 }
    402 
    403 void LocalDiscoveryUIHandler::RestoreWifi(const ResultCallback& callback) {
    404   callback.Run(true);
    405 }
    406 
    407 void LocalDiscoveryUIHandler::OnSetupDone() {
    408   std::string service_name = current_setup_operation_->service_name();
    409   current_setup_operation_.reset();
    410   current_register_operation_.reset();
    411   current_http_client_.reset();
    412   SendRegisterDone(service_name);
    413 }
    414 
    415 void LocalDiscoveryUIHandler::OnSetupError() {
    416   ResetCurrentRegistration();
    417   SendRegisterError();
    418 }
    419 
    420 void LocalDiscoveryUIHandler::OnConfirmDone(GCDApiFlow::Status status) {
    421   if (status == GCDApiFlow::SUCCESS) {
    422     confirm_api_call_flow_.reset();
    423     current_register_operation_->CompleteRegistration();
    424   } else {
    425     SendRegisterError();
    426   }
    427 }
    428 
    429 void LocalDiscoveryUIHandler::DeviceChanged(
    430     bool added,
    431     const std::string& name,
    432     const DeviceDescription& description) {
    433   device_descriptions_[name] = description;
    434 
    435   base::DictionaryValue info;
    436 
    437   base::StringValue service_key(kKeyPrefixMDns + name);
    438 
    439   if (description.id.empty()) {
    440     info.SetString(kDictionaryKeyServiceName, name);
    441     info.SetString(kDictionaryKeyDisplayName, description.name);
    442     info.SetString(kDictionaryKeyDescription, description.description);
    443     info.SetString(kDictionaryKeyType, description.type);
    444     info.SetBoolean(kDictionaryKeyIsWifi, false);
    445 
    446     web_ui()->CallJavascriptFunction(
    447         "local_discovery.onUnregisteredDeviceUpdate", service_key, info);
    448   } else {
    449     scoped_ptr<base::Value> null_value(base::Value::CreateNullValue());
    450 
    451     web_ui()->CallJavascriptFunction(
    452         "local_discovery.onUnregisteredDeviceUpdate", service_key, *null_value);
    453   }
    454 }
    455 
    456 void LocalDiscoveryUIHandler::DeviceRemoved(const std::string& name) {
    457   device_descriptions_.erase(name);
    458   scoped_ptr<base::Value> null_value(base::Value::CreateNullValue());
    459   base::StringValue name_value(kKeyPrefixMDns + name);
    460 
    461   web_ui()->CallJavascriptFunction("local_discovery.onUnregisteredDeviceUpdate",
    462                                    name_value, *null_value);
    463 }
    464 
    465 void LocalDiscoveryUIHandler::DeviceCacheFlushed() {
    466   web_ui()->CallJavascriptFunction("local_discovery.onDeviceCacheFlushed");
    467   privet_lister_->DiscoverNewDevices(false);
    468 }
    469 
    470 void LocalDiscoveryUIHandler::OnDeviceListReady(
    471     const std::vector<Device>& devices) {
    472   cloud_devices_.insert(cloud_devices_.end(), devices.begin(), devices.end());
    473   ++succeded_list_count_;
    474   CheckListingDone();
    475 }
    476 
    477 void LocalDiscoveryUIHandler::OnDeviceListUnavailable() {
    478   ++failed_list_count_;
    479   CheckListingDone();
    480 }
    481 
    482 void LocalDiscoveryUIHandler::GoogleSigninSucceeded(
    483     const std::string& account_id,
    484     const std::string& username,
    485     const std::string& password) {
    486   CheckUserLoggedIn();
    487 }
    488 
    489 void LocalDiscoveryUIHandler::GoogleSignedOut(const std::string& account_id,
    490                                               const std::string& username) {
    491   CheckUserLoggedIn();
    492 }
    493 
    494 void LocalDiscoveryUIHandler::SendRegisterError() {
    495   web_ui()->CallJavascriptFunction("local_discovery.onRegistrationFailed");
    496 }
    497 
    498 void LocalDiscoveryUIHandler::SendRegisterDone(
    499     const std::string& service_name) {
    500   // HACK(noamsml): Generate network traffic so the Windows firewall doesn't
    501   // block the printer's announcement.
    502   privet_lister_->DiscoverNewDevices(false);
    503 
    504   DeviceDescriptionMap::iterator found =
    505       device_descriptions_.find(service_name);
    506 
    507   if (found == device_descriptions_.end()) {
    508     // TODO(noamsml): Handle the case where a printer's record is not present at
    509     // the end of registration.
    510     SendRegisterError();
    511     return;
    512   }
    513 
    514   const DeviceDescription& device = found->second;
    515   base::DictionaryValue device_value;
    516 
    517   device_value.SetString(kDictionaryKeyType, device.type);
    518   device_value.SetString(kDictionaryKeyID, device.id);
    519   device_value.SetString(kDictionaryKeyDisplayName, device.name);
    520   device_value.SetString(kDictionaryKeyDescription, device.description);
    521   device_value.SetString(kDictionaryKeyServiceName, service_name);
    522 
    523   web_ui()->CallJavascriptFunction("local_discovery.onRegistrationSuccess",
    524                                    device_value);
    525 }
    526 
    527 void LocalDiscoveryUIHandler::SetIsVisible(bool visible) {
    528   if (visible != is_visible_) {
    529     g_num_visible += visible ? 1 : -1;
    530     is_visible_ = visible;
    531   }
    532 }
    533 
    534 std::string LocalDiscoveryUIHandler::GetSyncAccount() {
    535   Profile* profile = Profile::FromWebUI(web_ui());
    536   SigninManagerBase* signin_manager =
    537       SigninManagerFactory::GetForProfileIfExists(profile);
    538 
    539   if (!signin_manager) {
    540     return "";
    541   }
    542 
    543   return signin_manager->GetAuthenticatedUsername();
    544 }
    545 
    546 // TODO(noamsml): Create master object for registration flow.
    547 void LocalDiscoveryUIHandler::ResetCurrentRegistration() {
    548   if (current_register_operation_) {
    549     current_register_operation_->Cancel();
    550     current_register_operation_.reset();
    551   }
    552 
    553   current_setup_operation_.reset();
    554   confirm_api_call_flow_.reset();
    555   privet_resolution_.reset();
    556   current_http_client_.reset();
    557 }
    558 
    559 void LocalDiscoveryUIHandler::PrivetClientToV3(
    560     const PrivetClientCallback& callback,
    561     scoped_ptr<PrivetHTTPClient> client) {
    562   callback.Run(client.Pass());
    563 }
    564 
    565 void LocalDiscoveryUIHandler::CheckUserLoggedIn() {
    566   base::FundamentalValue logged_in_value(!GetSyncAccount().empty());
    567   base::FundamentalValue is_supervised_value(IsUserSupervisedOrOffTheRecord());
    568   web_ui()->CallJavascriptFunction(
    569       "local_discovery.setUserLoggedIn", logged_in_value, is_supervised_value);
    570 }
    571 
    572 void LocalDiscoveryUIHandler::CheckListingDone() {
    573   int started = 0;
    574   if (cloud_print_printer_list_)
    575     ++started;
    576   if (cloud_device_list_)
    577     ++started;
    578 
    579   if (started > failed_list_count_ + succeded_list_count_)
    580     return;
    581 
    582   if (succeded_list_count_ <= 0) {
    583     web_ui()->CallJavascriptFunction(
    584         "local_discovery.onCloudDeviceListUnavailable");
    585     return;
    586   }
    587 
    588   base::ListValue devices_list;
    589   std::set<std::string> local_ids;
    590 
    591   for (DeviceDescriptionMap::iterator i = device_descriptions_.begin();
    592        i != device_descriptions_.end(); i++) {
    593     local_ids.insert(i->second.id);
    594   }
    595 
    596   ReadDevicesList(cloud_devices_, local_ids, &devices_list);
    597 
    598   web_ui()->CallJavascriptFunction(
    599       "local_discovery.onCloudDeviceListAvailable", devices_list);
    600   cloud_print_printer_list_.reset();
    601   cloud_device_list_.reset();
    602 }
    603 
    604 scoped_ptr<GCDApiFlow> LocalDiscoveryUIHandler::CreateApiFlow() {
    605   Profile* profile = Profile::FromWebUI(web_ui());
    606   if (!profile)
    607     return scoped_ptr<GCDApiFlow>();
    608   ProfileOAuth2TokenService* token_service =
    609       ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
    610   if (!token_service)
    611     return scoped_ptr<GCDApiFlow>();
    612   SigninManagerBase* signin_manager =
    613       SigninManagerFactory::GetInstance()->GetForProfile(profile);
    614   if (!signin_manager)
    615     return scoped_ptr<GCDApiFlow>();
    616   return GCDApiFlow::Create(profile->GetRequestContext(),
    617                             token_service,
    618                             signin_manager->GetAuthenticatedAccountId());
    619 }
    620 
    621 void LocalDiscoveryUIHandler::CreatePrivetV3Client(
    622     const std::string& device,
    623     const PrivetClientCallback& callback) {
    624   DeviceDescriptionMap::iterator found = device_descriptions_.find(device);
    625   if (found == device_descriptions_.end())
    626     return callback.Run(scoped_ptr<PrivetHTTPClient>());
    627   PrivetHTTPAsynchronousFactory::ResultCallback new_callback =
    628       base::Bind(&LocalDiscoveryUIHandler::PrivetClientToV3,
    629                  base::Unretained(this),
    630                  callback);
    631   privet_resolution_ =
    632       privet_http_factory_->CreatePrivetHTTP(device,
    633                                              found->second.address,
    634                                              new_callback).Pass();
    635   if (!privet_resolution_)
    636     return callback.Run(scoped_ptr<PrivetHTTPClient>());
    637   privet_resolution_->Start();
    638 }
    639 
    640 bool LocalDiscoveryUIHandler::IsUserSupervisedOrOffTheRecord() {
    641   Profile* profile = Profile::FromWebUI(web_ui());
    642 
    643   return profile->IsSupervised() || profile->IsOffTheRecord();
    644 }
    645 
    646 #if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
    647 void LocalDiscoveryUIHandler::StartCloudPrintConnector() {
    648   Profile* profile = Profile::FromWebUI(web_ui());
    649 
    650   base::Closure cloud_print_callback = base::Bind(
    651       &LocalDiscoveryUIHandler::OnCloudPrintPrefsChanged,
    652           base::Unretained(this));
    653 
    654   if (cloud_print_connector_email_.GetPrefName().empty()) {
    655     cloud_print_connector_email_.Init(
    656         prefs::kCloudPrintEmail, profile->GetPrefs(), cloud_print_callback);
    657   }
    658 
    659   if (cloud_print_connector_enabled_.GetPrefName().empty()) {
    660     cloud_print_connector_enabled_.Init(
    661         prefs::kCloudPrintProxyEnabled, profile->GetPrefs(),
    662         cloud_print_callback);
    663   }
    664 
    665   if (cloud_print_connector_ui_enabled_) {
    666     SetupCloudPrintConnectorSection();
    667     RefreshCloudPrintStatusFromService();
    668   } else {
    669     RemoveCloudPrintConnectorSection();
    670   }
    671 }
    672 
    673 void LocalDiscoveryUIHandler::OnCloudPrintPrefsChanged() {
    674   if (cloud_print_connector_ui_enabled_)
    675     SetupCloudPrintConnectorSection();
    676 }
    677 
    678 void LocalDiscoveryUIHandler::ShowCloudPrintSetupDialog(
    679     const base::ListValue* args) {
    680   content::RecordAction(
    681       base::UserMetricsAction("Options_EnableCloudPrintProxy"));
    682   // Open the connector enable page in the current tab.
    683   Profile* profile = Profile::FromWebUI(web_ui());
    684   content::OpenURLParams params(
    685       cloud_devices::GetCloudPrintEnableURL(
    686           CloudPrintProxyServiceFactory::GetForProfile(profile)->proxy_id()),
    687       content::Referrer(),
    688       CURRENT_TAB,
    689       ui::PAGE_TRANSITION_LINK,
    690       false);
    691   web_ui()->GetWebContents()->OpenURL(params);
    692 }
    693 
    694 void LocalDiscoveryUIHandler::HandleDisableCloudPrintConnector(
    695     const base::ListValue* args) {
    696   content::RecordAction(
    697       base::UserMetricsAction("Options_DisableCloudPrintProxy"));
    698   CloudPrintProxyServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()))->
    699       DisableForUser();
    700 }
    701 
    702 void LocalDiscoveryUIHandler::SetupCloudPrintConnectorSection() {
    703   Profile* profile = Profile::FromWebUI(web_ui());
    704 
    705   if (!CloudPrintProxyServiceFactory::GetForProfile(profile)) {
    706     cloud_print_connector_ui_enabled_ = false;
    707     RemoveCloudPrintConnectorSection();
    708     return;
    709   }
    710 
    711   bool cloud_print_connector_allowed =
    712       !cloud_print_connector_enabled_.IsManaged() ||
    713       cloud_print_connector_enabled_.GetValue();
    714   base::FundamentalValue allowed(cloud_print_connector_allowed);
    715 
    716   std::string email;
    717   if (profile->GetPrefs()->HasPrefPath(prefs::kCloudPrintEmail) &&
    718       cloud_print_connector_allowed) {
    719     email = profile->GetPrefs()->GetString(prefs::kCloudPrintEmail);
    720   }
    721   base::FundamentalValue disabled(email.empty());
    722 
    723   base::string16 label_str;
    724   if (email.empty()) {
    725     label_str = l10n_util::GetStringFUTF16(
    726         IDS_LOCAL_DISCOVERY_CLOUD_PRINT_CONNECTOR_DISABLED_LABEL,
    727         l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT));
    728   } else {
    729     label_str = l10n_util::GetStringFUTF16(
    730         IDS_OPTIONS_CLOUD_PRINT_CONNECTOR_ENABLED_LABEL,
    731         l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT),
    732         base::UTF8ToUTF16(email));
    733   }
    734   base::StringValue label(label_str);
    735 
    736   web_ui()->CallJavascriptFunction(
    737       "local_discovery.setupCloudPrintConnectorSection", disabled, label,
    738       allowed);
    739 }
    740 
    741 void LocalDiscoveryUIHandler::RemoveCloudPrintConnectorSection() {
    742   web_ui()->CallJavascriptFunction(
    743       "local_discovery.removeCloudPrintConnectorSection");
    744 }
    745 
    746 void LocalDiscoveryUIHandler::RefreshCloudPrintStatusFromService() {
    747   if (cloud_print_connector_ui_enabled_)
    748     CloudPrintProxyServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()))->
    749         RefreshStatusFromService();
    750 }
    751 
    752 #endif // cloud print connector option stuff
    753 
    754 #if defined(ENABLE_WIFI_BOOTSTRAPPING)
    755 
    756 void LocalDiscoveryUIHandler::StartWifiBootstrapping() {
    757   // Since LocalDiscoveryUIHandler isn't destroyed every time the page is
    758   // refreshed, reset bootstrapping_device_lister_ so it's destoryed before
    759   // wifi_manager_ is.
    760   bootstrapping_device_lister_.reset();
    761 
    762   wifi_manager_ = wifi::WifiManager::Create();
    763   bootstrapping_device_lister_.reset(new wifi::BootstrappingDeviceLister(
    764       wifi_manager_.get(),
    765       base::Bind(&LocalDiscoveryUIHandler::OnBootstrappingDeviceChanged,
    766                  base::Unretained(this))));
    767 
    768   wifi_manager_->Start();
    769   bootstrapping_device_lister_->Start();
    770   wifi_manager_->RequestScan();
    771 }
    772 
    773 void LocalDiscoveryUIHandler::OnBootstrappingDeviceChanged(
    774     bool available,
    775     const wifi::BootstrappingDeviceDescription& description) {
    776   base::DictionaryValue info;
    777 
    778   base::StringValue service_key(kKeyPrefixWifi + description.device_ssid);
    779 
    780   if (available) {
    781     info.SetString(kDictionaryKeyServiceName, description.device_ssid);
    782     info.SetString(kDictionaryKeyDisplayName, description.device_name);
    783     info.SetString(kDictionaryKeyDescription, std::string());
    784     info.SetString(kDictionaryKeyType, description.device_kind);
    785     info.SetBoolean(kDictionaryKeyIsWifi, true);
    786 
    787     web_ui()->CallJavascriptFunction(
    788         "local_discovery.onUnregisteredDeviceUpdate", service_key, info);
    789   } else {
    790     scoped_ptr<base::Value> null_value(base::Value::CreateNullValue());
    791 
    792     web_ui()->CallJavascriptFunction(
    793         "local_discovery.onUnregisteredDeviceUpdate", service_key, *null_value);
    794   }
    795 }
    796 
    797 #endif  // ENABLE_WIFI_BOOTSTRAPPING
    798 
    799 }  // namespace local_discovery
    800