Simplified API of GCDBaseApiFlow::Delegate
[chromium-blink-merge.git] / chrome / browser / ui / webui / local_discovery / local_discovery_ui_handler.cc
blob83db5bd913bb47a885779377ab729e583b4574bf
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.
5 #include "chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h"
7 #include <set>
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 "components/cloud_devices/common/cloud_devices_switches.h"
33 #include "components/cloud_devices/common/cloud_devices_urls.h"
34 #include "components/signin/core/browser/profile_oauth2_token_service.h"
35 #include "components/signin/core/browser/signin_manager_base.h"
36 #include "content/public/browser/user_metrics.h"
37 #include "content/public/browser/web_ui.h"
38 #include "content/public/common/page_transition_types.h"
39 #include "grit/generated_resources.h"
40 #include "net/base/host_port_pair.h"
41 #include "net/base/net_util.h"
42 #include "net/base/url_util.h"
43 #include "net/http/http_status_code.h"
44 #include "ui/base/l10n/l10n_util.h"
46 #if defined(ENABLE_FULL_PRINTING) && !defined(OS_CHROMEOS)
47 #define CLOUD_PRINT_CONNECTOR_UI_AVAILABLE
48 #endif
50 namespace local_discovery {
52 namespace {
54 int g_num_visible = 0;
56 scoped_ptr<base::DictionaryValue> CreateDeviceInfo(
57 const CloudDeviceListDelegate::Device& description) {
58 scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue);
60 return_value->SetString("id", description.id);
61 return_value->SetString("display_name", description.display_name);
62 return_value->SetString("description", description.description);
63 return_value->SetString("type", description.type);
65 return return_value.Pass();
68 void ReadDevicesList(
69 const std::vector<CloudDeviceListDelegate::Device>& devices,
70 const std::set<std::string>& local_ids,
71 base::ListValue* devices_list) {
72 for (CloudDeviceList::iterator i = devices.begin(); i != devices.end(); i++) {
73 if (local_ids.count(i->id) > 0) {
74 devices_list->Append(CreateDeviceInfo(*i).release());
78 for (CloudDeviceList::iterator i = devices.begin(); i != devices.end(); i++) {
79 if (local_ids.count(i->id) == 0) {
80 devices_list->Append(CreateDeviceInfo(*i).release());
85 } // namespace
87 LocalDiscoveryUIHandler::LocalDiscoveryUIHandler()
88 : is_visible_(false),
89 failed_list_count_(0),
90 succeded_list_count_(0) {
91 #if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
92 #if !defined(GOOGLE_CHROME_BUILD) && defined(OS_WIN)
93 // On Windows, we need the PDF plugin which is only guaranteed to exist on
94 // Google Chrome builds. Use a command-line switch for Windows non-Google
95 // Chrome builds.
96 cloud_print_connector_ui_enabled_ =
97 CommandLine::ForCurrentProcess()->HasSwitch(
98 switches::kEnableCloudPrintProxy);
99 #else
100 // Always enabled for Linux and Google Chrome Windows builds.
101 // Never enabled for Chrome OS, we don't even need to indicate it.
102 cloud_print_connector_ui_enabled_ = true;
103 #endif
104 #endif // defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
107 LocalDiscoveryUIHandler::~LocalDiscoveryUIHandler() {
108 Profile* profile = Profile::FromWebUI(web_ui());
109 SigninManagerBase* signin_manager =
110 SigninManagerFactory::GetInstance()->GetForProfile(profile);
111 if (signin_manager)
112 signin_manager->RemoveObserver(this);
113 ResetCurrentRegistration();
114 SetIsVisible(false);
117 // static
118 bool LocalDiscoveryUIHandler::GetHasVisible() {
119 return g_num_visible != 0;
122 void LocalDiscoveryUIHandler::RegisterMessages() {
123 web_ui()->RegisterMessageCallback("start", base::Bind(
124 &LocalDiscoveryUIHandler::HandleStart,
125 base::Unretained(this)));
126 web_ui()->RegisterMessageCallback("isVisible", base::Bind(
127 &LocalDiscoveryUIHandler::HandleIsVisible,
128 base::Unretained(this)));
129 web_ui()->RegisterMessageCallback("registerDevice", base::Bind(
130 &LocalDiscoveryUIHandler::HandleRegisterDevice,
131 base::Unretained(this)));
132 web_ui()->RegisterMessageCallback("cancelRegistration", base::Bind(
133 &LocalDiscoveryUIHandler::HandleCancelRegistration,
134 base::Unretained(this)));
135 web_ui()->RegisterMessageCallback(
136 "requestDeviceList",
137 base::Bind(&LocalDiscoveryUIHandler::HandleRequestDeviceList,
138 base::Unretained(this)));
139 web_ui()->RegisterMessageCallback("openCloudPrintURL", base::Bind(
140 &LocalDiscoveryUIHandler::HandleOpenCloudPrintURL,
141 base::Unretained(this)));
142 web_ui()->RegisterMessageCallback("showSyncUI", base::Bind(
143 &LocalDiscoveryUIHandler::HandleShowSyncUI,
144 base::Unretained(this)));
146 // Cloud print connector related messages
147 #if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
148 if (cloud_print_connector_ui_enabled_) {
149 web_ui()->RegisterMessageCallback(
150 "showCloudPrintSetupDialog",
151 base::Bind(&LocalDiscoveryUIHandler::ShowCloudPrintSetupDialog,
152 base::Unretained(this)));
153 web_ui()->RegisterMessageCallback(
154 "disableCloudPrintConnector",
155 base::Bind(&LocalDiscoveryUIHandler::HandleDisableCloudPrintConnector,
156 base::Unretained(this)));
158 #endif // defined(ENABLE_FULL_PRINTING)
161 void LocalDiscoveryUIHandler::HandleStart(const base::ListValue* args) {
162 Profile* profile = Profile::FromWebUI(web_ui());
164 // If privet_lister_ is already set, it is a mock used for tests or the result
165 // of a reload.
166 if (!privet_lister_) {
167 service_discovery_client_ = ServiceDiscoverySharedClient::GetInstance();
168 privet_lister_.reset(
169 new PrivetDeviceListerImpl(service_discovery_client_.get(), this));
170 privet_http_factory_ =
171 PrivetHTTPAsynchronousFactory::CreateInstance(
172 service_discovery_client_.get(), profile->GetRequestContext());
174 SigninManagerBase* signin_manager =
175 SigninManagerFactory::GetInstance()->GetForProfile(profile);
176 if (signin_manager)
177 signin_manager->AddObserver(this);
180 privet_lister_->Start();
181 privet_lister_->DiscoverNewDevices(false);
183 #if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
184 StartCloudPrintConnector();
185 #endif
187 CheckUserLoggedIn();
190 void LocalDiscoveryUIHandler::HandleIsVisible(const base::ListValue* args) {
191 bool is_visible = false;
192 bool rv = args->GetBoolean(0, &is_visible);
193 DCHECK(rv);
194 SetIsVisible(is_visible);
197 void LocalDiscoveryUIHandler::HandleRegisterDevice(
198 const base::ListValue* args) {
199 std::string device;
201 bool rv = args->GetString(0, &device);
202 DCHECK(rv);
204 privet_resolution_ = privet_http_factory_->CreatePrivetHTTP(
205 device,
206 device_descriptions_[device].address,
207 base::Bind(&LocalDiscoveryUIHandler::StartRegisterHTTP,
208 base::Unretained(this)));
209 privet_resolution_->Start();
212 void LocalDiscoveryUIHandler::HandleCancelRegistration(
213 const base::ListValue* args) {
214 ResetCurrentRegistration();
217 void LocalDiscoveryUIHandler::HandleRequestDeviceList(
218 const base::ListValue* args) {
219 failed_list_count_ = 0;
220 succeded_list_count_ = 0;
221 cloud_devices_.clear();
223 cloud_print_printer_list_ = CreateApiFlow(
224 scoped_ptr<GCDApiFlow::Request>(new CloudPrintPrinterList(this)));
225 if (CommandLine::ForCurrentProcess()->HasSwitch(
226 switches::kEnableCloudDevices)) {
227 cloud_device_list_ = CreateApiFlow(
228 scoped_ptr<GCDApiFlow::Request>(new CloudDeviceList(this)));
231 if (cloud_print_printer_list_)
232 cloud_print_printer_list_->Start();
233 if (cloud_device_list_)
234 cloud_device_list_->Start();
235 CheckListingDone();
238 void LocalDiscoveryUIHandler::HandleOpenCloudPrintURL(
239 const base::ListValue* args) {
240 std::string id;
241 bool rv = args->GetString(0, &id);
242 DCHECK(rv);
244 Browser* browser = chrome::FindBrowserWithWebContents(
245 web_ui()->GetWebContents());
246 DCHECK(browser);
248 chrome::AddSelectedTabWithURL(browser,
249 cloud_devices::GetCloudPrintManageDeviceURL(id),
250 content::PAGE_TRANSITION_FROM_API);
253 void LocalDiscoveryUIHandler::HandleShowSyncUI(
254 const base::ListValue* args) {
255 Browser* browser = chrome::FindBrowserWithWebContents(
256 web_ui()->GetWebContents());
257 DCHECK(browser);
259 GURL url(signin::GetPromoURL(signin::SOURCE_DEVICES_PAGE,
260 true)); // auto close after success.
262 browser->OpenURL(
263 content::OpenURLParams(url, content::Referrer(), SINGLETON_TAB,
264 content::PAGE_TRANSITION_AUTO_BOOKMARK, false));
267 void LocalDiscoveryUIHandler::StartRegisterHTTP(
268 scoped_ptr<PrivetHTTPClient> http_client) {
269 current_http_client_.swap(http_client);
271 std::string user = GetSyncAccount();
273 if (!current_http_client_) {
274 SendRegisterError();
275 return;
278 current_register_operation_ =
279 current_http_client_->CreateRegisterOperation(user, this);
280 current_register_operation_->Start();
283 void LocalDiscoveryUIHandler::OnPrivetRegisterClaimToken(
284 PrivetRegisterOperation* operation,
285 const std::string& token,
286 const GURL& url) {
287 web_ui()->CallJavascriptFunction(
288 "local_discovery.onRegistrationConfirmedOnPrinter");
289 if (device_descriptions_.count(current_http_client_->GetName()) == 0) {
290 SendRegisterError();
291 return;
294 confirm_api_call_flow_ = CreateApiFlow(
295 scoped_ptr<GCDApiFlow::Request>(new PrivetConfirmApiCallFlow(
296 token,
297 base::Bind(&LocalDiscoveryUIHandler::OnConfirmDone,
298 base::Unretained(this)))));
299 if (!confirm_api_call_flow_) {
300 SendRegisterError();
301 return;
303 confirm_api_call_flow_->Start();
306 void LocalDiscoveryUIHandler::OnPrivetRegisterError(
307 PrivetRegisterOperation* operation,
308 const std::string& action,
309 PrivetRegisterOperation::FailureReason reason,
310 int printer_http_code,
311 const base::DictionaryValue* json) {
312 std::string error;
314 if (reason == PrivetRegisterOperation::FAILURE_JSON_ERROR &&
315 json->GetString(kPrivetKeyError, &error)) {
316 if (error == kPrivetErrorTimeout) {
317 web_ui()->CallJavascriptFunction(
318 "local_discovery.onRegistrationTimeout");
319 return;
320 } else if (error == kPrivetErrorCancel) {
321 web_ui()->CallJavascriptFunction(
322 "local_discovery.onRegistrationCanceledPrinter");
323 return;
327 SendRegisterError();
330 void LocalDiscoveryUIHandler::OnPrivetRegisterDone(
331 PrivetRegisterOperation* operation,
332 const std::string& device_id) {
333 std::string name = operation->GetHTTPClient()->GetName();
335 current_register_operation_.reset();
336 current_http_client_.reset();
338 // HACK(noamsml): Generate network traffic so the Windows firewall doesn't
339 // block the printer's announcement.
340 privet_lister_->DiscoverNewDevices(false);
342 DeviceDescriptionMap::iterator found = device_descriptions_.find(name);
344 if (found == device_descriptions_.end()) {
345 // TODO(noamsml): Handle the case where a printer's record is not present at
346 // the end of registration.
347 SendRegisterError();
348 return;
351 SendRegisterDone(found->first, found->second);
354 void LocalDiscoveryUIHandler::OnConfirmDone(GCDApiFlow::Status status) {
355 if (status == GCDApiFlow::SUCCESS) {
356 confirm_api_call_flow_.reset();
357 current_register_operation_->CompleteRegistration();
358 } else {
359 SendRegisterError();
363 void LocalDiscoveryUIHandler::DeviceChanged(
364 bool added,
365 const std::string& name,
366 const DeviceDescription& description) {
367 device_descriptions_[name] = description;
369 base::DictionaryValue info;
371 base::StringValue service_name(name);
372 scoped_ptr<base::Value> null_value(base::Value::CreateNullValue());
374 if (description.id.empty()) {
375 info.SetString("service_name", name);
376 info.SetString("human_readable_name", description.name);
377 info.SetString("description", description.description);
378 info.SetString("type", description.type);
380 web_ui()->CallJavascriptFunction(
381 "local_discovery.onUnregisteredDeviceUpdate",
382 service_name, info);
383 } else {
384 web_ui()->CallJavascriptFunction(
385 "local_discovery.onUnregisteredDeviceUpdate",
386 service_name, *null_value);
390 void LocalDiscoveryUIHandler::DeviceRemoved(const std::string& name) {
391 device_descriptions_.erase(name);
392 scoped_ptr<base::Value> null_value(base::Value::CreateNullValue());
393 base::StringValue name_value(name);
395 web_ui()->CallJavascriptFunction("local_discovery.onUnregisteredDeviceUpdate",
396 name_value, *null_value);
399 void LocalDiscoveryUIHandler::DeviceCacheFlushed() {
400 web_ui()->CallJavascriptFunction("local_discovery.onDeviceCacheFlushed");
401 privet_lister_->DiscoverNewDevices(false);
404 void LocalDiscoveryUIHandler::OnDeviceListReady(
405 const std::vector<Device>& devices) {
406 cloud_devices_.insert(cloud_devices_.end(), devices.begin(), devices.end());
407 ++succeded_list_count_;
408 CheckListingDone();
411 void LocalDiscoveryUIHandler::OnDeviceListUnavailable() {
412 ++failed_list_count_;
413 CheckListingDone();
416 void LocalDiscoveryUIHandler::GoogleSigninSucceeded(
417 const std::string& username,
418 const std::string& password) {
419 CheckUserLoggedIn();
422 void LocalDiscoveryUIHandler::GoogleSignedOut(const std::string& username) {
423 CheckUserLoggedIn();
426 void LocalDiscoveryUIHandler::SendRegisterError() {
427 web_ui()->CallJavascriptFunction("local_discovery.onRegistrationFailed");
430 void LocalDiscoveryUIHandler::SendRegisterDone(
431 const std::string& service_name, const DeviceDescription& device) {
432 base::DictionaryValue printer_value;
434 printer_value.SetString("id", device.id);
435 printer_value.SetString("display_name", device.name);
436 printer_value.SetString("description", device.description);
437 printer_value.SetString("service_name", service_name);
439 web_ui()->CallJavascriptFunction("local_discovery.onRegistrationSuccess",
440 printer_value);
443 void LocalDiscoveryUIHandler::SetIsVisible(bool visible) {
444 if (visible != is_visible_) {
445 g_num_visible += visible ? 1 : -1;
446 is_visible_ = visible;
450 std::string LocalDiscoveryUIHandler::GetSyncAccount() {
451 Profile* profile = Profile::FromWebUI(web_ui());
452 SigninManagerBase* signin_manager =
453 SigninManagerFactory::GetForProfileIfExists(profile);
455 if (!signin_manager) {
456 return "";
459 return signin_manager->GetAuthenticatedUsername();
462 // TODO(noamsml): Create master object for registration flow.
463 void LocalDiscoveryUIHandler::ResetCurrentRegistration() {
464 if (current_register_operation_.get()) {
465 current_register_operation_->Cancel();
466 current_register_operation_.reset();
469 confirm_api_call_flow_.reset();
470 privet_resolution_.reset();
471 current_http_client_.reset();
474 void LocalDiscoveryUIHandler::CheckUserLoggedIn() {
475 base::FundamentalValue logged_in_value(!GetSyncAccount().empty());
476 web_ui()->CallJavascriptFunction("local_discovery.setUserLoggedIn",
477 logged_in_value);
480 void LocalDiscoveryUIHandler::CheckListingDone() {
481 int started = 0;
482 if (cloud_print_printer_list_)
483 ++started;
484 if (cloud_device_list_)
485 ++started;
487 if (started > failed_list_count_ + succeded_list_count_)
488 return;
490 if (succeded_list_count_ <= 0) {
491 web_ui()->CallJavascriptFunction(
492 "local_discovery.onCloudDeviceListUnavailable");
493 return;
496 base::ListValue devices_list;
497 std::set<std::string> local_ids;
499 for (DeviceDescriptionMap::iterator i = device_descriptions_.begin();
500 i != device_descriptions_.end(); i++) {
501 local_ids.insert(i->second.id);
504 ReadDevicesList(cloud_devices_, local_ids, &devices_list);
506 web_ui()->CallJavascriptFunction(
507 "local_discovery.onCloudDeviceListAvailable", devices_list);
508 cloud_print_printer_list_.reset();
509 cloud_device_list_.reset();
512 scoped_ptr<GCDApiFlow> LocalDiscoveryUIHandler::CreateApiFlow(
513 scoped_ptr<GCDApiFlow::Request> request) {
514 Profile* profile = Profile::FromWebUI(web_ui());
515 if (!profile)
516 return scoped_ptr<GCDApiFlow>();
517 ProfileOAuth2TokenService* token_service =
518 ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
519 if (!token_service)
520 return scoped_ptr<GCDApiFlow>();
521 SigninManagerBase* signin_manager =
522 SigninManagerFactory::GetInstance()->GetForProfile(profile);
523 if (!signin_manager)
524 return scoped_ptr<GCDApiFlow>();
525 return make_scoped_ptr(
526 new GCDApiFlow(profile->GetRequestContext(),
527 token_service,
528 signin_manager->GetAuthenticatedAccountId(),
529 request.Pass()));
532 #if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
533 void LocalDiscoveryUIHandler::StartCloudPrintConnector() {
534 Profile* profile = Profile::FromWebUI(web_ui());
536 base::Closure cloud_print_callback = base::Bind(
537 &LocalDiscoveryUIHandler::OnCloudPrintPrefsChanged,
538 base::Unretained(this));
540 if (cloud_print_connector_email_.GetPrefName().empty()) {
541 cloud_print_connector_email_.Init(
542 prefs::kCloudPrintEmail, profile->GetPrefs(), cloud_print_callback);
545 if (cloud_print_connector_enabled_.GetPrefName().empty()) {
546 cloud_print_connector_enabled_.Init(
547 prefs::kCloudPrintProxyEnabled, profile->GetPrefs(),
548 cloud_print_callback);
551 if (cloud_print_connector_ui_enabled_) {
552 SetupCloudPrintConnectorSection();
553 RefreshCloudPrintStatusFromService();
554 } else {
555 RemoveCloudPrintConnectorSection();
559 void LocalDiscoveryUIHandler::OnCloudPrintPrefsChanged() {
560 if (cloud_print_connector_ui_enabled_)
561 SetupCloudPrintConnectorSection();
564 void LocalDiscoveryUIHandler::ShowCloudPrintSetupDialog(
565 const base::ListValue* args) {
566 content::RecordAction(
567 base::UserMetricsAction("Options_EnableCloudPrintProxy"));
568 // Open the connector enable page in the current tab.
569 Profile* profile = Profile::FromWebUI(web_ui());
570 content::OpenURLParams params(
571 cloud_devices::GetCloudPrintEnableURL(
572 CloudPrintProxyServiceFactory::GetForProfile(profile)->proxy_id()),
573 content::Referrer(),
574 CURRENT_TAB,
575 content::PAGE_TRANSITION_LINK,
576 false);
577 web_ui()->GetWebContents()->OpenURL(params);
580 void LocalDiscoveryUIHandler::HandleDisableCloudPrintConnector(
581 const base::ListValue* args) {
582 content::RecordAction(
583 base::UserMetricsAction("Options_DisableCloudPrintProxy"));
584 CloudPrintProxyServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()))->
585 DisableForUser();
588 void LocalDiscoveryUIHandler::SetupCloudPrintConnectorSection() {
589 Profile* profile = Profile::FromWebUI(web_ui());
591 if (!CloudPrintProxyServiceFactory::GetForProfile(profile)) {
592 cloud_print_connector_ui_enabled_ = false;
593 RemoveCloudPrintConnectorSection();
594 return;
597 bool cloud_print_connector_allowed =
598 !cloud_print_connector_enabled_.IsManaged() ||
599 cloud_print_connector_enabled_.GetValue();
600 base::FundamentalValue allowed(cloud_print_connector_allowed);
602 std::string email;
603 if (profile->GetPrefs()->HasPrefPath(prefs::kCloudPrintEmail) &&
604 cloud_print_connector_allowed) {
605 email = profile->GetPrefs()->GetString(prefs::kCloudPrintEmail);
607 base::FundamentalValue disabled(email.empty());
609 base::string16 label_str;
610 if (email.empty()) {
611 label_str = l10n_util::GetStringFUTF16(
612 IDS_LOCAL_DISCOVERY_CLOUD_PRINT_CONNECTOR_DISABLED_LABEL,
613 l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT));
614 } else {
615 label_str = l10n_util::GetStringFUTF16(
616 IDS_OPTIONS_CLOUD_PRINT_CONNECTOR_ENABLED_LABEL,
617 l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT),
618 base::UTF8ToUTF16(email));
620 base::StringValue label(label_str);
622 web_ui()->CallJavascriptFunction(
623 "local_discovery.setupCloudPrintConnectorSection", disabled, label,
624 allowed);
627 void LocalDiscoveryUIHandler::RemoveCloudPrintConnectorSection() {
628 web_ui()->CallJavascriptFunction(
629 "local_discovery.removeCloudPrintConnectorSection");
632 void LocalDiscoveryUIHandler::RefreshCloudPrintStatusFromService() {
633 if (cloud_print_connector_ui_enabled_)
634 CloudPrintProxyServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()))->
635 RefreshStatusFromService();
638 #endif // cloud print connector option stuff
640 } // namespace local_discovery