| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 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/settings/sync_handler.h" | |
| 6 | |
| 7 #include "base/basictypes.h" | |
| 8 #include "base/bind.h" | |
| 9 #include "base/bind_helpers.h" | |
| 10 #include "base/command_line.h" | |
| 11 #include "base/compiler_specific.h" | |
| 12 #include "base/i18n/time_formatting.h" | |
| 13 #include "base/json/json_reader.h" | |
| 14 #include "base/json/json_writer.h" | |
| 15 #include "base/metrics/histogram.h" | |
| 16 #include "base/prefs/pref_service.h" | |
| 17 #include "base/values.h" | |
| 18 #include "chrome/browser/lifetime/application_lifetime.h" | |
| 19 #include "chrome/browser/profiles/profile.h" | |
| 20 #include "chrome/browser/profiles/profile_info_cache.h" | |
| 21 #include "chrome/browser/profiles/profile_metrics.h" | |
| 22 #include "chrome/browser/profiles/profile_window.h" | |
| 23 #include "chrome/browser/signin/chrome_signin_helper.h" | |
| 24 #include "chrome/browser/signin/signin_error_controller_factory.h" | |
| 25 #include "chrome/browser/signin/signin_manager_factory.h" | |
| 26 #include "chrome/browser/signin/signin_promo.h" | |
| 27 #include "chrome/browser/sync/profile_sync_service_factory.h" | |
| 28 #include "chrome/browser/sync/sync_ui_util.h" | |
| 29 #include "chrome/browser/ui/browser_finder.h" | |
| 30 #include "chrome/browser/ui/browser_window.h" | |
| 31 #include "chrome/browser/ui/singleton_tabs.h" | |
| 32 #include "chrome/browser/ui/user_manager.h" | |
| 33 #include "chrome/browser/ui/webui/options/options_handlers_helper.h" | |
| 34 #include "chrome/browser/ui/webui/signin/login_ui_service.h" | |
| 35 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h" | |
| 36 #include "chrome/common/chrome_switches.h" | |
| 37 #include "chrome/grit/generated_resources.h" | |
| 38 #include "components/browser_sync/browser/profile_sync_service.h" | |
| 39 #include "components/signin/core/browser/signin_error_controller.h" | |
| 40 #include "components/signin/core/browser/signin_header_helper.h" | |
| 41 #include "components/signin/core/browser/signin_metrics.h" | |
| 42 #include "components/signin/core/common/profile_management_switches.h" | |
| 43 #include "components/signin/core/common/signin_pref_names.h" | |
| 44 #include "components/sync_driver/sync_prefs.h" | |
| 45 #include "content/public/browser/render_view_host.h" | |
| 46 #include "content/public/browser/web_contents.h" | |
| 47 #include "content/public/browser/web_contents_delegate.h" | |
| 48 #include "google_apis/gaia/gaia_auth_util.h" | |
| 49 #include "google_apis/gaia/gaia_constants.h" | |
| 50 #include "net/base/url_util.h" | |
| 51 #include "ui/base/l10n/l10n_util.h" | |
| 52 | |
| 53 #if defined(OS_CHROMEOS) | |
| 54 #include "components/signin/core/browser/signin_manager_base.h" | |
| 55 #else | |
| 56 #include "components/signin/core/browser/signin_manager.h" | |
| 57 #endif | |
| 58 | |
| 59 using content::WebContents; | |
| 60 using l10n_util::GetStringFUTF16; | |
| 61 using l10n_util::GetStringUTF16; | |
| 62 | |
| 63 namespace { | |
| 64 | |
| 65 // A structure which contains all the configuration information for sync. | |
| 66 struct SyncConfigInfo { | |
| 67 SyncConfigInfo(); | |
| 68 ~SyncConfigInfo(); | |
| 69 | |
| 70 bool encrypt_all; | |
| 71 bool sync_everything; | |
| 72 bool sync_nothing; | |
| 73 syncer::ModelTypeSet data_types; | |
| 74 std::string passphrase; | |
| 75 bool passphrase_is_gaia; | |
| 76 }; | |
| 77 | |
| 78 SyncConfigInfo::SyncConfigInfo() | |
| 79 : encrypt_all(false), | |
| 80 sync_everything(false), | |
| 81 sync_nothing(false), | |
| 82 passphrase_is_gaia(false) { | |
| 83 } | |
| 84 | |
| 85 SyncConfigInfo::~SyncConfigInfo() {} | |
| 86 | |
| 87 bool GetConfiguration(const std::string& json, SyncConfigInfo* config) { | |
| 88 scoped_ptr<base::Value> parsed_value = base::JSONReader::Read(json); | |
| 89 base::DictionaryValue* result; | |
| 90 if (!parsed_value || !parsed_value->GetAsDictionary(&result)) { | |
| 91 DLOG(ERROR) << "GetConfiguration() not passed a Dictionary"; | |
| 92 return false; | |
| 93 } | |
| 94 | |
| 95 if (!result->GetBoolean("syncAllDataTypes", &config->sync_everything)) { | |
| 96 DLOG(ERROR) << "GetConfiguration() not passed a syncAllDataTypes value"; | |
| 97 return false; | |
| 98 } | |
| 99 | |
| 100 if (!result->GetBoolean("syncNothing", &config->sync_nothing)) { | |
| 101 DLOG(ERROR) << "GetConfiguration() not passed a syncNothing value"; | |
| 102 return false; | |
| 103 } | |
| 104 | |
| 105 DCHECK(!(config->sync_everything && config->sync_nothing)) | |
| 106 << "syncAllDataTypes and syncNothing cannot both be true"; | |
| 107 | |
| 108 syncer::ModelTypeNameMap type_names = syncer::GetUserSelectableTypeNameMap(); | |
| 109 | |
| 110 for (syncer::ModelTypeNameMap::const_iterator it = type_names.begin(); | |
| 111 it != type_names.end(); ++it) { | |
| 112 std::string key_name = it->second + std::string("Synced"); | |
| 113 bool sync_value; | |
| 114 if (!result->GetBoolean(key_name, &sync_value)) { | |
| 115 DLOG(ERROR) << "GetConfiguration() not passed a value for " << key_name; | |
| 116 return false; | |
| 117 } | |
| 118 if (sync_value) | |
| 119 config->data_types.Put(it->first); | |
| 120 } | |
| 121 | |
| 122 // Encryption settings. | |
| 123 if (!result->GetBoolean("encryptAllData", &config->encrypt_all)) { | |
| 124 DLOG(ERROR) << "GetConfiguration() not passed a value for encryptAllData"; | |
| 125 return false; | |
| 126 } | |
| 127 | |
| 128 // Passphrase settings. | |
| 129 bool have_passphrase; | |
| 130 if (!result->GetBoolean("usePassphrase", &have_passphrase)) { | |
| 131 DLOG(ERROR) << "GetConfiguration() not passed a usePassphrase value"; | |
| 132 return false; | |
| 133 } | |
| 134 | |
| 135 if (have_passphrase) { | |
| 136 if (!result->GetBoolean("isGooglePassphrase", | |
| 137 &config->passphrase_is_gaia)) { | |
| 138 DLOG(ERROR) << "GetConfiguration() not passed isGooglePassphrase value"; | |
| 139 return false; | |
| 140 } | |
| 141 if (!result->GetString("passphrase", &config->passphrase)) { | |
| 142 DLOG(ERROR) << "GetConfiguration() not passed a passphrase value"; | |
| 143 return false; | |
| 144 } | |
| 145 } | |
| 146 return true; | |
| 147 } | |
| 148 | |
| 149 } // namespace | |
| 150 | |
| 151 namespace settings { | |
| 152 | |
| 153 SyncHandler::SyncHandler(Profile* profile) | |
| 154 : profile_(profile), | |
| 155 configuring_sync_(false), | |
| 156 sync_service_observer_(this) { | |
| 157 PrefService* prefs = profile_->GetPrefs(); | |
| 158 profile_pref_registrar_.Init(prefs); | |
| 159 profile_pref_registrar_.Add( | |
| 160 prefs::kSigninAllowed, base::Bind(&SyncHandler::OnSigninAllowedPrefChange, | |
| 161 base::Unretained(this))); | |
| 162 | |
| 163 ProfileSyncService* sync_service( | |
| 164 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile_)); | |
| 165 if (sync_service) | |
| 166 sync_service_observer_.Add(sync_service); | |
| 167 } | |
| 168 | |
| 169 SyncHandler::~SyncHandler() { | |
| 170 // Just exit if running unit tests (no actual WebUI is attached). | |
| 171 if (!web_ui()) | |
| 172 return; | |
| 173 | |
| 174 // This case is hit when the user performs a back navigation. | |
| 175 CloseSyncSetup(); | |
| 176 } | |
| 177 | |
| 178 void SyncHandler::ConfigureSyncDone() { | |
| 179 base::StringValue page("done"); | |
| 180 web_ui()->CallJavascriptFunction("settings.SyncPrivateApi.showSyncSetupPage", | |
| 181 page); | |
| 182 | |
| 183 // Suppress the sign in promo once the user starts sync. This way the user | |
| 184 // doesn't see the sign in promo even if they sign out later on. | |
| 185 signin::SetUserSkippedPromo(profile_); | |
| 186 | |
| 187 ProfileSyncService* service = GetSyncService(); | |
| 188 DCHECK(service); | |
| 189 if (!service->HasSyncSetupCompleted()) { | |
| 190 // This is the first time configuring sync, so log it. | |
| 191 base::FilePath profile_file_path = profile_->GetPath(); | |
| 192 ProfileMetrics::LogProfileSyncSignIn(profile_file_path); | |
| 193 | |
| 194 // We're done configuring, so notify ProfileSyncService that it is OK to | |
| 195 // start syncing. | |
| 196 service->SetSetupInProgress(false); | |
| 197 service->SetSyncSetupCompleted(); | |
| 198 } | |
| 199 } | |
| 200 | |
| 201 bool SyncHandler::IsActiveLogin() const { | |
| 202 // LoginUIService can be nullptr if page is brought up in incognito mode | |
| 203 // (i.e. if the user is running in guest mode in cros and brings up settings). | |
| 204 LoginUIService* service = GetLoginUIService(); | |
| 205 return service && (service->current_login_ui() == this); | |
| 206 } | |
| 207 | |
| 208 void SyncHandler::RegisterMessages() { | |
| 209 web_ui()->RegisterMessageCallback( | |
| 210 "SyncSetupDidClosePage", | |
| 211 base::Bind(&SyncHandler::OnDidClosePage, | |
| 212 base::Unretained(this))); | |
| 213 web_ui()->RegisterMessageCallback( | |
| 214 "SyncSetupConfigure", | |
| 215 base::Bind(&SyncHandler::HandleConfigure, | |
| 216 base::Unretained(this))); | |
| 217 web_ui()->RegisterMessageCallback( | |
| 218 "SyncSetupShowSetupUI", | |
| 219 base::Bind(&SyncHandler::HandleShowSetupUI, | |
| 220 base::Unretained(this))); | |
| 221 web_ui()->RegisterMessageCallback( | |
| 222 "SyncSetupCloseTimeout", | |
| 223 base::Bind(&SyncHandler::HandleCloseTimeout, base::Unretained(this))); | |
| 224 web_ui()->RegisterMessageCallback( | |
| 225 "SyncSetupGetSyncStatus", | |
| 226 base::Bind(&SyncHandler::HandleGetSyncStatus, base::Unretained(this))); | |
| 227 web_ui()->RegisterMessageCallback( | |
| 228 "SyncSetupManageOtherPeople", | |
| 229 base::Bind(&SyncHandler::HandleManageOtherPeople, | |
| 230 base::Unretained(this))); | |
| 231 #if defined(OS_CHROMEOS) | |
| 232 web_ui()->RegisterMessageCallback( | |
| 233 "SyncSetupDoSignOutOnAuthError", | |
| 234 base::Bind(&SyncHandler::HandleDoSignOutOnAuthError, | |
| 235 base::Unretained(this))); | |
| 236 #else | |
| 237 web_ui()->RegisterMessageCallback("SyncSetupStopSyncing", | |
| 238 base::Bind(&SyncHandler::HandleStopSyncing, | |
| 239 base::Unretained(this))); | |
| 240 web_ui()->RegisterMessageCallback("SyncSetupStartSignIn", | |
| 241 base::Bind(&SyncHandler::HandleStartSignin, | |
| 242 base::Unretained(this))); | |
| 243 #endif | |
| 244 } | |
| 245 | |
| 246 #if !defined(OS_CHROMEOS) | |
| 247 void SyncHandler::DisplayGaiaLogin(signin_metrics::AccessPoint access_point) { | |
| 248 DCHECK(!sync_startup_tracker_); | |
| 249 // Advanced options are no longer being configured if the login screen is | |
| 250 // visible. If the user exits the signin wizard after this without | |
| 251 // configuring sync, CloseSyncSetup() will ensure they are logged out. | |
| 252 configuring_sync_ = false; | |
| 253 DisplayGaiaLoginInNewTabOrWindow(access_point); | |
| 254 } | |
| 255 | |
| 256 void SyncHandler::DisplayGaiaLoginInNewTabOrWindow( | |
| 257 signin_metrics::AccessPoint access_point) { | |
| 258 Browser* browser = chrome::FindBrowserWithWebContents( | |
| 259 web_ui()->GetWebContents()); | |
| 260 bool force_new_tab = false; | |
| 261 if (!browser) { | |
| 262 // Settings is not displayed in a browser window. Open a new window. | |
| 263 browser = new Browser(Browser::CreateParams(Browser::TYPE_TABBED, profile_, | |
| 264 chrome::GetActiveDesktop())); | |
| 265 force_new_tab = true; | |
| 266 } | |
| 267 | |
| 268 // If the signin manager already has an authenticated username, this is a | |
| 269 // re-auth scenario, and we need to ensure that the user signs in with the | |
| 270 // same email address. | |
| 271 GURL url; | |
| 272 if (SigninManagerFactory::GetForProfile( | |
| 273 browser->profile())->IsAuthenticated()) { | |
| 274 UMA_HISTOGRAM_ENUMERATION("Signin.Reauth", | |
| 275 signin_metrics::HISTOGRAM_REAUTH_SHOWN, | |
| 276 signin_metrics::HISTOGRAM_REAUTH_MAX); | |
| 277 | |
| 278 SigninErrorController* error_controller = | |
| 279 SigninErrorControllerFactory::GetForProfile(browser->profile()); | |
| 280 DCHECK(error_controller->HasError()); | |
| 281 if (!force_new_tab) { | |
| 282 browser->window()->ShowAvatarBubbleFromAvatarButton( | |
| 283 BrowserWindow::AVATAR_BUBBLE_MODE_REAUTH, | |
| 284 signin::ManageAccountsParams(), access_point); | |
| 285 } else { | |
| 286 url = signin::GetReauthURL( | |
| 287 access_point, signin_metrics::Reason::REASON_REAUTHENTICATION, | |
| 288 browser->profile(), error_controller->error_account_id()); | |
| 289 } | |
| 290 } else { | |
| 291 if (!force_new_tab) { | |
| 292 browser->window()->ShowAvatarBubbleFromAvatarButton( | |
| 293 BrowserWindow::AVATAR_BUBBLE_MODE_SIGNIN, | |
| 294 signin::ManageAccountsParams(), access_point); | |
| 295 } else { | |
| 296 url = signin::GetPromoURL( | |
| 297 access_point, signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT, | |
| 298 true); | |
| 299 } | |
| 300 } | |
| 301 | |
| 302 if (url.is_valid()) | |
| 303 chrome::ShowSingletonTab(browser, url); | |
| 304 } | |
| 305 #endif | |
| 306 | |
| 307 bool SyncHandler::PrepareSyncSetup() { | |
| 308 // If the wizard is already visible, just focus that one. | |
| 309 if (FocusExistingWizardIfPresent()) { | |
| 310 if (!IsActiveLogin()) | |
| 311 CloseSyncSetup(); | |
| 312 return false; | |
| 313 } | |
| 314 | |
| 315 // Notify services that login UI is now active. | |
| 316 GetLoginUIService()->SetLoginUI(this); | |
| 317 | |
| 318 ProfileSyncService* service = GetSyncService(); | |
| 319 if (service) | |
| 320 service->SetSetupInProgress(true); | |
| 321 | |
| 322 return true; | |
| 323 } | |
| 324 | |
| 325 void SyncHandler::DisplaySpinner() { | |
| 326 configuring_sync_ = true; | |
| 327 base::StringValue page("spinner"); | |
| 328 base::DictionaryValue args; | |
| 329 | |
| 330 const int kTimeoutSec = 30; | |
| 331 DCHECK(!backend_start_timer_); | |
| 332 backend_start_timer_.reset(new base::OneShotTimer()); | |
| 333 backend_start_timer_->Start(FROM_HERE, | |
| 334 base::TimeDelta::FromSeconds(kTimeoutSec), | |
| 335 this, &SyncHandler::DisplayTimeout); | |
| 336 | |
| 337 web_ui()->CallJavascriptFunction("settings.SyncPrivateApi.showSyncSetupPage", | |
| 338 page, args); | |
| 339 } | |
| 340 | |
| 341 // TODO(kochi): Handle error conditions other than timeout. | |
| 342 // http://crbug.com/128692 | |
| 343 void SyncHandler::DisplayTimeout() { | |
| 344 // Stop a timer to handle timeout in waiting for checking network connection. | |
| 345 backend_start_timer_.reset(); | |
| 346 | |
| 347 // Do not listen to sync startup events. | |
| 348 sync_startup_tracker_.reset(); | |
| 349 | |
| 350 base::StringValue page("timeout"); | |
| 351 base::DictionaryValue args; | |
| 352 web_ui()->CallJavascriptFunction("settings.SyncPrivateApi.showSyncSetupPage", | |
| 353 page, args); | |
| 354 } | |
| 355 | |
| 356 void SyncHandler::OnDidClosePage(const base::ListValue* args) { | |
| 357 CloseSyncSetup(); | |
| 358 } | |
| 359 | |
| 360 void SyncHandler::SyncStartupFailed() { | |
| 361 // Stop a timer to handle timeout in waiting for checking network connection. | |
| 362 backend_start_timer_.reset(); | |
| 363 | |
| 364 // Just close the sync overlay (the idea is that the base settings page will | |
| 365 // display the current error.) | |
| 366 CloseUI(); | |
| 367 } | |
| 368 | |
| 369 void SyncHandler::SyncStartupCompleted() { | |
| 370 ProfileSyncService* service = GetSyncService(); | |
| 371 DCHECK(service->IsBackendInitialized()); | |
| 372 | |
| 373 // Stop a timer to handle timeout in waiting for checking network connection. | |
| 374 backend_start_timer_.reset(); | |
| 375 | |
| 376 DisplayConfigureSync(false); | |
| 377 } | |
| 378 | |
| 379 ProfileSyncService* SyncHandler::GetSyncService() const { | |
| 380 return profile_->IsSyncAllowed() | |
| 381 ? ProfileSyncServiceFactory::GetForProfile(profile_) | |
| 382 : nullptr; | |
| 383 } | |
| 384 | |
| 385 void SyncHandler::HandleConfigure(const base::ListValue* args) { | |
| 386 DCHECK(!sync_startup_tracker_); | |
| 387 std::string json; | |
| 388 if (!args->GetString(0, &json)) { | |
| 389 NOTREACHED() << "Could not read JSON argument"; | |
| 390 return; | |
| 391 } | |
| 392 if (json.empty()) { | |
| 393 NOTREACHED(); | |
| 394 return; | |
| 395 } | |
| 396 | |
| 397 SyncConfigInfo configuration; | |
| 398 if (!GetConfiguration(json, &configuration)) { | |
| 399 // The page sent us something that we didn't understand. | |
| 400 // This probably indicates a programming error. | |
| 401 NOTREACHED(); | |
| 402 return; | |
| 403 } | |
| 404 | |
| 405 // Start configuring the ProfileSyncService using the configuration passed | |
| 406 // to us from the JS layer. | |
| 407 ProfileSyncService* service = GetSyncService(); | |
| 408 | |
| 409 // If the sync engine has shutdown for some reason, just close the sync | |
| 410 // dialog. | |
| 411 if (!service || !service->IsBackendInitialized()) { | |
| 412 CloseUI(); | |
| 413 return; | |
| 414 } | |
| 415 | |
| 416 // Disable sync, but remain signed in if the user selected "Sync nothing" in | |
| 417 // the advanced settings dialog. Note: In order to disable sync across | |
| 418 // restarts on Chrome OS, we must call RequestStop(CLEAR_DATA), which | |
| 419 // suppresses sync startup in addition to disabling it. | |
| 420 if (configuration.sync_nothing) { | |
| 421 ProfileSyncService::SyncEvent( | |
| 422 ProfileSyncService::STOP_FROM_ADVANCED_DIALOG); | |
| 423 CloseUI(); | |
| 424 service->RequestStop(ProfileSyncService::CLEAR_DATA); | |
| 425 service->SetSetupInProgress(false); | |
| 426 return; | |
| 427 } | |
| 428 | |
| 429 // Don't allow "encrypt all" if the ProfileSyncService doesn't allow it. | |
| 430 // The UI is hidden, but the user may have enabled it e.g. by fiddling with | |
| 431 // the web inspector. | |
| 432 if (!service->IsEncryptEverythingAllowed()) | |
| 433 configuration.encrypt_all = false; | |
| 434 | |
| 435 // Note: Data encryption will not occur until configuration is complete | |
| 436 // (when the PSS receives its CONFIGURE_DONE notification from the sync | |
| 437 // backend), so the user still has a chance to cancel out of the operation | |
| 438 // if (for example) some kind of passphrase error is encountered. | |
| 439 if (configuration.encrypt_all) | |
| 440 service->EnableEncryptEverything(); | |
| 441 | |
| 442 bool passphrase_failed = false; | |
| 443 if (!configuration.passphrase.empty()) { | |
| 444 // We call IsPassphraseRequired() here (instead of | |
| 445 // IsPassphraseRequiredForDecryption()) because the user may try to enter | |
| 446 // a passphrase even though no encrypted data types are enabled. | |
| 447 if (service->IsPassphraseRequired()) { | |
| 448 // If we have pending keys, try to decrypt them with the provided | |
| 449 // passphrase. We track if this succeeds or fails because a failed | |
| 450 // decryption should result in an error even if there aren't any encrypted | |
| 451 // data types. | |
| 452 passphrase_failed = | |
| 453 !service->SetDecryptionPassphrase(configuration.passphrase); | |
| 454 } else { | |
| 455 // OK, the user sent us a passphrase, but we don't have pending keys. So | |
| 456 // it either means that the pending keys were resolved somehow since the | |
| 457 // time the UI was displayed (re-encryption, pending passphrase change, | |
| 458 // etc) or the user wants to re-encrypt. | |
| 459 if (!configuration.passphrase_is_gaia && | |
| 460 !service->IsUsingSecondaryPassphrase()) { | |
| 461 // User passed us a secondary passphrase, and the data is encrypted | |
| 462 // with a GAIA passphrase so they must want to encrypt. | |
| 463 service->SetEncryptionPassphrase(configuration.passphrase, | |
| 464 ProfileSyncService::EXPLICIT); | |
| 465 } | |
| 466 } | |
| 467 } | |
| 468 | |
| 469 bool user_was_prompted_for_passphrase = | |
| 470 service->IsPassphraseRequiredForDecryption(); | |
| 471 service->OnUserChoseDatatypes(configuration.sync_everything, | |
| 472 configuration.data_types); | |
| 473 | |
| 474 // Need to call IsPassphraseRequiredForDecryption() *after* calling | |
| 475 // OnUserChoseDatatypes() because the user may have just disabled the | |
| 476 // encrypted datatypes (in which case we just want to exit, not prompt the | |
| 477 // user for a passphrase). | |
| 478 if (passphrase_failed || service->IsPassphraseRequiredForDecryption()) { | |
| 479 // We need a passphrase, or the user's attempt to set a passphrase failed - | |
| 480 // prompt them again. This covers a few subtle cases: | |
| 481 // 1) The user enters an incorrect passphrase *and* disabled the encrypted | |
| 482 // data types. In that case we want to notify the user that the | |
| 483 // passphrase was incorrect even though there are no longer any encrypted | |
| 484 // types enabled (IsPassphraseRequiredForDecryption() == false). | |
| 485 // 2) The user doesn't enter any passphrase. In this case, we won't call | |
| 486 // SetDecryptionPassphrase() (passphrase_failed == false), but we still | |
| 487 // want to display an error message to let the user know that their | |
| 488 // blank passphrase entry is not acceptable. | |
| 489 // 3) The user just enabled an encrypted data type - in this case we don't | |
| 490 // want to display an "invalid passphrase" error, since it's the first | |
| 491 // time the user is seeing the prompt. | |
| 492 DisplayConfigureSync(passphrase_failed || user_was_prompted_for_passphrase); | |
| 493 } else { | |
| 494 // No passphrase is required from the user so mark the configuration as | |
| 495 // complete and close the sync setup overlay. | |
| 496 ConfigureSyncDone(); | |
| 497 } | |
| 498 | |
| 499 ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_CUSTOMIZE); | |
| 500 if (configuration.encrypt_all) | |
| 501 ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_ENCRYPT); | |
| 502 if (configuration.passphrase_is_gaia && !configuration.passphrase.empty()) | |
| 503 ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_PASSPHRASE); | |
| 504 if (!configuration.sync_everything) | |
| 505 ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_CHOOSE); | |
| 506 } | |
| 507 | |
| 508 void SyncHandler::HandleShowSetupUI(const base::ListValue* args) { | |
| 509 if (!GetSyncService()) { | |
| 510 CloseUI(); | |
| 511 return; | |
| 512 } | |
| 513 | |
| 514 SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile_); | |
| 515 if (!signin->IsAuthenticated()) { | |
| 516 // For web-based signin, the signin page is not displayed in an overlay | |
| 517 // on the settings page. So if we get here, it must be due to the user | |
| 518 // cancelling signin (by reloading the sync settings page during initial | |
| 519 // signin) or by directly navigating to settings/syncSetup | |
| 520 // (http://crbug.com/229836). So just exit and go back to the settings page. | |
| 521 DLOG(WARNING) << "Cannot display sync setup UI when not signed in"; | |
| 522 CloseUI(); | |
| 523 return; | |
| 524 } | |
| 525 | |
| 526 // If a setup wizard is already present, but not on this page, close the | |
| 527 // blank setup overlay on this page by showing the "done" page. This can | |
| 528 // happen if the user navigates to chrome://settings/syncSetup in more than | |
| 529 // one tab. See crbug.com/261566. | |
| 530 // Note: The following block will transfer focus to the existing wizard. | |
| 531 if (IsExistingWizardPresent() && !IsActiveLogin()) | |
| 532 CloseUI(); | |
| 533 | |
| 534 // If a setup wizard is present on this page or another, bring it to focus. | |
| 535 // Otherwise, display a new one on this page. | |
| 536 if (!FocusExistingWizardIfPresent()) | |
| 537 OpenSyncSetup(args); | |
| 538 } | |
| 539 | |
| 540 #if defined(OS_CHROMEOS) | |
| 541 // On ChromeOS, we need to sign out the user session to fix an auth error, so | |
| 542 // the user goes through the real signin flow to generate a new auth token. | |
| 543 void SyncHandler::HandleDoSignOutOnAuthError(const base::ListValue* args) { | |
| 544 DVLOG(1) << "Signing out the user to fix a sync error."; | |
| 545 chrome::AttemptUserExit(); | |
| 546 } | |
| 547 #endif | |
| 548 | |
| 549 #if !defined(OS_CHROMEOS) | |
| 550 void SyncHandler::HandleStartSignin(const base::ListValue* args) { | |
| 551 // Should only be called if the user is not already signed in. | |
| 552 DCHECK(!SigninManagerFactory::GetForProfile(profile_)->IsAuthenticated()); | |
| 553 OpenSyncSetup(args); | |
| 554 } | |
| 555 | |
| 556 void SyncHandler::HandleStopSyncing(const base::ListValue* args) { | |
| 557 if (GetSyncService()) | |
| 558 ProfileSyncService::SyncEvent(ProfileSyncService::STOP_FROM_OPTIONS); | |
| 559 SigninManagerFactory::GetForProfile(profile_) | |
| 560 ->SignOut(signin_metrics::USER_CLICKED_SIGNOUT_SETTINGS); | |
| 561 | |
| 562 bool delete_profile = false; | |
| 563 if (args->GetBoolean(0, &delete_profile) && delete_profile) { | |
| 564 // Do as BrowserOptionsHandler::DeleteProfile(). | |
| 565 options::helper::DeleteProfileAtPath(profile_->GetPath(), web_ui()); | |
| 566 } | |
| 567 } | |
| 568 #endif | |
| 569 | |
| 570 void SyncHandler::HandleCloseTimeout(const base::ListValue* args) { | |
| 571 CloseSyncSetup(); | |
| 572 } | |
| 573 | |
| 574 void SyncHandler::HandleGetSyncStatus(const base::ListValue* /* args */) { | |
| 575 UpdateSyncState(); | |
| 576 } | |
| 577 | |
| 578 void SyncHandler::HandleManageOtherPeople(const base::ListValue* /* args */) { | |
| 579 UserManager::Show(base::FilePath(), profiles::USER_MANAGER_NO_TUTORIAL, | |
| 580 profiles::USER_MANAGER_SELECT_PROFILE_NO_ACTION); | |
| 581 } | |
| 582 | |
| 583 void SyncHandler::CloseSyncSetup() { | |
| 584 // Stop a timer to handle timeout in waiting for checking network connection. | |
| 585 backend_start_timer_.reset(); | |
| 586 | |
| 587 // Clear the sync startup tracker, since the setup wizard is being closed. | |
| 588 sync_startup_tracker_.reset(); | |
| 589 | |
| 590 ProfileSyncService* sync_service = GetSyncService(); | |
| 591 if (IsActiveLogin()) { | |
| 592 // Don't log a cancel event if the sync setup dialog is being | |
| 593 // automatically closed due to an auth error. | |
| 594 if (!sync_service || (!sync_service->HasSyncSetupCompleted() && | |
| 595 sync_service->GetAuthError().state() == GoogleServiceAuthError::NONE)) { | |
| 596 if (configuring_sync_) { | |
| 597 ProfileSyncService::SyncEvent( | |
| 598 ProfileSyncService::CANCEL_DURING_CONFIGURE); | |
| 599 | |
| 600 // If the user clicked "Cancel" while setting up sync, disable sync | |
| 601 // because we don't want the sync backend to remain in the | |
| 602 // first-setup-incomplete state. | |
| 603 // Note: In order to disable sync across restarts on Chrome OS, | |
| 604 // we must call RequestStop(CLEAR_DATA), which suppresses sync startup | |
| 605 // in addition to disabling it. | |
| 606 if (sync_service) { | |
| 607 DVLOG(1) << "Sync setup aborted by user action"; | |
| 608 sync_service->RequestStop(ProfileSyncService::CLEAR_DATA); | |
| 609 #if !defined(OS_CHROMEOS) | |
| 610 // Sign out the user on desktop Chrome if they click cancel during | |
| 611 // initial setup. | |
| 612 // TODO(rsimha): Revisit this for M30. See http://crbug.com/252049. | |
| 613 if (sync_service->IsFirstSetupInProgress()) { | |
| 614 SigninManagerFactory::GetForProfile(profile_) | |
| 615 ->SignOut(signin_metrics::ABORT_SIGNIN); | |
| 616 } | |
| 617 #endif | |
| 618 } | |
| 619 } | |
| 620 } | |
| 621 | |
| 622 GetLoginUIService()->LoginUIClosed(this); | |
| 623 } | |
| 624 | |
| 625 // Alert the sync service anytime the sync setup dialog is closed. This can | |
| 626 // happen due to the user clicking the OK or Cancel button, or due to the | |
| 627 // dialog being closed by virtue of sync being disabled in the background. | |
| 628 if (sync_service) | |
| 629 sync_service->SetSetupInProgress(false); | |
| 630 | |
| 631 configuring_sync_ = false; | |
| 632 } | |
| 633 | |
| 634 void SyncHandler::OpenSyncSetup(const base::ListValue* args) { | |
| 635 if (!PrepareSyncSetup()) | |
| 636 return; | |
| 637 | |
| 638 // There are several different UI flows that can bring the user here: | |
| 639 // 1) Signin promo. | |
| 640 // 2) Normal signin through settings page (IsAuthenticated() is false). | |
| 641 // 3) Previously working credentials have expired. | |
| 642 // 4) User is signed in, but has stopped sync via the google dashboard, and | |
| 643 // signout is prohibited by policy so we need to force a re-auth. | |
| 644 // 5) User clicks [Advanced Settings] button on options page while already | |
| 645 // logged in. | |
| 646 // 6) One-click signin (credentials are already available, so should display | |
| 647 // sync configure UI, not login UI). | |
| 648 // 7) User re-enables sync after disabling it via advanced settings. | |
| 649 #if !defined(OS_CHROMEOS) | |
| 650 SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile_); | |
| 651 | |
| 652 if (!signin->IsAuthenticated() || | |
| 653 SigninErrorControllerFactory::GetForProfile(profile_)->HasError()) { | |
| 654 // User is not logged in (cases 1-2), or login has been specially requested | |
| 655 // because previously working credentials have expired (case 3). Close sync | |
| 656 // setup including any visible overlays, and display the gaia auth page. | |
| 657 // Control will be returned to the sync settings page once auth is complete. | |
| 658 CloseUI(); | |
| 659 if (args) { | |
| 660 std::string access_point = base::UTF16ToUTF8(ExtractStringValue(args)); | |
| 661 if (access_point == "access-point-supervised-user") { | |
| 662 DisplayGaiaLogin( | |
| 663 signin_metrics::AccessPoint::ACCESS_POINT_SUPERVISED_USER); | |
| 664 return; | |
| 665 } | |
| 666 } | |
| 667 DisplayGaiaLogin(signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS); | |
| 668 return; | |
| 669 } | |
| 670 #endif | |
| 671 if (!GetSyncService()) { | |
| 672 // This can happen if the user directly navigates to /settings/syncSetup. | |
| 673 DLOG(WARNING) << "Cannot display sync UI when sync is disabled"; | |
| 674 CloseUI(); | |
| 675 return; | |
| 676 } | |
| 677 | |
| 678 // User is already logged in. They must have brought up the config wizard | |
| 679 // via the "Advanced..." button or through One-Click signin (cases 4-6), or | |
| 680 // they are re-enabling sync after having disabled it (case 7). | |
| 681 DisplayConfigureSync(false); | |
| 682 } | |
| 683 | |
| 684 void SyncHandler::OpenConfigureSync() { | |
| 685 if (!PrepareSyncSetup()) | |
| 686 return; | |
| 687 | |
| 688 DisplayConfigureSync(false); | |
| 689 } | |
| 690 | |
| 691 void SyncHandler::FocusUI() { | |
| 692 DCHECK(IsActiveLogin()); | |
| 693 WebContents* web_contents = web_ui()->GetWebContents(); | |
| 694 web_contents->GetDelegate()->ActivateContents(web_contents); | |
| 695 } | |
| 696 | |
| 697 void SyncHandler::CloseUI() { | |
| 698 CloseSyncSetup(); | |
| 699 base::StringValue page("done"); | |
| 700 web_ui()->CallJavascriptFunction("settings.SyncPrivateApi.showSyncSetupPage", | |
| 701 page); | |
| 702 } | |
| 703 | |
| 704 void SyncHandler::GoogleSigninSucceeded(const std::string& /* account_id */, | |
| 705 const std::string& /* username */, | |
| 706 const std::string& /* password */) { | |
| 707 UpdateSyncState(); | |
| 708 } | |
| 709 | |
| 710 void SyncHandler::GoogleSignedOut(const std::string& /* account_id */, | |
| 711 const std::string& /* username */) { | |
| 712 UpdateSyncState(); | |
| 713 } | |
| 714 | |
| 715 void SyncHandler::OnStateChanged() { | |
| 716 UpdateSyncState(); | |
| 717 } | |
| 718 | |
| 719 scoped_ptr<base::DictionaryValue> SyncHandler::GetSyncStateDictionary() { | |
| 720 // The items which are to be written into |sync_status| are also described in | |
| 721 // chrome/browser/resources/options/browser_options.js in @typedef | |
| 722 // for SyncStatus. Please update it whenever you add or remove any keys here. | |
| 723 scoped_ptr<base::DictionaryValue> sync_status(new base::DictionaryValue); | |
| 724 if (profile_->IsGuestSession()) { | |
| 725 // Cannot display signin status when running in guest mode on chromeos | |
| 726 // because there is no SigninManager. | |
| 727 sync_status->SetBoolean("signinAllowed", false); | |
| 728 return sync_status.Pass(); | |
| 729 } | |
| 730 | |
| 731 sync_status->SetBoolean("supervisedUser", profile_->IsSupervised()); | |
| 732 sync_status->SetBoolean("childUser", profile_->IsChild()); | |
| 733 | |
| 734 bool signout_prohibited = false; | |
| 735 #if !defined(OS_CHROMEOS) | |
| 736 // Signout is not allowed if the user has policy (crbug.com/172204). | |
| 737 signout_prohibited = | |
| 738 SigninManagerFactory::GetForProfile(profile_)->IsSignoutProhibited(); | |
| 739 #endif | |
| 740 | |
| 741 ProfileSyncService* service = | |
| 742 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile_); | |
| 743 SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile_); | |
| 744 DCHECK(signin); | |
| 745 sync_status->SetBoolean("signoutAllowed", !signout_prohibited); | |
| 746 sync_status->SetBoolean("signinAllowed", signin->IsSigninAllowed()); | |
| 747 sync_status->SetBoolean("syncSystemEnabled", (service != nullptr)); | |
| 748 sync_status->SetBoolean("setupCompleted", | |
| 749 service && service->HasSyncSetupCompleted()); | |
| 750 sync_status->SetBoolean( | |
| 751 "setupInProgress", | |
| 752 service && !service->IsManaged() && service->IsFirstSetupInProgress()); | |
| 753 | |
| 754 base::string16 status_label; | |
| 755 base::string16 link_label; | |
| 756 bool status_has_error = | |
| 757 sync_ui_util::GetStatusLabels(profile_, service, *signin, | |
| 758 sync_ui_util::WITH_HTML, &status_label, | |
| 759 &link_label) == sync_ui_util::SYNC_ERROR; | |
| 760 sync_status->SetString("statusText", status_label); | |
| 761 sync_status->SetString("actionLinkText", link_label); | |
| 762 sync_status->SetBoolean("hasError", status_has_error); | |
| 763 | |
| 764 sync_status->SetBoolean("managed", service && service->IsManaged()); | |
| 765 sync_status->SetBoolean("signedIn", signin->IsAuthenticated()); | |
| 766 sync_status->SetBoolean("hasUnrecoverableError", | |
| 767 service && service->HasUnrecoverableError()); | |
| 768 | |
| 769 return sync_status.Pass(); | |
| 770 } | |
| 771 | |
| 772 bool SyncHandler::IsExistingWizardPresent() { | |
| 773 LoginUIService* service = GetLoginUIService(); | |
| 774 DCHECK(service); | |
| 775 return service->current_login_ui() != nullptr; | |
| 776 } | |
| 777 | |
| 778 bool SyncHandler::FocusExistingWizardIfPresent() { | |
| 779 if (!IsExistingWizardPresent()) | |
| 780 return false; | |
| 781 | |
| 782 LoginUIService* service = GetLoginUIService(); | |
| 783 DCHECK(service); | |
| 784 service->current_login_ui()->FocusUI(); | |
| 785 return true; | |
| 786 } | |
| 787 | |
| 788 void SyncHandler::DisplayConfigureSync(bool passphrase_failed) { | |
| 789 // Should never call this when we are not signed in. | |
| 790 DCHECK(SigninManagerFactory::GetForProfile(profile_)->IsAuthenticated()); | |
| 791 ProfileSyncService* service = GetSyncService(); | |
| 792 DCHECK(service); | |
| 793 if (!service->IsBackendInitialized()) { | |
| 794 service->RequestStart(); | |
| 795 | |
| 796 // See if it's even possible to bring up the sync backend - if not | |
| 797 // (unrecoverable error?), don't bother displaying a spinner that will be | |
| 798 // immediately closed because this leads to some ugly infinite UI loop (see | |
| 799 // http://crbug.com/244769). | |
| 800 if (SyncStartupTracker::GetSyncServiceState(profile_) != | |
| 801 SyncStartupTracker::SYNC_STARTUP_ERROR) { | |
| 802 DisplaySpinner(); | |
| 803 } | |
| 804 | |
| 805 // Start SyncSetupTracker to wait for sync to initialize. | |
| 806 sync_startup_tracker_.reset(new SyncStartupTracker(profile_, this)); | |
| 807 return; | |
| 808 } | |
| 809 | |
| 810 // Should only get here if user is signed in and sync is initialized, so no | |
| 811 // longer need a SyncStartupTracker. | |
| 812 sync_startup_tracker_.reset(); | |
| 813 configuring_sync_ = true; | |
| 814 DCHECK(service->IsBackendInitialized()) | |
| 815 << "Cannot configure sync until the sync backend is initialized"; | |
| 816 | |
| 817 // Setup args for the sync configure screen: | |
| 818 // syncAllDataTypes: true if the user wants to sync everything | |
| 819 // syncNothing: true if the user wants to sync nothing | |
| 820 // <data_type>Registered: true if the associated data type is supported | |
| 821 // <data_type>Synced: true if the user wants to sync that specific data type | |
| 822 // encryptionEnabled: true if sync supports encryption | |
| 823 // encryptAllData: true if user wants to encrypt all data (not just | |
| 824 // passwords) | |
| 825 // usePassphrase: true if the data is encrypted with a secondary passphrase | |
| 826 // show_passphrase: true if a passphrase is needed to decrypt the sync data | |
| 827 base::DictionaryValue args; | |
| 828 | |
| 829 // Tell the UI layer which data types are registered/enabled by the user. | |
| 830 const syncer::ModelTypeSet registered_types = | |
| 831 service->GetRegisteredDataTypes(); | |
| 832 const syncer::ModelTypeSet preferred_types = service->GetPreferredDataTypes(); | |
| 833 const syncer::ModelTypeSet enforced_types = service->GetForcedDataTypes(); | |
| 834 syncer::ModelTypeNameMap type_names = syncer::GetUserSelectableTypeNameMap(); | |
| 835 for (syncer::ModelTypeNameMap::const_iterator it = type_names.begin(); | |
| 836 it != type_names.end(); ++it) { | |
| 837 syncer::ModelType sync_type = it->first; | |
| 838 const std::string key_name = it->second; | |
| 839 args.SetBoolean(key_name + "Registered", registered_types.Has(sync_type)); | |
| 840 args.SetBoolean(key_name + "Synced", preferred_types.Has(sync_type)); | |
| 841 args.SetBoolean(key_name + "Enforced", enforced_types.Has(sync_type)); | |
| 842 // TODO(treib): How do we want to handle pref groups, i.e. when only some of | |
| 843 // the sync types behind a checkbox are force-enabled? crbug.com/403326 | |
| 844 } | |
| 845 sync_driver::SyncPrefs sync_prefs(profile_->GetPrefs()); | |
| 846 args.SetBoolean("passphraseFailed", passphrase_failed); | |
| 847 args.SetBoolean("syncAllDataTypes", sync_prefs.HasKeepEverythingSynced()); | |
| 848 args.SetBoolean("syncNothing", false); // Always false during initial setup. | |
| 849 args.SetBoolean("encryptAllData", service->IsEncryptEverythingEnabled()); | |
| 850 args.SetBoolean("encryptAllDataAllowed", | |
| 851 service->IsEncryptEverythingAllowed()); | |
| 852 | |
| 853 // We call IsPassphraseRequired() here, instead of calling | |
| 854 // IsPassphraseRequiredForDecryption(), because we want to show the passphrase | |
| 855 // UI even if no encrypted data types are enabled. | |
| 856 args.SetBoolean("showPassphrase", service->IsPassphraseRequired()); | |
| 857 | |
| 858 // To distinguish between FROZEN_IMPLICIT_PASSPHRASE and CUSTOM_PASSPHRASE | |
| 859 // we only set usePassphrase for CUSTOM_PASSPHRASE. | |
| 860 args.SetBoolean("usePassphrase", | |
| 861 service->GetPassphraseType() == syncer::CUSTOM_PASSPHRASE); | |
| 862 base::Time passphrase_time = service->GetExplicitPassphraseTime(); | |
| 863 syncer::PassphraseType passphrase_type = service->GetPassphraseType(); | |
| 864 if (!passphrase_time.is_null()) { | |
| 865 base::string16 passphrase_time_str = | |
| 866 base::TimeFormatShortDate(passphrase_time); | |
| 867 args.SetString( | |
| 868 "enterPassphraseBody", | |
| 869 GetStringFUTF16(IDS_SYNC_ENTER_PASSPHRASE_BODY_WITH_DATE, | |
| 870 passphrase_time_str)); | |
| 871 args.SetString( | |
| 872 "enterGooglePassphraseBody", | |
| 873 GetStringFUTF16(IDS_SYNC_ENTER_GOOGLE_PASSPHRASE_BODY_WITH_DATE, | |
| 874 passphrase_time_str)); | |
| 875 switch (passphrase_type) { | |
| 876 case syncer::FROZEN_IMPLICIT_PASSPHRASE: | |
| 877 args.SetString( | |
| 878 "fullEncryptionBody", | |
| 879 GetStringFUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_GOOGLE_WITH_DATE, | |
| 880 passphrase_time_str)); | |
| 881 break; | |
| 882 case syncer::CUSTOM_PASSPHRASE: | |
| 883 args.SetString( | |
| 884 "fullEncryptionBody", | |
| 885 GetStringFUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM_WITH_DATE, | |
| 886 passphrase_time_str)); | |
| 887 break; | |
| 888 default: | |
| 889 args.SetString( | |
| 890 "fullEncryptionBody", | |
| 891 GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM)); | |
| 892 break; | |
| 893 } | |
| 894 } else if (passphrase_type == syncer::CUSTOM_PASSPHRASE) { | |
| 895 args.SetString( | |
| 896 "fullEncryptionBody", | |
| 897 GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM)); | |
| 898 } else { | |
| 899 args.SetString( | |
| 900 "fullEncryptionBody", | |
| 901 GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_DATA)); | |
| 902 } | |
| 903 | |
| 904 base::StringValue page("configure"); | |
| 905 web_ui()->CallJavascriptFunction("settings.SyncPrivateApi.showSyncSetupPage", | |
| 906 page, args); | |
| 907 | |
| 908 // Make sure the tab used for the Gaia sign in does not cover the settings | |
| 909 // tab. | |
| 910 FocusUI(); | |
| 911 } | |
| 912 | |
| 913 LoginUIService* SyncHandler::GetLoginUIService() const { | |
| 914 return LoginUIServiceFactory::GetForProfile(profile_); | |
| 915 } | |
| 916 | |
| 917 void SyncHandler::UpdateSyncState() { | |
| 918 web_ui()->CallJavascriptFunction("settings.SyncPrivateApi.sendSyncStatus", | |
| 919 *GetSyncStateDictionary()); | |
| 920 } | |
| 921 | |
| 922 void SyncHandler::OnSigninAllowedPrefChange() { | |
| 923 UpdateSyncState(); | |
| 924 } | |
| 925 | |
| 926 } // namespace settings | |
| OLD | NEW |