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