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
50namespace local_discovery {
51
52namespace {
53
54const char kDictionaryKeyServiceName[] = "service_name";
55const char kDictionaryKeyDisplayName[] = "display_name";
56const char kDictionaryKeyDescription[] = "description";
57const char kDictionaryKeyType[] = "type";
58const char kDictionaryKeyIsWifi[] = "is_wifi";
59const char kDictionaryKeyID[] = "id";
60
61const char kKeyPrefixMDns[] = "MDns:";
62
63#if defined(ENABLE_WIFI_BOOTSTRAPPING)
64const char kKeyPrefixWifi[] = "WiFi:";
65#endif  // ENABLE_WIFI_BOOTSTRAPPING
66
67int g_num_visible = 0;
68
69const int kCloudDevicesPrivetVersion = 3;
70
71scoped_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
83void 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
102LocalDiscoveryUIHandler::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
122LocalDiscoveryUIHandler::~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
133bool LocalDiscoveryUIHandler::GetHasVisible() {
134  return g_num_visible != 0;
135}
136
137void 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
180void 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
215void 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
222void 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
250void LocalDiscoveryUIHandler::HandleConfirmCode(const base::ListValue* args) {
251  device_code_callback_.Run(true);
252}
253
254void LocalDiscoveryUIHandler::HandleCancelRegistration(
255    const base::ListValue* args) {
256  ResetCurrentRegistration();
257}
258
259void 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
282void 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
297void 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
311void 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
327void 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
350void 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
374void 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
384void LocalDiscoveryUIHandler::GetWiFiCredentials(
385    const CredentialsCallback& callback) {
386  callback.Run("", "");
387}
388
389void LocalDiscoveryUIHandler::SwitchToSetupWiFi(
390    const ResultCallback& callback) {
391  callback.Run(true);
392}
393
394void 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
403void LocalDiscoveryUIHandler::RestoreWifi(const ResultCallback& callback) {
404  callback.Run(true);
405}
406
407void 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
415void LocalDiscoveryUIHandler::OnSetupError() {
416  ResetCurrentRegistration();
417  SendRegisterError();
418}
419
420void 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
429void 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
456void 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
465void LocalDiscoveryUIHandler::DeviceCacheFlushed() {
466  web_ui()->CallJavascriptFunction("local_discovery.onDeviceCacheFlushed");
467  privet_lister_->DiscoverNewDevices(false);
468}
469
470void 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
477void LocalDiscoveryUIHandler::OnDeviceListUnavailable() {
478  ++failed_list_count_;
479  CheckListingDone();
480}
481
482void LocalDiscoveryUIHandler::GoogleSigninSucceeded(
483    const std::string& account_id,
484    const std::string& username,
485    const std::string& password) {
486  CheckUserLoggedIn();
487}
488
489void LocalDiscoveryUIHandler::GoogleSignedOut(const std::string& account_id,
490                                              const std::string& username) {
491  CheckUserLoggedIn();
492}
493
494void LocalDiscoveryUIHandler::SendRegisterError() {
495  web_ui()->CallJavascriptFunction("local_discovery.onRegistrationFailed");
496}
497
498void 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
527void LocalDiscoveryUIHandler::SetIsVisible(bool visible) {
528  if (visible != is_visible_) {
529    g_num_visible += visible ? 1 : -1;
530    is_visible_ = visible;
531  }
532}
533
534std::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.
547void 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
559void LocalDiscoveryUIHandler::PrivetClientToV3(
560    const PrivetClientCallback& callback,
561    scoped_ptr<PrivetHTTPClient> client) {
562  callback.Run(client.Pass());
563}
564
565void 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
572void 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
604scoped_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
621void 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
640bool 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)
647void 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
673void LocalDiscoveryUIHandler::OnCloudPrintPrefsChanged() {
674  if (cloud_print_connector_ui_enabled_)
675    SetupCloudPrintConnectorSection();
676}
677
678void 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
694void 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
702void 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
741void LocalDiscoveryUIHandler::RemoveCloudPrintConnectorSection() {
742  web_ui()->CallJavascriptFunction(
743      "local_discovery.removeCloudPrintConnectorSection");
744}
745
746void 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
756void 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
773void 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