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