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