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