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