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