NaCl: Update revision in DEPS, r12770 -> r12773
[chromium-blink-merge.git] / chrome / browser / ui / webui / local_discovery / local_discovery_ui_handler.cc
blobb84cc25425848871fbbe7ad1cd41dad3900ba811
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/privet_confirm_api_flow.h"
18 #include "chrome/browser/local_discovery/privet_constants.h"
19 #include "chrome/browser/local_discovery/privet_device_lister_impl.h"
20 #include "chrome/browser/local_discovery/privet_http_asynchronous_factory.h"
21 #include "chrome/browser/local_discovery/privet_http_impl.h"
22 #include "chrome/browser/local_discovery/service_discovery_shared_client.h"
23 #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h"
24 #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service_factory.h"
25 #include "chrome/browser/printing/cloud_print/cloud_print_url.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/browser/signin/profile_oauth2_token_service.h"
28 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
29 #include "chrome/browser/signin/signin_manager.h"
30 #include "chrome/browser/signin/signin_manager_base.h"
31 #include "chrome/browser/signin/signin_manager_factory.h"
32 #include "chrome/browser/signin/signin_promo.h"
33 #include "chrome/browser/ui/browser_finder.h"
34 #include "chrome/browser/ui/browser_tabstrip.h"
35 #include "chrome/common/chrome_switches.h"
36 #include "chrome/common/pref_names.h"
37 #include "content/public/browser/notification_source.h"
38 #include "content/public/browser/user_metrics.h"
39 #include "content/public/browser/user_metrics.h"
40 #include "content/public/browser/web_ui.h"
41 #include "content/public/common/page_transition_types.h"
42 #include "grit/generated_resources.h"
43 #include "net/base/host_port_pair.h"
44 #include "net/base/net_util.h"
45 #include "net/http/http_status_code.h"
46 #include "ui/base/l10n/l10n_util.h"
48 #if defined(ENABLE_FULL_PRINTING) && !defined(OS_CHROMEOS) && \
49 !defined(OS_MACOSX)
50 #define CLOUD_PRINT_CONNECTOR_UI_AVAILABLE
51 #endif
53 namespace local_discovery {
55 namespace {
56 const char kPrivetAutomatedClaimURLFormat[] = "%s/confirm?token=%s";
58 int g_num_visible = 0;
59 } // namespace
61 LocalDiscoveryUIHandler::LocalDiscoveryUIHandler() : is_visible_(false) {
62 #if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
63 #if !defined(GOOGLE_CHROME_BUILD) && defined(OS_WIN)
64 // On Windows, we need the PDF plugin which is only guaranteed to exist on
65 // Google Chrome builds. Use a command-line switch for Windows non-Google
66 // Chrome builds.
67 cloud_print_connector_ui_enabled_ =
68 CommandLine::ForCurrentProcess()->HasSwitch(
69 switches::kEnableCloudPrintProxy);
70 #elif !defined(OS_CHROMEOS)
71 // Always enabled for Linux and Google Chrome Windows builds.
72 // Never enabled for Chrome OS, we don't even need to indicate it.
73 cloud_print_connector_ui_enabled_ = true;
74 #endif
75 #endif // !defined(OS_MACOSX)
78 LocalDiscoveryUIHandler::~LocalDiscoveryUIHandler() {
79 ResetCurrentRegistration();
80 SetIsVisible(false);
83 // static
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
130 // of a reload.
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());
140 privet_lister_->Start();
141 privet_lister_->DiscoverNewDevices(false);
143 #if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
144 StartCloudPrintConnector();
145 #endif
147 CheckUserLoggedIn();
149 notification_registrar_.RemoveAll();
150 notification_registrar_.Add(this,
151 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL,
152 content::Source<Profile>(profile));
153 notification_registrar_.Add(this, chrome::NOTIFICATION_GOOGLE_SIGNED_OUT,
154 content::Source<Profile>(profile));
157 void LocalDiscoveryUIHandler::HandleIsVisible(const base::ListValue* args) {
158 bool is_visible = false;
159 bool rv = args->GetBoolean(0, &is_visible);
160 DCHECK(rv);
161 SetIsVisible(is_visible);
164 void LocalDiscoveryUIHandler::HandleRegisterDevice(
165 const base::ListValue* args) {
166 std::string device;
168 bool rv = args->GetString(0, &device);
169 DCHECK(rv);
171 privet_resolution_ = privet_http_factory_->CreatePrivetHTTP(
172 device,
173 device_descriptions_[device].address,
174 base::Bind(&LocalDiscoveryUIHandler::StartRegisterHTTP,
175 base::Unretained(this)));
176 privet_resolution_->Start();
179 void LocalDiscoveryUIHandler::HandleCancelRegistration(
180 const base::ListValue* args) {
181 ResetCurrentRegistration();
184 void LocalDiscoveryUIHandler::HandleRequestPrinterList(
185 const base::ListValue* args) {
186 Profile* profile = Profile::FromWebUI(web_ui());
187 ProfileOAuth2TokenService* token_service =
188 ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
190 SigninManagerBase* signin_manager =
191 SigninManagerFactory::GetInstance()->GetForProfile(profile);
193 cloud_print_printer_list_.reset(new CloudPrintPrinterList(
194 profile->GetRequestContext(),
195 GetCloudPrintBaseUrl(),
196 token_service,
197 signin_manager->GetAuthenticatedAccountId(),
198 this));
199 cloud_print_printer_list_->Start();
202 void LocalDiscoveryUIHandler::HandleOpenCloudPrintURL(
203 const base::ListValue* args) {
204 std::string url;
205 bool rv = args->GetString(0, &url);
206 DCHECK(rv);
208 GURL url_full(GetCloudPrintBaseUrl() + url);
210 Browser* browser = chrome::FindBrowserWithWebContents(
211 web_ui()->GetWebContents());
212 DCHECK(browser);
214 chrome::AddSelectedTabWithURL(browser,
215 url_full,
216 content::PAGE_TRANSITION_FROM_API);
219 void LocalDiscoveryUIHandler::HandleShowSyncUI(
220 const base::ListValue* args) {
221 Browser* browser = chrome::FindBrowserWithWebContents(
222 web_ui()->GetWebContents());
223 DCHECK(browser);
225 // We use SOURCE_SETTINGS because the URL for SOURCE_SETTINGS is detected on
226 // redirect.
227 GURL url(signin::GetPromoURL(signin::SOURCE_SETTINGS,
228 true)); // auto close after success.
230 browser->OpenURL(
231 content::OpenURLParams(url, content::Referrer(), SINGLETON_TAB,
232 content::PAGE_TRANSITION_AUTO_BOOKMARK, false));
235 void LocalDiscoveryUIHandler::StartRegisterHTTP(
236 scoped_ptr<PrivetHTTPClient> http_client) {
237 current_http_client_.swap(http_client);
239 std::string user = GetSyncAccount();
241 if (!current_http_client_) {
242 SendRegisterError();
243 return;
246 current_register_operation_ =
247 current_http_client_->CreateRegisterOperation(user, this);
248 current_register_operation_->Start();
251 void LocalDiscoveryUIHandler::OnPrivetRegisterClaimToken(
252 PrivetRegisterOperation* operation,
253 const std::string& token,
254 const GURL& url) {
255 web_ui()->CallJavascriptFunction(
256 "local_discovery.onRegistrationConfirmedOnPrinter");
257 if (device_descriptions_.count(current_http_client_->GetName()) == 0) {
258 SendRegisterError();
259 return;
262 std::string base_url = GetCloudPrintBaseUrl();
264 GURL automated_claim_url(base::StringPrintf(
265 kPrivetAutomatedClaimURLFormat,
266 base_url.c_str(),
267 token.c_str()));
269 Profile* profile = Profile::FromWebUI(web_ui());
271 ProfileOAuth2TokenService* token_service =
272 ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
274 if (!token_service) {
275 SendRegisterError();
276 return;
279 SigninManagerBase* signin_manager =
280 SigninManagerFactory::GetInstance()->GetForProfile(profile);
281 if (!signin_manager) {
282 SendRegisterError();
283 return;
286 confirm_api_call_flow_.reset(new PrivetConfirmApiCallFlow(
287 profile->GetRequestContext(),
288 token_service,
289 signin_manager->GetAuthenticatedAccountId(),
290 automated_claim_url,
291 base::Bind(&LocalDiscoveryUIHandler::OnConfirmDone,
292 base::Unretained(this))));
293 confirm_api_call_flow_->Start();
296 void LocalDiscoveryUIHandler::OnPrivetRegisterError(
297 PrivetRegisterOperation* operation,
298 const std::string& action,
299 PrivetRegisterOperation::FailureReason reason,
300 int printer_http_code,
301 const base::DictionaryValue* json) {
302 std::string error;
304 if (reason == PrivetRegisterOperation::FAILURE_JSON_ERROR &&
305 json->GetString(kPrivetKeyError, &error)) {
306 if (error == kPrivetErrorTimeout) {
307 web_ui()->CallJavascriptFunction(
308 "local_discovery.onRegistrationTimeout");
309 return;
310 } else if (error == kPrivetErrorCancel) {
311 web_ui()->CallJavascriptFunction(
312 "local_discovery.onRegistrationCanceledPrinter");
313 return;
317 SendRegisterError();
320 void LocalDiscoveryUIHandler::OnPrivetRegisterDone(
321 PrivetRegisterOperation* operation,
322 const std::string& device_id) {
323 std::string name = operation->GetHTTPClient()->GetName();
325 current_register_operation_.reset();
326 current_http_client_.reset();
328 // HACK(noamsml): Generate network traffic so the Windows firewall doesn't
329 // block the printer's announcement.
330 privet_lister_->DiscoverNewDevices(false);
332 DeviceDescriptionMap::iterator found = device_descriptions_.find(name);
334 if (found == device_descriptions_.end()) {
335 // TODO(noamsml): Handle the case where a printer's record is not present at
336 // the end of registration.
337 SendRegisterError();
338 return;
341 SendRegisterDone(found->first, found->second);
344 void LocalDiscoveryUIHandler::OnConfirmDone(
345 CloudPrintBaseApiFlow::Status status) {
346 if (status == CloudPrintBaseApiFlow::SUCCESS) {
347 confirm_api_call_flow_.reset();
348 current_register_operation_->CompleteRegistration();
349 } else {
350 SendRegisterError();
354 void LocalDiscoveryUIHandler::DeviceChanged(
355 bool added,
356 const std::string& name,
357 const DeviceDescription& description) {
358 device_descriptions_[name] = description;
360 base::DictionaryValue info;
362 base::StringValue service_name(name);
363 scoped_ptr<base::Value> null_value(base::Value::CreateNullValue());
365 if (description.id.empty()) {
366 info.SetString("service_name", name);
367 info.SetString("human_readable_name", description.name);
368 info.SetString("description", description.description);
370 web_ui()->CallJavascriptFunction(
371 "local_discovery.onUnregisteredDeviceUpdate",
372 service_name, info);
373 } else {
374 web_ui()->CallJavascriptFunction(
375 "local_discovery.onUnregisteredDeviceUpdate",
376 service_name, *null_value);
380 void LocalDiscoveryUIHandler::DeviceRemoved(const std::string& name) {
381 device_descriptions_.erase(name);
382 scoped_ptr<base::Value> null_value(base::Value::CreateNullValue());
383 base::StringValue name_value(name);
385 web_ui()->CallJavascriptFunction("local_discovery.onUnregisteredDeviceUpdate",
386 name_value, *null_value);
389 void LocalDiscoveryUIHandler::DeviceCacheFlushed() {
390 web_ui()->CallJavascriptFunction("local_discovery.onDeviceCacheFlushed");
391 privet_lister_->DiscoverNewDevices(false);
394 void LocalDiscoveryUIHandler::OnCloudPrintPrinterListReady() {
395 base::ListValue printer_object_list;
396 std::set<std::string> local_ids;
398 for (DeviceDescriptionMap::iterator i = device_descriptions_.begin();
399 i != device_descriptions_.end();
400 i++) {
401 std::string device_id = i->second.id;
402 if (!device_id.empty()) {
403 const CloudPrintPrinterList::PrinterDetails* details =
404 cloud_print_printer_list_->GetDetailsFor(device_id);
406 if (details) {
407 local_ids.insert(device_id);
408 printer_object_list.Append(CreatePrinterInfo(*details).release());
413 for (CloudPrintPrinterList::iterator i = cloud_print_printer_list_->begin();
414 i != cloud_print_printer_list_->end(); i++) {
415 if (local_ids.count(i->id) == 0) {
416 printer_object_list.Append(CreatePrinterInfo(*i).release());
420 web_ui()->CallJavascriptFunction(
421 "local_discovery.onCloudDeviceListAvailable", printer_object_list);
424 void LocalDiscoveryUIHandler::OnCloudPrintPrinterListUnavailable() {
425 web_ui()->CallJavascriptFunction(
426 "local_discovery.onCloudDeviceListUnavailable");
429 void LocalDiscoveryUIHandler::Observe(
430 int type,
431 const content::NotificationSource& source,
432 const content::NotificationDetails& details) {
433 switch (type) {
434 case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL:
435 case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT:
436 CheckUserLoggedIn();
437 break;
438 default:
439 NOTREACHED();
443 void LocalDiscoveryUIHandler::SendRegisterError() {
444 web_ui()->CallJavascriptFunction("local_discovery.onRegistrationFailed");
447 void LocalDiscoveryUIHandler::SendRegisterDone(
448 const std::string& service_name, const DeviceDescription& device) {
449 base::DictionaryValue printer_value;
451 printer_value.SetString("id", device.id);
452 printer_value.SetString("display_name", device.name);
453 printer_value.SetString("description", device.description);
454 printer_value.SetString("service_name", service_name);
456 web_ui()->CallJavascriptFunction("local_discovery.onRegistrationSuccess",
457 printer_value);
460 void LocalDiscoveryUIHandler::SetIsVisible(bool visible) {
461 if (visible != is_visible_) {
462 g_num_visible += visible ? 1 : -1;
463 is_visible_ = visible;
467 std::string LocalDiscoveryUIHandler::GetSyncAccount() {
468 Profile* profile = Profile::FromWebUI(web_ui());
469 SigninManagerBase* signin_manager =
470 SigninManagerFactory::GetForProfileIfExists(profile);
472 if (!signin_manager) {
473 return "";
476 return signin_manager->GetAuthenticatedUsername();
479 std::string LocalDiscoveryUIHandler::GetCloudPrintBaseUrl() {
480 CloudPrintURL cloud_print_url(Profile::FromWebUI(web_ui()));
482 return cloud_print_url.GetCloudPrintServiceURL().spec();
485 // TODO(noamsml): Create master object for registration flow.
486 void LocalDiscoveryUIHandler::ResetCurrentRegistration() {
487 if (current_register_operation_.get()) {
488 current_register_operation_->Cancel();
489 current_register_operation_.reset();
492 confirm_api_call_flow_.reset();
493 privet_resolution_.reset();
494 current_http_client_.reset();
497 scoped_ptr<base::DictionaryValue> LocalDiscoveryUIHandler::CreatePrinterInfo(
498 const CloudPrintPrinterList::PrinterDetails& description) {
499 scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue);
501 return_value->SetString("id", description.id);
502 return_value->SetString("display_name", description.display_name);
503 return_value->SetString("description", description.description);
505 return return_value.Pass();
508 void LocalDiscoveryUIHandler::CheckUserLoggedIn() {
509 base::FundamentalValue logged_in_value(!GetSyncAccount().empty());
510 web_ui()->CallJavascriptFunction("local_discovery.setUserLoggedIn",
511 logged_in_value);
514 #if defined(CLOUD_PRINT_CONNECTOR_UI_AVAILABLE)
515 void LocalDiscoveryUIHandler::StartCloudPrintConnector() {
516 Profile* profile = Profile::FromWebUI(web_ui());
518 base::Closure cloud_print_callback = base::Bind(
519 &LocalDiscoveryUIHandler::OnCloudPrintPrefsChanged,
520 base::Unretained(this));
522 if (cloud_print_connector_email_.GetPrefName().empty()) {
523 cloud_print_connector_email_.Init(
524 prefs::kCloudPrintEmail, profile->GetPrefs(), cloud_print_callback);
527 if (cloud_print_connector_enabled_.GetPrefName().empty()) {
528 cloud_print_connector_enabled_.Init(
529 prefs::kCloudPrintProxyEnabled, profile->GetPrefs(),
530 cloud_print_callback);
533 if (cloud_print_connector_ui_enabled_) {
534 SetupCloudPrintConnectorSection();
535 RefreshCloudPrintStatusFromService();
536 } else {
537 RemoveCloudPrintConnectorSection();
541 void LocalDiscoveryUIHandler::OnCloudPrintPrefsChanged() {
542 if (cloud_print_connector_ui_enabled_)
543 SetupCloudPrintConnectorSection();
546 void LocalDiscoveryUIHandler::ShowCloudPrintSetupDialog(
547 const base::ListValue* args) {
548 content::RecordAction(
549 base::UserMetricsAction("Options_EnableCloudPrintProxy"));
550 // Open the connector enable page in the current tab.
551 Profile* profile = Profile::FromWebUI(web_ui());
552 content::OpenURLParams params(
553 CloudPrintURL(profile).GetCloudPrintServiceEnableURL(
554 CloudPrintProxyServiceFactory::GetForProfile(profile)->proxy_id()),
555 content::Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_LINK, false);
556 web_ui()->GetWebContents()->OpenURL(params);
559 void LocalDiscoveryUIHandler::HandleDisableCloudPrintConnector(
560 const base::ListValue* args) {
561 content::RecordAction(
562 base::UserMetricsAction("Options_DisableCloudPrintProxy"));
563 CloudPrintProxyServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()))->
564 DisableForUser();
567 void LocalDiscoveryUIHandler::SetupCloudPrintConnectorSection() {
568 Profile* profile = Profile::FromWebUI(web_ui());
570 if (!CloudPrintProxyServiceFactory::GetForProfile(profile)) {
571 cloud_print_connector_ui_enabled_ = false;
572 RemoveCloudPrintConnectorSection();
573 return;
576 bool cloud_print_connector_allowed =
577 !cloud_print_connector_enabled_.IsManaged() ||
578 cloud_print_connector_enabled_.GetValue();
579 base::FundamentalValue allowed(cloud_print_connector_allowed);
581 std::string email;
582 if (profile->GetPrefs()->HasPrefPath(prefs::kCloudPrintEmail) &&
583 cloud_print_connector_allowed) {
584 email = profile->GetPrefs()->GetString(prefs::kCloudPrintEmail);
586 base::FundamentalValue disabled(email.empty());
588 base::string16 label_str;
589 if (email.empty()) {
590 label_str = l10n_util::GetStringFUTF16(
591 IDS_LOCAL_DISCOVERY_CLOUD_PRINT_CONNECTOR_DISABLED_LABEL,
592 l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT));
593 } else {
594 label_str = l10n_util::GetStringFUTF16(
595 IDS_OPTIONS_CLOUD_PRINT_CONNECTOR_ENABLED_LABEL,
596 l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT),
597 base::UTF8ToUTF16(email));
599 base::StringValue label(label_str);
601 web_ui()->CallJavascriptFunction(
602 "local_discovery.setupCloudPrintConnectorSection", disabled, label,
603 allowed);
606 void LocalDiscoveryUIHandler::RemoveCloudPrintConnectorSection() {
607 web_ui()->CallJavascriptFunction(
608 "local_discovery.removeCloudPrintConnectorSection");
611 void LocalDiscoveryUIHandler::RefreshCloudPrintStatusFromService() {
612 if (cloud_print_connector_ui_enabled_)
613 CloudPrintProxyServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()))->
614 RefreshStatusFromService();
616 #endif // cloud print connector option stuff
618 } // namespace local_discovery