Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / webui / local_discovery / local_discovery_ui_handler.cc
blob37e6ddc4398369a1ac5a3ed3d43ca5806db34b3d
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/chrome_notification_types.h"
17 #include "chrome/browser/local_discovery/cloud_print_account_manager.h"
18 #include "chrome/browser/local_discovery/privet_confirm_api_flow.h"
19 #include "chrome/browser/local_discovery/privet_constants.h"
20 #include "chrome/browser/local_discovery/privet_device_lister_impl.h"
21 #include "chrome/browser/local_discovery/privet_http_asynchronous_factory.h"
22 #include "chrome/browser/local_discovery/privet_http_impl.h"
23 #include "chrome/browser/local_discovery/service_discovery_shared_client.h"
24 #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h"
25 #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service_factory.h"
26 #include "chrome/browser/printing/cloud_print/cloud_print_url.h"
27 #include "chrome/browser/profiles/profile.h"
28 #include "chrome/browser/signin/profile_oauth2_token_service.h"
29 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
30 #include "chrome/browser/signin/signin_manager.h"
31 #include "chrome/browser/signin/signin_manager_base.h"
32 #include "chrome/browser/signin/signin_manager_factory.h"
33 #include "chrome/browser/signin/signin_promo.h"
34 #include "chrome/browser/ui/browser_finder.h"
35 #include "chrome/browser/ui/browser_tabstrip.h"
36 #include "chrome/common/chrome_switches.h"
37 #include "chrome/common/pref_names.h"
38 #include "content/public/browser/notification_source.h"
39 #include "content/public/browser/user_metrics.h"
40 #include "content/public/browser/user_metrics.h"
41 #include "content/public/browser/web_ui.h"
42 #include "content/public/common/page_transition_types.h"
43 #include "grit/generated_resources.h"
44 #include "net/base/host_port_pair.h"
45 #include "net/base/net_util.h"
46 #include "net/http/http_status_code.h"
47 #include "ui/base/l10n/l10n_util.h"
49 #if defined(ENABLE_FULL_PRINTING) && !defined(OS_CHROMEOS) && \
50 !defined(OS_MACOSX)
51 #define CLOUD_PRINT_CONNECTOR_UI_AVAILABLE
52 #endif
54 namespace local_discovery {
56 namespace {
57 const char kPrivetAutomatedClaimURLFormat[] = "%s/confirm?token=%s";
59 int g_num_visible = 0;
60 } // namespace
62 LocalDiscoveryUIHandler::LocalDiscoveryUIHandler() : is_visible_(false) {
63 #if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
64 #if !defined(GOOGLE_CHROME_BUILD) && defined(OS_WIN)
65 // On Windows, we need the PDF plugin which is only guaranteed to exist on
66 // Google Chrome builds. Use a command-line switch for Windows non-Google
67 // Chrome builds.
68 cloud_print_connector_ui_enabled_ =
69 CommandLine::ForCurrentProcess()->HasSwitch(
70 switches::kEnableCloudPrintProxy);
71 #elif !defined(OS_CHROMEOS)
72 // Always enabled for Linux and Google Chrome Windows builds.
73 // Never enabled for Chrome OS, we don't even need to indicate it.
74 cloud_print_connector_ui_enabled_ = true;
75 #endif
76 #endif // !defined(OS_MACOSX)
79 LocalDiscoveryUIHandler::~LocalDiscoveryUIHandler() {
80 ResetCurrentRegistration();
81 SetIsVisible(false);
84 // static
85 bool LocalDiscoveryUIHandler::GetHasVisible() {
86 return g_num_visible != 0;
89 void LocalDiscoveryUIHandler::RegisterMessages() {
90 web_ui()->RegisterMessageCallback("start", base::Bind(
91 &LocalDiscoveryUIHandler::HandleStart,
92 base::Unretained(this)));
93 web_ui()->RegisterMessageCallback("isVisible", base::Bind(
94 &LocalDiscoveryUIHandler::HandleIsVisible,
95 base::Unretained(this)));
96 web_ui()->RegisterMessageCallback("registerDevice", base::Bind(
97 &LocalDiscoveryUIHandler::HandleRegisterDevice,
98 base::Unretained(this)));
99 web_ui()->RegisterMessageCallback("cancelRegistration", base::Bind(
100 &LocalDiscoveryUIHandler::HandleCancelRegistration,
101 base::Unretained(this)));
102 web_ui()->RegisterMessageCallback("requestPrinterList", base::Bind(
103 &LocalDiscoveryUIHandler::HandleRequestPrinterList,
104 base::Unretained(this)));
105 web_ui()->RegisterMessageCallback("openCloudPrintURL", base::Bind(
106 &LocalDiscoveryUIHandler::HandleOpenCloudPrintURL,
107 base::Unretained(this)));
108 web_ui()->RegisterMessageCallback("showSyncUI", base::Bind(
109 &LocalDiscoveryUIHandler::HandleShowSyncUI,
110 base::Unretained(this)));
112 // Cloud print connector related messages
113 #if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
114 if (cloud_print_connector_ui_enabled_) {
115 web_ui()->RegisterMessageCallback(
116 "showCloudPrintSetupDialog",
117 base::Bind(&LocalDiscoveryUIHandler::ShowCloudPrintSetupDialog,
118 base::Unretained(this)));
119 web_ui()->RegisterMessageCallback(
120 "disableCloudPrintConnector",
121 base::Bind(&LocalDiscoveryUIHandler::HandleDisableCloudPrintConnector,
122 base::Unretained(this)));
124 #endif // defined(ENABLE_FULL_PRINTING)
127 void LocalDiscoveryUIHandler::HandleStart(const base::ListValue* args) {
128 Profile* profile = Profile::FromWebUI(web_ui());
130 // If privet_lister_ is already set, it is a mock used for tests or the result
131 // of a reload.
132 if (!privet_lister_) {
133 service_discovery_client_ = ServiceDiscoverySharedClient::GetInstance();
134 privet_lister_.reset(new PrivetDeviceListerImpl(
135 service_discovery_client_.get(), this));
136 privet_http_factory_ =
137 PrivetHTTPAsynchronousFactory::CreateInstance(
138 service_discovery_client_.get(), profile->GetRequestContext());
141 privet_lister_->Start();
142 privet_lister_->DiscoverNewDevices(false);
144 #if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
145 StartCloudPrintConnector();
146 #endif
148 CheckUserLoggedIn();
150 notification_registrar_.RemoveAll();
151 notification_registrar_.Add(this,
152 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL,
153 content::Source<Profile>(profile));
154 notification_registrar_.Add(this, chrome::NOTIFICATION_GOOGLE_SIGNED_OUT,
155 content::Source<Profile>(profile));
158 void LocalDiscoveryUIHandler::HandleIsVisible(const base::ListValue* args) {
159 bool is_visible = false;
160 bool rv = args->GetBoolean(0, &is_visible);
161 DCHECK(rv);
162 SetIsVisible(is_visible);
165 void LocalDiscoveryUIHandler::HandleRegisterDevice(
166 const base::ListValue* args) {
167 std::string device;
169 bool rv = args->GetString(0, &device);
170 DCHECK(rv);
172 privet_resolution_ = privet_http_factory_->CreatePrivetHTTP(
173 device,
174 device_descriptions_[device].address,
175 base::Bind(&LocalDiscoveryUIHandler::StartRegisterHTTP,
176 base::Unretained(this)));
177 privet_resolution_->Start();
180 void LocalDiscoveryUIHandler::HandleCancelRegistration(
181 const base::ListValue* args) {
182 ResetCurrentRegistration();
185 void LocalDiscoveryUIHandler::HandleRequestPrinterList(
186 const base::ListValue* args) {
187 Profile* profile = Profile::FromWebUI(web_ui());
188 ProfileOAuth2TokenService* token_service =
189 ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
191 cloud_print_printer_list_.reset(new CloudPrintPrinterList(
192 profile->GetRequestContext(),
193 GetCloudPrintBaseUrl(),
194 token_service,
195 token_service->GetPrimaryAccountId(),
196 this));
197 cloud_print_printer_list_->Start();
200 void LocalDiscoveryUIHandler::HandleOpenCloudPrintURL(
201 const base::ListValue* args) {
202 std::string url;
203 bool rv = args->GetString(0, &url);
204 DCHECK(rv);
206 GURL url_full(GetCloudPrintBaseUrl() + url);
208 Browser* browser = chrome::FindBrowserWithWebContents(
209 web_ui()->GetWebContents());
210 DCHECK(browser);
212 chrome::AddSelectedTabWithURL(browser,
213 url_full,
214 content::PAGE_TRANSITION_FROM_API);
217 void LocalDiscoveryUIHandler::HandleShowSyncUI(
218 const base::ListValue* args) {
219 Browser* browser = chrome::FindBrowserWithWebContents(
220 web_ui()->GetWebContents());
221 DCHECK(browser);
223 // We use SOURCE_SETTINGS because the URL for SOURCE_SETTINGS is detected on
224 // redirect.
225 GURL url(signin::GetPromoURL(signin::SOURCE_SETTINGS,
226 true)); // auto close after success.
228 browser->OpenURL(
229 content::OpenURLParams(url, content::Referrer(), SINGLETON_TAB,
230 content::PAGE_TRANSITION_AUTO_BOOKMARK, false));
233 void LocalDiscoveryUIHandler::StartRegisterHTTP(
234 scoped_ptr<PrivetHTTPClient> http_client) {
235 current_http_client_.swap(http_client);
237 std::string user = GetSyncAccount();
239 if (!current_http_client_) {
240 SendRegisterError();
241 return;
244 current_register_operation_ =
245 current_http_client_->CreateRegisterOperation(user, this);
246 current_register_operation_->Start();
249 void LocalDiscoveryUIHandler::OnPrivetRegisterClaimToken(
250 PrivetRegisterOperation* operation,
251 const std::string& token,
252 const GURL& url) {
253 web_ui()->CallJavascriptFunction(
254 "local_discovery.onRegistrationConfirmedOnPrinter");
255 if (device_descriptions_.count(current_http_client_->GetName()) == 0) {
256 SendRegisterError();
257 return;
260 std::string base_url = GetCloudPrintBaseUrl();
262 GURL automated_claim_url(base::StringPrintf(
263 kPrivetAutomatedClaimURLFormat,
264 base_url.c_str(),
265 token.c_str()));
267 Profile* profile = Profile::FromWebUI(web_ui());
269 ProfileOAuth2TokenService* token_service =
270 ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
272 if (!token_service) {
273 SendRegisterError();
274 return;
277 confirm_api_call_flow_.reset(new PrivetConfirmApiCallFlow(
278 profile->GetRequestContext(),
279 token_service,
280 token_service->GetPrimaryAccountId(),
281 automated_claim_url,
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) {
293 std::string error;
295 if (reason == PrivetRegisterOperation::FAILURE_JSON_ERROR &&
296 json->GetString(kPrivetKeyError, &error)) {
297 if (error == kPrivetErrorTimeout) {
298 web_ui()->CallJavascriptFunction(
299 "local_discovery.onRegistrationTimeout");
300 return;
301 } else if (error == kPrivetErrorCancel) {
302 web_ui()->CallJavascriptFunction(
303 "local_discovery.onRegistrationCanceledPrinter");
304 return;
308 SendRegisterError();
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.
328 SendRegisterError();
329 return;
332 SendRegisterDone(found->first, found->second);
335 void LocalDiscoveryUIHandler::OnConfirmDone(
336 CloudPrintBaseApiFlow::Status status) {
337 if (status == CloudPrintBaseApiFlow::SUCCESS) {
338 confirm_api_call_flow_.reset();
339 current_register_operation_->CompleteRegistration();
340 } else {
341 SendRegisterError();
345 void LocalDiscoveryUIHandler::DeviceChanged(
346 bool added,
347 const std::string& name,
348 const DeviceDescription& description) {
349 device_descriptions_[name] = description;
351 base::DictionaryValue info;
353 base::StringValue service_name(name);
354 scoped_ptr<base::Value> null_value(base::Value::CreateNullValue());
356 if (description.id.empty()) {
357 info.SetString("service_name", name);
358 info.SetString("human_readable_name", description.name);
359 info.SetString("description", description.description);
361 web_ui()->CallJavascriptFunction(
362 "local_discovery.onUnregisteredDeviceUpdate",
363 service_name, info);
364 } else {
365 web_ui()->CallJavascriptFunction(
366 "local_discovery.onUnregisteredDeviceUpdate",
367 service_name, *null_value);
371 void LocalDiscoveryUIHandler::DeviceRemoved(const std::string& name) {
372 device_descriptions_.erase(name);
373 scoped_ptr<base::Value> null_value(base::Value::CreateNullValue());
374 base::StringValue name_value(name);
376 web_ui()->CallJavascriptFunction("local_discovery.onUnregisteredDeviceUpdate",
377 name_value, *null_value);
380 void LocalDiscoveryUIHandler::DeviceCacheFlushed() {
381 web_ui()->CallJavascriptFunction("local_discovery.onDeviceCacheFlushed");
382 privet_lister_->DiscoverNewDevices(false);
385 void LocalDiscoveryUIHandler::OnCloudPrintPrinterListReady() {
386 base::ListValue printer_object_list;
387 std::set<std::string> local_ids;
389 for (DeviceDescriptionMap::iterator i = device_descriptions_.begin();
390 i != device_descriptions_.end();
391 i++) {
392 std::string device_id = i->second.id;
393 if (!device_id.empty()) {
394 const CloudPrintPrinterList::PrinterDetails* details =
395 cloud_print_printer_list_->GetDetailsFor(device_id);
397 if (details) {
398 local_ids.insert(device_id);
399 printer_object_list.Append(CreatePrinterInfo(*details).release());
404 for (CloudPrintPrinterList::iterator i = cloud_print_printer_list_->begin();
405 i != cloud_print_printer_list_->end(); i++) {
406 if (local_ids.count(i->id) == 0) {
407 printer_object_list.Append(CreatePrinterInfo(*i).release());
411 web_ui()->CallJavascriptFunction(
412 "local_discovery.onCloudDeviceListAvailable", printer_object_list);
415 void LocalDiscoveryUIHandler::OnCloudPrintPrinterListUnavailable() {
416 web_ui()->CallJavascriptFunction(
417 "local_discovery.onCloudDeviceListUnavailable");
420 void LocalDiscoveryUIHandler::Observe(
421 int type,
422 const content::NotificationSource& source,
423 const content::NotificationDetails& details) {
424 switch (type) {
425 case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL:
426 case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT:
427 CheckUserLoggedIn();
428 break;
429 default:
430 NOTREACHED();
434 void LocalDiscoveryUIHandler::SendRegisterError() {
435 web_ui()->CallJavascriptFunction("local_discovery.onRegistrationFailed");
438 void LocalDiscoveryUIHandler::SendRegisterDone(
439 const std::string& service_name, const DeviceDescription& device) {
440 base::DictionaryValue printer_value;
442 printer_value.SetString("id", device.id);
443 printer_value.SetString("display_name", device.name);
444 printer_value.SetString("description", device.description);
445 printer_value.SetString("service_name", service_name);
447 web_ui()->CallJavascriptFunction("local_discovery.onRegistrationSuccess",
448 printer_value);
451 void LocalDiscoveryUIHandler::SetIsVisible(bool visible) {
452 if (visible != is_visible_) {
453 g_num_visible += visible ? 1 : -1;
454 is_visible_ = visible;
458 std::string LocalDiscoveryUIHandler::GetSyncAccount() {
459 Profile* profile = Profile::FromWebUI(web_ui());
460 SigninManagerBase* signin_manager =
461 SigninManagerFactory::GetForProfileIfExists(profile);
463 if (!signin_manager) {
464 return "";
467 return signin_manager->GetAuthenticatedUsername();
470 std::string LocalDiscoveryUIHandler::GetCloudPrintBaseUrl() {
471 CloudPrintURL cloud_print_url(Profile::FromWebUI(web_ui()));
473 return cloud_print_url.GetCloudPrintServiceURL().spec();
476 // TODO(noamsml): Create master object for registration flow.
477 void LocalDiscoveryUIHandler::ResetCurrentRegistration() {
478 if (current_register_operation_.get()) {
479 current_register_operation_->Cancel();
480 current_register_operation_.reset();
483 confirm_api_call_flow_.reset();
484 privet_resolution_.reset();
485 current_http_client_.reset();
488 scoped_ptr<base::DictionaryValue> LocalDiscoveryUIHandler::CreatePrinterInfo(
489 const CloudPrintPrinterList::PrinterDetails& description) {
490 scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue);
492 return_value->SetString("id", description.id);
493 return_value->SetString("display_name", description.display_name);
494 return_value->SetString("description", description.description);
496 return return_value.Pass();
499 void LocalDiscoveryUIHandler::CheckUserLoggedIn() {
500 base::FundamentalValue logged_in_value(!GetSyncAccount().empty());
501 web_ui()->CallJavascriptFunction("local_discovery.setUserLoggedIn",
502 logged_in_value);
505 #if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
506 void LocalDiscoveryUIHandler::StartCloudPrintConnector() {
507 Profile* profile = Profile::FromWebUI(web_ui());
509 base::Closure cloud_print_callback = base::Bind(
510 &LocalDiscoveryUIHandler::OnCloudPrintPrefsChanged,
511 base::Unretained(this));
513 if (cloud_print_connector_email_.GetPrefName().empty()) {
514 cloud_print_connector_email_.Init(
515 prefs::kCloudPrintEmail, profile->GetPrefs(), cloud_print_callback);
518 if (cloud_print_connector_enabled_.GetPrefName().empty()) {
519 cloud_print_connector_enabled_.Init(
520 prefs::kCloudPrintProxyEnabled, profile->GetPrefs(),
521 cloud_print_callback);
524 if (cloud_print_connector_ui_enabled_) {
525 SetupCloudPrintConnectorSection();
526 RefreshCloudPrintStatusFromService();
527 } else {
528 RemoveCloudPrintConnectorSection();
532 void LocalDiscoveryUIHandler::OnCloudPrintPrefsChanged() {
533 if (cloud_print_connector_ui_enabled_)
534 SetupCloudPrintConnectorSection();
537 void LocalDiscoveryUIHandler::ShowCloudPrintSetupDialog(
538 const base::ListValue* args) {
539 content::RecordAction(
540 base::UserMetricsAction("Options_EnableCloudPrintProxy"));
541 // Open the connector enable page in the current tab.
542 Profile* profile = Profile::FromWebUI(web_ui());
543 content::OpenURLParams params(
544 CloudPrintURL(profile).GetCloudPrintServiceEnableURL(
545 CloudPrintProxyServiceFactory::GetForProfile(profile)->proxy_id()),
546 content::Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_LINK, false);
547 web_ui()->GetWebContents()->OpenURL(params);
550 void LocalDiscoveryUIHandler::HandleDisableCloudPrintConnector(
551 const base::ListValue* args) {
552 content::RecordAction(
553 base::UserMetricsAction("Options_DisableCloudPrintProxy"));
554 CloudPrintProxyServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()))->
555 DisableForUser();
558 void LocalDiscoveryUIHandler::SetupCloudPrintConnectorSection() {
559 Profile* profile = Profile::FromWebUI(web_ui());
561 if (!CloudPrintProxyServiceFactory::GetForProfile(profile)) {
562 cloud_print_connector_ui_enabled_ = false;
563 RemoveCloudPrintConnectorSection();
564 return;
567 bool cloud_print_connector_allowed =
568 !cloud_print_connector_enabled_.IsManaged() ||
569 cloud_print_connector_enabled_.GetValue();
570 base::FundamentalValue allowed(cloud_print_connector_allowed);
572 std::string email;
573 if (profile->GetPrefs()->HasPrefPath(prefs::kCloudPrintEmail) &&
574 cloud_print_connector_allowed) {
575 email = profile->GetPrefs()->GetString(prefs::kCloudPrintEmail);
577 base::FundamentalValue disabled(email.empty());
579 base::string16 label_str;
580 if (email.empty()) {
581 label_str = l10n_util::GetStringFUTF16(
582 IDS_LOCAL_DISCOVERY_CLOUD_PRINT_CONNECTOR_DISABLED_LABEL,
583 l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT));
584 } else {
585 label_str = l10n_util::GetStringFUTF16(
586 IDS_OPTIONS_CLOUD_PRINT_CONNECTOR_ENABLED_LABEL,
587 l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT),
588 base::UTF8ToUTF16(email));
590 base::StringValue label(label_str);
592 web_ui()->CallJavascriptFunction(
593 "local_discovery.setupCloudPrintConnectorSection", disabled, label,
594 allowed);
597 void LocalDiscoveryUIHandler::RemoveCloudPrintConnectorSection() {
598 web_ui()->CallJavascriptFunction(
599 "local_discovery.removeCloudPrintConnectorSection");
602 void LocalDiscoveryUIHandler::RefreshCloudPrintStatusFromService() {
603 if (cloud_print_connector_ui_enabled_)
604 CloudPrintProxyServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()))->
605 RefreshStatusFromService();
607 #endif // cloud print connector option stuff
609 } // namespace local_discovery