Home | History | Annotate | Download | only in sync
      1 // Copyright (c) 2011 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/command_line.h"
      8 #include "base/i18n/number_formatting.h"
      9 #include "base/i18n/time_formatting.h"
     10 #include "base/string_util.h"
     11 #include "base/utf_string_conversions.h"
     12 #include "chrome/browser/profiles/profile.h"
     13 #include "chrome/browser/sync/profile_sync_service.h"
     14 #include "chrome/browser/ui/browser.h"
     15 #include "chrome/browser/ui/browser_window.h"
     16 #include "chrome/browser/ui/options/options_window.h"
     17 #include "chrome/common/chrome_switches.h"
     18 #include "chrome/common/net/gaia/google_service_auth_error.h"
     19 #include "chrome/common/url_constants.h"
     20 #include "grit/browser_resources.h"
     21 #include "grit/chromium_strings.h"
     22 #include "grit/generated_resources.h"
     23 #include "ui/base/l10n/l10n_util.h"
     24 #include "ui/base/resource/resource_bundle.h"
     25 
     26 typedef GoogleServiceAuthError AuthError;
     27 
     28 namespace sync_ui_util {
     29 
     30 namespace {
     31 
     32 // Given an authentication state, this helper function returns the appropriate
     33 // status message and, if necessary, the text that should appear in the
     34 // re-login link.
     35 void GetStatusLabelsForAuthError(const AuthError& auth_error,
     36     ProfileSyncService* service, string16* status_label,
     37     string16* link_label) {
     38   if (link_label)
     39     link_label->assign(l10n_util::GetStringUTF16(IDS_SYNC_RELOGIN_LINK_LABEL));
     40   if (auth_error.state() == AuthError::INVALID_GAIA_CREDENTIALS ||
     41       auth_error.state() == AuthError::ACCOUNT_DELETED ||
     42       auth_error.state() == AuthError::ACCOUNT_DISABLED) {
     43     // If the user name is empty then the first login failed, otherwise the
     44     // credentials are out-of-date.
     45     if (service->GetAuthenticatedUsername().empty())
     46       status_label->assign(
     47           l10n_util::GetStringUTF16(IDS_SYNC_INVALID_USER_CREDENTIALS));
     48     else
     49       status_label->assign(
     50           l10n_util::GetStringUTF16(IDS_SYNC_LOGIN_INFO_OUT_OF_DATE));
     51   } else if (auth_error.state() == AuthError::SERVICE_UNAVAILABLE) {
     52     DCHECK(service->GetAuthenticatedUsername().empty());
     53     status_label->assign(
     54         l10n_util::GetStringUTF16(IDS_SYNC_SERVICE_UNAVAILABLE));
     55   } else if (auth_error.state() == AuthError::CONNECTION_FAILED) {
     56     // Note that there is little the user can do if the server is not
     57     // reachable. Since attempting to re-connect is done automatically by
     58     // the Syncer, we do not show the (re)login link.
     59     status_label->assign(
     60         l10n_util::GetStringFUTF16(IDS_SYNC_SERVER_IS_UNREACHABLE,
     61                               l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
     62     if (link_label)
     63       link_label->clear();
     64   } else {
     65     status_label->assign(l10n_util::GetStringUTF16(IDS_SYNC_ERROR_SIGNING_IN));
     66   }
     67 }
     68 
     69 // Returns the message that should be displayed when the user is authenticated
     70 // and can connect to the sync server. If the user hasn't yet authenticated, an
     71 // empty string is returned.
     72 string16 GetSyncedStateStatusLabel(ProfileSyncService* service) {
     73   string16 label;
     74   string16 user_name(service->GetAuthenticatedUsername());
     75   if (user_name.empty())
     76     return label;
     77 
     78   const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
     79   return l10n_util::GetStringFUTF16(
     80       browser_command_line.HasSwitch(switches::kMultiProfiles) ?
     81           IDS_PROFILES_SYNCED_TO_USER_WITH_TIME :
     82           IDS_SYNC_ACCOUNT_SYNCED_TO_USER_WITH_TIME,
     83       user_name,
     84       service->GetLastSyncedTimeString());
     85 }
     86 
     87 // TODO(akalin): Write unit tests for these three functions below.
     88 
     89 // status_label and link_label must either be both NULL or both non-NULL.
     90 MessageType GetStatusInfo(ProfileSyncService* service,
     91                           string16* status_label,
     92                           string16* link_label) {
     93   DCHECK_EQ(status_label == NULL, link_label == NULL);
     94 
     95   MessageType result_type(SYNCED);
     96 
     97   if (!service) {
     98     return PRE_SYNCED;
     99   }
    100 
    101   if (service->HasSyncSetupCompleted()) {
    102     ProfileSyncService::Status status(service->QueryDetailedSyncStatus());
    103     const AuthError& auth_error = service->GetAuthError();
    104 
    105     // Either show auth error information with a link to re-login, auth in prog,
    106     // or note that everything is OK with the last synced time.
    107     if (status.authenticated && !service->observed_passphrase_required()) {
    108       // Everything is peachy.
    109       if (status_label) {
    110         status_label->assign(GetSyncedStateStatusLabel(service));
    111       }
    112       DCHECK_EQ(auth_error.state(), AuthError::NONE);
    113     } else if (service->UIShouldDepictAuthInProgress()) {
    114       if (status_label) {
    115         status_label->assign(
    116           l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL));
    117       }
    118       result_type = PRE_SYNCED;
    119     } else if (service->observed_passphrase_required()) {
    120       if (service->passphrase_required_for_decryption()) {
    121         // NOT first machine.
    122         // Show a link ("needs attention"), but still indicate the
    123         // current synced status.  Return SYNC_PROMO so that
    124         // the configure link will still be shown.
    125         if (status_label && link_label) {
    126           status_label->assign(GetSyncedStateStatusLabel(service));
    127           link_label->assign(
    128               l10n_util::GetStringUTF16(IDS_SYNC_PASSWORD_SYNC_ATTENTION));
    129         }
    130         result_type = SYNC_PROMO;
    131       } else {
    132         // First machine.  Don't show promotion, just show everything
    133         // normal.
    134         if (status_label)
    135           status_label->assign(GetSyncedStateStatusLabel(service));
    136       }
    137     } else if (auth_error.state() != AuthError::NONE) {
    138       if (status_label && link_label) {
    139         GetStatusLabelsForAuthError(auth_error, service,
    140                                     status_label, link_label);
    141       }
    142       result_type = SYNC_ERROR;
    143     }
    144   } else {
    145     // Either show auth error information with a link to re-login, auth in prog,
    146     // or provide a link to continue with setup.
    147     result_type = PRE_SYNCED;
    148     if (service->SetupInProgress()) {
    149       ProfileSyncService::Status status(service->QueryDetailedSyncStatus());
    150       const AuthError& auth_error = service->GetAuthError();
    151       if (status_label) {
    152         status_label->assign(
    153             l10n_util::GetStringUTF16(IDS_SYNC_NTP_SETUP_IN_PROGRESS));
    154       }
    155       if (service->UIShouldDepictAuthInProgress()) {
    156         if (status_label) {
    157           status_label->assign(
    158               l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL));
    159         }
    160       } else if (auth_error.state() != AuthError::NONE) {
    161         if (status_label) {
    162           status_label->clear();
    163           GetStatusLabelsForAuthError(auth_error, service, status_label, NULL);
    164         }
    165         result_type = SYNC_ERROR;
    166       } else if (!status.authenticated) {
    167         if (status_label) {
    168           status_label->assign(
    169               l10n_util::GetStringUTF16(IDS_SYNC_ACCOUNT_DETAILS_NOT_ENTERED));
    170         }
    171       }
    172     } else if (service->unrecoverable_error_detected()) {
    173       result_type = SYNC_ERROR;
    174       if (status_label) {
    175         status_label->assign(l10n_util::GetStringUTF16(IDS_SYNC_SETUP_ERROR));
    176       }
    177     }
    178   }
    179   return result_type;
    180 }
    181 
    182 // Returns the status info for use on the new tab page, where we want slightly
    183 // different information than in the settings panel.
    184 MessageType GetStatusInfoForNewTabPage(ProfileSyncService* service,
    185                                        string16* status_label,
    186                                        string16* link_label) {
    187   DCHECK(status_label);
    188   DCHECK(link_label);
    189 
    190   if (service->HasSyncSetupCompleted() &&
    191       service->observed_passphrase_required()) {
    192     if (!service->passphrase_required_for_decryption()) {
    193       // First machine migrating to passwords.  Show as a promotion.
    194       if (status_label && link_label) {
    195         status_label->assign(
    196             l10n_util::GetStringFUTF16(
    197                 IDS_SYNC_NTP_PASSWORD_PROMO,
    198                 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
    199         link_label->assign(
    200             l10n_util::GetStringUTF16(IDS_SYNC_NTP_PASSWORD_ENABLE));
    201       }
    202       return SYNC_PROMO;
    203     } else {
    204       // NOT first machine.
    205       // Show a link and present as an error ("needs attention").
    206       if (status_label && link_label) {
    207         status_label->assign(string16());
    208         link_label->assign(
    209             l10n_util::GetStringUTF16(IDS_SYNC_CONFIGURE_ENCRYPTION));
    210       }
    211       return SYNC_ERROR;
    212     }
    213   }
    214 
    215   // Fallback to default.
    216   return GetStatusInfo(service, status_label, link_label);
    217 }
    218 
    219 }  // namespace
    220 
    221 MessageType GetStatusLabels(ProfileSyncService* service,
    222                             string16* status_label,
    223                             string16* link_label) {
    224   DCHECK(status_label);
    225   DCHECK(link_label);
    226   return sync_ui_util::GetStatusInfo(service, status_label, link_label);
    227 }
    228 
    229 MessageType GetStatusLabelsForNewTabPage(ProfileSyncService* service,
    230                                          string16* status_label,
    231                                          string16* link_label) {
    232   DCHECK(status_label);
    233   DCHECK(link_label);
    234   return sync_ui_util::GetStatusInfoForNewTabPage(
    235       service, status_label, link_label);
    236 }
    237 
    238 MessageType GetStatus(ProfileSyncService* service) {
    239   return sync_ui_util::GetStatusInfo(service, NULL, NULL);
    240 }
    241 
    242 bool ShouldShowSyncErrorButton(ProfileSyncService* service) {
    243   return service &&
    244          ((!service->IsManaged() &&
    245            service->HasSyncSetupCompleted()) &&
    246          (GetStatus(service) == sync_ui_util::SYNC_ERROR ||
    247           service->observed_passphrase_required()));
    248 }
    249 
    250 string16 GetSyncMenuLabel(ProfileSyncService* service) {
    251   MessageType type = GetStatus(service);
    252 
    253   if (type == sync_ui_util::SYNCED)
    254     return l10n_util::GetStringUTF16(IDS_SYNC_MENU_SYNCED_LABEL);
    255   else if (type == sync_ui_util::SYNC_ERROR)
    256     return l10n_util::GetStringUTF16(IDS_SYNC_MENU_SYNC_ERROR_LABEL);
    257   else
    258     return l10n_util::GetStringUTF16(IDS_SYNC_START_SYNC_BUTTON_LABEL);
    259 }
    260 
    261 void OpenSyncMyBookmarksDialog(Profile* profile,
    262                                Browser* browser,
    263                                ProfileSyncService::SyncEventCodes code) {
    264   ProfileSyncService* service =
    265     profile->GetOriginalProfile()->GetProfileSyncService();
    266   if (!service || !service->IsSyncEnabled()) {
    267     LOG(DFATAL) << "OpenSyncMyBookmarksDialog called with sync disabled";
    268     return;
    269   }
    270 
    271   if (service->HasSyncSetupCompleted()) {
    272     bool create_window = browser == NULL;
    273     if (create_window)
    274       browser = Browser::Create(profile);
    275     browser->ShowOptionsTab(chrome::kPersonalOptionsSubPage);
    276     if (create_window)
    277       browser->window()->Show();
    278   } else {
    279     service->ShowLoginDialog(NULL);
    280     ProfileSyncService::SyncEvent(code);  // UMA stats
    281   }
    282 }
    283 
    284 void AddBoolSyncDetail(ListValue* details,
    285                        const std::string& stat_name,
    286                        bool stat_value) {
    287   DictionaryValue* val = new DictionaryValue;
    288   val->SetString("stat_name", stat_name);
    289   val->SetBoolean("stat_value", stat_value);
    290   details->Append(val);
    291 }
    292 
    293 void AddIntSyncDetail(ListValue* details, const std::string& stat_name,
    294                       int64 stat_value) {
    295   DictionaryValue* val = new DictionaryValue;
    296   val->SetString("stat_name", stat_name);
    297   val->SetString("stat_value", base::FormatNumber(stat_value));
    298   details->Append(val);
    299 }
    300 
    301 string16 ConstructTime(int64 time_in_int) {
    302   base::Time time = base::Time::FromInternalValue(time_in_int);
    303 
    304   // If time is null the format function returns a time in 1969.
    305   if (time.is_null())
    306     return string16();
    307   return base::TimeFormatFriendlyDateAndTime(time);
    308 }
    309 
    310 std::string MakeSyncAuthErrorText(
    311     const GoogleServiceAuthError::State& state) {
    312   switch (state) {
    313     case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS:
    314     case GoogleServiceAuthError::ACCOUNT_DELETED:
    315     case GoogleServiceAuthError::ACCOUNT_DISABLED:
    316     case GoogleServiceAuthError::SERVICE_UNAVAILABLE:
    317       return "INVALID_GAIA_CREDENTIALS";
    318     case GoogleServiceAuthError::USER_NOT_SIGNED_UP:
    319       return "USER_NOT_SIGNED_UP";
    320     case GoogleServiceAuthError::CONNECTION_FAILED:
    321       return "CONNECTION_FAILED";
    322     default:
    323       return std::string();
    324   }
    325 }
    326 
    327 void ConstructAboutInformation(ProfileSyncService* service,
    328                                DictionaryValue* strings) {
    329   CHECK(strings);
    330   if (!service || !service->HasSyncSetupCompleted()) {
    331     strings->SetString("summary", "SYNC DISABLED");
    332   } else {
    333     sync_api::SyncManager::Status full_status(
    334         service->QueryDetailedSyncStatus());
    335 
    336     strings->SetString("service_url", service->sync_service_url().spec());
    337     strings->SetString("summary",
    338                        ProfileSyncService::BuildSyncStatusSummaryText(
    339                        full_status.summary));
    340 
    341     strings->Set("authenticated",
    342                  new FundamentalValue(full_status.authenticated));
    343     strings->SetString("auth_problem",
    344                        sync_ui_util::MakeSyncAuthErrorText(
    345                        service->GetAuthError().state()));
    346 
    347     strings->SetString("time_since_sync", service->GetLastSyncedTimeString());
    348 
    349     ListValue* details = new ListValue();
    350     strings->Set("details", details);
    351     sync_ui_util::AddBoolSyncDetail(details,
    352                                     "Server Up",
    353                                     full_status.server_up);
    354     sync_ui_util::AddBoolSyncDetail(details,
    355                                     "Server Reachable",
    356                                     full_status.server_reachable);
    357     sync_ui_util::AddBoolSyncDetail(details,
    358                                     "Server Broken",
    359                                     full_status.server_broken);
    360     sync_ui_util::AddBoolSyncDetail(details,
    361                                     "Notifications Enabled",
    362                                     full_status.notifications_enabled);
    363     sync_ui_util::AddIntSyncDetail(details,
    364                                    "Notifications Received",
    365                                    full_status.notifications_received);
    366     sync_ui_util::AddIntSyncDetail(details,
    367                                    "Notifications Sent",
    368                                    full_status.notifications_sent);
    369     sync_ui_util::AddIntSyncDetail(details,
    370                                    "Unsynced Count",
    371                                    full_status.unsynced_count);
    372     sync_ui_util::AddIntSyncDetail(details,
    373                                    "Conflicting Count",
    374                                    full_status.conflicting_count);
    375     sync_ui_util::AddBoolSyncDetail(details, "Syncing", full_status.syncing);
    376     sync_ui_util::AddBoolSyncDetail(details,
    377                                     "Initial Sync Ended",
    378                                     full_status.initial_sync_ended);
    379     sync_ui_util::AddBoolSyncDetail(details,
    380                                     "Syncer Stuck",
    381                                     full_status.syncer_stuck);
    382     sync_ui_util::AddIntSyncDetail(details,
    383                                    "Updates Available",
    384                                    full_status.updates_available);
    385     sync_ui_util::AddIntSyncDetail(details,
    386                                    "Updates Downloaded (All)",
    387                                    full_status.updates_received);
    388     sync_ui_util::AddIntSyncDetail(details,
    389                                    "Updates Downloaded (Tombstones)",
    390                                    full_status.tombstone_updates_received);
    391     sync_ui_util::AddBoolSyncDetail(details,
    392                                     "Disk Full",
    393                                     full_status.disk_full);
    394     sync_ui_util::AddIntSyncDetail(details,
    395                                    "Max Consecutive Errors",
    396                                    full_status.max_consecutive_errors);
    397 
    398     if (service->unrecoverable_error_detected()) {
    399       strings->Set("unrecoverable_error_detected", new FundamentalValue(true));
    400       strings->SetString("unrecoverable_error_message",
    401                          service->unrecoverable_error_message());
    402       tracked_objects::Location loc(service->unrecoverable_error_location());
    403       std::string location_str;
    404       loc.Write(true, true, &location_str);
    405       strings->SetString("unrecoverable_error_location", location_str);
    406     } else if (!service->sync_initialized()) {
    407       strings->SetString("summary", "Sync not yet initialized");
    408     } else {
    409       browser_sync::ModelSafeRoutingInfo routes;
    410       service->GetModelSafeRoutingInfo(&routes);
    411       ListValue* routing_info = new ListValue();
    412       strings->Set("routing_info", routing_info);
    413       browser_sync::ModelSafeRoutingInfo::const_iterator it = routes.begin();
    414       for (; it != routes.end(); ++it) {
    415         DictionaryValue* val = new DictionaryValue;
    416         val->SetString("model_type", ModelTypeToString(it->first));
    417         val->SetString("group", ModelSafeGroupToString(it->second));
    418         routing_info->Append(val);
    419       }
    420 
    421       sync_ui_util::AddBoolSyncDetail(details,
    422           "Autofill Migrated",
    423           service->GetAutofillMigrationState() ==
    424           syncable::MIGRATED);
    425       syncable::AutofillMigrationDebugInfo info =
    426           service->GetAutofillMigrationDebugInfo();
    427 
    428       sync_ui_util::AddIntSyncDetail(details,
    429                                      "Bookmarks created during migration",
    430                                      info.bookmarks_added_during_migration);
    431       sync_ui_util::AddIntSyncDetail(details,
    432           "Autofill entries created during migration",
    433           info.autofill_entries_added_during_migration);
    434       sync_ui_util::AddIntSyncDetail(details,
    435           "Autofill Profiles created during migration",
    436           info.autofill_profile_added_during_migration);
    437 
    438       DictionaryValue* val = new DictionaryValue;
    439       val->SetString("stat_name", "Autofill Migration Time");
    440       val->SetString("stat_value", ConstructTime(info.autofill_migration_time));
    441       details->Append(val);
    442     }
    443   }
    444 }
    445 
    446 }  // namespace sync_ui_util
    447