1 // Copyright (c) 2013 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/signin/signin_global_error.h" 6 7 #include "base/logging.h" 8 #include "base/prefs/pref_service.h" 9 #include "chrome/app/chrome_command_ids.h" 10 #include "chrome/browser/profiles/profile.h" 11 #include "chrome/browser/signin/profile_oauth2_token_service.h" 12 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 13 #include "chrome/browser/signin/signin_manager.h" 14 #include "chrome/browser/signin/signin_manager_factory.h" 15 #include "chrome/browser/ui/browser_commands.h" 16 #include "chrome/browser/ui/chrome_pages.h" 17 #include "chrome/browser/ui/global_error/global_error_service.h" 18 #include "chrome/browser/ui/global_error/global_error_service_factory.h" 19 #include "chrome/browser/ui/webui/signin/login_ui_service.h" 20 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h" 21 #include "chrome/common/pref_names.h" 22 #include "chrome/common/url_constants.h" 23 #include "grit/chromium_strings.h" 24 #include "grit/generated_resources.h" 25 #include "ui/base/l10n/l10n_util.h" 26 27 SigninGlobalError::SigninGlobalError(Profile* profile) 28 : auth_error_(GoogleServiceAuthError::AuthErrorNone()), profile_(profile) { 29 } 30 31 SigninGlobalError::~SigninGlobalError() { 32 DCHECK(provider_set_.empty()) 33 << "All AuthStatusProviders should be unregistered before" 34 << " SigninManager::Shutdown() is called"; 35 } 36 37 void SigninGlobalError::AddProvider(const AuthStatusProvider* provider) { 38 DCHECK(provider_set_.find(provider) == provider_set_.end()) 39 << "Adding same AuthStatusProvider multiple times"; 40 provider_set_.insert(provider); 41 AuthStatusChanged(); 42 } 43 44 void SigninGlobalError::RemoveProvider(const AuthStatusProvider* provider) { 45 std::set<const AuthStatusProvider*>::iterator iter = 46 provider_set_.find(provider); 47 DCHECK(iter != provider_set_.end()) 48 << "Removing provider that was never added"; 49 provider_set_.erase(iter); 50 AuthStatusChanged(); 51 } 52 53 SigninGlobalError::AuthStatusProvider::AuthStatusProvider() { 54 } 55 56 SigninGlobalError::AuthStatusProvider::~AuthStatusProvider() { 57 } 58 59 void SigninGlobalError::AuthStatusChanged() { 60 // Walk all of the status providers and collect any error. 61 GoogleServiceAuthError current_error(GoogleServiceAuthError::AuthErrorNone()); 62 for (std::set<const AuthStatusProvider*>::const_iterator it = 63 provider_set_.begin(); it != provider_set_.end(); ++it) { 64 current_error = (*it)->GetAuthStatus(); 65 // Break out if any provider reports an error (ignoring ordinary network 66 // errors, which are not surfaced to the user). This logic may eventually 67 // need to be extended to prioritize different auth errors, but for now 68 // all auth errors are treated the same. 69 if (current_error.state() != GoogleServiceAuthError::NONE && 70 current_error.state() != GoogleServiceAuthError::CONNECTION_FAILED) { 71 break; 72 } 73 } 74 if (current_error.state() != auth_error_.state()) { 75 auth_error_ = current_error; 76 GlobalErrorServiceFactory::GetForProfile(profile_)->NotifyErrorsChanged( 77 this); 78 } 79 } 80 81 bool SigninGlobalError::HasMenuItem() { 82 return !MenuItemLabel().empty(); 83 } 84 85 int SigninGlobalError::MenuItemCommandID() { 86 return IDC_SHOW_SIGNIN_ERROR; 87 } 88 89 string16 SigninGlobalError::MenuItemLabel() { 90 std::string username; 91 SigninManagerBase* signin_manager = 92 SigninManagerFactory::GetForProfileIfExists(profile_); 93 if (signin_manager) 94 username = signin_manager->GetAuthenticatedUsername(); 95 if (username.empty() || 96 auth_error_.state() == GoogleServiceAuthError::NONE || 97 auth_error_.state() == GoogleServiceAuthError::CONNECTION_FAILED) { 98 // If the user isn't signed in, or there's no auth error worth elevating to 99 // the user, don't display any menu item. 100 return string16(); 101 } else { 102 // There's an auth error the user should know about - notify the user. 103 return l10n_util::GetStringUTF16(IDS_SYNC_SIGN_IN_ERROR_WRENCH_MENU_ITEM); 104 } 105 } 106 107 void SigninGlobalError::ExecuteMenuItem(Browser* browser) { 108 #if defined(OS_CHROMEOS) 109 if (auth_error_.state() != GoogleServiceAuthError::NONE) { 110 DLOG(INFO) << "Signing out the user to fix a sync error."; 111 // TODO(beng): seems like this could just call chrome::AttemptUserExit(). 112 chrome::ExecuteCommand(browser, IDC_EXIT); 113 return; 114 } 115 #endif 116 117 // Global errors don't show up in the wrench menu on android. 118 #if !defined(OS_ANDROID) 119 LoginUIService* login_ui = LoginUIServiceFactory::GetForProfile(profile_); 120 if (login_ui->current_login_ui()) { 121 login_ui->current_login_ui()->FocusUI(); 122 return; 123 } 124 // Need to navigate to the settings page and display the UI. 125 chrome::ShowSettingsSubPage(browser, chrome::kSyncSetupSubPage); 126 #endif 127 } 128 129 bool SigninGlobalError::HasBubbleView() { 130 return !GetBubbleViewMessages().empty(); 131 } 132 133 string16 SigninGlobalError::GetBubbleViewTitle() { 134 return l10n_util::GetStringUTF16(IDS_SIGNIN_ERROR_BUBBLE_VIEW_TITLE); 135 } 136 137 std::vector<string16> SigninGlobalError::GetBubbleViewMessages() { 138 std::vector<string16> messages; 139 140 // If the user isn't signed in, no need to display an error bubble. 141 SigninManagerBase* signin_manager = 142 SigninManagerFactory::GetForProfileIfExists(profile_); 143 if (signin_manager) { 144 std::string username = signin_manager->GetAuthenticatedUsername(); 145 if (username.empty()) 146 return messages; 147 } 148 149 switch (auth_error_.state()) { 150 // In the case of no error, or a simple network error, don't bother 151 // displaying a popup bubble. 152 case GoogleServiceAuthError::CONNECTION_FAILED: 153 case GoogleServiceAuthError::NONE: 154 return messages; 155 156 // User credentials are invalid (bad acct, etc). 157 case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS: 158 case GoogleServiceAuthError::ACCOUNT_DELETED: 159 case GoogleServiceAuthError::ACCOUNT_DISABLED: 160 messages.push_back(l10n_util::GetStringFUTF16( 161 IDS_SYNC_SIGN_IN_ERROR_BUBBLE_VIEW_MESSAGE, 162 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); 163 break; 164 165 // Sync service is not available for this account's domain. 166 case GoogleServiceAuthError::SERVICE_UNAVAILABLE: 167 messages.push_back(l10n_util::GetStringFUTF16( 168 IDS_SYNC_UNAVAILABLE_ERROR_BUBBLE_VIEW_MESSAGE, 169 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); 170 break; 171 172 // Generic message for "other" errors. 173 default: 174 messages.push_back(l10n_util::GetStringFUTF16( 175 IDS_SYNC_OTHER_SIGN_IN_ERROR_BUBBLE_VIEW_MESSAGE, 176 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); 177 } 178 return messages; 179 } 180 181 string16 SigninGlobalError::GetBubbleViewAcceptButtonLabel() { 182 // If the service is unavailable, don't give the user the option to try 183 // signing in again. 184 if (auth_error_.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE) { 185 return l10n_util::GetStringUTF16( 186 IDS_SYNC_UNAVAILABLE_ERROR_BUBBLE_VIEW_ACCEPT); 187 } else { 188 return l10n_util::GetStringUTF16(IDS_SYNC_SIGN_IN_ERROR_BUBBLE_VIEW_ACCEPT); 189 } 190 } 191 192 string16 SigninGlobalError::GetBubbleViewCancelButtonLabel() { 193 return string16(); 194 } 195 196 void SigninGlobalError::OnBubbleViewDidClose(Browser* browser) { 197 } 198 199 void SigninGlobalError::BubbleViewAcceptButtonPressed(Browser* browser) { 200 ExecuteMenuItem(browser); 201 } 202 203 void SigninGlobalError::BubbleViewCancelButtonPressed(Browser* browser) { 204 NOTREACHED(); 205 } 206 207 // static 208 SigninGlobalError* SigninGlobalError::GetForProfile(Profile* profile) { 209 return ProfileOAuth2TokenServiceFactory::GetForProfile(profile)-> 210 signin_global_error(); 211 } 212