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"
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_urls.h"
33 #include "components/signin/core/browser/profile_oauth2_token_service.h"
34 #include "components/signin/core/browser/signin_manager_base.h"
35 #include "content/public/browser/user_metrics.h"
36 #include "content/public/browser/web_ui.h"
37 #include "content/public/common/page_transition_types.h"
38 #include "grit/generated_resources.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"
45 #if defined(ENABLE_FULL_PRINTING) && !defined(OS_CHROMEOS)
46 #define CLOUD_PRINT_CONNECTOR_UI_AVAILABLE
49 namespace local_discovery {
52 const char kDeviceTypePrinter[] = "printer";
53 int g_num_visible = 0;
56 LocalDiscoveryUIHandler::LocalDiscoveryUIHandler() : is_visible_(false) {
57 #if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
58 #if !defined(GOOGLE_CHROME_BUILD) && defined(OS_WIN)
59 // On Windows, we need the PDF plugin which is only guaranteed to exist on
60 // Google Chrome builds. Use a command-line switch for Windows non-Google
62 cloud_print_connector_ui_enabled_ =
63 CommandLine::ForCurrentProcess()->HasSwitch(
64 switches::kEnableCloudPrintProxy);
66 // Always enabled for Linux and Google Chrome Windows builds.
67 // Never enabled for Chrome OS, we don't even need to indicate it.
68 cloud_print_connector_ui_enabled_ = true;
70 #endif // defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
73 LocalDiscoveryUIHandler::~LocalDiscoveryUIHandler() {
74 Profile* profile = Profile::FromWebUI(web_ui());
75 SigninManagerBase* signin_manager =
76 SigninManagerFactory::GetInstance()->GetForProfile(profile);
78 signin_manager->RemoveObserver(this);
79 ResetCurrentRegistration();
84 bool LocalDiscoveryUIHandler::GetHasVisible() {
85 return g_num_visible != 0;
88 void 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)));
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)));
123 #endif // defined(ENABLE_FULL_PRINTING)
126 void LocalDiscoveryUIHandler::HandleStart(const base::ListValue* args) {
127 Profile* profile = Profile::FromWebUI(web_ui());
129 // If privet_lister_ is already set, it is a mock used for tests or the result
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());
139 SigninManagerBase* signin_manager =
140 SigninManagerFactory::GetInstance()->GetForProfile(profile);
142 signin_manager->AddObserver(this);
145 privet_lister_->Start();
146 privet_lister_->DiscoverNewDevices(false);
148 #if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
149 StartCloudPrintConnector();
155 void LocalDiscoveryUIHandler::HandleIsVisible(const base::ListValue* args) {
156 bool is_visible = false;
157 bool rv = args->GetBoolean(0, &is_visible);
159 SetIsVisible(is_visible);
162 void LocalDiscoveryUIHandler::HandleRegisterDevice(
163 const base::ListValue* args) {
166 bool rv = args->GetString(0, &device);
169 privet_resolution_ = privet_http_factory_->CreatePrivetHTTP(
171 device_descriptions_[device].address,
172 base::Bind(&LocalDiscoveryUIHandler::StartRegisterHTTP,
173 base::Unretained(this)));
174 privet_resolution_->Start();
177 void LocalDiscoveryUIHandler::HandleCancelRegistration(
178 const base::ListValue* args) {
179 ResetCurrentRegistration();
182 void LocalDiscoveryUIHandler::HandleRequestPrinterList(
183 const base::ListValue* args) {
184 Profile* profile = Profile::FromWebUI(web_ui());
185 ProfileOAuth2TokenService* token_service =
186 ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
188 SigninManagerBase* signin_manager =
189 SigninManagerFactory::GetInstance()->GetForProfile(profile);
191 cloud_print_printer_list_.reset(
192 new CloudPrintPrinterList(profile->GetRequestContext(),
194 signin_manager->GetAuthenticatedAccountId(),
196 cloud_print_printer_list_->Start();
199 void LocalDiscoveryUIHandler::HandleOpenCloudPrintURL(
200 const base::ListValue* args) {
202 bool rv = args->GetString(0, &id);
205 Browser* browser = chrome::FindBrowserWithWebContents(
206 web_ui()->GetWebContents());
209 chrome::AddSelectedTabWithURL(browser,
210 cloud_devices::GetCloudPrintManageDeviceURL(id),
211 content::PAGE_TRANSITION_FROM_API);
214 void LocalDiscoveryUIHandler::HandleShowSyncUI(
215 const base::ListValue* args) {
216 Browser* browser = chrome::FindBrowserWithWebContents(
217 web_ui()->GetWebContents());
220 GURL url(signin::GetPromoURL(signin::SOURCE_DEVICES_PAGE,
221 true)); // auto close after success.
224 content::OpenURLParams(url, content::Referrer(), SINGLETON_TAB,
225 content::PAGE_TRANSITION_AUTO_BOOKMARK, false));
228 void LocalDiscoveryUIHandler::StartRegisterHTTP(
229 scoped_ptr<PrivetHTTPClient> http_client) {
230 current_http_client_.swap(http_client);
232 std::string user = GetSyncAccount();
234 if (!current_http_client_) {
239 current_register_operation_ =
240 current_http_client_->CreateRegisterOperation(user, this);
241 current_register_operation_->Start();
244 void LocalDiscoveryUIHandler::OnPrivetRegisterClaimToken(
245 PrivetRegisterOperation* operation,
246 const std::string& token,
248 web_ui()->CallJavascriptFunction(
249 "local_discovery.onRegistrationConfirmedOnPrinter");
250 if (device_descriptions_.count(current_http_client_->GetName()) == 0) {
255 bool is_cloud_print =
256 device_descriptions_[current_http_client_->GetName()].type ==
259 Profile* profile = Profile::FromWebUI(web_ui());
261 ProfileOAuth2TokenService* token_service =
262 ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
264 if (!token_service) {
269 SigninManagerBase* signin_manager =
270 SigninManagerFactory::GetInstance()->GetForProfile(profile);
271 if (!signin_manager) {
276 confirm_api_call_flow_.reset(new PrivetConfirmApiCallFlow(
277 profile->GetRequestContext(),
279 signin_manager->GetAuthenticatedAccountId(),
282 base::Bind(&LocalDiscoveryUIHandler::OnConfirmDone,
283 base::Unretained(this))));
284 confirm_api_call_flow_->Start();
287 void LocalDiscoveryUIHandler::OnPrivetRegisterError(
288 PrivetRegisterOperation* operation,
289 const std::string& action,
290 PrivetRegisterOperation::FailureReason reason,
291 int printer_http_code,
292 const base::DictionaryValue* json) {
295 if (reason == PrivetRegisterOperation::FAILURE_JSON_ERROR &&
296 json->GetString(kPrivetKeyError, &error)) {
297 if (error == kPrivetErrorTimeout) {
298 web_ui()->CallJavascriptFunction(
299 "local_discovery.onRegistrationTimeout");
301 } else if (error == kPrivetErrorCancel) {
302 web_ui()->CallJavascriptFunction(
303 "local_discovery.onRegistrationCanceledPrinter");
311 void LocalDiscoveryUIHandler::OnPrivetRegisterDone(
312 PrivetRegisterOperation* operation,
313 const std::string& device_id) {
314 std::string name = operation->GetHTTPClient()->GetName();
316 current_register_operation_.reset();
317 current_http_client_.reset();
319 // HACK(noamsml): Generate network traffic so the Windows firewall doesn't
320 // block the printer's announcement.
321 privet_lister_->DiscoverNewDevices(false);
323 DeviceDescriptionMap::iterator found = device_descriptions_.find(name);
325 if (found == device_descriptions_.end()) {
326 // TODO(noamsml): Handle the case where a printer's record is not present at
327 // the end of registration.
332 SendRegisterDone(found->first, found->second);
335 void LocalDiscoveryUIHandler::OnConfirmDone(GCDBaseApiFlow::Status status) {
336 if (status == GCDBaseApiFlow::SUCCESS) {
337 confirm_api_call_flow_.reset();
338 current_register_operation_->CompleteRegistration();
344 void LocalDiscoveryUIHandler::DeviceChanged(
346 const std::string& name,
347 const DeviceDescription& description) {
348 device_descriptions_[name] = description;
350 base::DictionaryValue info;
352 base::StringValue service_name(name);
353 scoped_ptr<base::Value> null_value(base::Value::CreateNullValue());
355 if (description.id.empty()) {
356 info.SetString("service_name", name);
357 info.SetString("human_readable_name", description.name);
358 info.SetString("description", description.description);
360 web_ui()->CallJavascriptFunction(
361 "local_discovery.onUnregisteredDeviceUpdate",
364 web_ui()->CallJavascriptFunction(
365 "local_discovery.onUnregisteredDeviceUpdate",
366 service_name, *null_value);
370 void LocalDiscoveryUIHandler::DeviceRemoved(const std::string& name) {
371 device_descriptions_.erase(name);
372 scoped_ptr<base::Value> null_value(base::Value::CreateNullValue());
373 base::StringValue name_value(name);
375 web_ui()->CallJavascriptFunction("local_discovery.onUnregisteredDeviceUpdate",
376 name_value, *null_value);
379 void LocalDiscoveryUIHandler::DeviceCacheFlushed() {
380 web_ui()->CallJavascriptFunction("local_discovery.onDeviceCacheFlushed");
381 privet_lister_->DiscoverNewDevices(false);
384 void LocalDiscoveryUIHandler::OnCloudPrintPrinterListReady() {
385 base::ListValue printer_object_list;
386 std::set<std::string> local_ids;
388 for (DeviceDescriptionMap::iterator i = device_descriptions_.begin();
389 i != device_descriptions_.end();
391 std::string device_id = i->second.id;
392 if (!device_id.empty()) {
393 const CloudPrintPrinterList::PrinterDetails* details =
394 cloud_print_printer_list_->GetDetailsFor(device_id);
397 local_ids.insert(device_id);
398 printer_object_list.Append(CreatePrinterInfo(*details).release());
403 for (CloudPrintPrinterList::iterator i = cloud_print_printer_list_->begin();
404 i != cloud_print_printer_list_->end(); i++) {
405 if (local_ids.count(i->id) == 0) {
406 printer_object_list.Append(CreatePrinterInfo(*i).release());
410 web_ui()->CallJavascriptFunction(
411 "local_discovery.onCloudDeviceListAvailable", printer_object_list);
414 void LocalDiscoveryUIHandler::OnCloudPrintPrinterListUnavailable() {
415 web_ui()->CallJavascriptFunction(
416 "local_discovery.onCloudDeviceListUnavailable");
419 void LocalDiscoveryUIHandler::GoogleSigninSucceeded(
420 const std::string& username,
421 const std::string& password) {
425 void LocalDiscoveryUIHandler::GoogleSignedOut(const std::string& username) {
429 void LocalDiscoveryUIHandler::SendRegisterError() {
430 web_ui()->CallJavascriptFunction("local_discovery.onRegistrationFailed");
433 void LocalDiscoveryUIHandler::SendRegisterDone(
434 const std::string& service_name, const DeviceDescription& device) {
435 base::DictionaryValue printer_value;
437 printer_value.SetString("id", device.id);
438 printer_value.SetString("display_name", device.name);
439 printer_value.SetString("description", device.description);
440 printer_value.SetString("service_name", service_name);
442 web_ui()->CallJavascriptFunction("local_discovery.onRegistrationSuccess",
446 void LocalDiscoveryUIHandler::SetIsVisible(bool visible) {
447 if (visible != is_visible_) {
448 g_num_visible += visible ? 1 : -1;
449 is_visible_ = visible;
453 std::string LocalDiscoveryUIHandler::GetSyncAccount() {
454 Profile* profile = Profile::FromWebUI(web_ui());
455 SigninManagerBase* signin_manager =
456 SigninManagerFactory::GetForProfileIfExists(profile);
458 if (!signin_manager) {
462 return signin_manager->GetAuthenticatedUsername();
465 // TODO(noamsml): Create master object for registration flow.
466 void LocalDiscoveryUIHandler::ResetCurrentRegistration() {
467 if (current_register_operation_.get()) {
468 current_register_operation_->Cancel();
469 current_register_operation_.reset();
472 confirm_api_call_flow_.reset();
473 privet_resolution_.reset();
474 current_http_client_.reset();
477 scoped_ptr<base::DictionaryValue> LocalDiscoveryUIHandler::CreatePrinterInfo(
478 const CloudPrintPrinterList::PrinterDetails& description) {
479 scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue);
481 return_value->SetString("id", description.id);
482 return_value->SetString("display_name", description.display_name);
483 return_value->SetString("description", description.description);
485 return return_value.Pass();
488 void LocalDiscoveryUIHandler::CheckUserLoggedIn() {
489 base::FundamentalValue logged_in_value(!GetSyncAccount().empty());
490 web_ui()->CallJavascriptFunction("local_discovery.setUserLoggedIn",
494 #if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
495 void LocalDiscoveryUIHandler::StartCloudPrintConnector() {
496 Profile* profile = Profile::FromWebUI(web_ui());
498 base::Closure cloud_print_callback = base::Bind(
499 &LocalDiscoveryUIHandler::OnCloudPrintPrefsChanged,
500 base::Unretained(this));
502 if (cloud_print_connector_email_.GetPrefName().empty()) {
503 cloud_print_connector_email_.Init(
504 prefs::kCloudPrintEmail, profile->GetPrefs(), cloud_print_callback);
507 if (cloud_print_connector_enabled_.GetPrefName().empty()) {
508 cloud_print_connector_enabled_.Init(
509 prefs::kCloudPrintProxyEnabled, profile->GetPrefs(),
510 cloud_print_callback);
513 if (cloud_print_connector_ui_enabled_) {
514 SetupCloudPrintConnectorSection();
515 RefreshCloudPrintStatusFromService();
517 RemoveCloudPrintConnectorSection();
521 void LocalDiscoveryUIHandler::OnCloudPrintPrefsChanged() {
522 if (cloud_print_connector_ui_enabled_)
523 SetupCloudPrintConnectorSection();
526 void LocalDiscoveryUIHandler::ShowCloudPrintSetupDialog(
527 const base::ListValue* args) {
528 content::RecordAction(
529 base::UserMetricsAction("Options_EnableCloudPrintProxy"));
530 // Open the connector enable page in the current tab.
531 Profile* profile = Profile::FromWebUI(web_ui());
532 content::OpenURLParams params(
533 cloud_devices::GetCloudPrintEnableURL(
534 CloudPrintProxyServiceFactory::GetForProfile(profile)->proxy_id()),
537 content::PAGE_TRANSITION_LINK,
539 web_ui()->GetWebContents()->OpenURL(params);
542 void LocalDiscoveryUIHandler::HandleDisableCloudPrintConnector(
543 const base::ListValue* args) {
544 content::RecordAction(
545 base::UserMetricsAction("Options_DisableCloudPrintProxy"));
546 CloudPrintProxyServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()))->
550 void LocalDiscoveryUIHandler::SetupCloudPrintConnectorSection() {
551 Profile* profile = Profile::FromWebUI(web_ui());
553 if (!CloudPrintProxyServiceFactory::GetForProfile(profile)) {
554 cloud_print_connector_ui_enabled_ = false;
555 RemoveCloudPrintConnectorSection();
559 bool cloud_print_connector_allowed =
560 !cloud_print_connector_enabled_.IsManaged() ||
561 cloud_print_connector_enabled_.GetValue();
562 base::FundamentalValue allowed(cloud_print_connector_allowed);
565 if (profile->GetPrefs()->HasPrefPath(prefs::kCloudPrintEmail) &&
566 cloud_print_connector_allowed) {
567 email = profile->GetPrefs()->GetString(prefs::kCloudPrintEmail);
569 base::FundamentalValue disabled(email.empty());
571 base::string16 label_str;
573 label_str = l10n_util::GetStringFUTF16(
574 IDS_LOCAL_DISCOVERY_CLOUD_PRINT_CONNECTOR_DISABLED_LABEL,
575 l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT));
577 label_str = l10n_util::GetStringFUTF16(
578 IDS_OPTIONS_CLOUD_PRINT_CONNECTOR_ENABLED_LABEL,
579 l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT),
580 base::UTF8ToUTF16(email));
582 base::StringValue label(label_str);
584 web_ui()->CallJavascriptFunction(
585 "local_discovery.setupCloudPrintConnectorSection", disabled, label,
589 void LocalDiscoveryUIHandler::RemoveCloudPrintConnectorSection() {
590 web_ui()->CallJavascriptFunction(
591 "local_discovery.removeCloudPrintConnectorSection");
594 void LocalDiscoveryUIHandler::RefreshCloudPrintStatusFromService() {
595 if (cloud_print_connector_ui_enabled_)
596 CloudPrintProxyServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()))->
597 RefreshStatusFromService();
599 #endif // cloud print connector option stuff
601 } // namespace local_discovery