Home | History | Annotate | Download | only in sync
      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/sync/sync_ui_util.h"
      6 
      7 #include "base/i18n/number_formatting.h"
      8 #include "base/i18n/time_formatting.h"
      9 #include "base/prefs/pref_service.h"
     10 #include "base/strings/string_util.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "chrome/browser/profiles/profile.h"
     13 #include "chrome/browser/profiles/profile_manager.h"
     14 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
     15 #include "chrome/browser/signin/signin_ui_util.h"
     16 #include "chrome/browser/sync/profile_sync_service.h"
     17 #include "chrome/browser/sync/profile_sync_service_factory.h"
     18 #include "chrome/browser/ui/browser.h"
     19 #include "chrome/browser/ui/browser_window.h"
     20 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
     21 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
     22 #include "chrome/common/chrome_switches.h"
     23 #include "chrome/common/pref_names.h"
     24 #include "chrome/common/url_constants.h"
     25 #include "chrome/grit/chromium_strings.h"
     26 #include "chrome/grit/generated_resources.h"
     27 #include "chrome/grit/locale_settings.h"
     28 #include "components/signin/core/browser/profile_oauth2_token_service.h"
     29 #include "components/signin/core/browser/signin_error_controller.h"
     30 #include "components/signin/core/browser/signin_manager_base.h"
     31 #include "google_apis/gaia/google_service_auth_error.h"
     32 #include "sync/internal_api/public/base/model_type.h"
     33 #include "sync/internal_api/public/sessions/sync_session_snapshot.h"
     34 #include "sync/protocol/proto_enum_conversions.h"
     35 #include "sync/protocol/sync_protocol_error.h"
     36 #include "ui/base/l10n/l10n_util.h"
     37 
     38 #if defined(OS_CHROMEOS)
     39 #include "components/user_manager/user_manager.h"
     40 #endif  // defined(OS_CHROMEOS)
     41 
     42 typedef GoogleServiceAuthError AuthError;
     43 
     44 namespace sync_ui_util {
     45 
     46 namespace {
     47 
     48 // Returns the message that should be displayed when the user is authenticated
     49 // and can connect to the sync server. If the user hasn't yet authenticated, an
     50 // empty string is returned.
     51 base::string16 GetSyncedStateStatusLabel(ProfileSyncService* service,
     52                                          const SigninManagerBase& signin,
     53                                          StatusLabelStyle style) {
     54   std::string user_display_name = signin.GetAuthenticatedUsername();
     55 
     56 #if defined(OS_CHROMEOS)
     57   if (user_manager::UserManager::IsInitialized()) {
     58     // On CrOS user email is sanitized and then passed to the signin manager.
     59     // Original email (containing dots) is stored as "display email".
     60     user_display_name = user_manager::UserManager::Get()->GetUserDisplayEmail(
     61         user_display_name);
     62   }
     63 #endif  // defined(OS_CHROMEOS)
     64 
     65   base::string16 user_name = base::UTF8ToUTF16(user_display_name);
     66 
     67   if (!user_name.empty()) {
     68     if (!service || service->IsManaged()) {
     69       // User is signed in, but sync is disabled.
     70       return l10n_util::GetStringFUTF16(IDS_SIGNED_IN_WITH_SYNC_DISABLED,
     71                                         user_name);
     72     } else if (service->IsStartSuppressed()) {
     73       // User is signed in, but sync has been stopped.
     74       return l10n_util::GetStringFUTF16(IDS_SIGNED_IN_WITH_SYNC_SUPPRESSED,
     75                                         user_name);
     76     }
     77   }
     78 
     79   if (!service || !service->sync_initialized()) {
     80     // User is not signed in, or sync is still initializing.
     81     return base::string16();
     82   }
     83 
     84   DCHECK(!user_name.empty());
     85 
     86   // Message may also carry additional advice with an HTML link, if acceptable.
     87   switch (style) {
     88     case PLAIN_TEXT:
     89       return l10n_util::GetStringFUTF16(
     90           IDS_SYNC_ACCOUNT_SYNCING_TO_USER,
     91           user_name);
     92     case WITH_HTML:
     93       return l10n_util::GetStringFUTF16(
     94           IDS_SYNC_ACCOUNT_SYNCING_TO_USER_WITH_MANAGE_LINK,
     95           user_name,
     96           base::ASCIIToUTF16(chrome::kSyncGoogleDashboardURL));
     97     default:
     98       NOTREACHED();
     99       return NULL;
    100   }
    101 }
    102 
    103 void GetStatusForActionableError(
    104     const syncer::SyncProtocolError& error,
    105     base::string16* status_label) {
    106   DCHECK(status_label);
    107   switch (error.action) {
    108     case syncer::STOP_AND_RESTART_SYNC:
    109        status_label->assign(
    110            l10n_util::GetStringUTF16(IDS_SYNC_STOP_AND_RESTART_SYNC));
    111       break;
    112     case syncer::UPGRADE_CLIENT:
    113        status_label->assign(
    114            l10n_util::GetStringFUTF16(IDS_SYNC_UPGRADE_CLIENT,
    115                l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
    116       break;
    117     case syncer::ENABLE_SYNC_ON_ACCOUNT:
    118        status_label->assign(
    119            l10n_util::GetStringUTF16(IDS_SYNC_ENABLE_SYNC_ON_ACCOUNT));
    120     break;
    121     case syncer::CLEAR_USER_DATA_AND_RESYNC:
    122        status_label->assign(
    123            l10n_util::GetStringUTF16(IDS_SYNC_CLEAR_USER_DATA));
    124       break;
    125     default:
    126       NOTREACHED();
    127   }
    128 }
    129 
    130 // TODO(akalin): Write unit tests for these three functions below.
    131 
    132 // status_label and link_label must either be both NULL or both non-NULL.
    133 MessageType GetStatusInfo(ProfileSyncService* service,
    134                           const SigninManagerBase& signin,
    135                           StatusLabelStyle style,
    136                           base::string16* status_label,
    137                           base::string16* link_label) {
    138   DCHECK_EQ(status_label == NULL, link_label == NULL);
    139 
    140   MessageType result_type(SYNCED);
    141 
    142   if (!signin.IsAuthenticated())
    143     return PRE_SYNCED;
    144 
    145   if (!service || service->IsManaged() || service->HasSyncSetupCompleted() ||
    146       service->IsStartSuppressed()) {
    147     // The order or priority is going to be: 1. Unrecoverable errors.
    148     // 2. Auth errors. 3. Protocol errors. 4. Passphrase errors.
    149 
    150     if (service && service->HasUnrecoverableError()) {
    151       if (status_label) {
    152         status_label->assign(l10n_util::GetStringFUTF16(
    153             IDS_SYNC_STATUS_UNRECOVERABLE_ERROR,
    154             l10n_util::GetStringUTF16(IDS_SYNC_UNRECOVERABLE_ERROR_HELP_URL)));
    155       }
    156       return SYNC_ERROR;
    157     }
    158 
    159     // For auth errors first check if an auth is in progress.
    160     if (signin.AuthInProgress()) {
    161       if (status_label) {
    162         status_label->assign(
    163           l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL));
    164       }
    165       return PRE_SYNCED;
    166     }
    167 
    168     // Check for sync errors if the sync service is enabled.
    169     if (service) {
    170       // Since there is no auth in progress, check for an auth error first.
    171       AuthError auth_error =
    172           ProfileOAuth2TokenServiceFactory::GetForProfile(service->profile())->
    173               signin_error_controller()->auth_error();
    174       if (auth_error.state() != AuthError::NONE) {
    175         if (status_label && link_label)
    176           signin_ui_util::GetStatusLabelsForAuthError(
    177               service->profile(), signin, status_label, link_label);
    178         return SYNC_ERROR;
    179       }
    180 
    181       // We don't have an auth error. Check for an actionable error.
    182       ProfileSyncService::Status status;
    183       service->QueryDetailedSyncStatus(&status);
    184       if (ShouldShowActionOnUI(status.sync_protocol_error)) {
    185         if (status_label) {
    186           GetStatusForActionableError(status.sync_protocol_error,
    187                                       status_label);
    188         }
    189         return SYNC_ERROR;
    190       }
    191 
    192       // Check for a passphrase error.
    193       if (service->IsPassphraseRequired()) {
    194         if (service->IsPassphraseRequiredForDecryption()) {
    195           // TODO(lipalani) : Ask tim if this is still needed.
    196           // NOT first machine.
    197           // Show a link ("needs attention"), but still indicate the
    198           // current synced status.  Return SYNC_PROMO so that
    199           // the configure link will still be shown.
    200           if (status_label && link_label) {
    201             status_label->assign(GetSyncedStateStatusLabel(
    202                 service, signin, style));
    203             link_label->assign(
    204                 l10n_util::GetStringUTF16(IDS_SYNC_PASSWORD_SYNC_ATTENTION));
    205           }
    206           return SYNC_PROMO;
    207         }
    208       }
    209 
    210       // Check to see if sync has been disabled via the dasboard and needs to be
    211       // set up once again.
    212       if (service->IsStartSuppressed() &&
    213           status.sync_protocol_error.error_type == syncer::NOT_MY_BIRTHDAY) {
    214         if (status_label) {
    215           status_label->assign(GetSyncedStateStatusLabel(service,
    216                                                          signin,
    217                                                          style));
    218         }
    219         return PRE_SYNCED;
    220       }
    221     }
    222 
    223     // There is no error. Display "Last synced..." message.
    224     if (status_label)
    225       status_label->assign(GetSyncedStateStatusLabel(service, signin, style));
    226     return SYNCED;
    227   } else {
    228     // Either show auth error information with a link to re-login, auth in prog,
    229     // or provide a link to continue with setup.
    230     if (service->FirstSetupInProgress()) {
    231       result_type = PRE_SYNCED;
    232       ProfileSyncService::Status status;
    233       service->QueryDetailedSyncStatus(&status);
    234       AuthError auth_error =
    235           ProfileOAuth2TokenServiceFactory::GetForProfile(service->profile())->
    236               signin_error_controller()->auth_error();
    237       if (status_label) {
    238         status_label->assign(
    239             l10n_util::GetStringUTF16(IDS_SYNC_NTP_SETUP_IN_PROGRESS));
    240       }
    241       if (signin.AuthInProgress()) {
    242         if (status_label) {
    243           status_label->assign(
    244               l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL));
    245         }
    246       } else if (auth_error.state() != AuthError::NONE &&
    247                  auth_error.state() != AuthError::TWO_FACTOR) {
    248         if (status_label && link_label) {
    249           status_label->clear();
    250           signin_ui_util::GetStatusLabelsForAuthError(
    251               service->profile(), signin, status_label, link_label);
    252         }
    253         result_type = SYNC_ERROR;
    254       }
    255     } else if (service->HasUnrecoverableError()) {
    256       result_type = SYNC_ERROR;
    257       ProfileSyncService::Status status;
    258       service->QueryDetailedSyncStatus(&status);
    259       if (ShouldShowActionOnUI(status.sync_protocol_error)) {
    260         if (status_label) {
    261           GetStatusForActionableError(status.sync_protocol_error,
    262               status_label);
    263         }
    264       } else if (status_label) {
    265         status_label->assign(l10n_util::GetStringUTF16(IDS_SYNC_SETUP_ERROR));
    266       }
    267     } else if (signin.IsAuthenticated()) {
    268       // The user is signed in, but sync has been stopped.
    269       if (status_label) {
    270         base::string16 label = l10n_util::GetStringFUTF16(
    271             IDS_SIGNED_IN_WITH_SYNC_SUPPRESSED,
    272             base::UTF8ToUTF16(signin.GetAuthenticatedUsername()));
    273         status_label->assign(label);
    274         result_type = PRE_SYNCED;
    275       }
    276     }
    277   }
    278   return result_type;
    279 }
    280 
    281 // Returns the status info for use on the new tab page, where we want slightly
    282 // different information than in the settings panel.
    283 MessageType GetStatusInfoForNewTabPage(ProfileSyncService* service,
    284                                        const SigninManagerBase& signin,
    285                                        base::string16* status_label,
    286                                        base::string16* link_label) {
    287   DCHECK(status_label);
    288   DCHECK(link_label);
    289 
    290   if (service->HasSyncSetupCompleted() &&
    291       service->IsPassphraseRequired()) {
    292     if (service->passphrase_required_reason() == syncer::REASON_ENCRYPTION) {
    293       // First machine migrating to passwords.  Show as a promotion.
    294       if (status_label && link_label) {
    295         status_label->assign(
    296             l10n_util::GetStringFUTF16(
    297                 IDS_SYNC_NTP_PASSWORD_PROMO,
    298                 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
    299         link_label->assign(
    300             l10n_util::GetStringUTF16(IDS_SYNC_NTP_PASSWORD_ENABLE));
    301       }
    302       return SYNC_PROMO;
    303     } else {
    304       // NOT first machine.
    305       // Show a link and present as an error ("needs attention").
    306       if (status_label && link_label) {
    307         status_label->assign(base::string16());
    308         link_label->assign(
    309             l10n_util::GetStringUTF16(IDS_SYNC_CONFIGURE_ENCRYPTION));
    310       }
    311       return SYNC_ERROR;
    312     }
    313   }
    314 
    315   // Fallback to default.
    316   return GetStatusInfo(service, signin, WITH_HTML, status_label, link_label);
    317 }
    318 
    319 }  // namespace
    320 
    321 MessageType GetStatusLabels(ProfileSyncService* service,
    322                             const SigninManagerBase& signin,
    323                             StatusLabelStyle style,
    324                             base::string16* status_label,
    325                             base::string16* link_label) {
    326   DCHECK(status_label);
    327   DCHECK(link_label);
    328   return sync_ui_util::GetStatusInfo(
    329       service, signin, style, status_label, link_label);
    330 }
    331 
    332 MessageType GetStatusLabelsForNewTabPage(ProfileSyncService* service,
    333                                          const SigninManagerBase& signin,
    334                                          base::string16* status_label,
    335                                          base::string16* link_label) {
    336   DCHECK(status_label);
    337   DCHECK(link_label);
    338   return sync_ui_util::GetStatusInfoForNewTabPage(
    339       service, signin, status_label, link_label);
    340 }
    341 
    342 #if !defined(OS_CHROMEOS)
    343 void GetStatusLabelsForSyncGlobalError(const ProfileSyncService* service,
    344                                        base::string16* menu_label,
    345                                        base::string16* bubble_message,
    346                                        base::string16* bubble_accept_label) {
    347   DCHECK(menu_label);
    348   DCHECK(bubble_message);
    349   DCHECK(bubble_accept_label);
    350   *menu_label = base::string16();
    351   *bubble_message = base::string16();
    352   *bubble_accept_label = base::string16();
    353 
    354   // Only display an error if we've completed sync setup.
    355   if (!service->HasSyncSetupCompleted())
    356     return;
    357 
    358   // Display a passphrase error if we have one.
    359   if (service->IsPassphraseRequired() &&
    360       service->IsPassphraseRequiredForDecryption()) {
    361     // This is not the first machine so ask user to enter passphrase.
    362     *menu_label = l10n_util::GetStringUTF16(
    363         IDS_SYNC_PASSPHRASE_ERROR_WRENCH_MENU_ITEM);
    364     *bubble_message = l10n_util::GetStringUTF16(
    365         IDS_SYNC_PASSPHRASE_ERROR_BUBBLE_VIEW_MESSAGE);
    366     *bubble_accept_label = l10n_util::GetStringUTF16(
    367         IDS_SYNC_PASSPHRASE_ERROR_BUBBLE_VIEW_ACCEPT);
    368     return;
    369   }
    370 }
    371 #endif
    372 
    373 MessageType GetStatus(
    374     ProfileSyncService* service, const SigninManagerBase& signin) {
    375   return sync_ui_util::GetStatusInfo(service, signin, WITH_HTML, NULL, NULL);
    376 }
    377 
    378 base::string16 ConstructTime(int64 time_in_int) {
    379   base::Time time = base::Time::FromInternalValue(time_in_int);
    380 
    381   // If time is null the format function returns a time in 1969.
    382   if (time.is_null())
    383     return base::string16();
    384   return base::TimeFormatFriendlyDateAndTime(time);
    385 }
    386 
    387 }  // namespace sync_ui_util
    388