Remove browsing data when the user tries to delete a profile.
[chromium-blink-merge.git] / chrome / browser / ui / webui / options / sync_setup_handler.cc
blobea7bd98951f7d357d10160a7f10b98eebc7f2ffb
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/options/sync_setup_handler.h"
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/strings/utf_string_conversions.h"
18 #include "base/values.h"
19 #include "chrome/app/chrome_command_ids.h"
20 #include "chrome/browser/browser_process.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_metrics.h"
25 #include "chrome/browser/profiles/profiles_state.h"
26 #include "chrome/browser/signin/signin_error_controller_factory.h"
27 #include "chrome/browser/signin/signin_header_helper.h"
28 #include "chrome/browser/signin/signin_manager_factory.h"
29 #include "chrome/browser/signin/signin_promo.h"
30 #include "chrome/browser/sync/profile_sync_service.h"
31 #include "chrome/browser/sync/profile_sync_service_factory.h"
32 #include "chrome/browser/ui/browser_finder.h"
33 #include "chrome/browser/ui/browser_navigator.h"
34 #include "chrome/browser/ui/browser_window.h"
35 #include "chrome/browser/ui/singleton_tabs.h"
36 #include "chrome/browser/ui/webui/options/options_handlers_helper.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/url_constants.h"
41 #include "chrome/grit/chromium_strings.h"
42 #include "chrome/grit/generated_resources.h"
43 #include "chrome/grit/locale_settings.h"
44 #include "components/google/core/browser/google_util.h"
45 #include "components/signin/core/browser/signin_error_controller.h"
46 #include "components/signin/core/browser/signin_metrics.h"
47 #include "components/signin/core/common/profile_management_switches.h"
48 #include "components/sync_driver/sync_prefs.h"
49 #include "content/public/browser/render_view_host.h"
50 #include "content/public/browser/web_contents.h"
51 #include "content/public/browser/web_contents_delegate.h"
52 #include "google_apis/gaia/gaia_auth_util.h"
53 #include "google_apis/gaia/gaia_constants.h"
54 #include "grit/components_strings.h"
55 #include "net/base/url_util.h"
56 #include "ui/base/l10n/l10n_util.h"
58 #if defined(OS_CHROMEOS)
59 #include "components/signin/core/browser/signin_manager_base.h"
60 #else
61 #include "components/signin/core/browser/signin_manager.h"
62 #endif
64 using content::WebContents;
65 using l10n_util::GetStringFUTF16;
66 using l10n_util::GetStringUTF16;
68 namespace {
70 // A structure which contains all the configuration information for sync.
71 struct SyncConfigInfo {
72 SyncConfigInfo();
73 ~SyncConfigInfo();
75 bool encrypt_all;
76 bool sync_everything;
77 bool sync_nothing;
78 syncer::ModelTypeSet data_types;
79 std::string passphrase;
80 bool passphrase_is_gaia;
83 SyncConfigInfo::SyncConfigInfo()
84 : encrypt_all(false),
85 sync_everything(false),
86 sync_nothing(false),
87 passphrase_is_gaia(false) {
90 SyncConfigInfo::~SyncConfigInfo() {}
92 bool GetConfiguration(const std::string& json, SyncConfigInfo* config) {
93 scoped_ptr<base::Value> parsed_value(base::JSONReader::Read(json));
94 base::DictionaryValue* result;
95 if (!parsed_value || !parsed_value->GetAsDictionary(&result)) {
96 DLOG(ERROR) << "GetConfiguration() not passed a Dictionary";
97 return false;
100 if (!result->GetBoolean("syncAllDataTypes", &config->sync_everything)) {
101 DLOG(ERROR) << "GetConfiguration() not passed a syncAllDataTypes value";
102 return false;
105 if (!result->GetBoolean("syncNothing", &config->sync_nothing)) {
106 DLOG(ERROR) << "GetConfiguration() not passed a syncNothing value";
107 return false;
110 DCHECK(!(config->sync_everything && config->sync_nothing))
111 << "syncAllDataTypes and syncNothing cannot both be true";
113 syncer::ModelTypeNameMap type_names = syncer::GetUserSelectableTypeNameMap();
115 for (syncer::ModelTypeNameMap::const_iterator it = type_names.begin();
116 it != type_names.end(); ++it) {
117 std::string key_name = it->second + std::string("Synced");
118 bool sync_value;
119 if (!result->GetBoolean(key_name, &sync_value)) {
120 DLOG(ERROR) << "GetConfiguration() not passed a value for " << key_name;
121 return false;
123 if (sync_value)
124 config->data_types.Put(it->first);
127 // Encryption settings.
128 if (!result->GetBoolean("encryptAllData", &config->encrypt_all)) {
129 DLOG(ERROR) << "GetConfiguration() not passed a value for encryptAllData";
130 return false;
133 // Passphrase settings.
134 bool have_passphrase;
135 if (!result->GetBoolean("usePassphrase", &have_passphrase)) {
136 DLOG(ERROR) << "GetConfiguration() not passed a usePassphrase value";
137 return false;
140 if (have_passphrase) {
141 if (!result->GetBoolean("isGooglePassphrase",
142 &config->passphrase_is_gaia)) {
143 DLOG(ERROR) << "GetConfiguration() not passed isGooglePassphrase value";
144 return false;
146 if (!result->GetString("passphrase", &config->passphrase)) {
147 DLOG(ERROR) << "GetConfiguration() not passed a passphrase value";
148 return false;
151 return true;
154 } // namespace
156 SyncSetupHandler::SyncSetupHandler()
157 : configuring_sync_(false) {
160 SyncSetupHandler::~SyncSetupHandler() {
161 // Just exit if running unit tests (no actual WebUI is attached).
162 if (!web_ui())
163 return;
165 // This case is hit when the user performs a back navigation.
166 CloseSyncSetup();
169 void SyncSetupHandler::GetLocalizedValues(
170 base::DictionaryValue* localized_strings) {
171 GetStaticLocalizedValues(localized_strings, web_ui());
174 void SyncSetupHandler::GetStaticLocalizedValues(
175 base::DictionaryValue* localized_strings,
176 content::WebUI* web_ui) {
177 DCHECK(localized_strings);
179 base::string16 product_name(GetStringUTF16(IDS_PRODUCT_NAME));
180 localized_strings->SetString(
181 "chooseDataTypesInstructions",
182 GetStringFUTF16(IDS_SYNC_CHOOSE_DATATYPES_INSTRUCTIONS, product_name));
183 localized_strings->SetString(
184 "encryptionInstructions",
185 GetStringFUTF16(IDS_SYNC_ENCRYPTION_INSTRUCTIONS, product_name));
186 localized_strings->SetString(
187 "encryptionHelpURL", chrome::kSyncEncryptionHelpURL);
188 localized_strings->SetString(
189 "encryptionSectionMessage",
190 GetStringFUTF16(IDS_SYNC_ENCRYPTION_SECTION_MESSAGE, product_name));
191 localized_strings->SetString(
192 "passphraseRecover",
193 GetStringFUTF16(
194 IDS_SYNC_PASSPHRASE_RECOVER,
195 base::ASCIIToUTF16(
196 google_util::AppendGoogleLocaleParam(
197 GURL(chrome::kSyncGoogleDashboardURL),
198 g_browser_process->GetApplicationLocale()).spec())));
199 localized_strings->SetString(
200 "stopSyncingExplanation",
201 l10n_util::GetStringFUTF16(
202 IDS_SYNC_STOP_SYNCING_EXPLANATION_LABEL,
203 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
204 base::ASCIIToUTF16(
205 google_util::AppendGoogleLocaleParam(
206 GURL(chrome::kSyncGoogleDashboardURL),
207 g_browser_process->GetApplicationLocale()).spec())));
208 localized_strings->SetString("deleteProfileLabel",
209 l10n_util::GetStringUTF16(IDS_SYNC_STOP_DELETE_PROFILE_LABEL));
210 localized_strings->SetString("stopSyncingTitle",
211 l10n_util::GetStringUTF16(IDS_SYNC_STOP_SYNCING_DIALOG_TITLE));
212 localized_strings->SetString("stopSyncingConfirm",
213 l10n_util::GetStringUTF16(IDS_SYNC_STOP_SYNCING_CONFIRM_BUTTON_LABEL));
215 localized_strings->SetString(
216 "syncEverythingHelpURL", chrome::kSyncEverythingLearnMoreURL);
217 localized_strings->SetString(
218 "syncErrorHelpURL", chrome::kSyncErrorsHelpURL);
220 static OptionsStringResource resources[] = {
221 { "syncSetupConfigureTitle", IDS_SYNC_SETUP_CONFIGURE_TITLE },
222 { "syncSetupSpinnerTitle", IDS_SYNC_SETUP_SPINNER_TITLE },
223 { "syncSetupTimeoutTitle", IDS_SYNC_SETUP_TIME_OUT_TITLE },
224 { "syncSetupTimeoutContent", IDS_SYNC_SETUP_TIME_OUT_CONTENT },
225 { "errorLearnMore", IDS_LEARN_MORE },
226 { "cancel", IDS_CANCEL },
227 { "loginSuccess", IDS_SYNC_SUCCESS },
228 { "settingUp", IDS_SYNC_LOGIN_SETTING_UP },
229 { "syncAllDataTypes", IDS_SYNC_EVERYTHING },
230 { "chooseDataTypes", IDS_SYNC_CHOOSE_DATATYPES },
231 { "syncNothing", IDS_SYNC_NOTHING },
232 { "bookmarks", IDS_SYNC_DATATYPE_BOOKMARKS },
233 { "preferences", IDS_SYNC_DATATYPE_PREFERENCES },
234 { "autofill", IDS_SYNC_DATATYPE_AUTOFILL },
235 { "themes", IDS_SYNC_DATATYPE_THEMES },
236 { "passwords", IDS_SYNC_DATATYPE_PASSWORDS },
237 { "extensions", IDS_SYNC_DATATYPE_EXTENSIONS },
238 { "typedURLs", IDS_SYNC_DATATYPE_TYPED_URLS },
239 { "apps", IDS_SYNC_DATATYPE_APPS },
240 { "wifiCredentials", IDS_SYNC_DATATYPE_WIFI_CREDENTIALS },
241 { "openTabs", IDS_SYNC_DATATYPE_TABS },
242 { "serviceUnavailableError", IDS_SYNC_SETUP_ABORTED_BY_PENDING_CLEAR },
243 { "confirmLabel", IDS_SYNC_CONFIRM_PASSPHRASE_LABEL },
244 { "emptyErrorMessage", IDS_SYNC_EMPTY_PASSPHRASE_ERROR },
245 { "mismatchErrorMessage", IDS_SYNC_PASSPHRASE_MISMATCH_ERROR },
246 { "customizeLinkLabel", IDS_SYNC_CUSTOMIZE_LINK_LABEL },
247 { "confirmSyncPreferences", IDS_SYNC_CONFIRM_SYNC_PREFERENCES },
248 { "syncEverything", IDS_SYNC_SYNC_EVERYTHING },
249 { "useDefaultSettings", IDS_SYNC_USE_DEFAULT_SETTINGS },
250 { "enterPassphraseBody", IDS_SYNC_ENTER_PASSPHRASE_BODY },
251 { "enterGooglePassphraseBody", IDS_SYNC_ENTER_GOOGLE_PASSPHRASE_BODY },
252 { "passphraseLabel", IDS_SYNC_PASSPHRASE_LABEL },
253 { "incorrectPassphrase", IDS_SYNC_INCORRECT_PASSPHRASE },
254 { "passphraseWarning", IDS_SYNC_PASSPHRASE_WARNING },
255 { "yes", IDS_SYNC_PASSPHRASE_CANCEL_YES },
256 { "no", IDS_SYNC_PASSPHRASE_CANCEL_NO },
257 { "sectionExplicitMessagePrefix", IDS_SYNC_PASSPHRASE_MSG_EXPLICIT_PREFIX },
258 { "sectionExplicitMessagePostfix",
259 IDS_SYNC_PASSPHRASE_MSG_EXPLICIT_POSTFIX },
260 // TODO(rogerta): browser/resource/sync_promo/sync_promo.html and related
261 // file may not be needed any more. If not, then the following promo
262 // strings can also be removed.
263 { "promoPageTitle", IDS_SYNC_PROMO_TAB_TITLE },
264 { "promoSkipButton", IDS_SYNC_PROMO_SKIP_BUTTON },
265 { "promoAdvanced", IDS_SYNC_PROMO_ADVANCED },
266 { "promoLearnMore", IDS_LEARN_MORE },
267 { "promoTitleShort", IDS_SYNC_PROMO_MESSAGE_TITLE_SHORT },
268 { "encryptionSectionTitle", IDS_SYNC_ENCRYPTION_SECTION_TITLE },
269 { "basicEncryptionOption", IDS_SYNC_BASIC_ENCRYPTION_DATA },
270 { "fullEncryptionOption", IDS_SYNC_FULL_ENCRYPTION_DATA },
273 RegisterStrings(localized_strings, resources, arraysize(resources));
274 RegisterTitle(localized_strings, "syncSetupOverlay", IDS_SYNC_SETUP_TITLE);
277 void SyncSetupHandler::ConfigureSyncDone() {
278 base::StringValue page("done");
279 web_ui()->CallJavascriptFunction(
280 "SyncSetupOverlay.showSyncSetupPage", page);
282 // Suppress the sign in promo once the user starts sync. This way the user
283 // doesn't see the sign in promo even if they sign out later on.
284 signin::SetUserSkippedPromo(GetProfile());
286 ProfileSyncService* service = GetSyncService();
287 DCHECK(service);
288 if (!service->HasSyncSetupCompleted()) {
289 // This is the first time configuring sync, so log it.
290 base::FilePath profile_file_path = GetProfile()->GetPath();
291 ProfileMetrics::LogProfileSyncSignIn(profile_file_path);
293 // We're done configuring, so notify ProfileSyncService that it is OK to
294 // start syncing.
295 service->SetSetupInProgress(false);
296 service->SetSyncSetupCompleted();
300 bool SyncSetupHandler::IsActiveLogin() const {
301 // LoginUIService can be NULL if page is brought up in incognito mode
302 // (i.e. if the user is running in guest mode in cros and brings up settings).
303 LoginUIService* service = GetLoginUIService();
304 return service && (service->current_login_ui() == this);
307 void SyncSetupHandler::RegisterMessages() {
308 web_ui()->RegisterMessageCallback(
309 "SyncSetupDidClosePage",
310 base::Bind(&SyncSetupHandler::OnDidClosePage,
311 base::Unretained(this)));
312 web_ui()->RegisterMessageCallback(
313 "SyncSetupConfigure",
314 base::Bind(&SyncSetupHandler::HandleConfigure,
315 base::Unretained(this)));
316 web_ui()->RegisterMessageCallback(
317 "SyncSetupShowSetupUI",
318 base::Bind(&SyncSetupHandler::HandleShowSetupUI,
319 base::Unretained(this)));
320 web_ui()->RegisterMessageCallback("CloseTimeout",
321 base::Bind(&SyncSetupHandler::HandleCloseTimeout,
322 base::Unretained(this)));
323 #if defined(OS_CHROMEOS)
324 web_ui()->RegisterMessageCallback(
325 "SyncSetupDoSignOutOnAuthError",
326 base::Bind(&SyncSetupHandler::HandleDoSignOutOnAuthError,
327 base::Unretained(this)));
328 #else
329 web_ui()->RegisterMessageCallback("SyncSetupStopSyncing",
330 base::Bind(&SyncSetupHandler::HandleStopSyncing,
331 base::Unretained(this)));
332 web_ui()->RegisterMessageCallback("SyncSetupStartSignIn",
333 base::Bind(&SyncSetupHandler::HandleStartSignin,
334 base::Unretained(this)));
335 #endif
338 #if !defined(OS_CHROMEOS)
339 void SyncSetupHandler::DisplayGaiaLogin() {
340 DCHECK(!sync_startup_tracker_);
341 // Advanced options are no longer being configured if the login screen is
342 // visible. If the user exits the signin wizard after this without
343 // configuring sync, CloseSyncSetup() will ensure they are logged out.
344 configuring_sync_ = false;
345 DisplayGaiaLoginInNewTabOrWindow();
348 void SyncSetupHandler::DisplayGaiaLoginInNewTabOrWindow() {
349 Browser* browser = chrome::FindBrowserWithWebContents(
350 web_ui()->GetWebContents());
351 bool force_new_tab = false;
352 if (!browser) {
353 // Settings is not displayed in a browser window. Open a new window.
354 browser = new Browser(Browser::CreateParams(
355 Browser::TYPE_TABBED, GetProfile(), chrome::GetActiveDesktop()));
356 force_new_tab = true;
359 // If the signin manager already has an authenticated username, this is a
360 // re-auth scenario, and we need to ensure that the user signs in with the
361 // same email address.
362 GURL url;
363 if (SigninManagerFactory::GetForProfile(
364 browser->profile())->IsAuthenticated()) {
365 UMA_HISTOGRAM_ENUMERATION("Signin.Reauth",
366 signin_metrics::HISTOGRAM_REAUTH_SHOWN,
367 signin_metrics::HISTOGRAM_REAUTH_MAX);
369 SigninErrorController* error_controller =
370 SigninErrorControllerFactory::GetForProfile(browser->profile());
371 DCHECK(error_controller->HasError());
372 if (switches::IsNewAvatarMenu() && !force_new_tab) {
373 browser->window()->ShowAvatarBubbleFromAvatarButton(
374 BrowserWindow::AVATAR_BUBBLE_MODE_REAUTH,
375 signin::ManageAccountsParams());
376 } else {
377 url = signin::GetReauthURL(browser->profile(),
378 error_controller->error_account_id());
380 } else {
381 signin_metrics::LogSigninSource(signin_metrics::SOURCE_SETTINGS);
382 if (switches::IsNewAvatarMenu() && !force_new_tab) {
383 browser->window()->ShowAvatarBubbleFromAvatarButton(
384 BrowserWindow::AVATAR_BUBBLE_MODE_SIGNIN,
385 signin::ManageAccountsParams());
386 } else {
387 url = signin::GetPromoURL(signin_metrics::SOURCE_SETTINGS, true);
391 if (url.is_valid())
392 chrome::ShowSingletonTab(browser, url);
394 #endif
396 bool SyncSetupHandler::PrepareSyncSetup() {
397 // If the wizard is already visible, just focus that one.
398 if (FocusExistingWizardIfPresent()) {
399 if (!IsActiveLogin())
400 CloseSyncSetup();
401 return false;
404 // Notify services that login UI is now active.
405 GetLoginUIService()->SetLoginUI(this);
407 ProfileSyncService* service = GetSyncService();
408 if (service)
409 service->SetSetupInProgress(true);
411 return true;
414 void SyncSetupHandler::DisplaySpinner() {
415 configuring_sync_ = true;
416 base::StringValue page("spinner");
417 base::DictionaryValue args;
419 const int kTimeoutSec = 30;
420 DCHECK(!backend_start_timer_);
421 backend_start_timer_.reset(new base::OneShotTimer<SyncSetupHandler>());
422 backend_start_timer_->Start(FROM_HERE,
423 base::TimeDelta::FromSeconds(kTimeoutSec),
424 this, &SyncSetupHandler::DisplayTimeout);
426 web_ui()->CallJavascriptFunction(
427 "SyncSetupOverlay.showSyncSetupPage", page, args);
430 // TODO(kochi): Handle error conditions other than timeout.
431 // http://crbug.com/128692
432 void SyncSetupHandler::DisplayTimeout() {
433 // Stop a timer to handle timeout in waiting for checking network connection.
434 backend_start_timer_.reset();
436 // Do not listen to sync startup events.
437 sync_startup_tracker_.reset();
439 base::StringValue page("timeout");
440 base::DictionaryValue args;
441 web_ui()->CallJavascriptFunction(
442 "SyncSetupOverlay.showSyncSetupPage", page, args);
445 void SyncSetupHandler::OnDidClosePage(const base::ListValue* args) {
446 CloseSyncSetup();
449 void SyncSetupHandler::SyncStartupFailed() {
450 // Stop a timer to handle timeout in waiting for checking network connection.
451 backend_start_timer_.reset();
453 // Just close the sync overlay (the idea is that the base settings page will
454 // display the current error.)
455 CloseUI();
458 void SyncSetupHandler::SyncStartupCompleted() {
459 ProfileSyncService* service = GetSyncService();
460 DCHECK(service->backend_initialized());
462 // Stop a timer to handle timeout in waiting for checking network connection.
463 backend_start_timer_.reset();
465 DisplayConfigureSync(false);
468 Profile* SyncSetupHandler::GetProfile() const {
469 return Profile::FromWebUI(web_ui());
472 ProfileSyncService* SyncSetupHandler::GetSyncService() const {
473 Profile* profile = GetProfile();
474 return profile->IsSyncAccessible() ?
475 ProfileSyncServiceFactory::GetForProfile(GetProfile()) : NULL;
478 void SyncSetupHandler::HandleConfigure(const base::ListValue* args) {
479 DCHECK(!sync_startup_tracker_);
480 std::string json;
481 if (!args->GetString(0, &json)) {
482 NOTREACHED() << "Could not read JSON argument";
483 return;
485 if (json.empty()) {
486 NOTREACHED();
487 return;
490 SyncConfigInfo configuration;
491 if (!GetConfiguration(json, &configuration)) {
492 // The page sent us something that we didn't understand.
493 // This probably indicates a programming error.
494 NOTREACHED();
495 return;
498 // Start configuring the ProfileSyncService using the configuration passed
499 // to us from the JS layer.
500 ProfileSyncService* service = GetSyncService();
502 // If the sync engine has shutdown for some reason, just close the sync
503 // dialog.
504 if (!service || !service->backend_initialized()) {
505 CloseUI();
506 return;
509 // Disable sync, but remain signed in if the user selected "Sync nothing" in
510 // the advanced settings dialog. Note: In order to disable sync across
511 // restarts on Chrome OS, we must call StopSyncingPermanently(), which
512 // suppresses sync startup in addition to disabling it.
513 if (configuration.sync_nothing) {
514 ProfileSyncService::SyncEvent(
515 ProfileSyncService::STOP_FROM_ADVANCED_DIALOG);
516 CloseUI();
517 service->StopSyncingPermanently();
518 service->SetSetupInProgress(false);
519 return;
522 // Don't allow "encrypt all" if the ProfileSyncService doesn't allow it.
523 // The UI is hidden, but the user may have enabled it e.g. by fiddling with
524 // the web inspector.
525 if (!service->EncryptEverythingAllowed())
526 configuration.encrypt_all = false;
528 // Note: Data encryption will not occur until configuration is complete
529 // (when the PSS receives its CONFIGURE_DONE notification from the sync
530 // backend), so the user still has a chance to cancel out of the operation
531 // if (for example) some kind of passphrase error is encountered.
532 if (configuration.encrypt_all)
533 service->EnableEncryptEverything();
535 bool passphrase_failed = false;
536 if (!configuration.passphrase.empty()) {
537 // We call IsPassphraseRequired() here (instead of
538 // IsPassphraseRequiredForDecryption()) because the user may try to enter
539 // a passphrase even though no encrypted data types are enabled.
540 if (service->IsPassphraseRequired()) {
541 // If we have pending keys, try to decrypt them with the provided
542 // passphrase. We track if this succeeds or fails because a failed
543 // decryption should result in an error even if there aren't any encrypted
544 // data types.
545 passphrase_failed =
546 !service->SetDecryptionPassphrase(configuration.passphrase);
547 } else {
548 // OK, the user sent us a passphrase, but we don't have pending keys. So
549 // it either means that the pending keys were resolved somehow since the
550 // time the UI was displayed (re-encryption, pending passphrase change,
551 // etc) or the user wants to re-encrypt.
552 if (!configuration.passphrase_is_gaia &&
553 !service->IsUsingSecondaryPassphrase()) {
554 // User passed us a secondary passphrase, and the data is encrypted
555 // with a GAIA passphrase so they must want to encrypt.
556 service->SetEncryptionPassphrase(configuration.passphrase,
557 ProfileSyncService::EXPLICIT);
562 bool user_was_prompted_for_passphrase =
563 service->IsPassphraseRequiredForDecryption();
564 service->OnUserChoseDatatypes(configuration.sync_everything,
565 configuration.data_types);
567 // Need to call IsPassphraseRequiredForDecryption() *after* calling
568 // OnUserChoseDatatypes() because the user may have just disabled the
569 // encrypted datatypes (in which case we just want to exit, not prompt the
570 // user for a passphrase).
571 if (passphrase_failed || service->IsPassphraseRequiredForDecryption()) {
572 // We need a passphrase, or the user's attempt to set a passphrase failed -
573 // prompt them again. This covers a few subtle cases:
574 // 1) The user enters an incorrect passphrase *and* disabled the encrypted
575 // data types. In that case we want to notify the user that the
576 // passphrase was incorrect even though there are no longer any encrypted
577 // types enabled (IsPassphraseRequiredForDecryption() == false).
578 // 2) The user doesn't enter any passphrase. In this case, we won't call
579 // SetDecryptionPassphrase() (passphrase_failed == false), but we still
580 // want to display an error message to let the user know that their
581 // blank passphrase entry is not acceptable.
582 // 3) The user just enabled an encrypted data type - in this case we don't
583 // want to display an "invalid passphrase" error, since it's the first
584 // time the user is seeing the prompt.
585 DisplayConfigureSync(passphrase_failed || user_was_prompted_for_passphrase);
586 } else {
587 // No passphrase is required from the user so mark the configuration as
588 // complete and close the sync setup overlay.
589 ConfigureSyncDone();
592 ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_CUSTOMIZE);
593 if (configuration.encrypt_all)
594 ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_ENCRYPT);
595 if (configuration.passphrase_is_gaia && !configuration.passphrase.empty())
596 ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_PASSPHRASE);
597 if (!configuration.sync_everything)
598 ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_CHOOSE);
601 void SyncSetupHandler::HandleShowSetupUI(const base::ListValue* args) {
602 if (!GetSyncService()) {
603 DLOG(WARNING) << "Cannot display sync UI when sync is disabled";
604 CloseUI();
605 return;
608 SigninManagerBase* signin =
609 SigninManagerFactory::GetForProfile(GetProfile());
610 if (!signin->IsAuthenticated()) {
611 // For web-based signin, the signin page is not displayed in an overlay
612 // on the settings page. So if we get here, it must be due to the user
613 // cancelling signin (by reloading the sync settings page during initial
614 // signin) or by directly navigating to settings/syncSetup
615 // (http://crbug.com/229836). So just exit and go back to the settings page.
616 DLOG(WARNING) << "Cannot display sync setup UI when not signed in";
617 CloseUI();
618 return;
621 // If a setup wizard is already present, but not on this page, close the
622 // blank setup overlay on this page by showing the "done" page. This can
623 // happen if the user navigates to chrome://settings/syncSetup in more than
624 // one tab. See crbug.com/261566.
625 // Note: The following block will transfer focus to the existing wizard.
626 if (IsExistingWizardPresent() && !IsActiveLogin())
627 CloseUI();
629 // If a setup wizard is present on this page or another, bring it to focus.
630 // Otherwise, display a new one on this page.
631 if (!FocusExistingWizardIfPresent())
632 OpenSyncSetup();
635 #if defined(OS_CHROMEOS)
636 // On ChromeOS, we need to sign out the user session to fix an auth error, so
637 // the user goes through the real signin flow to generate a new auth token.
638 void SyncSetupHandler::HandleDoSignOutOnAuthError(const base::ListValue* args) {
639 DVLOG(1) << "Signing out the user to fix a sync error.";
640 chrome::AttemptUserExit();
642 #endif
644 #if !defined(OS_CHROMEOS)
645 void SyncSetupHandler::HandleStartSignin(const base::ListValue* args) {
646 // Should only be called if the user is not already signed in.
647 DCHECK(!SigninManagerFactory::GetForProfile(GetProfile())->
648 IsAuthenticated());
649 OpenSyncSetup();
652 void SyncSetupHandler::HandleStopSyncing(const base::ListValue* args) {
653 if (GetSyncService())
654 ProfileSyncService::SyncEvent(ProfileSyncService::STOP_FROM_OPTIONS);
655 SigninManagerFactory::GetForProfile(GetProfile())->SignOut(
656 signin_metrics::USER_CLICKED_SIGNOUT_SETTINGS);
658 bool delete_profile = false;
659 if (args->GetBoolean(0, &delete_profile) && delete_profile) {
660 profiles::RemoveBrowsingDataForProfile(GetProfile()->GetPath());
661 // Do as BrowserOptionsHandler::DeleteProfile().
662 options::helper::DeleteProfileAtPath(GetProfile()->GetPath(), web_ui());
665 #endif
667 void SyncSetupHandler::HandleCloseTimeout(const base::ListValue* args) {
668 CloseSyncSetup();
671 void SyncSetupHandler::CloseSyncSetup() {
672 // Stop a timer to handle timeout in waiting for checking network connection.
673 backend_start_timer_.reset();
675 // Clear the sync startup tracker, since the setup wizard is being closed.
676 sync_startup_tracker_.reset();
678 ProfileSyncService* sync_service = GetSyncService();
679 if (IsActiveLogin()) {
680 // Don't log a cancel event if the sync setup dialog is being
681 // automatically closed due to an auth error.
682 if (!sync_service || (!sync_service->HasSyncSetupCompleted() &&
683 sync_service->GetAuthError().state() == GoogleServiceAuthError::NONE)) {
684 if (configuring_sync_) {
685 ProfileSyncService::SyncEvent(
686 ProfileSyncService::CANCEL_DURING_CONFIGURE);
688 // If the user clicked "Cancel" while setting up sync, disable sync
689 // because we don't want the sync backend to remain in the
690 // first-setup-incomplete state.
691 // Note: In order to disable sync across restarts on Chrome OS,
692 // we must call StopSyncingPermanently(), which suppresses sync startup
693 // in addition to disabling it.
694 if (sync_service) {
695 DVLOG(1) << "Sync setup aborted by user action";
696 sync_service->StopSyncingPermanently();
697 #if !defined(OS_CHROMEOS)
698 // Sign out the user on desktop Chrome if they click cancel during
699 // initial setup.
700 // TODO(rsimha): Revisit this for M30. See http://crbug.com/252049.
701 if (sync_service->FirstSetupInProgress()) {
702 SigninManagerFactory::GetForProfile(GetProfile())->SignOut(
703 signin_metrics::ABORT_SIGNIN);
705 #endif
710 GetLoginUIService()->LoginUIClosed(this);
713 // Alert the sync service anytime the sync setup dialog is closed. This can
714 // happen due to the user clicking the OK or Cancel button, or due to the
715 // dialog being closed by virtue of sync being disabled in the background.
716 if (sync_service)
717 sync_service->SetSetupInProgress(false);
719 configuring_sync_ = false;
722 void SyncSetupHandler::OpenSyncSetup() {
723 if (!PrepareSyncSetup())
724 return;
726 // There are several different UI flows that can bring the user here:
727 // 1) Signin promo.
728 // 2) Normal signin through settings page (IsAuthenticated() is false).
729 // 3) Previously working credentials have expired.
730 // 4) User is signed in, but has stopped sync via the google dashboard, and
731 // signout is prohibited by policy so we need to force a re-auth.
732 // 5) User clicks [Advanced Settings] button on options page while already
733 // logged in.
734 // 6) One-click signin (credentials are already available, so should display
735 // sync configure UI, not login UI).
736 // 7) User re-enables sync after disabling it via advanced settings.
737 #if !defined(OS_CHROMEOS)
738 SigninManagerBase* signin =
739 SigninManagerFactory::GetForProfile(GetProfile());
741 if (!signin->IsAuthenticated() ||
742 SigninErrorControllerFactory::GetForProfile(GetProfile())->HasError()) {
743 // User is not logged in (cases 1-2), or login has been specially requested
744 // because previously working credentials have expired (case 3). Close sync
745 // setup including any visible overlays, and display the gaia auth page.
746 // Control will be returned to the sync settings page once auth is complete.
747 CloseUI();
748 DisplayGaiaLogin();
749 return;
751 #endif
752 if (!GetSyncService()) {
753 // This can happen if the user directly navigates to /settings/syncSetup.
754 DLOG(WARNING) << "Cannot display sync UI when sync is disabled";
755 CloseUI();
756 return;
759 // User is already logged in. They must have brought up the config wizard
760 // via the "Advanced..." button or through One-Click signin (cases 4-6), or
761 // they are re-enabling sync after having disabled it (case 7).
762 DisplayConfigureSync(false);
765 void SyncSetupHandler::OpenConfigureSync() {
766 if (!PrepareSyncSetup())
767 return;
769 DisplayConfigureSync(false);
772 void SyncSetupHandler::FocusUI() {
773 DCHECK(IsActiveLogin());
774 WebContents* web_contents = web_ui()->GetWebContents();
775 web_contents->GetDelegate()->ActivateContents(web_contents);
778 void SyncSetupHandler::CloseUI() {
779 CloseSyncSetup();
780 base::StringValue page("done");
781 web_ui()->CallJavascriptFunction(
782 "SyncSetupOverlay.showSyncSetupPage", page);
785 bool SyncSetupHandler::IsExistingWizardPresent() {
786 LoginUIService* service = GetLoginUIService();
787 DCHECK(service);
788 return service->current_login_ui() != NULL;
791 bool SyncSetupHandler::FocusExistingWizardIfPresent() {
792 if (!IsExistingWizardPresent())
793 return false;
795 LoginUIService* service = GetLoginUIService();
796 DCHECK(service);
797 service->current_login_ui()->FocusUI();
798 return true;
801 void SyncSetupHandler::DisplayConfigureSync(bool passphrase_failed) {
802 // Should never call this when we are not signed in.
803 DCHECK(SigninManagerFactory::GetForProfile(
804 GetProfile())->IsAuthenticated());
805 ProfileSyncService* service = GetSyncService();
806 DCHECK(service);
807 if (!service->backend_initialized()) {
808 service->UnsuppressAndStart();
810 // See if it's even possible to bring up the sync backend - if not
811 // (unrecoverable error?), don't bother displaying a spinner that will be
812 // immediately closed because this leads to some ugly infinite UI loop (see
813 // http://crbug.com/244769).
814 if (SyncStartupTracker::GetSyncServiceState(GetProfile()) !=
815 SyncStartupTracker::SYNC_STARTUP_ERROR) {
816 DisplaySpinner();
819 // Start SyncSetupTracker to wait for sync to initialize.
820 sync_startup_tracker_.reset(
821 new SyncStartupTracker(GetProfile(), this));
822 return;
825 // Should only get here if user is signed in and sync is initialized, so no
826 // longer need a SyncStartupTracker.
827 sync_startup_tracker_.reset();
828 configuring_sync_ = true;
829 DCHECK(service->backend_initialized()) <<
830 "Cannot configure sync until the sync backend is initialized";
832 // Setup args for the sync configure screen:
833 // syncAllDataTypes: true if the user wants to sync everything
834 // syncNothing: true if the user wants to sync nothing
835 // <data_type>Registered: true if the associated data type is supported
836 // <data_type>Synced: true if the user wants to sync that specific data type
837 // encryptionEnabled: true if sync supports encryption
838 // encryptAllData: true if user wants to encrypt all data (not just
839 // passwords)
840 // usePassphrase: true if the data is encrypted with a secondary passphrase
841 // show_passphrase: true if a passphrase is needed to decrypt the sync data
842 base::DictionaryValue args;
844 // Tell the UI layer which data types are registered/enabled by the user.
845 const syncer::ModelTypeSet registered_types =
846 service->GetRegisteredDataTypes();
847 const syncer::ModelTypeSet preferred_types = service->GetPreferredDataTypes();
848 const syncer::ModelTypeSet enforced_types = service->GetForcedDataTypes();
849 syncer::ModelTypeNameMap type_names = syncer::GetUserSelectableTypeNameMap();
850 for (syncer::ModelTypeNameMap::const_iterator it = type_names.begin();
851 it != type_names.end(); ++it) {
852 syncer::ModelType sync_type = it->first;
853 const std::string key_name = it->second;
854 args.SetBoolean(key_name + "Registered", registered_types.Has(sync_type));
855 args.SetBoolean(key_name + "Synced", preferred_types.Has(sync_type));
856 args.SetBoolean(key_name + "Enforced", enforced_types.Has(sync_type));
857 // TODO(treib): How do we want to handle pref groups, i.e. when only some of
858 // the sync types behind a checkbox are force-enabled? crbug.com/403326
860 sync_driver::SyncPrefs sync_prefs(GetProfile()->GetPrefs());
861 args.SetBoolean("passphraseFailed", passphrase_failed);
862 args.SetBoolean("syncAllDataTypes", sync_prefs.HasKeepEverythingSynced());
863 args.SetBoolean("syncNothing", false); // Always false during initial setup.
864 args.SetBoolean("encryptAllData", service->EncryptEverythingEnabled());
865 args.SetBoolean("encryptAllDataAllowed", service->EncryptEverythingAllowed());
867 // We call IsPassphraseRequired() here, instead of calling
868 // IsPassphraseRequiredForDecryption(), because we want to show the passphrase
869 // UI even if no encrypted data types are enabled.
870 args.SetBoolean("showPassphrase", service->IsPassphraseRequired());
872 // To distinguish between FROZEN_IMPLICIT_PASSPHRASE and CUSTOM_PASSPHRASE
873 // we only set usePassphrase for CUSTOM_PASSPHRASE.
874 args.SetBoolean("usePassphrase",
875 service->GetPassphraseType() == syncer::CUSTOM_PASSPHRASE);
876 base::Time passphrase_time = service->GetExplicitPassphraseTime();
877 syncer::PassphraseType passphrase_type = service->GetPassphraseType();
878 if (!passphrase_time.is_null()) {
879 base::string16 passphrase_time_str =
880 base::TimeFormatShortDate(passphrase_time);
881 args.SetString(
882 "enterPassphraseBody",
883 GetStringFUTF16(IDS_SYNC_ENTER_PASSPHRASE_BODY_WITH_DATE,
884 passphrase_time_str));
885 args.SetString(
886 "enterGooglePassphraseBody",
887 GetStringFUTF16(IDS_SYNC_ENTER_GOOGLE_PASSPHRASE_BODY_WITH_DATE,
888 passphrase_time_str));
889 switch (passphrase_type) {
890 case syncer::FROZEN_IMPLICIT_PASSPHRASE:
891 args.SetString(
892 "fullEncryptionBody",
893 GetStringFUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_GOOGLE_WITH_DATE,
894 passphrase_time_str));
895 break;
896 case syncer::CUSTOM_PASSPHRASE:
897 args.SetString(
898 "fullEncryptionBody",
899 GetStringFUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM_WITH_DATE,
900 passphrase_time_str));
901 break;
902 default:
903 args.SetString(
904 "fullEncryptionBody",
905 GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM));
906 break;
908 } else if (passphrase_type == syncer::CUSTOM_PASSPHRASE) {
909 args.SetString(
910 "fullEncryptionBody",
911 GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM));
912 } else {
913 args.SetString(
914 "fullEncryptionBody",
915 GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_DATA));
918 base::StringValue page("configure");
919 web_ui()->CallJavascriptFunction(
920 "SyncSetupOverlay.showSyncSetupPage", page, args);
922 // Make sure the tab used for the Gaia sign in does not cover the settings
923 // tab.
924 FocusUI();
927 LoginUIService* SyncSetupHandler::GetLoginUIService() const {
928 return LoginUIServiceFactory::GetForProfile(GetProfile());