local_discovery_ui_handler.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
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/message_loop/message_loop.h" 11#include "base/strings/stringprintf.h" 12#include "base/values.h" 13#include "chrome/browser/local_discovery/cloud_print_account_manager.h" 14#include "chrome/browser/local_discovery/privet_confirm_api_flow.h" 15#include "chrome/browser/local_discovery/privet_constants.h" 16#include "chrome/browser/local_discovery/privet_device_lister_impl.h" 17#include "chrome/browser/local_discovery/privet_http_asynchronous_factory.h" 18#include "chrome/browser/local_discovery/privet_http_asynchronous_factory.h" 19#include "chrome/browser/local_discovery/privet_http_impl.h" 20#include "chrome/browser/local_discovery/service_discovery_host_client.h" 21#include "chrome/browser/printing/cloud_print/cloud_print_url.h" 22#include "chrome/browser/profiles/profile.h" 23#include "chrome/browser/signin/profile_oauth2_token_service.h" 24#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 25#include "chrome/browser/signin/signin_manager.h" 26#include "chrome/browser/signin/signin_manager_base.h" 27#include "chrome/browser/signin/signin_manager_factory.h" 28#include "chrome/browser/signin/signin_promo.h" 29#include "chrome/browser/ui/browser_finder.h" 30#include "chrome/browser/ui/browser_tabstrip.h" 31#include "content/public/browser/user_metrics.h" 32#include "content/public/browser/web_ui.h" 33#include "content/public/common/page_transition_types.h" 34#include "net/base/host_port_pair.h" 35#include "net/base/net_util.h" 36#include "net/http/http_status_code.h" 37 38namespace local_discovery { 39 40namespace { 41const char kPrivetAutomatedClaimURLFormat[] = "%s/confirm?token=%s"; 42const int kRegistrationAnnouncementTimeoutSeconds = 5; 43 44LocalDiscoveryUIHandler::Factory* g_factory = NULL; 45int g_num_visible = 0; 46 47} // namespace 48 49LocalDiscoveryUIHandler::LocalDiscoveryUIHandler() : is_visible_(false) { 50} 51 52LocalDiscoveryUIHandler::LocalDiscoveryUIHandler( 53 scoped_ptr<PrivetDeviceLister> privet_lister) : is_visible_(false) { 54 privet_lister.swap(privet_lister_); 55} 56 57LocalDiscoveryUIHandler::~LocalDiscoveryUIHandler() { 58 ResetCurrentRegistration(); 59 SetIsVisible(false); 60} 61 62// static 63LocalDiscoveryUIHandler* LocalDiscoveryUIHandler::Create() { 64 if (g_factory) return g_factory->CreateLocalDiscoveryUIHandler(); 65 return new LocalDiscoveryUIHandler(); 66} 67 68// static 69void LocalDiscoveryUIHandler::SetFactory(Factory* factory) { 70 g_factory = factory; 71} 72 73// static 74bool LocalDiscoveryUIHandler::GetHasVisible() { 75 return g_num_visible != 0; 76} 77 78void LocalDiscoveryUIHandler::RegisterMessages() { 79 web_ui()->RegisterMessageCallback("start", base::Bind( 80 &LocalDiscoveryUIHandler::HandleStart, 81 base::Unretained(this))); 82 web_ui()->RegisterMessageCallback("isVisible", base::Bind( 83 &LocalDiscoveryUIHandler::HandleIsVisible, 84 base::Unretained(this))); 85 web_ui()->RegisterMessageCallback("registerDevice", base::Bind( 86 &LocalDiscoveryUIHandler::HandleRegisterDevice, 87 base::Unretained(this))); 88 web_ui()->RegisterMessageCallback("cancelRegistration", base::Bind( 89 &LocalDiscoveryUIHandler::HandleCancelRegistration, 90 base::Unretained(this))); 91 web_ui()->RegisterMessageCallback("requestPrinterList", base::Bind( 92 &LocalDiscoveryUIHandler::HandleRequestPrinterList, 93 base::Unretained(this))); 94 web_ui()->RegisterMessageCallback("openCloudPrintURL", base::Bind( 95 &LocalDiscoveryUIHandler::HandleOpenCloudPrintURL, 96 base::Unretained(this))); 97 web_ui()->RegisterMessageCallback("showSyncUI", base::Bind( 98 &LocalDiscoveryUIHandler::HandleShowSyncUI, 99 base::Unretained(this))); 100} 101 102void LocalDiscoveryUIHandler::HandleStart(const base::ListValue* args) { 103 Profile* profile = Profile::FromWebUI(web_ui()); 104 105 // If privet_lister_ is already set, it is a mock used for tests or the result 106 // of a reload. 107 if (!privet_lister_) { 108 service_discovery_client_ = ServiceDiscoverySharedClient::GetInstance(); 109 privet_lister_.reset(new PrivetDeviceListerImpl( 110 service_discovery_client_.get(), this)); 111 privet_http_factory_ = 112 PrivetHTTPAsynchronousFactory::CreateInstance( 113 service_discovery_client_.get(), profile->GetRequestContext()); 114 } 115 116 privet_lister_->Start(); 117 privet_lister_->DiscoverNewDevices(false); 118 119 CheckUserLoggedIn(); 120} 121 122void LocalDiscoveryUIHandler::HandleIsVisible(const base::ListValue* args) { 123 bool is_visible = false; 124 bool rv = args->GetBoolean(0, &is_visible); 125 DCHECK(rv); 126 SetIsVisible(is_visible); 127} 128 129void LocalDiscoveryUIHandler::HandleRegisterDevice( 130 const base::ListValue* args) { 131 std::string device; 132 133 bool rv = args->GetString(0, &device); 134 DCHECK(rv); 135 136 privet_resolution_ = privet_http_factory_->CreatePrivetHTTP( 137 device, 138 device_descriptions_[device].address, 139 base::Bind(&LocalDiscoveryUIHandler::StartRegisterHTTP, 140 base::Unretained(this))); 141 privet_resolution_->Start(); 142} 143 144void LocalDiscoveryUIHandler::HandleCancelRegistration( 145 const base::ListValue* args) { 146 ResetCurrentRegistration(); 147} 148 149void LocalDiscoveryUIHandler::HandleRequestPrinterList( 150 const base::ListValue* args) { 151 Profile* profile = Profile::FromWebUI(web_ui()); 152 OAuth2TokenService* token_service = 153 ProfileOAuth2TokenServiceFactory::GetForProfile(profile); 154 155 cloud_print_printer_list_.reset(new CloudPrintPrinterList( 156 profile->GetRequestContext(), 157 GetCloudPrintBaseUrl(), 158 token_service, 159 this)); 160 cloud_print_printer_list_->Start(); 161} 162 163void LocalDiscoveryUIHandler::HandleOpenCloudPrintURL( 164 const base::ListValue* args) { 165 std::string url; 166 bool rv = args->GetString(0, &url); 167 DCHECK(rv); 168 169 GURL url_full(GetCloudPrintBaseUrl() + url); 170 171 Browser* browser = chrome::FindBrowserWithWebContents( 172 web_ui()->GetWebContents()); 173 DCHECK(browser); 174 175 chrome::AddSelectedTabWithURL(browser, 176 url_full, 177 content::PAGE_TRANSITION_FROM_API); 178} 179 180void LocalDiscoveryUIHandler::HandleShowSyncUI( 181 const base::ListValue* args) { 182 Browser* browser = chrome::FindBrowserWithWebContents( 183 web_ui()->GetWebContents()); 184 DCHECK(browser); 185 186 // We use SOURCE_SETTINGS because the URL for SOURCE_SETTINGS is detected on 187 // redirect. 188 GURL url(signin::GetPromoURL(signin::SOURCE_SETTINGS, 189 true)); // auto close after success. 190 191 browser->OpenURL( 192 content::OpenURLParams(url, content::Referrer(), SINGLETON_TAB, 193 content::PAGE_TRANSITION_AUTO_BOOKMARK, false)); 194} 195 196void LocalDiscoveryUIHandler::StartRegisterHTTP( 197 scoped_ptr<PrivetHTTPClient> http_client) { 198 current_http_client_.swap(http_client); 199 200 std::string user = GetSyncAccount(); 201 202 if (!current_http_client_) { 203 SendRegisterError(); 204 return; 205 } 206 207 current_register_operation_ = 208 current_http_client_->CreateRegisterOperation(user, this); 209 current_register_operation_->Start(); 210} 211 212void LocalDiscoveryUIHandler::OnPrivetRegisterClaimToken( 213 PrivetRegisterOperation* operation, 214 const std::string& token, 215 const GURL& url) { 216 web_ui()->CallJavascriptFunction( 217 "local_discovery.onRegistrationConfirmedOnPrinter"); 218 if (device_descriptions_.count(current_http_client_->GetName()) == 0) { 219 SendRegisterError(); 220 return; 221 } 222 223 std::string base_url = GetCloudPrintBaseUrl(); 224 225 GURL automated_claim_url(base::StringPrintf( 226 kPrivetAutomatedClaimURLFormat, 227 base_url.c_str(), 228 token.c_str())); 229 230 Profile* profile = Profile::FromWebUI(web_ui()); 231 232 OAuth2TokenService* token_service = 233 ProfileOAuth2TokenServiceFactory::GetForProfile(profile); 234 235 if (!token_service) { 236 SendRegisterError(); 237 return; 238 } 239 240 confirm_api_call_flow_.reset(new PrivetConfirmApiCallFlow( 241 profile->GetRequestContext(), 242 token_service, 243 automated_claim_url, 244 base::Bind(&LocalDiscoveryUIHandler::OnConfirmDone, 245 base::Unretained(this)))); 246 confirm_api_call_flow_->Start(); 247} 248 249void LocalDiscoveryUIHandler::OnPrivetRegisterError( 250 PrivetRegisterOperation* operation, 251 const std::string& action, 252 PrivetRegisterOperation::FailureReason reason, 253 int printer_http_code, 254 const DictionaryValue* json) { 255 // TODO(noamsml): Add detailed error message. 256 SendRegisterError(); 257} 258 259void LocalDiscoveryUIHandler::OnPrivetRegisterDone( 260 PrivetRegisterOperation* operation, 261 const std::string& device_id) { 262 std::string name = operation->GetHTTPClient()->GetName(); 263 264 current_register_operation_.reset(); 265 current_http_client_.reset(); 266 267 DeviceDescriptionMap::iterator found = device_descriptions_.find(name); 268 269 if (found == device_descriptions_.end() || found->second.id.empty()) { 270 new_register_device_ = name; 271 registration_announce_timeout_.Reset(base::Bind( 272 &LocalDiscoveryUIHandler::OnAnnouncementTimeoutReached, 273 base::Unretained(this))); 274 275 base::MessageLoop::current()->PostDelayedTask( 276 FROM_HERE, 277 registration_announce_timeout_.callback(), 278 base::TimeDelta::FromSeconds(kRegistrationAnnouncementTimeoutSeconds)); 279 } 280} 281 282void LocalDiscoveryUIHandler::OnAnnouncementTimeoutReached() { 283 new_register_device_.clear(); 284 registration_announce_timeout_.Cancel(); 285 286 SendRegisterError(); 287} 288 289void LocalDiscoveryUIHandler::OnConfirmDone( 290 CloudPrintBaseApiFlow::Status status) { 291 if (status == CloudPrintBaseApiFlow::SUCCESS) { 292 confirm_api_call_flow_.reset(); 293 current_register_operation_->CompleteRegistration(); 294 } else { 295 // TODO(noamsml): Add detailed error message. 296 SendRegisterError(); 297 } 298} 299 300void LocalDiscoveryUIHandler::DeviceChanged( 301 bool added, 302 const std::string& name, 303 const DeviceDescription& description) { 304 device_descriptions_[name] = description; 305 306 base::DictionaryValue info; 307 308 base::StringValue service_name(name); 309 scoped_ptr<base::Value> null_value(base::Value::CreateNullValue()); 310 311 if (description.id.empty()) { 312 info.SetString("service_name", name); 313 info.SetString("human_readable_name", description.name); 314 info.SetString("description", description.description); 315 316 web_ui()->CallJavascriptFunction( 317 "local_discovery.onUnregisteredDeviceUpdate", 318 service_name, info); 319 } else { 320 web_ui()->CallJavascriptFunction( 321 "local_discovery.onUnregisteredDeviceUpdate", 322 service_name, *null_value); 323 324 if (name == new_register_device_) { 325 new_register_device_.clear(); 326 SendRegisterDone(); 327 } 328 } 329} 330 331void LocalDiscoveryUIHandler::DeviceRemoved(const std::string& name) { 332 device_descriptions_.erase(name); 333 scoped_ptr<base::Value> null_value(base::Value::CreateNullValue()); 334 base::StringValue name_value(name); 335 336 web_ui()->CallJavascriptFunction("local_discovery.onUnregisteredDeviceUpdate", 337 name_value, *null_value); 338} 339 340void LocalDiscoveryUIHandler::DeviceCacheFlushed() { 341 web_ui()->CallJavascriptFunction("local_discovery.onDeviceCacheFlushed"); 342 privet_lister_->DiscoverNewDevices(false); 343} 344 345void LocalDiscoveryUIHandler::OnCloudPrintPrinterListReady() { 346 base::ListValue printer_object_list; 347 std::set<std::string> local_ids; 348 349 for (DeviceDescriptionMap::iterator i = device_descriptions_.begin(); 350 i != device_descriptions_.end(); 351 i++) { 352 std::string device_id = i->second.id; 353 if (!device_id.empty()) { 354 const CloudPrintPrinterList::PrinterDetails* details = 355 cloud_print_printer_list_->GetDetailsFor(device_id); 356 357 if (details) { 358 local_ids.insert(device_id); 359 printer_object_list.Append(CreatePrinterInfo(*details).release()); 360 } 361 } 362 } 363 364 for (CloudPrintPrinterList::iterator i = cloud_print_printer_list_->begin(); 365 i != cloud_print_printer_list_->end(); i++) { 366 if (local_ids.count(i->id) == 0) { 367 printer_object_list.Append(CreatePrinterInfo(*i).release()); 368 } 369 } 370 371 web_ui()->CallJavascriptFunction( 372 "local_discovery.onCloudDeviceListAvailable", printer_object_list); 373} 374 375void LocalDiscoveryUIHandler::OnCloudPrintPrinterListUnavailable() { 376 web_ui()->CallJavascriptFunction( 377 "local_discovery.onCloudDeviceListUnavailable"); 378} 379 380 381void LocalDiscoveryUIHandler::SendRegisterError() { 382 web_ui()->CallJavascriptFunction("local_discovery.onRegistrationFailed"); 383} 384 385void LocalDiscoveryUIHandler::SendRegisterDone() { 386 web_ui()->CallJavascriptFunction("local_discovery.onRegistrationSuccess"); 387} 388 389void LocalDiscoveryUIHandler::SetIsVisible(bool visible) { 390 if (visible != is_visible_) { 391 g_num_visible += visible ? 1 : -1; 392 is_visible_ = visible; 393 } 394} 395 396std::string LocalDiscoveryUIHandler::GetSyncAccount() { 397 Profile* profile = Profile::FromWebUI(web_ui()); 398 SigninManagerBase* signin_manager = 399 SigninManagerFactory::GetForProfileIfExists(profile); 400 401 if (!signin_manager) { 402 return ""; 403 } 404 405 return signin_manager->GetAuthenticatedUsername(); 406} 407 408std::string LocalDiscoveryUIHandler::GetCloudPrintBaseUrl() { 409 CloudPrintURL cloud_print_url(Profile::FromWebUI(web_ui())); 410 411 return cloud_print_url.GetCloudPrintServiceURL().spec(); 412} 413 414// TODO(noamsml): Create master object for registration flow. 415void LocalDiscoveryUIHandler::ResetCurrentRegistration() { 416 if (current_register_operation_.get()) { 417 current_register_operation_->Cancel(); 418 current_register_operation_.reset(); 419 } 420 421 confirm_api_call_flow_.reset(); 422 privet_resolution_.reset(); 423 current_http_client_.reset(); 424} 425 426scoped_ptr<base::DictionaryValue> LocalDiscoveryUIHandler::CreatePrinterInfo( 427 const CloudPrintPrinterList::PrinterDetails& description) { 428 scoped_ptr<base::DictionaryValue> return_value(new base::DictionaryValue); 429 430 return_value->SetString("id", description.id); 431 return_value->SetString("display_name", description.display_name); 432 return_value->SetString("description", description.description); 433 434 return return_value.Pass(); 435} 436 437void LocalDiscoveryUIHandler::CheckUserLoggedIn() { 438 base::FundamentalValue logged_in_value(!GetSyncAccount().empty()); 439 web_ui()->CallJavascriptFunction("local_discovery.setUserLoggedIn", 440 logged_in_value); 441} 442 443} // namespace local_discovery 444