• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SyncConfigInfo()84 SyncConfigInfo::SyncConfigInfo()
85     : encrypt_all(false),
86       sync_everything(false),
87       sync_nothing(false),
88       passphrase_is_gaia(false) {
89 }
90 
~SyncConfigInfo()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 
GetSelectableTypeNameMap()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 
GetConfiguration(const std::string & json,SyncConfigInfo * config)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 
SyncSetupHandler(ProfileManager * profile_manager)188 SyncSetupHandler::SyncSetupHandler(ProfileManager* profile_manager)
189     : configuring_sync_(false),
190       profile_manager_(profile_manager) {
191 }
192 
~SyncSetupHandler()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 
GetLocalizedValues(base::DictionaryValue * localized_strings)202 void SyncSetupHandler::GetLocalizedValues(
203     base::DictionaryValue* localized_strings) {
204   GetStaticLocalizedValues(localized_strings, web_ui());
205 }
206 
GetStaticLocalizedValues(base::DictionaryValue * localized_strings,content::WebUI * web_ui)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 
DisplayConfigureSync(bool show_advanced,bool passphrase_failed)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 
ConfigureSyncDone()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 
IsActiveLogin() const458 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 
RegisterMessages()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)
DisplayGaiaLogin()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 
DisplayGaiaLoginInNewTabOrWindow()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 
PrepareSyncSetup()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 
DisplaySpinner()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
DisplayTimeout()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 
OnDidClosePage(const base::ListValue * args)603 void SyncSetupHandler::OnDidClosePage(const base::ListValue* args) {
604   CloseSyncSetup();
605 }
606 
SyncStartupFailed()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 
SyncStartupCompleted()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 
GetProfile() const626 Profile* SyncSetupHandler::GetProfile() const {
627   return Profile::FromWebUI(web_ui());
628 }
629 
GetSyncService() const630 ProfileSyncService* SyncSetupHandler::GetSyncService() const {
631   Profile* profile = GetProfile();
632   return profile->IsSyncAccessible() ?
633       ProfileSyncServiceFactory::GetForProfile(GetProfile()) : NULL;
634 }
635 
HandleConfigure(const base::ListValue * args)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 
HandleShowSetupUI(const base::ListValue * args)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.
HandleDoSignOutOnAuthError(const base::ListValue * args)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)
HandleStartSignin(const base::ListValue * args)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 
HandleStopSyncing(const base::ListValue * args)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 
HandleCloseTimeout(const base::ListValue * args)820 void SyncSetupHandler::HandleCloseTimeout(const base::ListValue* args) {
821   CloseSyncSetup();
822 }
823 
CloseSyncSetup()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 
OpenSyncSetup()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 
OpenConfigureSync()920 void SyncSetupHandler::OpenConfigureSync() {
921   if (!PrepareSyncSetup())
922     return;
923 
924   DisplayConfigureSync(true, false);
925 }
926 
FocusUI()927 void SyncSetupHandler::FocusUI() {
928   DCHECK(IsActiveLogin());
929   WebContents* web_contents = web_ui()->GetWebContents();
930   web_contents->GetDelegate()->ActivateContents(web_contents);
931 }
932 
CloseUI()933 void SyncSetupHandler::CloseUI() {
934   CloseSyncSetup();
935   base::StringValue page("done");
936   web_ui()->CallJavascriptFunction(
937       "SyncSetupOverlay.showSyncSetupPage", page);
938 }
939 
IsExistingWizardPresent()940 bool SyncSetupHandler::IsExistingWizardPresent() {
941   LoginUIService* service = GetLoginUIService();
942   DCHECK(service);
943   return service->current_login_ui() != NULL;
944 }
945 
FocusExistingWizardIfPresent()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 
GetLoginUIService() const956 LoginUIService* SyncSetupHandler::GetLoginUIService() const {
957   return LoginUIServiceFactory::GetForProfile(GetProfile());
958 }
959