1 // Copyright (c) 2012 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/sync_setup_handler.h"
7 #include "base/basictypes.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/strings/utf_string_conversions.h"
18 #include "base/values.h"
19 #include "chrome/app/chrome_command_ids.h"
20 #include "chrome/browser/google/google_util.h"
21 #include "chrome/browser/lifetime/application_lifetime.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/profiles/profile_info_cache.h"
24 #include "chrome/browser/profiles/profile_manager.h"
25 #include "chrome/browser/profiles/profile_metrics.h"
26 #include "chrome/browser/signin/profile_oauth2_token_service.h"
27 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
28 #include "chrome/browser/signin/signin_global_error.h"
29 #include "chrome/browser/signin/signin_manager_factory.h"
30 #include "chrome/browser/signin/signin_promo.h"
31 #include "chrome/browser/sync/profile_sync_service.h"
32 #include "chrome/browser/sync/profile_sync_service_factory.h"
33 #include "chrome/browser/ui/browser_finder.h"
34 #include "chrome/browser/ui/browser_navigator.h"
35 #include "chrome/browser/ui/singleton_tabs.h"
36 #include "chrome/browser/ui/sync/signin_histogram.h"
37 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
38 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
39 #include "chrome/common/chrome_switches.h"
40 #include "chrome/common/pref_names.h"
41 #include "chrome/common/url_constants.h"
42 #include "content/public/browser/render_view_host.h"
43 #include "content/public/browser/web_contents.h"
44 #include "content/public/browser/web_contents_delegate.h"
45 #include "google_apis/gaia/gaia_auth_util.h"
46 #include "google_apis/gaia/gaia_constants.h"
47 #include "grit/chromium_strings.h"
48 #include "grit/generated_resources.h"
49 #include "grit/locale_settings.h"
50 #include "net/base/url_util.h"
51 #include "ui/base/l10n/l10n_util.h"
53 #if defined(OS_CHROMEOS)
54 #include "chrome/browser/signin/signin_manager_base.h"
56 #include "chrome/browser/signin/signin_manager.h"
59 using content::WebContents;
60 using l10n_util::GetStringFUTF16;
61 using l10n_util::GetStringUTF16;
65 // A structure which contains all the configuration information for sync.
66 struct SyncConfigInfo {
73 syncer::ModelTypeSet data_types;
74 std::string passphrase;
75 bool passphrase_is_gaia;
78 SyncConfigInfo::SyncConfigInfo()
80 sync_everything(false),
82 passphrase_is_gaia(false) {
85 SyncConfigInfo::~SyncConfigInfo() {}
87 // Note: The order of these types must match the ordering of
88 // the respective types in ModelType
89 const char* kDataTypeNames[] = {
101 COMPILE_ASSERT(32 == syncer::MODEL_TYPE_COUNT,
102 update_kDataTypeNames_to_match_UserSelectableTypes);
104 typedef std::map<syncer::ModelType, const char*> ModelTypeNameMap;
106 ModelTypeNameMap GetSelectableTypeNameMap() {
107 ModelTypeNameMap type_names;
108 syncer::ModelTypeSet type_set = syncer::UserSelectableTypes();
109 syncer::ModelTypeSet::Iterator it = type_set.First();
110 DCHECK_EQ(arraysize(kDataTypeNames), type_set.Size());
111 for (size_t i = 0; i < arraysize(kDataTypeNames) && it.Good();
113 type_names[it.Get()] = kDataTypeNames[i];
118 bool GetConfiguration(const std::string& json, SyncConfigInfo* config) {
119 scoped_ptr<base::Value> parsed_value(base::JSONReader::Read(json));
120 base::DictionaryValue* result;
121 if (!parsed_value || !parsed_value->GetAsDictionary(&result)) {
122 DLOG(ERROR) << "GetConfiguration() not passed a Dictionary";
126 if (!result->GetBoolean("syncAllDataTypes", &config->sync_everything)) {
127 DLOG(ERROR) << "GetConfiguration() not passed a syncAllDataTypes value";
131 if (!result->GetBoolean("syncNothing", &config->sync_nothing)) {
132 DLOG(ERROR) << "GetConfiguration() not passed a syncNothing value";
136 DCHECK(!(config->sync_everything && config->sync_nothing))
137 << "syncAllDataTypes and syncNothing cannot both be true";
139 ModelTypeNameMap type_names = GetSelectableTypeNameMap();
141 for (ModelTypeNameMap::const_iterator it = type_names.begin();
142 it != type_names.end(); ++it) {
143 std::string key_name = it->second + std::string("Synced");
145 if (!result->GetBoolean(key_name, &sync_value)) {
146 DLOG(ERROR) << "GetConfiguration() not passed a value for " << key_name;
150 config->data_types.Put(it->first);
153 // Encryption settings.
154 if (!result->GetBoolean("encryptAllData", &config->encrypt_all)) {
155 DLOG(ERROR) << "GetConfiguration() not passed a value for encryptAllData";
159 // Passphrase settings.
160 bool have_passphrase;
161 if (!result->GetBoolean("usePassphrase", &have_passphrase)) {
162 DLOG(ERROR) << "GetConfiguration() not passed a usePassphrase value";
166 if (have_passphrase) {
167 if (!result->GetBoolean("isGooglePassphrase",
168 &config->passphrase_is_gaia)) {
169 DLOG(ERROR) << "GetConfiguration() not passed isGooglePassphrase value";
172 if (!result->GetString("passphrase", &config->passphrase)) {
173 DLOG(ERROR) << "GetConfiguration() not passed a passphrase value";
182 SyncSetupHandler::SyncSetupHandler(ProfileManager* profile_manager)
183 : configuring_sync_(false),
184 profile_manager_(profile_manager) {
187 SyncSetupHandler::~SyncSetupHandler() {
188 // Just exit if running unit tests (no actual WebUI is attached).
192 // This case is hit when the user performs a back navigation.
196 void SyncSetupHandler::GetLocalizedValues(
197 base::DictionaryValue* localized_strings) {
198 GetStaticLocalizedValues(localized_strings, web_ui());
201 void SyncSetupHandler::GetStaticLocalizedValues(
202 base::DictionaryValue* localized_strings,
203 content::WebUI* web_ui) {
204 DCHECK(localized_strings);
206 base::string16 product_name(GetStringUTF16(IDS_PRODUCT_NAME));
207 localized_strings->SetString(
208 "chooseDataTypesInstructions",
209 GetStringFUTF16(IDS_SYNC_CHOOSE_DATATYPES_INSTRUCTIONS, product_name));
210 localized_strings->SetString(
211 "encryptionInstructions",
212 GetStringFUTF16(IDS_SYNC_ENCRYPTION_INSTRUCTIONS, product_name));
213 localized_strings->SetString(
214 "encryptionHelpURL", chrome::kSyncEncryptionHelpURL);
215 localized_strings->SetString(
216 "encryptionSectionMessage",
217 GetStringFUTF16(IDS_SYNC_ENCRYPTION_SECTION_MESSAGE, product_name));
218 localized_strings->SetString(
220 GetStringFUTF16(IDS_SYNC_PASSPHRASE_RECOVER,
222 google_util::StringAppendGoogleLocaleParam(
223 chrome::kSyncGoogleDashboardURL))));
224 localized_strings->SetString("stopSyncingExplanation",
225 l10n_util::GetStringFUTF16(
226 IDS_SYNC_STOP_SYNCING_EXPLANATION_LABEL,
227 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
228 base::ASCIIToUTF16(google_util::StringAppendGoogleLocaleParam(
229 chrome::kSyncGoogleDashboardURL))));
230 localized_strings->SetString("stopSyncingTitle",
231 l10n_util::GetStringUTF16(IDS_SYNC_STOP_SYNCING_DIALOG_TITLE));
232 localized_strings->SetString("stopSyncingConfirm",
233 l10n_util::GetStringUTF16(IDS_SYNC_STOP_SYNCING_CONFIRM_BUTTON_LABEL));
235 localized_strings->SetString(
236 "syncEverythingHelpURL", chrome::kSyncEverythingLearnMoreURL);
237 localized_strings->SetString(
238 "syncErrorHelpURL", chrome::kSyncErrorsHelpURL);
240 static OptionsStringResource resources[] = {
241 { "syncSetupConfigureTitle", IDS_SYNC_SETUP_CONFIGURE_TITLE },
242 { "syncSetupSpinnerTitle", IDS_SYNC_SETUP_SPINNER_TITLE },
243 { "syncSetupTimeoutTitle", IDS_SYNC_SETUP_TIME_OUT_TITLE },
244 { "syncSetupTimeoutContent", IDS_SYNC_SETUP_TIME_OUT_CONTENT },
245 { "errorLearnMore", IDS_LEARN_MORE },
246 { "cancel", IDS_CANCEL },
247 { "loginSuccess", IDS_SYNC_SUCCESS },
248 { "settingUp", IDS_SYNC_LOGIN_SETTING_UP },
249 { "syncAllDataTypes", IDS_SYNC_EVERYTHING },
250 { "chooseDataTypes", IDS_SYNC_CHOOSE_DATATYPES },
251 { "syncNothing", IDS_SYNC_NOTHING },
252 { "bookmarks", IDS_SYNC_DATATYPE_BOOKMARKS },
253 { "preferences", IDS_SYNC_DATATYPE_PREFERENCES },
254 { "autofill", IDS_SYNC_DATATYPE_AUTOFILL },
255 { "themes", IDS_SYNC_DATATYPE_THEMES },
256 { "passwords", IDS_SYNC_DATATYPE_PASSWORDS },
257 { "extensions", IDS_SYNC_DATATYPE_EXTENSIONS },
258 { "typedURLs", IDS_SYNC_DATATYPE_TYPED_URLS },
259 { "apps", IDS_SYNC_DATATYPE_APPS },
260 { "openTabs", IDS_SYNC_DATATYPE_TABS },
261 { "serviceUnavailableError", IDS_SYNC_SETUP_ABORTED_BY_PENDING_CLEAR },
262 { "confirmLabel", IDS_SYNC_CONFIRM_PASSPHRASE_LABEL },
263 { "emptyErrorMessage", IDS_SYNC_EMPTY_PASSPHRASE_ERROR },
264 { "mismatchErrorMessage", IDS_SYNC_PASSPHRASE_MISMATCH_ERROR },
265 { "customizeLinkLabel", IDS_SYNC_CUSTOMIZE_LINK_LABEL },
266 { "confirmSyncPreferences", IDS_SYNC_CONFIRM_SYNC_PREFERENCES },
267 { "syncEverything", IDS_SYNC_SYNC_EVERYTHING },
268 { "useDefaultSettings", IDS_SYNC_USE_DEFAULT_SETTINGS },
269 { "enterPassphraseBody", IDS_SYNC_ENTER_PASSPHRASE_BODY },
270 { "enterGooglePassphraseBody", IDS_SYNC_ENTER_GOOGLE_PASSPHRASE_BODY },
271 { "passphraseLabel", IDS_SYNC_PASSPHRASE_LABEL },
272 { "incorrectPassphrase", IDS_SYNC_INCORRECT_PASSPHRASE },
273 { "passphraseWarning", IDS_SYNC_PASSPHRASE_WARNING },
274 { "yes", IDS_SYNC_PASSPHRASE_CANCEL_YES },
275 { "no", IDS_SYNC_PASSPHRASE_CANCEL_NO },
276 { "sectionExplicitMessagePrefix", IDS_SYNC_PASSPHRASE_MSG_EXPLICIT_PREFIX },
277 { "sectionExplicitMessagePostfix",
278 IDS_SYNC_PASSPHRASE_MSG_EXPLICIT_POSTFIX },
279 // TODO(rogerta): browser/resource/sync_promo/sync_promo.html and related
280 // file may not be needed any more. If not, then the following promo
281 // strings can also be removed.
282 { "promoPageTitle", IDS_SYNC_PROMO_TAB_TITLE },
283 { "promoSkipButton", IDS_SYNC_PROMO_SKIP_BUTTON },
284 { "promoAdvanced", IDS_SYNC_PROMO_ADVANCED },
285 { "promoLearnMore", IDS_LEARN_MORE },
286 { "promoTitleShort", IDS_SYNC_PROMO_MESSAGE_TITLE_SHORT },
287 { "encryptionSectionTitle", IDS_SYNC_ENCRYPTION_SECTION_TITLE },
288 { "basicEncryptionOption", IDS_SYNC_BASIC_ENCRYPTION_DATA },
289 { "fullEncryptionOption", IDS_SYNC_FULL_ENCRYPTION_DATA },
292 RegisterStrings(localized_strings, resources, arraysize(resources));
293 RegisterTitle(localized_strings, "syncSetupOverlay", IDS_SYNC_SETUP_TITLE);
296 void SyncSetupHandler::DisplayConfigureSync(bool show_advanced,
297 bool passphrase_failed) {
298 // Should never call this when we are not signed in.
299 DCHECK(!SigninManagerFactory::GetForProfile(
300 GetProfile())->GetAuthenticatedUsername().empty());
301 ProfileSyncService* service = GetSyncService();
303 if (!service->sync_initialized()) {
304 service->UnsuppressAndStart();
306 // See if it's even possible to bring up the sync backend - if not
307 // (unrecoverable error?), don't bother displaying a spinner that will be
308 // immediately closed because this leads to some ugly infinite UI loop (see
309 // http://crbug.com/244769).
310 if (SyncStartupTracker::GetSyncServiceState(GetProfile()) !=
311 SyncStartupTracker::SYNC_STARTUP_ERROR) {
315 // Start SyncSetupTracker to wait for sync to initialize.
316 sync_startup_tracker_.reset(
317 new SyncStartupTracker(GetProfile(), this));
321 // Should only get here if user is signed in and sync is initialized, so no
322 // longer need a SyncStartupTracker.
323 sync_startup_tracker_.reset();
324 configuring_sync_ = true;
325 DCHECK(service->sync_initialized()) <<
326 "Cannot configure sync until the sync backend is initialized";
328 // Setup args for the sync configure screen:
329 // showSyncEverythingPage: false to skip directly to the configure screen
330 // syncAllDataTypes: true if the user wants to sync everything
331 // syncNothing: true if the user wants to sync nothing
332 // <data_type>Registered: true if the associated data type is supported
333 // <data_type>Synced: true if the user wants to sync that specific data type
334 // encryptionEnabled: true if sync supports encryption
335 // encryptAllData: true if user wants to encrypt all data (not just
337 // usePassphrase: true if the data is encrypted with a secondary passphrase
338 // show_passphrase: true if a passphrase is needed to decrypt the sync data
339 base::DictionaryValue args;
341 // Tell the UI layer which data types are registered/enabled by the user.
342 const syncer::ModelTypeSet registered_types =
343 service->GetRegisteredDataTypes();
344 const syncer::ModelTypeSet preferred_types =
345 service->GetPreferredDataTypes();
346 ModelTypeNameMap type_names = GetSelectableTypeNameMap();
347 for (ModelTypeNameMap::const_iterator it = type_names.begin();
348 it != type_names.end(); ++it) {
349 syncer::ModelType sync_type = it->first;
350 const std::string key_name = it->second;
351 args.SetBoolean(key_name + "Registered",
352 registered_types.Has(sync_type));
353 args.SetBoolean(key_name + "Synced", preferred_types.Has(sync_type));
355 browser_sync::SyncPrefs sync_prefs(GetProfile()->GetPrefs());
356 args.SetBoolean("passphraseFailed", passphrase_failed);
357 args.SetBoolean("showSyncEverythingPage", !show_advanced);
358 args.SetBoolean("syncAllDataTypes", sync_prefs.HasKeepEverythingSynced());
359 args.SetBoolean("syncNothing", false); // Always false during initial setup.
360 args.SetBoolean("encryptAllData", service->EncryptEverythingEnabled());
362 // We call IsPassphraseRequired() here, instead of calling
363 // IsPassphraseRequiredForDecryption(), because we want to show the passphrase
364 // UI even if no encrypted data types are enabled.
365 args.SetBoolean("showPassphrase", service->IsPassphraseRequired());
367 // To distinguish between FROZEN_IMPLICIT_PASSPHRASE and CUSTOM_PASSPHRASE
368 // we only set usePassphrase for CUSTOM_PASSPHRASE.
369 args.SetBoolean("usePassphrase",
370 service->GetPassphraseType() == syncer::CUSTOM_PASSPHRASE);
371 base::Time passphrase_time = service->GetExplicitPassphraseTime();
372 syncer::PassphraseType passphrase_type = service->GetPassphraseType();
373 if (!passphrase_time.is_null()) {
374 base::string16 passphrase_time_str =
375 base::TimeFormatShortDate(passphrase_time);
377 "enterPassphraseBody",
378 GetStringFUTF16(IDS_SYNC_ENTER_PASSPHRASE_BODY_WITH_DATE,
379 passphrase_time_str));
381 "enterGooglePassphraseBody",
382 GetStringFUTF16(IDS_SYNC_ENTER_GOOGLE_PASSPHRASE_BODY_WITH_DATE,
383 passphrase_time_str));
384 switch (passphrase_type) {
385 case syncer::FROZEN_IMPLICIT_PASSPHRASE:
387 "fullEncryptionBody",
388 GetStringFUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_GOOGLE_WITH_DATE,
389 passphrase_time_str));
391 case syncer::CUSTOM_PASSPHRASE:
393 "fullEncryptionBody",
394 GetStringFUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM_WITH_DATE,
395 passphrase_time_str));
399 "fullEncryptionBody",
400 GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM));
403 } else if (passphrase_type == syncer::CUSTOM_PASSPHRASE) {
405 "fullEncryptionBody",
406 GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM));
409 "fullEncryptionBody",
410 GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_DATA));
413 base::StringValue page("configure");
414 web_ui()->CallJavascriptFunction(
415 "SyncSetupOverlay.showSyncSetupPage", page, args);
417 // Make sure the tab used for the Gaia sign in does not cover the settings
422 void SyncSetupHandler::ConfigureSyncDone() {
423 base::StringValue page("done");
424 web_ui()->CallJavascriptFunction(
425 "SyncSetupOverlay.showSyncSetupPage", page);
427 // Suppress the sign in promo once the user starts sync. This way the user
428 // doesn't see the sign in promo even if they sign out later on.
429 signin::SetUserSkippedPromo(GetProfile());
431 ProfileSyncService* service = GetSyncService();
433 if (!service->HasSyncSetupCompleted()) {
434 // This is the first time configuring sync, so log it.
435 base::FilePath profile_file_path = GetProfile()->GetPath();
436 ProfileMetrics::LogProfileSyncSignIn(profile_file_path);
438 // We're done configuring, so notify ProfileSyncService that it is OK to
440 service->SetSetupInProgress(false);
441 service->SetSyncSetupCompleted();
445 bool SyncSetupHandler::IsActiveLogin() const {
446 // LoginUIService can be NULL if page is brought up in incognito mode
447 // (i.e. if the user is running in guest mode in cros and brings up settings).
448 LoginUIService* service = GetLoginUIService();
449 return service && (service->current_login_ui() == this);
452 void SyncSetupHandler::RegisterMessages() {
453 web_ui()->RegisterMessageCallback(
454 "SyncSetupDidClosePage",
455 base::Bind(&SyncSetupHandler::OnDidClosePage,
456 base::Unretained(this)));
457 web_ui()->RegisterMessageCallback(
458 "SyncSetupConfigure",
459 base::Bind(&SyncSetupHandler::HandleConfigure,
460 base::Unretained(this)));
461 web_ui()->RegisterMessageCallback(
462 "SyncSetupShowSetupUI",
463 base::Bind(&SyncSetupHandler::HandleShowSetupUI,
464 base::Unretained(this)));
465 web_ui()->RegisterMessageCallback("CloseTimeout",
466 base::Bind(&SyncSetupHandler::HandleCloseTimeout,
467 base::Unretained(this)));
468 #if defined(OS_CHROMEOS)
469 web_ui()->RegisterMessageCallback(
470 "SyncSetupDoSignOutOnAuthError",
471 base::Bind(&SyncSetupHandler::HandleDoSignOutOnAuthError,
472 base::Unretained(this)));
474 web_ui()->RegisterMessageCallback("SyncSetupStopSyncing",
475 base::Bind(&SyncSetupHandler::HandleStopSyncing,
476 base::Unretained(this)));
477 web_ui()->RegisterMessageCallback("SyncSetupStartSignIn",
478 base::Bind(&SyncSetupHandler::HandleStartSignin,
479 base::Unretained(this)));
483 #if !defined(OS_CHROMEOS)
484 void SyncSetupHandler::DisplayGaiaLogin() {
485 DCHECK(!sync_startup_tracker_);
486 // Advanced options are no longer being configured if the login screen is
487 // visible. If the user exits the signin wizard after this without
488 // configuring sync, CloseSyncSetup() will ensure they are logged out.
489 configuring_sync_ = false;
490 DisplayGaiaLoginInNewTabOrWindow();
493 void SyncSetupHandler::DisplayGaiaLoginInNewTabOrWindow() {
494 Browser* browser = chrome::FindBrowserWithWebContents(
495 web_ui()->GetWebContents());
497 // Settings is not displayed in a browser window. Open a new window.
498 browser = new Browser(Browser::CreateParams(
499 Browser::TYPE_TABBED, GetProfile(), chrome::GetActiveDesktop()));
502 // If the signin manager already has an authenticated username, this is a
503 // re-auth scenario, and we need to ensure that the user signs in with the
504 // same email address.
506 std::string email = SigninManagerFactory::GetForProfile(
507 browser->profile())->GetAuthenticatedUsername();
508 if (!email.empty()) {
509 UMA_HISTOGRAM_ENUMERATION("Signin.Reauth",
510 signin::HISTOGRAM_SHOWN,
511 signin::HISTOGRAM_MAX);
513 SigninGlobalError* signin_error =
514 ProfileOAuth2TokenServiceFactory::GetForProfile(browser->profile())->
515 signin_global_error();
516 DCHECK(signin_error->HasMenuItem());
517 url = signin::GetReauthURL(browser->profile(),
518 signin_error->GetAccountIdOfLastAuthError());
520 url = signin::GetPromoURL(signin::SOURCE_SETTINGS, true);
523 chrome::ShowSingletonTab(browser, url);
527 bool SyncSetupHandler::PrepareSyncSetup() {
529 // If the wizard is already visible, just focus that one.
530 if (FocusExistingWizardIfPresent()) {
531 if (!IsActiveLogin())
536 // Notify services that login UI is now active.
537 GetLoginUIService()->SetLoginUI(this);
539 ProfileSyncService* service = GetSyncService();
541 service->SetSetupInProgress(true);
546 void SyncSetupHandler::DisplaySpinner() {
547 configuring_sync_ = true;
548 base::StringValue page("spinner");
549 base::DictionaryValue args;
551 const int kTimeoutSec = 30;
552 DCHECK(!backend_start_timer_);
553 backend_start_timer_.reset(new base::OneShotTimer<SyncSetupHandler>());
554 backend_start_timer_->Start(FROM_HERE,
555 base::TimeDelta::FromSeconds(kTimeoutSec),
556 this, &SyncSetupHandler::DisplayTimeout);
558 web_ui()->CallJavascriptFunction(
559 "SyncSetupOverlay.showSyncSetupPage", page, args);
562 // TODO(kochi): Handle error conditions other than timeout.
563 // http://crbug.com/128692
564 void SyncSetupHandler::DisplayTimeout() {
565 // Stop a timer to handle timeout in waiting for checking network connection.
566 backend_start_timer_.reset();
568 // Do not listen to sync startup events.
569 sync_startup_tracker_.reset();
571 base::StringValue page("timeout");
572 base::DictionaryValue args;
573 web_ui()->CallJavascriptFunction(
574 "SyncSetupOverlay.showSyncSetupPage", page, args);
577 void SyncSetupHandler::OnDidClosePage(const base::ListValue* args) {
581 void SyncSetupHandler::SyncStartupFailed() {
582 // Stop a timer to handle timeout in waiting for checking network connection.
583 backend_start_timer_.reset();
585 // Just close the sync overlay (the idea is that the base settings page will
586 // display the current error.)
590 void SyncSetupHandler::SyncStartupCompleted() {
591 ProfileSyncService* service = GetSyncService();
592 DCHECK(service->sync_initialized());
594 // Stop a timer to handle timeout in waiting for checking network connection.
595 backend_start_timer_.reset();
597 DisplayConfigureSync(true, false);
600 Profile* SyncSetupHandler::GetProfile() const {
601 return Profile::FromWebUI(web_ui());
604 ProfileSyncService* SyncSetupHandler::GetSyncService() const {
605 Profile* profile = GetProfile();
606 return profile->IsSyncAccessible() ?
607 ProfileSyncServiceFactory::GetForProfile(GetProfile()) : NULL;
610 void SyncSetupHandler::HandleConfigure(const base::ListValue* args) {
611 DCHECK(!sync_startup_tracker_);
613 if (!args->GetString(0, &json)) {
614 NOTREACHED() << "Could not read JSON argument";
622 SyncConfigInfo configuration;
623 if (!GetConfiguration(json, &configuration)) {
624 // The page sent us something that we didn't understand.
625 // This probably indicates a programming error.
630 // Start configuring the ProfileSyncService using the configuration passed
631 // to us from the JS layer.
632 ProfileSyncService* service = GetSyncService();
634 // If the sync engine has shutdown for some reason, just close the sync
636 if (!service || !service->sync_initialized()) {
641 // Disable sync, but remain signed in if the user selected "Sync nothing" in
642 // the advanced settings dialog. Note: In order to disable sync across
643 // restarts on Chrome OS, we must call StopSyncingPermanently(), which
644 // suppresses sync startup in addition to disabling it.
645 if (configuration.sync_nothing) {
646 ProfileSyncService::SyncEvent(
647 ProfileSyncService::STOP_FROM_ADVANCED_DIALOG);
649 service->StopSyncingPermanently();
650 service->SetSetupInProgress(false);
654 // Note: Data encryption will not occur until configuration is complete
655 // (when the PSS receives its CONFIGURE_DONE notification from the sync
656 // backend), so the user still has a chance to cancel out of the operation
657 // if (for example) some kind of passphrase error is encountered.
658 if (configuration.encrypt_all)
659 service->EnableEncryptEverything();
661 bool passphrase_failed = false;
662 if (!configuration.passphrase.empty()) {
663 // We call IsPassphraseRequired() here (instead of
664 // IsPassphraseRequiredForDecryption()) because the user may try to enter
665 // a passphrase even though no encrypted data types are enabled.
666 if (service->IsPassphraseRequired()) {
667 // If we have pending keys, try to decrypt them with the provided
668 // passphrase. We track if this succeeds or fails because a failed
669 // decryption should result in an error even if there aren't any encrypted
672 !service->SetDecryptionPassphrase(configuration.passphrase);
674 // OK, the user sent us a passphrase, but we don't have pending keys. So
675 // it either means that the pending keys were resolved somehow since the
676 // time the UI was displayed (re-encryption, pending passphrase change,
677 // etc) or the user wants to re-encrypt.
678 if (!configuration.passphrase_is_gaia &&
679 !service->IsUsingSecondaryPassphrase()) {
680 // User passed us a secondary passphrase, and the data is encrypted
681 // with a GAIA passphrase so they must want to encrypt.
682 service->SetEncryptionPassphrase(configuration.passphrase,
683 ProfileSyncService::EXPLICIT);
688 bool user_was_prompted_for_passphrase =
689 service->IsPassphraseRequiredForDecryption();
690 service->OnUserChoseDatatypes(configuration.sync_everything,
691 configuration.data_types);
693 // Need to call IsPassphraseRequiredForDecryption() *after* calling
694 // OnUserChoseDatatypes() because the user may have just disabled the
695 // encrypted datatypes (in which case we just want to exit, not prompt the
696 // user for a passphrase).
697 if (passphrase_failed || service->IsPassphraseRequiredForDecryption()) {
698 // We need a passphrase, or the user's attempt to set a passphrase failed -
699 // prompt them again. This covers a few subtle cases:
700 // 1) The user enters an incorrect passphrase *and* disabled the encrypted
701 // data types. In that case we want to notify the user that the
702 // passphrase was incorrect even though there are no longer any encrypted
703 // types enabled (IsPassphraseRequiredForDecryption() == false).
704 // 2) The user doesn't enter any passphrase. In this case, we won't call
705 // SetDecryptionPassphrase() (passphrase_failed == false), but we still
706 // want to display an error message to let the user know that their
707 // blank passphrase entry is not acceptable.
708 // 3) The user just enabled an encrypted data type - in this case we don't
709 // want to display an "invalid passphrase" error, since it's the first
710 // time the user is seeing the prompt.
711 DisplayConfigureSync(
712 true, passphrase_failed || user_was_prompted_for_passphrase);
714 // No passphrase is required from the user so mark the configuration as
715 // complete and close the sync setup overlay.
719 ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_CUSTOMIZE);
720 if (configuration.encrypt_all)
721 ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_ENCRYPT);
722 if (configuration.passphrase_is_gaia && !configuration.passphrase.empty())
723 ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_PASSPHRASE);
724 if (!configuration.sync_everything)
725 ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_CHOOSE);
728 void SyncSetupHandler::HandleShowSetupUI(const base::ListValue* args) {
729 ProfileSyncService* service = GetSyncService();
732 SigninManagerBase* signin =
733 SigninManagerFactory::GetForProfile(GetProfile());
734 if (signin->GetAuthenticatedUsername().empty()) {
735 // For web-based signin, the signin page is not displayed in an overlay
736 // on the settings page. So if we get here, it must be due to the user
737 // cancelling signin (by reloading the sync settings page during initial
738 // signin) or by directly navigating to settings/syncSetup
739 // (http://crbug.com/229836). So just exit and go back to the settings page.
740 DLOG(WARNING) << "Cannot display sync setup UI when not signed in";
745 // If a setup wizard is already present, but not on this page, close the
746 // blank setup overlay on this page by showing the "done" page. This can
747 // happen if the user navigates to chrome://settings/syncSetup in more than
748 // one tab. See crbug.com/261566.
749 // Note: The following block will transfer focus to the existing wizard.
750 if (IsExistingWizardPresent() && !IsActiveLogin()) {
754 // If a setup wizard is present on this page or another, bring it to focus.
755 // Otherwise, display a new one on this page.
756 if (!FocusExistingWizardIfPresent())
760 #if defined(OS_CHROMEOS)
761 // On ChromeOS, we need to sign out the user session to fix an auth error, so
762 // the user goes through the real signin flow to generate a new auth token.
763 void SyncSetupHandler::HandleDoSignOutOnAuthError(const base::ListValue* args) {
764 DVLOG(1) << "Signing out the user to fix a sync error.";
765 chrome::AttemptUserExit();
769 #if !defined(OS_CHROMEOS)
770 void SyncSetupHandler::HandleStartSignin(const base::ListValue* args) {
771 // Should only be called if the user is not already signed in.
772 DCHECK(SigninManagerFactory::GetForProfile(GetProfile())->
773 GetAuthenticatedUsername().empty());
777 void SyncSetupHandler::HandleStopSyncing(const base::ListValue* args) {
778 if (GetSyncService())
779 ProfileSyncService::SyncEvent(ProfileSyncService::STOP_FROM_OPTIONS);
780 SigninManagerFactory::GetForProfile(GetProfile())->SignOut();
784 void SyncSetupHandler::HandleCloseTimeout(const base::ListValue* args) {
788 void SyncSetupHandler::CloseSyncSetup() {
789 // Stop a timer to handle timeout in waiting for checking network connection.
790 backend_start_timer_.reset();
792 // Clear the sync startup tracker, since the setup wizard is being closed.
793 sync_startup_tracker_.reset();
795 ProfileSyncService* sync_service = GetSyncService();
796 if (IsActiveLogin()) {
797 // Don't log a cancel event if the sync setup dialog is being
798 // automatically closed due to an auth error.
799 if (!sync_service || (!sync_service->HasSyncSetupCompleted() &&
800 sync_service->GetAuthError().state() == GoogleServiceAuthError::NONE)) {
801 if (configuring_sync_) {
802 ProfileSyncService::SyncEvent(
803 ProfileSyncService::CANCEL_DURING_CONFIGURE);
806 // If the user clicked "Cancel" while setting up sync, disable sync
807 // because we don't want the sync backend to remain in the initialized
808 // state. Note: In order to disable sync across restarts on Chrome OS, we
809 // must call StopSyncingPermanently(), which suppresses sync startup in
810 // addition to disabling it.
812 DVLOG(1) << "Sync setup aborted by user action";
813 sync_service->StopSyncingPermanently();
814 #if !defined(OS_CHROMEOS)
815 // Sign out the user on desktop Chrome if they click cancel during
817 // TODO(rsimha): Revisit this for M30. See http://crbug.com/252049.
818 if (sync_service->FirstSetupInProgress())
819 SigninManagerFactory::GetForProfile(GetProfile())->SignOut();
824 GetLoginUIService()->LoginUIClosed(this);
827 // Alert the sync service anytime the sync setup dialog is closed. This can
828 // happen due to the user clicking the OK or Cancel button, or due to the
829 // dialog being closed by virtue of sync being disabled in the background.
831 sync_service->SetSetupInProgress(false);
833 configuring_sync_ = false;
836 void SyncSetupHandler::OpenSyncSetup() {
837 if (!PrepareSyncSetup())
840 // There are several different UI flows that can bring the user here:
842 // 2) Normal signin through settings page (GetAuthenticatedUsername() is
844 // 3) Previously working credentials have expired.
845 // 4) User is signed in, but has stopped sync via the google dashboard, and
846 // signout is prohibited by policy so we need to force a re-auth.
847 // 5) User clicks [Advanced Settings] button on options page while already
849 // 6) One-click signin (credentials are already available, so should display
850 // sync configure UI, not login UI).
851 // 7) User re-enables sync after disabling it via advanced settings.
852 #if !defined(OS_CHROMEOS)
853 SigninManagerBase* signin =
854 SigninManagerFactory::GetForProfile(GetProfile());
856 if (signin->GetAuthenticatedUsername().empty() ||
857 SigninGlobalError::GetForProfile(GetProfile())->HasMenuItem()) {
858 // User is not logged in (cases 1-2), or login has been specially requested
859 // because previously working credentials have expired (case 3). Close sync
860 // setup including any visible overlays, and display the gaia auth page.
861 // Control will be returned to the sync settings page once auth is complete.
867 if (!GetSyncService()) {
868 // This can happen if the user directly navigates to /settings/syncSetup.
869 DLOG(WARNING) << "Cannot display sync UI when sync is disabled";
874 // User is already logged in. They must have brought up the config wizard
875 // via the "Advanced..." button or through One-Click signin (cases 4-6), or
876 // they are re-enabling sync after having disabled it (case 7).
877 DisplayConfigureSync(true, false);
880 void SyncSetupHandler::OpenConfigureSync() {
881 if (!PrepareSyncSetup())
884 DisplayConfigureSync(true, false);
887 void SyncSetupHandler::FocusUI() {
888 DCHECK(IsActiveLogin());
889 WebContents* web_contents = web_ui()->GetWebContents();
890 web_contents->GetDelegate()->ActivateContents(web_contents);
893 void SyncSetupHandler::CloseUI() {
895 base::StringValue page("done");
896 web_ui()->CallJavascriptFunction(
897 "SyncSetupOverlay.showSyncSetupPage", page);
900 bool SyncSetupHandler::IsExistingWizardPresent() {
901 LoginUIService* service = GetLoginUIService();
903 return service->current_login_ui() != NULL;
906 bool SyncSetupHandler::FocusExistingWizardIfPresent() {
907 if (!IsExistingWizardPresent())
910 LoginUIService* service = GetLoginUIService();
912 service->current_login_ui()->FocusUI();
916 LoginUIService* SyncSetupHandler::GetLoginUIService() const {
917 return LoginUIServiceFactory::GetForProfile(GetProfile());