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