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