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