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/signin_global_error.h"
27 #include "chrome/browser/signin/signin_manager_factory.h"
28 #include "chrome/browser/signin/signin_promo.h"
29 #include "chrome/browser/sync/profile_sync_service.h"
30 #include "chrome/browser/sync/profile_sync_service_factory.h"
31 #include "chrome/browser/ui/browser_finder.h"
32 #include "chrome/browser/ui/browser_navigator.h"
33 #include "chrome/browser/ui/sync/signin_histogram.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/common/pref_names.h"
38 #include "chrome/common/url_constants.h"
39 #include "content/public/browser/render_view_host.h"
40 #include "content/public/browser/web_contents.h"
41 #include "content/public/browser/web_contents_delegate.h"
42 #include "google_apis/gaia/gaia_auth_util.h"
43 #include "google_apis/gaia/gaia_constants.h"
44 #include "grit/chromium_strings.h"
45 #include "grit/generated_resources.h"
46 #include "grit/locale_settings.h"
47 #include "net/base/url_util.h"
48 #include "ui/base/l10n/l10n_util.h"
50 #if defined(OS_CHROMEOS)
51 #include "chrome/browser/signin/signin_manager_base.h"
53 #include "chrome/browser/signin/signin_manager.h"
56 using content::WebContents
;
57 using l10n_util::GetStringFUTF16
;
58 using l10n_util::GetStringUTF16
;
62 // A structure which contains all the configuration information for sync.
63 struct SyncConfigInfo
{
70 syncer::ModelTypeSet data_types
;
71 std::string passphrase
;
72 bool passphrase_is_gaia
;
75 SyncConfigInfo::SyncConfigInfo()
77 sync_everything(false),
79 passphrase_is_gaia(false) {
82 SyncConfigInfo::~SyncConfigInfo() {}
84 // Note: The order of these types must match the ordering of
85 // the respective types in ModelType
86 const char* kDataTypeNames
[] = {
98 COMPILE_ASSERT(31 == syncer::MODEL_TYPE_COUNT
,
99 update_kDataTypeNames_to_match_UserSelectableTypes
);
101 typedef std::map
<syncer::ModelType
, const char*> ModelTypeNameMap
;
103 ModelTypeNameMap
GetSelectableTypeNameMap() {
104 ModelTypeNameMap type_names
;
105 syncer::ModelTypeSet type_set
= syncer::UserSelectableTypes();
106 syncer::ModelTypeSet::Iterator it
= type_set
.First();
107 DCHECK_EQ(arraysize(kDataTypeNames
), type_set
.Size());
108 for (size_t i
= 0; i
< arraysize(kDataTypeNames
) && it
.Good();
110 type_names
[it
.Get()] = kDataTypeNames
[i
];
115 bool GetConfiguration(const std::string
& json
, SyncConfigInfo
* config
) {
116 scoped_ptr
<base::Value
> parsed_value(base::JSONReader::Read(json
));
117 base::DictionaryValue
* result
;
118 if (!parsed_value
|| !parsed_value
->GetAsDictionary(&result
)) {
119 DLOG(ERROR
) << "GetConfiguration() not passed a Dictionary";
123 if (!result
->GetBoolean("syncAllDataTypes", &config
->sync_everything
)) {
124 DLOG(ERROR
) << "GetConfiguration() not passed a syncAllDataTypes value";
128 if (!result
->GetBoolean("syncNothing", &config
->sync_nothing
)) {
129 DLOG(ERROR
) << "GetConfiguration() not passed a syncNothing value";
133 DCHECK(!(config
->sync_everything
&& config
->sync_nothing
))
134 << "syncAllDataTypes and syncNothing cannot both be true";
136 ModelTypeNameMap type_names
= GetSelectableTypeNameMap();
138 for (ModelTypeNameMap::const_iterator it
= type_names
.begin();
139 it
!= type_names
.end(); ++it
) {
140 std::string key_name
= it
->second
+ std::string("Synced");
142 if (!result
->GetBoolean(key_name
, &sync_value
)) {
143 DLOG(ERROR
) << "GetConfiguration() not passed a value for " << key_name
;
147 config
->data_types
.Put(it
->first
);
150 // Encryption settings.
151 if (!result
->GetBoolean("encryptAllData", &config
->encrypt_all
)) {
152 DLOG(ERROR
) << "GetConfiguration() not passed a value for encryptAllData";
156 // Passphrase settings.
157 bool have_passphrase
;
158 if (!result
->GetBoolean("usePassphrase", &have_passphrase
)) {
159 DLOG(ERROR
) << "GetConfiguration() not passed a usePassphrase value";
163 if (have_passphrase
) {
164 if (!result
->GetBoolean("isGooglePassphrase",
165 &config
->passphrase_is_gaia
)) {
166 DLOG(ERROR
) << "GetConfiguration() not passed isGooglePassphrase value";
169 if (!result
->GetString("passphrase", &config
->passphrase
)) {
170 DLOG(ERROR
) << "GetConfiguration() not passed a passphrase value";
179 SyncSetupHandler::SyncSetupHandler(ProfileManager
* profile_manager
)
180 : configuring_sync_(false),
181 profile_manager_(profile_manager
) {
184 SyncSetupHandler::~SyncSetupHandler() {
185 // Just exit if running unit tests (no actual WebUI is attached).
189 // This case is hit when the user performs a back navigation.
193 void SyncSetupHandler::GetLocalizedValues(
194 base::DictionaryValue
* localized_strings
) {
195 GetStaticLocalizedValues(localized_strings
, web_ui());
198 void SyncSetupHandler::GetStaticLocalizedValues(
199 base::DictionaryValue
* localized_strings
,
200 content::WebUI
* web_ui
) {
201 DCHECK(localized_strings
);
203 base::string16
product_name(GetStringUTF16(IDS_PRODUCT_NAME
));
204 localized_strings
->SetString(
205 "chooseDataTypesInstructions",
206 GetStringFUTF16(IDS_SYNC_CHOOSE_DATATYPES_INSTRUCTIONS
, product_name
));
207 localized_strings
->SetString(
208 "encryptionInstructions",
209 GetStringFUTF16(IDS_SYNC_ENCRYPTION_INSTRUCTIONS
, product_name
));
210 localized_strings
->SetString(
211 "encryptionHelpURL", chrome::kSyncEncryptionHelpURL
);
212 localized_strings
->SetString(
213 "encryptionSectionMessage",
214 GetStringFUTF16(IDS_SYNC_ENCRYPTION_SECTION_MESSAGE
, product_name
));
215 localized_strings
->SetString(
217 GetStringFUTF16(IDS_SYNC_PASSPHRASE_RECOVER
,
219 google_util::StringAppendGoogleLocaleParam(
220 chrome::kSyncGoogleDashboardURL
))));
221 localized_strings
->SetString("stopSyncingExplanation",
222 l10n_util::GetStringFUTF16(
223 IDS_SYNC_STOP_SYNCING_EXPLANATION_LABEL
,
224 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME
),
225 base::ASCIIToUTF16(google_util::StringAppendGoogleLocaleParam(
226 chrome::kSyncGoogleDashboardURL
))));
227 localized_strings
->SetString("stopSyncingTitle",
228 l10n_util::GetStringUTF16(IDS_SYNC_STOP_SYNCING_DIALOG_TITLE
));
229 localized_strings
->SetString("stopSyncingConfirm",
230 l10n_util::GetStringUTF16(IDS_SYNC_STOP_SYNCING_CONFIRM_BUTTON_LABEL
));
232 localized_strings
->SetString(
233 "syncEverythingHelpURL", chrome::kSyncEverythingLearnMoreURL
);
234 localized_strings
->SetString(
235 "syncErrorHelpURL", chrome::kSyncErrorsHelpURL
);
237 static OptionsStringResource resources
[] = {
238 { "syncSetupConfigureTitle", IDS_SYNC_SETUP_CONFIGURE_TITLE
},
239 { "syncSetupSpinnerTitle", IDS_SYNC_SETUP_SPINNER_TITLE
},
240 { "syncSetupTimeoutTitle", IDS_SYNC_SETUP_TIME_OUT_TITLE
},
241 { "syncSetupTimeoutContent", IDS_SYNC_SETUP_TIME_OUT_CONTENT
},
242 { "errorLearnMore", IDS_LEARN_MORE
},
243 { "cancel", IDS_CANCEL
},
244 { "loginSuccess", IDS_SYNC_SUCCESS
},
245 { "settingUp", IDS_SYNC_LOGIN_SETTING_UP
},
246 { "syncAllDataTypes", IDS_SYNC_EVERYTHING
},
247 { "chooseDataTypes", IDS_SYNC_CHOOSE_DATATYPES
},
248 { "syncNothing", IDS_SYNC_NOTHING
},
249 { "bookmarks", IDS_SYNC_DATATYPE_BOOKMARKS
},
250 { "preferences", IDS_SYNC_DATATYPE_PREFERENCES
},
251 { "autofill", IDS_SYNC_DATATYPE_AUTOFILL
},
252 { "themes", IDS_SYNC_DATATYPE_THEMES
},
253 { "passwords", IDS_SYNC_DATATYPE_PASSWORDS
},
254 { "extensions", IDS_SYNC_DATATYPE_EXTENSIONS
},
255 { "typedURLs", IDS_SYNC_DATATYPE_TYPED_URLS
},
256 { "apps", IDS_SYNC_DATATYPE_APPS
},
257 { "openTabs", IDS_SYNC_DATATYPE_TABS
},
258 { "serviceUnavailableError", IDS_SYNC_SETUP_ABORTED_BY_PENDING_CLEAR
},
259 { "confirmLabel", IDS_SYNC_CONFIRM_PASSPHRASE_LABEL
},
260 { "emptyErrorMessage", IDS_SYNC_EMPTY_PASSPHRASE_ERROR
},
261 { "mismatchErrorMessage", IDS_SYNC_PASSPHRASE_MISMATCH_ERROR
},
262 { "customizeLinkLabel", IDS_SYNC_CUSTOMIZE_LINK_LABEL
},
263 { "confirmSyncPreferences", IDS_SYNC_CONFIRM_SYNC_PREFERENCES
},
264 { "syncEverything", IDS_SYNC_SYNC_EVERYTHING
},
265 { "useDefaultSettings", IDS_SYNC_USE_DEFAULT_SETTINGS
},
266 { "enterPassphraseBody", IDS_SYNC_ENTER_PASSPHRASE_BODY
},
267 { "enterGooglePassphraseBody", IDS_SYNC_ENTER_GOOGLE_PASSPHRASE_BODY
},
268 { "passphraseLabel", IDS_SYNC_PASSPHRASE_LABEL
},
269 { "incorrectPassphrase", IDS_SYNC_INCORRECT_PASSPHRASE
},
270 { "passphraseWarning", IDS_SYNC_PASSPHRASE_WARNING
},
271 { "yes", IDS_SYNC_PASSPHRASE_CANCEL_YES
},
272 { "no", IDS_SYNC_PASSPHRASE_CANCEL_NO
},
273 { "sectionExplicitMessagePrefix", IDS_SYNC_PASSPHRASE_MSG_EXPLICIT_PREFIX
},
274 { "sectionExplicitMessagePostfix",
275 IDS_SYNC_PASSPHRASE_MSG_EXPLICIT_POSTFIX
},
276 // TODO(rogerta): browser/resource/sync_promo/sync_promo.html and related
277 // file may not be needed any more. If not, then the following promo
278 // strings can also be removed.
279 { "promoPageTitle", IDS_SYNC_PROMO_TAB_TITLE
},
280 { "promoSkipButton", IDS_SYNC_PROMO_SKIP_BUTTON
},
281 { "promoAdvanced", IDS_SYNC_PROMO_ADVANCED
},
282 { "promoLearnMore", IDS_LEARN_MORE
},
283 { "promoTitleShort", IDS_SYNC_PROMO_MESSAGE_TITLE_SHORT
},
284 { "encryptionSectionTitle", IDS_SYNC_ENCRYPTION_SECTION_TITLE
},
285 { "basicEncryptionOption", IDS_SYNC_BASIC_ENCRYPTION_DATA
},
286 { "fullEncryptionOption", IDS_SYNC_FULL_ENCRYPTION_DATA
},
289 RegisterStrings(localized_strings
, resources
, arraysize(resources
));
290 RegisterTitle(localized_strings
, "syncSetupOverlay", IDS_SYNC_SETUP_TITLE
);
293 void SyncSetupHandler::DisplayConfigureSync(bool show_advanced
,
294 bool passphrase_failed
) {
295 // Should never call this when we are not signed in.
296 DCHECK(!SigninManagerFactory::GetForProfile(
297 GetProfile())->GetAuthenticatedUsername().empty());
298 ProfileSyncService
* service
= GetSyncService();
300 if (!service
->sync_initialized()) {
301 service
->UnsuppressAndStart();
303 // See if it's even possible to bring up the sync backend - if not
304 // (unrecoverable error?), don't bother displaying a spinner that will be
305 // immediately closed because this leads to some ugly infinite UI loop (see
306 // http://crbug.com/244769).
307 if (SyncStartupTracker::GetSyncServiceState(GetProfile()) !=
308 SyncStartupTracker::SYNC_STARTUP_ERROR
) {
312 // Start SyncSetupTracker to wait for sync to initialize.
313 sync_startup_tracker_
.reset(
314 new SyncStartupTracker(GetProfile(), this));
318 // Should only get here if user is signed in and sync is initialized, so no
319 // longer need a SyncStartupTracker.
320 sync_startup_tracker_
.reset();
321 configuring_sync_
= true;
322 DCHECK(service
->sync_initialized()) <<
323 "Cannot configure sync until the sync backend is initialized";
325 // Setup args for the sync configure screen:
326 // showSyncEverythingPage: false to skip directly to the configure screen
327 // syncAllDataTypes: true if the user wants to sync everything
328 // syncNothing: true if the user wants to sync nothing
329 // <data_type>Registered: true if the associated data type is supported
330 // <data_type>Synced: true if the user wants to sync that specific data type
331 // encryptionEnabled: true if sync supports encryption
332 // encryptAllData: true if user wants to encrypt all data (not just
334 // usePassphrase: true if the data is encrypted with a secondary passphrase
335 // show_passphrase: true if a passphrase is needed to decrypt the sync data
336 base::DictionaryValue args
;
338 // Tell the UI layer which data types are registered/enabled by the user.
339 const syncer::ModelTypeSet registered_types
=
340 service
->GetRegisteredDataTypes();
341 const syncer::ModelTypeSet preferred_types
=
342 service
->GetPreferredDataTypes();
343 ModelTypeNameMap type_names
= GetSelectableTypeNameMap();
344 for (ModelTypeNameMap::const_iterator it
= type_names
.begin();
345 it
!= type_names
.end(); ++it
) {
346 syncer::ModelType sync_type
= it
->first
;
347 const std::string key_name
= it
->second
;
348 args
.SetBoolean(key_name
+ "Registered",
349 registered_types
.Has(sync_type
));
350 args
.SetBoolean(key_name
+ "Synced", preferred_types
.Has(sync_type
));
352 browser_sync::SyncPrefs
sync_prefs(GetProfile()->GetPrefs());
353 args
.SetBoolean("passphraseFailed", passphrase_failed
);
354 args
.SetBoolean("showSyncEverythingPage", !show_advanced
);
355 args
.SetBoolean("syncAllDataTypes", sync_prefs
.HasKeepEverythingSynced());
356 args
.SetBoolean("syncNothing", false); // Always false during initial setup.
357 args
.SetBoolean("encryptAllData", service
->EncryptEverythingEnabled());
359 // We call IsPassphraseRequired() here, instead of calling
360 // IsPassphraseRequiredForDecryption(), because we want to show the passphrase
361 // UI even if no encrypted data types are enabled.
362 args
.SetBoolean("showPassphrase", service
->IsPassphraseRequired());
364 // To distinguish between FROZEN_IMPLICIT_PASSPHRASE and CUSTOM_PASSPHRASE
365 // we only set usePassphrase for CUSTOM_PASSPHRASE.
366 args
.SetBoolean("usePassphrase",
367 service
->GetPassphraseType() == syncer::CUSTOM_PASSPHRASE
);
368 base::Time passphrase_time
= service
->GetExplicitPassphraseTime();
369 syncer::PassphraseType passphrase_type
= service
->GetPassphraseType();
370 if (!passphrase_time
.is_null()) {
371 base::string16 passphrase_time_str
=
372 base::TimeFormatShortDate(passphrase_time
);
374 "enterPassphraseBody",
375 GetStringFUTF16(IDS_SYNC_ENTER_PASSPHRASE_BODY_WITH_DATE
,
376 passphrase_time_str
));
378 "enterGooglePassphraseBody",
379 GetStringFUTF16(IDS_SYNC_ENTER_GOOGLE_PASSPHRASE_BODY_WITH_DATE
,
380 passphrase_time_str
));
381 switch (passphrase_type
) {
382 case syncer::FROZEN_IMPLICIT_PASSPHRASE
:
384 "fullEncryptionBody",
385 GetStringFUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_GOOGLE_WITH_DATE
,
386 passphrase_time_str
));
388 case syncer::CUSTOM_PASSPHRASE
:
390 "fullEncryptionBody",
391 GetStringFUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM_WITH_DATE
,
392 passphrase_time_str
));
396 "fullEncryptionBody",
397 GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM
));
400 } else if (passphrase_type
== syncer::CUSTOM_PASSPHRASE
) {
402 "fullEncryptionBody",
403 GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM
));
406 "fullEncryptionBody",
407 GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_DATA
));
410 base::StringValue
page("configure");
411 web_ui()->CallJavascriptFunction(
412 "SyncSetupOverlay.showSyncSetupPage", page
, args
);
414 // Make sure the tab used for the Gaia sign in does not cover the settings
419 void SyncSetupHandler::ConfigureSyncDone() {
420 base::StringValue
page("done");
421 web_ui()->CallJavascriptFunction(
422 "SyncSetupOverlay.showSyncSetupPage", page
);
424 // Suppress the sign in promo once the user starts sync. This way the user
425 // doesn't see the sign in promo even if they sign out later on.
426 signin::SetUserSkippedPromo(GetProfile());
428 ProfileSyncService
* service
= GetSyncService();
430 if (!service
->HasSyncSetupCompleted()) {
431 // This is the first time configuring sync, so log it.
432 base::FilePath profile_file_path
= GetProfile()->GetPath();
433 ProfileMetrics::LogProfileSyncSignIn(profile_file_path
);
435 // We're done configuring, so notify ProfileSyncService that it is OK to
437 service
->SetSetupInProgress(false);
438 service
->SetSyncSetupCompleted();
442 bool SyncSetupHandler::IsActiveLogin() const {
443 // LoginUIService can be NULL if page is brought up in incognito mode
444 // (i.e. if the user is running in guest mode in cros and brings up settings).
445 LoginUIService
* service
= GetLoginUIService();
446 return service
&& (service
->current_login_ui() == this);
449 void SyncSetupHandler::RegisterMessages() {
450 web_ui()->RegisterMessageCallback(
451 "SyncSetupDidClosePage",
452 base::Bind(&SyncSetupHandler::OnDidClosePage
,
453 base::Unretained(this)));
454 web_ui()->RegisterMessageCallback(
455 "SyncSetupConfigure",
456 base::Bind(&SyncSetupHandler::HandleConfigure
,
457 base::Unretained(this)));
458 web_ui()->RegisterMessageCallback(
459 "SyncSetupShowSetupUI",
460 base::Bind(&SyncSetupHandler::HandleShowSetupUI
,
461 base::Unretained(this)));
462 web_ui()->RegisterMessageCallback("CloseTimeout",
463 base::Bind(&SyncSetupHandler::HandleCloseTimeout
,
464 base::Unretained(this)));
465 #if defined(OS_CHROMEOS)
466 web_ui()->RegisterMessageCallback(
467 "SyncSetupDoSignOutOnAuthError",
468 base::Bind(&SyncSetupHandler::HandleDoSignOutOnAuthError
,
469 base::Unretained(this)));
471 web_ui()->RegisterMessageCallback("SyncSetupStopSyncing",
472 base::Bind(&SyncSetupHandler::HandleStopSyncing
,
473 base::Unretained(this)));
474 web_ui()->RegisterMessageCallback("SyncSetupStartSignIn",
475 base::Bind(&SyncSetupHandler::HandleStartSignin
,
476 base::Unretained(this)));
480 #if !defined(OS_CHROMEOS)
481 void SyncSetupHandler::DisplayGaiaLogin() {
482 DCHECK(!sync_startup_tracker_
);
483 // Advanced options are no longer being configured if the login screen is
484 // visible. If the user exits the signin wizard after this without
485 // configuring sync, CloseSyncSetup() will ensure they are logged out.
486 configuring_sync_
= false;
487 DisplayGaiaLoginInNewTabOrWindow();
490 void SyncSetupHandler::DisplayGaiaLoginInNewTabOrWindow() {
491 GURL
url(signin::GetPromoURL(signin::SOURCE_SETTINGS
,
492 true)); // auto close after success.
493 Browser
* browser
= chrome::FindBrowserWithWebContents(
494 web_ui()->GetWebContents());
496 // Settings is not displayed in a browser window. Open a new window.
497 browser
= new Browser(Browser::CreateParams(
498 Browser::TYPE_TABBED
, GetProfile(), chrome::GetActiveDesktop()));
501 // If the signin manager already has an authenticated username, this is a
502 // re-auth scenario, and we need to ensure that the user signs in with the
503 // same email address.
504 std::string email
= SigninManagerFactory::GetForProfile(
505 browser
->profile())->GetAuthenticatedUsername();
506 if (!email
.empty()) {
507 UMA_HISTOGRAM_ENUMERATION("Signin.Reauth",
508 signin::HISTOGRAM_SHOWN
,
509 signin::HISTOGRAM_MAX
);
510 url
= net::AppendQueryParameter(url
, "Email", email
);
514 content::OpenURLParams(url
, content::Referrer(), SINGLETON_TAB
,
515 content::PAGE_TRANSITION_AUTO_BOOKMARK
, false));
519 bool SyncSetupHandler::PrepareSyncSetup() {
521 // If the wizard is already visible, just focus that one.
522 if (FocusExistingWizardIfPresent()) {
523 if (!IsActiveLogin())
528 // Notify services that login UI is now active.
529 GetLoginUIService()->SetLoginUI(this);
531 ProfileSyncService
* service
= GetSyncService();
533 service
->SetSetupInProgress(true);
538 void SyncSetupHandler::DisplaySpinner() {
539 configuring_sync_
= true;
540 base::StringValue
page("spinner");
541 base::DictionaryValue args
;
543 const int kTimeoutSec
= 30;
544 DCHECK(!backend_start_timer_
);
545 backend_start_timer_
.reset(new base::OneShotTimer
<SyncSetupHandler
>());
546 backend_start_timer_
->Start(FROM_HERE
,
547 base::TimeDelta::FromSeconds(kTimeoutSec
),
548 this, &SyncSetupHandler::DisplayTimeout
);
550 web_ui()->CallJavascriptFunction(
551 "SyncSetupOverlay.showSyncSetupPage", page
, args
);
554 // TODO(kochi): Handle error conditions other than timeout.
555 // http://crbug.com/128692
556 void SyncSetupHandler::DisplayTimeout() {
557 // Stop a timer to handle timeout in waiting for checking network connection.
558 backend_start_timer_
.reset();
560 // Do not listen to sync startup events.
561 sync_startup_tracker_
.reset();
563 base::StringValue
page("timeout");
564 base::DictionaryValue args
;
565 web_ui()->CallJavascriptFunction(
566 "SyncSetupOverlay.showSyncSetupPage", page
, args
);
569 void SyncSetupHandler::OnDidClosePage(const base::ListValue
* args
) {
573 void SyncSetupHandler::SyncStartupFailed() {
574 // Stop a timer to handle timeout in waiting for checking network connection.
575 backend_start_timer_
.reset();
577 // Just close the sync overlay (the idea is that the base settings page will
578 // display the current error.)
582 void SyncSetupHandler::SyncStartupCompleted() {
583 ProfileSyncService
* service
= GetSyncService();
584 DCHECK(service
->sync_initialized());
586 // Stop a timer to handle timeout in waiting for checking network connection.
587 backend_start_timer_
.reset();
589 DisplayConfigureSync(true, false);
592 Profile
* SyncSetupHandler::GetProfile() const {
593 return Profile::FromWebUI(web_ui());
596 ProfileSyncService
* SyncSetupHandler::GetSyncService() const {
597 Profile
* profile
= GetProfile();
598 return profile
->IsSyncAccessible() ?
599 ProfileSyncServiceFactory::GetForProfile(GetProfile()) : NULL
;
602 void SyncSetupHandler::HandleConfigure(const base::ListValue
* args
) {
603 DCHECK(!sync_startup_tracker_
);
605 if (!args
->GetString(0, &json
)) {
606 NOTREACHED() << "Could not read JSON argument";
614 SyncConfigInfo configuration
;
615 if (!GetConfiguration(json
, &configuration
)) {
616 // The page sent us something that we didn't understand.
617 // This probably indicates a programming error.
622 // Start configuring the ProfileSyncService using the configuration passed
623 // to us from the JS layer.
624 ProfileSyncService
* service
= GetSyncService();
626 // If the sync engine has shutdown for some reason, just close the sync
628 if (!service
|| !service
->sync_initialized()) {
633 // Disable sync, but remain signed in if the user selected "Sync nothing" in
634 // the advanced settings dialog. Note: In order to disable sync across
635 // restarts on Chrome OS, we must call OnStopSyncingPermanently(), which
636 // suppresses sync startup in addition to disabling it.
637 if (configuration
.sync_nothing
) {
638 ProfileSyncService::SyncEvent(
639 ProfileSyncService::STOP_FROM_ADVANCED_DIALOG
);
641 service
->OnStopSyncingPermanently();
642 service
->SetSetupInProgress(false);
646 // Note: Data encryption will not occur until configuration is complete
647 // (when the PSS receives its CONFIGURE_DONE notification from the sync
648 // backend), so the user still has a chance to cancel out of the operation
649 // if (for example) some kind of passphrase error is encountered.
650 if (configuration
.encrypt_all
)
651 service
->EnableEncryptEverything();
653 bool passphrase_failed
= false;
654 if (!configuration
.passphrase
.empty()) {
655 // We call IsPassphraseRequired() here (instead of
656 // IsPassphraseRequiredForDecryption()) because the user may try to enter
657 // a passphrase even though no encrypted data types are enabled.
658 if (service
->IsPassphraseRequired()) {
659 // If we have pending keys, try to decrypt them with the provided
660 // passphrase. We track if this succeeds or fails because a failed
661 // decryption should result in an error even if there aren't any encrypted
664 !service
->SetDecryptionPassphrase(configuration
.passphrase
);
666 // OK, the user sent us a passphrase, but we don't have pending keys. So
667 // it either means that the pending keys were resolved somehow since the
668 // time the UI was displayed (re-encryption, pending passphrase change,
669 // etc) or the user wants to re-encrypt.
670 if (!configuration
.passphrase_is_gaia
&&
671 !service
->IsUsingSecondaryPassphrase()) {
672 // User passed us a secondary passphrase, and the data is encrypted
673 // with a GAIA passphrase so they must want to encrypt.
674 service
->SetEncryptionPassphrase(configuration
.passphrase
,
675 ProfileSyncService::EXPLICIT
);
680 bool user_was_prompted_for_passphrase
=
681 service
->IsPassphraseRequiredForDecryption();
682 service
->OnUserChoseDatatypes(configuration
.sync_everything
,
683 configuration
.data_types
);
685 // Need to call IsPassphraseRequiredForDecryption() *after* calling
686 // OnUserChoseDatatypes() because the user may have just disabled the
687 // encrypted datatypes (in which case we just want to exit, not prompt the
688 // user for a passphrase).
689 if (passphrase_failed
|| service
->IsPassphraseRequiredForDecryption()) {
690 // We need a passphrase, or the user's attempt to set a passphrase failed -
691 // prompt them again. This covers a few subtle cases:
692 // 1) The user enters an incorrect passphrase *and* disabled the encrypted
693 // data types. In that case we want to notify the user that the
694 // passphrase was incorrect even though there are no longer any encrypted
695 // types enabled (IsPassphraseRequiredForDecryption() == false).
696 // 2) The user doesn't enter any passphrase. In this case, we won't call
697 // SetDecryptionPassphrase() (passphrase_failed == false), but we still
698 // want to display an error message to let the user know that their
699 // blank passphrase entry is not acceptable.
700 // 3) The user just enabled an encrypted data type - in this case we don't
701 // want to display an "invalid passphrase" error, since it's the first
702 // time the user is seeing the prompt.
703 DisplayConfigureSync(
704 true, passphrase_failed
|| user_was_prompted_for_passphrase
);
706 // No passphrase is required from the user so mark the configuration as
707 // complete and close the sync setup overlay.
711 ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_CUSTOMIZE
);
712 if (configuration
.encrypt_all
)
713 ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_ENCRYPT
);
714 if (configuration
.passphrase_is_gaia
&& !configuration
.passphrase
.empty())
715 ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_PASSPHRASE
);
716 if (!configuration
.sync_everything
)
717 ProfileMetrics::LogProfileSyncInfo(ProfileMetrics::SYNC_CHOOSE
);
720 void SyncSetupHandler::HandleShowSetupUI(const base::ListValue
* args
) {
721 ProfileSyncService
* service
= GetSyncService();
724 SigninManagerBase
* signin
=
725 SigninManagerFactory::GetForProfile(GetProfile());
726 if (signin
->GetAuthenticatedUsername().empty()) {
727 // For web-based signin, the signin page is not displayed in an overlay
728 // on the settings page. So if we get here, it must be due to the user
729 // cancelling signin (by reloading the sync settings page during initial
730 // signin) or by directly navigating to settings/syncSetup
731 // (http://crbug.com/229836). So just exit and go back to the settings page.
732 DLOG(WARNING
) << "Cannot display sync setup UI when not signed in";
737 // If a setup wizard is already present, but not on this page, close the
738 // blank setup overlay on this page by showing the "done" page. This can
739 // happen if the user navigates to chrome://settings/syncSetup in more than
740 // one tab. See crbug.com/261566.
741 // Note: The following block will transfer focus to the existing wizard.
742 if (IsExistingWizardPresent() && !IsActiveLogin()) {
746 // If a setup wizard is present on this page or another, bring it to focus.
747 // Otherwise, display a new one on this page.
748 if (!FocusExistingWizardIfPresent())
752 #if defined(OS_CHROMEOS)
753 // On ChromeOS, we need to sign out the user session to fix an auth error, so
754 // the user goes through the real signin flow to generate a new auth token.
755 void SyncSetupHandler::HandleDoSignOutOnAuthError(const base::ListValue
* args
) {
756 DVLOG(1) << "Signing out the user to fix a sync error.";
757 chrome::AttemptUserExit();
761 #if !defined(OS_CHROMEOS)
762 void SyncSetupHandler::HandleStartSignin(const base::ListValue
* args
) {
763 // Should only be called if the user is not already signed in.
764 DCHECK(SigninManagerFactory::GetForProfile(GetProfile())->
765 GetAuthenticatedUsername().empty());
769 void SyncSetupHandler::HandleStopSyncing(const base::ListValue
* args
) {
770 if (GetSyncService())
771 ProfileSyncService::SyncEvent(ProfileSyncService::STOP_FROM_OPTIONS
);
772 SigninManagerFactory::GetForProfile(GetProfile())->SignOut();
776 void SyncSetupHandler::HandleCloseTimeout(const base::ListValue
* args
) {
780 void SyncSetupHandler::CloseSyncSetup() {
781 // Stop a timer to handle timeout in waiting for checking network connection.
782 backend_start_timer_
.reset();
784 // Clear the sync startup tracker, since the setup wizard is being closed.
785 sync_startup_tracker_
.reset();
787 ProfileSyncService
* sync_service
= GetSyncService();
788 if (IsActiveLogin()) {
789 // Don't log a cancel event if the sync setup dialog is being
790 // automatically closed due to an auth error.
791 if (!sync_service
|| (!sync_service
->HasSyncSetupCompleted() &&
792 sync_service
->GetAuthError().state() == GoogleServiceAuthError::NONE
)) {
793 if (configuring_sync_
) {
794 ProfileSyncService::SyncEvent(
795 ProfileSyncService::CANCEL_DURING_CONFIGURE
);
798 // If the user clicked "Cancel" while setting up sync, disable sync
799 // because we don't want the sync backend to remain in the initialized
800 // state. Note: In order to disable sync across restarts on Chrome OS, we
801 // must call OnStopSyncingPermanently(), which suppresses sync startup in
802 // addition to disabling it.
804 DVLOG(1) << "Sync setup aborted by user action";
805 sync_service
->OnStopSyncingPermanently();
806 #if !defined(OS_CHROMEOS)
807 // Sign out the user on desktop Chrome if they click cancel during
809 // TODO(rsimha): Revisit this for M30. See http://crbug.com/252049.
810 if (sync_service
->FirstSetupInProgress())
811 SigninManagerFactory::GetForProfile(GetProfile())->SignOut();
816 GetLoginUIService()->LoginUIClosed(this);
819 // Alert the sync service anytime the sync setup dialog is closed. This can
820 // happen due to the user clicking the OK or Cancel button, or due to the
821 // dialog being closed by virtue of sync being disabled in the background.
823 sync_service
->SetSetupInProgress(false);
825 configuring_sync_
= false;
828 void SyncSetupHandler::OpenSyncSetup() {
829 if (!PrepareSyncSetup())
832 // There are several different UI flows that can bring the user here:
834 // 2) Normal signin through settings page (GetAuthenticatedUsername() is
836 // 3) Previously working credentials have expired.
837 // 4) User is signed in, but has stopped sync via the google dashboard, and
838 // signout is prohibited by policy so we need to force a re-auth.
839 // 5) User clicks [Advanced Settings] button on options page while already
841 // 6) One-click signin (credentials are already available, so should display
842 // sync configure UI, not login UI).
843 // 7) User re-enables sync after disabling it via advanced settings.
844 #if !defined(OS_CHROMEOS)
845 SigninManagerBase
* signin
=
846 SigninManagerFactory::GetForProfile(GetProfile());
848 if (signin
->GetAuthenticatedUsername().empty() ||
849 SigninGlobalError::GetForProfile(GetProfile())->HasMenuItem()) {
850 // User is not logged in (cases 1-2), or login has been specially requested
851 // because previously working credentials have expired (case 3). Close sync
852 // setup including any visible overlays, and display the gaia auth page.
853 // Control will be returned to the sync settings page once auth is complete.
859 if (!GetSyncService()) {
860 // This can happen if the user directly navigates to /settings/syncSetup.
861 DLOG(WARNING
) << "Cannot display sync UI when sync is disabled";
866 // User is already logged in. They must have brought up the config wizard
867 // via the "Advanced..." button or through One-Click signin (cases 4-6), or
868 // they are re-enabling sync after having disabled it (case 7).
869 DisplayConfigureSync(true, false);
872 void SyncSetupHandler::OpenConfigureSync() {
873 if (!PrepareSyncSetup())
876 DisplayConfigureSync(true, false);
879 void SyncSetupHandler::FocusUI() {
880 DCHECK(IsActiveLogin());
881 WebContents
* web_contents
= web_ui()->GetWebContents();
882 web_contents
->GetDelegate()->ActivateContents(web_contents
);
885 void SyncSetupHandler::CloseUI() {
887 base::StringValue
page("done");
888 web_ui()->CallJavascriptFunction(
889 "SyncSetupOverlay.showSyncSetupPage", page
);
892 bool SyncSetupHandler::IsExistingWizardPresent() {
893 LoginUIService
* service
= GetLoginUIService();
895 return service
->current_login_ui() != NULL
;
898 bool SyncSetupHandler::FocusExistingWizardIfPresent() {
899 if (!IsExistingWizardPresent())
902 LoginUIService
* service
= GetLoginUIService();
904 service
->current_login_ui()->FocusUI();
908 LoginUIService
* SyncSetupHandler::GetLoginUIService() const {
909 return LoginUIServiceFactory::GetForProfile(GetProfile());