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/translate/translate_infobar_delegate.h" 6 7 #include <algorithm> 8 9 #include "base/i18n/string_compare.h" 10 #include "base/metrics/histogram.h" 11 #include "base/prefs/pref_service.h" 12 #include "chrome/browser/browser_process.h" 13 #include "chrome/browser/infobars/infobar.h" 14 #include "chrome/browser/infobars/infobar_service.h" 15 #include "chrome/browser/profiles/profile.h" 16 #include "chrome/browser/translate/translate_accept_languages.h" 17 #include "chrome/browser/translate/translate_manager.h" 18 #include "chrome/browser/translate/translate_tab_helper.h" 19 #include "components/translate/common/translate_constants.h" 20 #include "content/public/browser/navigation_details.h" 21 #include "content/public/browser/navigation_entry.h" 22 #include "content/public/browser/web_contents.h" 23 #include "grit/generated_resources.h" 24 #include "grit/theme_resources.h" 25 #include "third_party/icu/source/i18n/unicode/coll.h" 26 #include "ui/base/l10n/l10n_util.h" 27 28 29 const size_t TranslateInfoBarDelegate::kNoIndex = TranslateUIDelegate::NO_INDEX; 30 31 TranslateInfoBarDelegate::~TranslateInfoBarDelegate() { 32 } 33 34 // static 35 void TranslateInfoBarDelegate::Create( 36 bool replace_existing_infobar, 37 content::WebContents* web_contents, 38 Type infobar_type, 39 const std::string& original_language, 40 const std::string& target_language, 41 TranslateErrors::Type error_type, 42 PrefService* prefs, 43 const ShortcutConfiguration& shortcut_config) { 44 // Check preconditions. 45 if (infobar_type != TRANSLATION_ERROR) { 46 DCHECK(TranslateManager::IsSupportedLanguage(target_language)); 47 if (!TranslateManager::IsSupportedLanguage(original_language)) { 48 // The original language can only be "unknown" for the "translating" 49 // infobar, which is the case when the user started a translation from the 50 // context menu. 51 DCHECK(infobar_type == TRANSLATING || infobar_type == AFTER_TRANSLATE); 52 DCHECK_EQ(translate::kUnknownLanguageCode, original_language); 53 } 54 } 55 56 // Do not create the after translate infobar if we are auto translating. 57 if ((infobar_type == TranslateInfoBarDelegate::AFTER_TRANSLATE) || 58 (infobar_type == TranslateInfoBarDelegate::TRANSLATING)) { 59 TranslateTabHelper* translate_tab_helper = 60 TranslateTabHelper::FromWebContents(web_contents); 61 if (!translate_tab_helper || 62 translate_tab_helper->language_state().InTranslateNavigation()) 63 return; 64 } 65 66 // Find any existing translate infobar delegate. 67 InfoBar* old_infobar = NULL; 68 InfoBarService* infobar_service = 69 InfoBarService::FromWebContents(web_contents); 70 TranslateInfoBarDelegate* old_delegate = NULL; 71 for (size_t i = 0; i < infobar_service->infobar_count(); ++i) { 72 old_infobar = infobar_service->infobar_at(i); 73 old_delegate = old_infobar->delegate()->AsTranslateInfoBarDelegate(); 74 if (old_delegate) { 75 if (!replace_existing_infobar) 76 return; 77 break; 78 } 79 } 80 81 // Add the new delegate. 82 scoped_ptr<InfoBar> infobar(CreateInfoBar( 83 scoped_ptr<TranslateInfoBarDelegate>(new TranslateInfoBarDelegate( 84 web_contents, infobar_type, old_delegate, original_language, 85 target_language, error_type, prefs, shortcut_config)))); 86 if (old_delegate) 87 infobar_service->ReplaceInfoBar(old_infobar, infobar.Pass()); 88 else 89 infobar_service->AddInfoBar(infobar.Pass()); 90 } 91 92 93 void TranslateInfoBarDelegate::UpdateOriginalLanguageIndex( 94 size_t language_index) { 95 ui_delegate_.UpdateOriginalLanguageIndex(language_index); 96 } 97 98 void TranslateInfoBarDelegate::UpdateTargetLanguageIndex( 99 size_t language_index) { 100 ui_delegate_.UpdateTargetLanguageIndex(language_index); 101 } 102 103 void TranslateInfoBarDelegate::Translate() { 104 ui_delegate_.Translate(); 105 } 106 107 void TranslateInfoBarDelegate::RevertTranslation() { 108 ui_delegate_.RevertTranslation(); 109 infobar()->RemoveSelf(); 110 } 111 112 void TranslateInfoBarDelegate::ReportLanguageDetectionError() { 113 TranslateManager::GetInstance()->ReportLanguageDetectionError( 114 web_contents()); 115 } 116 117 void TranslateInfoBarDelegate::TranslationDeclined() { 118 ui_delegate_.TranslationDeclined(false); 119 } 120 121 bool TranslateInfoBarDelegate::IsTranslatableLanguageByPrefs() { 122 Profile* profile = 123 Profile::FromBrowserContext(web_contents()->GetBrowserContext()); 124 Profile* original_profile = profile->GetOriginalProfile(); 125 return TranslatePrefs::CanTranslateLanguage(original_profile, 126 original_language_code()); 127 } 128 129 void TranslateInfoBarDelegate::ToggleTranslatableLanguageByPrefs() { 130 if (ui_delegate_.IsLanguageBlocked()) { 131 ui_delegate_.SetLanguageBlocked(false); 132 } else { 133 ui_delegate_.SetLanguageBlocked(true); 134 infobar()->RemoveSelf(); 135 } 136 } 137 138 bool TranslateInfoBarDelegate::IsSiteBlacklisted() { 139 return ui_delegate_.IsSiteBlacklisted(); 140 } 141 142 void TranslateInfoBarDelegate::ToggleSiteBlacklist() { 143 if (ui_delegate_.IsSiteBlacklisted()) { 144 ui_delegate_.SetSiteBlacklist(false); 145 } else { 146 ui_delegate_.SetSiteBlacklist(true); 147 infobar()->RemoveSelf(); 148 } 149 } 150 151 bool TranslateInfoBarDelegate::ShouldAlwaysTranslate() { 152 return ui_delegate_.ShouldAlwaysTranslate(); 153 } 154 155 void TranslateInfoBarDelegate::ToggleAlwaysTranslate() { 156 ui_delegate_.SetAlwaysTranslate(!ui_delegate_.ShouldAlwaysTranslate()); 157 } 158 159 void TranslateInfoBarDelegate::AlwaysTranslatePageLanguage() { 160 DCHECK(!ui_delegate_.ShouldAlwaysTranslate()); 161 ui_delegate_.SetAlwaysTranslate(true); 162 Translate(); 163 } 164 165 void TranslateInfoBarDelegate::NeverTranslatePageLanguage() { 166 DCHECK(!ui_delegate_.IsLanguageBlocked()); 167 ui_delegate_.SetLanguageBlocked(true); 168 infobar()->RemoveSelf(); 169 } 170 171 base::string16 TranslateInfoBarDelegate::GetMessageInfoBarText() { 172 if (infobar_type_ == TRANSLATING) { 173 base::string16 target_language_name = 174 language_name_at(target_language_index()); 175 return l10n_util::GetStringFUTF16(IDS_TRANSLATE_INFOBAR_TRANSLATING_TO, 176 target_language_name); 177 } 178 179 DCHECK_EQ(TRANSLATION_ERROR, infobar_type_); 180 UMA_HISTOGRAM_ENUMERATION("Translate.ShowErrorInfobar", 181 error_type_, 182 TranslateErrors::TRANSLATE_ERROR_MAX); 183 ui_delegate_.OnErrorShown(error_type_); 184 switch (error_type_) { 185 case TranslateErrors::NETWORK: 186 return l10n_util::GetStringUTF16( 187 IDS_TRANSLATE_INFOBAR_ERROR_CANT_CONNECT); 188 case TranslateErrors::INITIALIZATION_ERROR: 189 case TranslateErrors::TRANSLATION_ERROR: 190 return l10n_util::GetStringUTF16( 191 IDS_TRANSLATE_INFOBAR_ERROR_CANT_TRANSLATE); 192 case TranslateErrors::UNKNOWN_LANGUAGE: 193 return l10n_util::GetStringUTF16( 194 IDS_TRANSLATE_INFOBAR_UNKNOWN_PAGE_LANGUAGE); 195 case TranslateErrors::UNSUPPORTED_LANGUAGE: 196 return l10n_util::GetStringFUTF16( 197 IDS_TRANSLATE_INFOBAR_UNSUPPORTED_PAGE_LANGUAGE, 198 language_name_at(target_language_index())); 199 case TranslateErrors::IDENTICAL_LANGUAGES: 200 return l10n_util::GetStringFUTF16( 201 IDS_TRANSLATE_INFOBAR_ERROR_SAME_LANGUAGE, 202 language_name_at(target_language_index())); 203 default: 204 NOTREACHED(); 205 return base::string16(); 206 } 207 } 208 209 base::string16 TranslateInfoBarDelegate::GetMessageInfoBarButtonText() { 210 if (infobar_type_ != TRANSLATION_ERROR) { 211 DCHECK_EQ(TRANSLATING, infobar_type_); 212 } else if ((error_type_ != TranslateErrors::IDENTICAL_LANGUAGES) && 213 (error_type_ != TranslateErrors::UNKNOWN_LANGUAGE)) { 214 return l10n_util::GetStringUTF16( 215 (error_type_ == TranslateErrors::UNSUPPORTED_LANGUAGE) ? 216 IDS_TRANSLATE_INFOBAR_REVERT : IDS_TRANSLATE_INFOBAR_RETRY); 217 } 218 return base::string16(); 219 } 220 221 void TranslateInfoBarDelegate::MessageInfoBarButtonPressed() { 222 DCHECK_EQ(TRANSLATION_ERROR, infobar_type_); 223 if (error_type_ == TranslateErrors::UNSUPPORTED_LANGUAGE) { 224 RevertTranslation(); 225 return; 226 } 227 // This is the "Try again..." case. 228 TranslateManager::GetInstance()->TranslatePage( 229 web_contents(), original_language_code(), target_language_code()); 230 } 231 232 bool TranslateInfoBarDelegate::ShouldShowMessageInfoBarButton() { 233 return !GetMessageInfoBarButtonText().empty(); 234 } 235 236 bool TranslateInfoBarDelegate::ShouldShowNeverTranslateShortcut() { 237 DCHECK_EQ(BEFORE_TRANSLATE, infobar_type_); 238 return !web_contents()->GetBrowserContext()->IsOffTheRecord() && 239 (prefs_.GetTranslationDeniedCount(original_language_code()) >= 240 shortcut_config_.never_translate_min_count); 241 } 242 243 bool TranslateInfoBarDelegate::ShouldShowAlwaysTranslateShortcut() { 244 DCHECK_EQ(BEFORE_TRANSLATE, infobar_type_); 245 return !web_contents()->GetBrowserContext()->IsOffTheRecord() && 246 (prefs_.GetTranslationAcceptedCount(original_language_code()) >= 247 shortcut_config_.always_translate_min_count); 248 } 249 250 // static 251 base::string16 TranslateInfoBarDelegate::GetLanguageDisplayableName( 252 const std::string& language_code) { 253 return l10n_util::GetDisplayNameForLocale( 254 language_code, g_browser_process->GetApplicationLocale(), true); 255 } 256 257 // static 258 void TranslateInfoBarDelegate::GetAfterTranslateStrings( 259 std::vector<base::string16>* strings, 260 bool* swap_languages, 261 bool autodetermined_source_language) { 262 DCHECK(strings); 263 264 if (autodetermined_source_language) { 265 size_t offset; 266 base::string16 text = l10n_util::GetStringFUTF16( 267 IDS_TRANSLATE_INFOBAR_AFTER_MESSAGE_AUTODETERMINED_SOURCE_LANGUAGE, 268 base::string16(), 269 &offset); 270 271 strings->push_back(text.substr(0, offset)); 272 strings->push_back(text.substr(offset)); 273 return; 274 } 275 DCHECK(swap_languages); 276 277 std::vector<size_t> offsets; 278 base::string16 text = l10n_util::GetStringFUTF16( 279 IDS_TRANSLATE_INFOBAR_AFTER_MESSAGE, base::string16(), base::string16(), 280 &offsets); 281 DCHECK_EQ(2U, offsets.size()); 282 283 *swap_languages = (offsets[0] > offsets[1]); 284 if (*swap_languages) 285 std::swap(offsets[0], offsets[1]); 286 287 strings->push_back(text.substr(0, offsets[0])); 288 strings->push_back(text.substr(offsets[0], offsets[1] - offsets[0])); 289 strings->push_back(text.substr(offsets[1])); 290 } 291 292 TranslateInfoBarDelegate::TranslateInfoBarDelegate( 293 content::WebContents* web_contents, 294 Type infobar_type, 295 TranslateInfoBarDelegate* old_delegate, 296 const std::string& original_language, 297 const std::string& target_language, 298 TranslateErrors::Type error_type, 299 PrefService* prefs, 300 ShortcutConfiguration shortcut_config) 301 : InfoBarDelegate(), 302 infobar_type_(infobar_type), 303 background_animation_(NONE), 304 ui_delegate_(web_contents, original_language, target_language), 305 error_type_(error_type), 306 prefs_(prefs), 307 shortcut_config_(shortcut_config) { 308 DCHECK_NE((infobar_type_ == TRANSLATION_ERROR), 309 (error_type_ == TranslateErrors::NONE)); 310 311 if (old_delegate && (old_delegate->is_error() != is_error())) 312 background_animation_ = is_error() ? NORMAL_TO_ERROR : ERROR_TO_NORMAL; 313 } 314 315 // TranslateInfoBarDelegate::CreateInfoBar() is implemented in platform-specific 316 // files. 317 318 void TranslateInfoBarDelegate::InfoBarDismissed() { 319 if (infobar_type_ != BEFORE_TRANSLATE) 320 return; 321 322 // The user closed the infobar without clicking the translate button. 323 TranslationDeclined(); 324 UMA_HISTOGRAM_BOOLEAN("Translate.DeclineTranslateCloseInfobar", true); 325 } 326 327 int TranslateInfoBarDelegate::GetIconID() const { 328 return IDR_INFOBAR_TRANSLATE; 329 } 330 331 InfoBarDelegate::Type TranslateInfoBarDelegate::GetInfoBarType() const { 332 return PAGE_ACTION_TYPE; 333 } 334 335 bool TranslateInfoBarDelegate::ShouldExpire( 336 const content::LoadCommittedDetails& details) const { 337 // Note: we allow closing this infobar even if the main frame navigation 338 // was programmatic and not initiated by the user - crbug.com/70261 . 339 if (!details.is_navigation_to_different_page() && !details.is_main_frame) 340 return false; 341 342 return InfoBarDelegate::ShouldExpireInternal(details); 343 } 344 345 TranslateInfoBarDelegate* 346 TranslateInfoBarDelegate::AsTranslateInfoBarDelegate() { 347 return this; 348 } 349