23ae8b1e8c9e8b6727bf224295b7e6492eba52b1
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / webui / sync_setup_handler.cc
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.
4
5 #include "chrome/browser/ui/webui/sync_setup_handler.h"
6
7 #include "base/basictypes.h"
8 #include "base/bind.h"
9 #include "base/bind_helpers.h"
10 #include "base/command_line.h"
11 #include "base/compiler_specific.h"
12 #include "base/i18n/time_formatting.h"
13 #include "base/json/json_reader.h"
14 #include "base/json/json_writer.h"
15 #include "base/metrics/histogram.h"
16 #include "base/prefs/pref_service.h"
17 #include "base/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"
52
53 #if defined(OS_CHROMEOS)
54 #include "chrome/browser/signin/signin_manager_base.h"
55 #else
56 #include "chrome/browser/signin/signin_manager.h"
57 #endif
58
59 using content::WebContents;
60 using l10n_util::GetStringFUTF16;
61 using l10n_util::GetStringUTF16;
62
63 namespace {
64
65 // A structure which contains all the configuration information for sync.
66 struct SyncConfigInfo {
67   SyncConfigInfo();
68   ~SyncConfigInfo();
69
70   bool encrypt_all;
71   bool sync_everything;
72   bool sync_nothing;
73   syncer::ModelTypeSet data_types;
74   std::string passphrase;
75   bool passphrase_is_gaia;
76 };
77
78 SyncConfigInfo::SyncConfigInfo()
79     : encrypt_all(false),
80       sync_everything(false),
81       sync_nothing(false),
82       passphrase_is_gaia(false) {
83 }
84
85 SyncConfigInfo::~SyncConfigInfo() {}
86
87 // Note: The order of these types must match the ordering of
88 // the respective types in ModelType
89 const char* kDataTypeNames[] = {
90   "bookmarks",
91   "preferences",
92   "passwords",
93   "autofill",
94   "themes",
95   "typedUrls",
96   "extensions",
97   "apps",
98   "tabs"
99 };
100
101 COMPILE_ASSERT(32 == syncer::MODEL_TYPE_COUNT,
102                update_kDataTypeNames_to_match_UserSelectableTypes);
103
104 typedef std::map<syncer::ModelType, const char*> ModelTypeNameMap;
105
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();
112        ++i, it.Inc()) {
113     type_names[it.Get()] = kDataTypeNames[i];
114   }
115   return type_names;
116 }
117
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";
123     return false;
124   }
125
126   if (!result->GetBoolean("syncAllDataTypes", &config->sync_everything)) {
127     DLOG(ERROR) << "GetConfiguration() not passed a syncAllDataTypes value";
128     return false;
129   }
130
131   if (!result->GetBoolean("syncNothing", &config->sync_nothing)) {
132     DLOG(ERROR) << "GetConfiguration() not passed a syncNothing value";
133     return false;
134   }
135
136   DCHECK(!(config->sync_everything && config->sync_nothing))
137       << "syncAllDataTypes and syncNothing cannot both be true";
138
139   ModelTypeNameMap type_names = GetSelectableTypeNameMap();
140
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");
144     bool sync_value;
145     if (!result->GetBoolean(key_name, &sync_value)) {
146       DLOG(ERROR) << "GetConfiguration() not passed a value for " << key_name;
147       return false;
148     }
149     if (sync_value)
150       config->data_types.Put(it->first);
151   }
152
153   // Encryption settings.
154   if (!result->GetBoolean("encryptAllData", &config->encrypt_all)) {
155     DLOG(ERROR) << "GetConfiguration() not passed a value for encryptAllData";
156     return false;
157   }
158
159   // Passphrase settings.
160   bool have_passphrase;
161   if (!result->GetBoolean("usePassphrase", &have_passphrase)) {
162     DLOG(ERROR) << "GetConfiguration() not passed a usePassphrase value";
163     return false;
164   }
165
166   if (have_passphrase) {
167     if (!result->GetBoolean("isGooglePassphrase",
168                             &config->passphrase_is_gaia)) {
169       DLOG(ERROR) << "GetConfiguration() not passed isGooglePassphrase value";
170       return false;
171     }
172     if (!result->GetString("passphrase", &config->passphrase)) {
173       DLOG(ERROR) << "GetConfiguration() not passed a passphrase value";
174       return false;
175     }
176   }
177   return true;
178 }
179
180 }  // namespace
181
182 SyncSetupHandler::SyncSetupHandler(ProfileManager* profile_manager)
183     : configuring_sync_(false),
184       profile_manager_(profile_manager) {
185 }
186
187 SyncSetupHandler::~SyncSetupHandler() {
188   // Just exit if running unit tests (no actual WebUI is attached).
189   if (!web_ui())
190     return;
191
192   // This case is hit when the user performs a back navigation.
193   CloseSyncSetup();
194 }
195
196 void SyncSetupHandler::GetLocalizedValues(
197     base::DictionaryValue* localized_strings) {
198   GetStaticLocalizedValues(localized_strings, web_ui());
199 }
200
201 void SyncSetupHandler::GetStaticLocalizedValues(
202     base::DictionaryValue* localized_strings,
203     content::WebUI* web_ui) {
204   DCHECK(localized_strings);
205
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(
219       "passphraseRecover",
220       GetStringFUTF16(IDS_SYNC_PASSPHRASE_RECOVER,
221                       base::ASCIIToUTF16(
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));
234
235   localized_strings->SetString(
236       "syncEverythingHelpURL", chrome::kSyncEverythingLearnMoreURL);
237   localized_strings->SetString(
238       "syncErrorHelpURL", chrome::kSyncErrorsHelpURL);
239
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 },
290   };
291
292   RegisterStrings(localized_strings, resources, arraysize(resources));
293   RegisterTitle(localized_strings, "syncSetupOverlay", IDS_SYNC_SETUP_TITLE);
294 }
295
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();
302   DCHECK(service);
303   if (!service->sync_initialized()) {
304     service->UnsuppressAndStart();
305
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) {
312       DisplaySpinner();
313     }
314
315     // Start SyncSetupTracker to wait for sync to initialize.
316     sync_startup_tracker_.reset(
317         new SyncStartupTracker(GetProfile(), this));
318     return;
319   }
320
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";
327
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
336   //       passwords)
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;
340
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));
354   }
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());
361
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());
366
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);
376     args.SetString(
377         "enterPassphraseBody",
378         GetStringFUTF16(IDS_SYNC_ENTER_PASSPHRASE_BODY_WITH_DATE,
379                         passphrase_time_str));
380     args.SetString(
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:
386         args.SetString(
387             "fullEncryptionBody",
388             GetStringFUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_GOOGLE_WITH_DATE,
389                             passphrase_time_str));
390         break;
391       case syncer::CUSTOM_PASSPHRASE:
392         args.SetString(
393             "fullEncryptionBody",
394             GetStringFUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM_WITH_DATE,
395                             passphrase_time_str));
396         break;
397       default:
398         args.SetString(
399             "fullEncryptionBody",
400             GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM));
401         break;
402     }
403   } else if (passphrase_type == syncer::CUSTOM_PASSPHRASE) {
404     args.SetString(
405         "fullEncryptionBody",
406         GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_BODY_CUSTOM));
407   } else {
408     args.SetString(
409         "fullEncryptionBody",
410         GetStringUTF16(IDS_SYNC_FULL_ENCRYPTION_DATA));
411   }
412
413   base::StringValue page("configure");
414   web_ui()->CallJavascriptFunction(
415       "SyncSetupOverlay.showSyncSetupPage", page, args);
416
417   // Make sure the tab used for the Gaia sign in does not cover the settings
418   // tab.
419   FocusUI();
420 }
421
422 void SyncSetupHandler::ConfigureSyncDone() {
423   base::StringValue page("done");
424   web_ui()->CallJavascriptFunction(
425       "SyncSetupOverlay.showSyncSetupPage", page);
426
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());
430
431   ProfileSyncService* service = GetSyncService();
432   DCHECK(service);
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);
437
438     // We're done configuring, so notify ProfileSyncService that it is OK to
439     // start syncing.
440     service->SetSetupInProgress(false);
441     service->SetSyncSetupCompleted();
442   }
443 }
444
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);
450 }
451
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)));
473 #else
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)));
480 #endif
481 }
482
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();
491 }
492
493 void SyncSetupHandler::DisplayGaiaLoginInNewTabOrWindow() {
494   Browser* browser = chrome::FindBrowserWithWebContents(
495       web_ui()->GetWebContents());
496   if (!browser) {
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()));
500   }
501
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.
505   GURL url;
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);
512
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());
519   } else {
520     url = signin::GetPromoURL(signin::SOURCE_SETTINGS, true);
521   }
522
523   chrome::ShowSingletonTab(browser, url);
524 }
525 #endif
526
527 bool SyncSetupHandler::PrepareSyncSetup() {
528
529   // If the wizard is already visible, just focus that one.
530   if (FocusExistingWizardIfPresent()) {
531     if (!IsActiveLogin())
532       CloseSyncSetup();
533     return false;
534   }
535
536   // Notify services that login UI is now active.
537   GetLoginUIService()->SetLoginUI(this);
538
539   ProfileSyncService* service = GetSyncService();
540   if (service)
541     service->SetSetupInProgress(true);
542
543   return true;
544 }
545
546 void SyncSetupHandler::DisplaySpinner() {
547   configuring_sync_ = true;
548   base::StringValue page("spinner");
549   base::DictionaryValue args;
550
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);
557
558   web_ui()->CallJavascriptFunction(
559       "SyncSetupOverlay.showSyncSetupPage", page, args);
560 }
561
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();
567
568   // Do not listen to sync startup events.
569   sync_startup_tracker_.reset();
570
571   base::StringValue page("timeout");
572   base::DictionaryValue args;
573   web_ui()->CallJavascriptFunction(
574       "SyncSetupOverlay.showSyncSetupPage", page, args);
575 }
576
577 void SyncSetupHandler::OnDidClosePage(const base::ListValue* args) {
578   CloseSyncSetup();
579 }
580
581 void SyncSetupHandler::SyncStartupFailed() {
582   // Stop a timer to handle timeout in waiting for checking network connection.
583   backend_start_timer_.reset();
584
585   // Just close the sync overlay (the idea is that the base settings page will
586   // display the current error.)
587   CloseUI();
588 }
589
590 void SyncSetupHandler::SyncStartupCompleted() {
591   ProfileSyncService* service = GetSyncService();
592   DCHECK(service->sync_initialized());
593
594   // Stop a timer to handle timeout in waiting for checking network connection.
595   backend_start_timer_.reset();
596
597   DisplayConfigureSync(true, false);
598 }
599
600 Profile* SyncSetupHandler::GetProfile() const {
601   return Profile::FromWebUI(web_ui());
602 }
603
604 ProfileSyncService* SyncSetupHandler::GetSyncService() const {
605   Profile* profile = GetProfile();
606   return profile->IsSyncAccessible() ?
607       ProfileSyncServiceFactory::GetForProfile(GetProfile()) : NULL;
608 }
609
610 void SyncSetupHandler::HandleConfigure(const base::ListValue* args) {
611   DCHECK(!sync_startup_tracker_);
612   std::string json;
613   if (!args->GetString(0, &json)) {
614     NOTREACHED() << "Could not read JSON argument";
615     return;
616   }
617   if (json.empty()) {
618     NOTREACHED();
619     return;
620   }
621
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.
626     NOTREACHED();
627     return;
628   }
629
630   // Start configuring the ProfileSyncService using the configuration passed
631   // to us from the JS layer.
632   ProfileSyncService* service = GetSyncService();
633
634   // If the sync engine has shutdown for some reason, just close the sync
635   // dialog.
636   if (!service || !service->sync_initialized()) {
637     CloseUI();
638     return;
639   }
640
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);
648     CloseUI();
649     service->StopSyncingPermanently();
650     service->SetSetupInProgress(false);
651     return;
652   }
653
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();
660
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
670       // data types.
671       passphrase_failed =
672           !service->SetDecryptionPassphrase(configuration.passphrase);
673     } else {
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);
684       }
685     }
686   }
687
688   bool user_was_prompted_for_passphrase =
689       service->IsPassphraseRequiredForDecryption();
690   service->OnUserChoseDatatypes(configuration.sync_everything,
691                                 configuration.data_types);
692
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);
713   } else {
714     // No passphrase is required from the user so mark the configuration as
715     // complete and close the sync setup overlay.
716     ConfigureSyncDone();
717   }
718
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);
726 }
727
728 void SyncSetupHandler::HandleShowSetupUI(const base::ListValue* args) {
729   ProfileSyncService* service = GetSyncService();
730   DCHECK(service);
731
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";
741     CloseUI();
742     return;
743   }
744
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()) {
751     CloseUI();
752   }
753
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())
757     OpenSyncSetup();
758 }
759
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();
766 }
767 #endif
768
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());
774   OpenSyncSetup();
775 }
776
777 void SyncSetupHandler::HandleStopSyncing(const base::ListValue* args) {
778   if (GetSyncService())
779     ProfileSyncService::SyncEvent(ProfileSyncService::STOP_FROM_OPTIONS);
780   SigninManagerFactory::GetForProfile(GetProfile())->SignOut();
781 }
782 #endif
783
784 void SyncSetupHandler::HandleCloseTimeout(const base::ListValue* args) {
785   CloseSyncSetup();
786 }
787
788 void SyncSetupHandler::CloseSyncSetup() {
789   // Stop a timer to handle timeout in waiting for checking network connection.
790   backend_start_timer_.reset();
791
792   // Clear the sync startup tracker, since the setup wizard is being closed.
793   sync_startup_tracker_.reset();
794
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);
804       }
805
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.
811       if (sync_service) {
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
816         // initial setup.
817         // TODO(rsimha): Revisit this for M30. See http://crbug.com/252049.
818         if (sync_service->FirstSetupInProgress())
819           SigninManagerFactory::GetForProfile(GetProfile())->SignOut();
820 #endif
821       }
822     }
823
824     GetLoginUIService()->LoginUIClosed(this);
825   }
826
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.
830   if (sync_service)
831     sync_service->SetSetupInProgress(false);
832
833   configuring_sync_ = false;
834 }
835
836 void SyncSetupHandler::OpenSyncSetup() {
837   if (!PrepareSyncSetup())
838     return;
839
840   // There are several different UI flows that can bring the user here:
841   // 1) Signin promo.
842   // 2) Normal signin through settings page (GetAuthenticatedUsername() is
843   //    empty).
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
848   //    logged in.
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());
855
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.
862     CloseUI();
863     DisplayGaiaLogin();
864     return;
865   }
866 #endif
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";
870     CloseUI();
871     return;
872   }
873
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);
878 }
879
880 void SyncSetupHandler::OpenConfigureSync() {
881   if (!PrepareSyncSetup())
882     return;
883
884   DisplayConfigureSync(true, false);
885 }
886
887 void SyncSetupHandler::FocusUI() {
888   DCHECK(IsActiveLogin());
889   WebContents* web_contents = web_ui()->GetWebContents();
890   web_contents->GetDelegate()->ActivateContents(web_contents);
891 }
892
893 void SyncSetupHandler::CloseUI() {
894   CloseSyncSetup();
895   base::StringValue page("done");
896   web_ui()->CallJavascriptFunction(
897       "SyncSetupOverlay.showSyncSetupPage", page);
898 }
899
900 bool SyncSetupHandler::IsExistingWizardPresent() {
901   LoginUIService* service = GetLoginUIService();
902   DCHECK(service);
903   return service->current_login_ui() != NULL;
904 }
905
906 bool SyncSetupHandler::FocusExistingWizardIfPresent() {
907   if (!IsExistingWizardPresent())
908     return false;
909
910   LoginUIService* service = GetLoginUIService();
911   DCHECK(service);
912   service->current_login_ui()->FocusUI();
913   return true;
914 }
915
916 LoginUIService* SyncSetupHandler::GetLoginUIService() const {
917   return LoginUIServiceFactory::GetForProfile(GetProfile());
918 }