1 // Copyright 2014 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 "components/translate/core/browser/translate_infobar_delegate.h" 6 7 #include <algorithm> 8 9 #include "base/i18n/string_compare.h" 10 #include "base/metrics/histogram.h" 11 #include "components/infobars/core/infobar.h" 12 #include "components/infobars/core/infobar_manager.h" 13 #include "components/translate/core/browser/language_state.h" 14 #include "components/translate/core/browser/translate_accept_languages.h" 15 #include "components/translate/core/browser/translate_client.h" 16 #include "components/translate/core/browser/translate_download_manager.h" 17 #include "components/translate/core/browser/translate_driver.h" 18 #include "components/translate/core/browser/translate_manager.h" 19 #include "components/translate/core/common/translate_constants.h" 20 #include "grit/components_strings.h" 21 #include "ui/base/l10n/l10n_util.h" 22 23 namespace translate { 24 25 namespace { 26 27 // Counts used to decide whether infobars should be shown. 28 // Android and iOS implementations do not offer a drop down (for space reasons), 29 // so we are more aggressive about showing the shortcut to never translate. 30 // The "Always Translate" option is always shown on iOS and Android. 31 #if defined(OS_ANDROID) 32 const int kAlwaysTranslateMinCount = 1; 33 const int kNeverTranslateMinCount = 1; 34 #elif defined(OS_IOS) 35 // The iOS implementation, like the Android implementation, shows the "Never 36 // translate" infobar after two denials. There is an offset of one because on 37 // Android the last event is not counted. 38 const int kAlwaysTranslateMinCount = 1; 39 const int kNeverTranslateMinCount = 2; 40 #else 41 const int kAlwaysTranslateMinCount = 3; 42 const int kNeverTranslateMinCount = 3; 43 #endif 44 45 } // namespace 46 47 const size_t TranslateInfoBarDelegate::kNoIndex = TranslateUIDelegate::kNoIndex; 48 49 TranslateInfoBarDelegate::~TranslateInfoBarDelegate() { 50 } 51 52 // static 53 void TranslateInfoBarDelegate::Create( 54 bool replace_existing_infobar, 55 const base::WeakPtr<TranslateManager>& translate_manager, 56 infobars::InfoBarManager* infobar_manager, 57 bool is_off_the_record, 58 translate::TranslateStep step, 59 const std::string& original_language, 60 const std::string& target_language, 61 TranslateErrors::Type error_type, 62 bool triggered_from_menu) { 63 DCHECK(translate_manager); 64 DCHECK(infobar_manager); 65 66 // Check preconditions. 67 if (step != translate::TRANSLATE_STEP_TRANSLATE_ERROR) { 68 DCHECK(TranslateDownloadManager::IsSupportedLanguage(target_language)); 69 if (!TranslateDownloadManager::IsSupportedLanguage(original_language)) { 70 // The original language can only be "unknown" for the "translating" 71 // infobar, which is the case when the user started a translation from the 72 // context menu. 73 DCHECK(step == translate::TRANSLATE_STEP_TRANSLATING || 74 step == translate::TRANSLATE_STEP_AFTER_TRANSLATE); 75 DCHECK_EQ(translate::kUnknownLanguageCode, original_language); 76 } 77 } 78 79 // Do not create the after translate infobar if we are auto translating. 80 TranslateClient* translate_client = translate_manager->translate_client(); 81 if (((step == translate::TRANSLATE_STEP_AFTER_TRANSLATE) || 82 (step == translate::TRANSLATE_STEP_TRANSLATING)) && 83 translate_manager->GetLanguageState().InTranslateNavigation()) { 84 return; 85 } 86 87 // Find any existing translate infobar delegate. 88 infobars::InfoBar* old_infobar = NULL; 89 TranslateInfoBarDelegate* old_delegate = NULL; 90 for (size_t i = 0; i < infobar_manager->infobar_count(); ++i) { 91 old_infobar = infobar_manager->infobar_at(i); 92 old_delegate = old_infobar->delegate()->AsTranslateInfoBarDelegate(); 93 if (old_delegate) { 94 if (!replace_existing_infobar) 95 return; 96 break; 97 } 98 } 99 100 // Add the new delegate. 101 scoped_ptr<infobars::InfoBar> infobar(translate_client->CreateInfoBar( 102 scoped_ptr<TranslateInfoBarDelegate>(new TranslateInfoBarDelegate( 103 translate_manager, is_off_the_record, step, old_delegate, 104 original_language, target_language, error_type, 105 triggered_from_menu)))); 106 if (old_delegate) 107 infobar_manager->ReplaceInfoBar(old_infobar, infobar.Pass()); 108 else 109 infobar_manager->AddInfoBar(infobar.Pass()); 110 } 111 112 void TranslateInfoBarDelegate::UpdateOriginalLanguageIndex( 113 size_t language_index) { 114 ui_delegate_.UpdateOriginalLanguageIndex(language_index); 115 } 116 117 void TranslateInfoBarDelegate::UpdateTargetLanguageIndex( 118 size_t language_index) { 119 ui_delegate_.UpdateTargetLanguageIndex(language_index); 120 } 121 122 void TranslateInfoBarDelegate::Translate() { 123 ui_delegate_.Translate(); 124 } 125 126 void TranslateInfoBarDelegate::RevertTranslation() { 127 ui_delegate_.RevertTranslation(); 128 infobar()->RemoveSelf(); 129 } 130 131 void TranslateInfoBarDelegate::ReportLanguageDetectionError() { 132 if (translate_manager_) 133 translate_manager_->ReportLanguageDetectionError(); 134 } 135 136 void TranslateInfoBarDelegate::TranslationDeclined() { 137 ui_delegate_.TranslationDeclined(false); 138 } 139 140 bool TranslateInfoBarDelegate::IsTranslatableLanguageByPrefs() { 141 TranslateClient* client = translate_manager_->translate_client(); 142 scoped_ptr<TranslatePrefs> translate_prefs(client->GetTranslatePrefs()); 143 TranslateAcceptLanguages* accept_languages = 144 client->GetTranslateAcceptLanguages(); 145 return translate_prefs->CanTranslateLanguage(accept_languages, 146 original_language_code()); 147 } 148 149 void TranslateInfoBarDelegate::ToggleTranslatableLanguageByPrefs() { 150 if (ui_delegate_.IsLanguageBlocked()) { 151 ui_delegate_.SetLanguageBlocked(false); 152 } else { 153 ui_delegate_.SetLanguageBlocked(true); 154 infobar()->RemoveSelf(); 155 } 156 } 157 158 bool TranslateInfoBarDelegate::IsSiteBlacklisted() { 159 return ui_delegate_.IsSiteBlacklisted(); 160 } 161 162 void TranslateInfoBarDelegate::ToggleSiteBlacklist() { 163 if (ui_delegate_.IsSiteBlacklisted()) { 164 ui_delegate_.SetSiteBlacklist(false); 165 } else { 166 ui_delegate_.SetSiteBlacklist(true); 167 infobar()->RemoveSelf(); 168 } 169 } 170 171 bool TranslateInfoBarDelegate::ShouldAlwaysTranslate() { 172 return ui_delegate_.ShouldAlwaysTranslate(); 173 } 174 175 void TranslateInfoBarDelegate::ToggleAlwaysTranslate() { 176 ui_delegate_.SetAlwaysTranslate(!ui_delegate_.ShouldAlwaysTranslate()); 177 } 178 179 void TranslateInfoBarDelegate::AlwaysTranslatePageLanguage() { 180 DCHECK(!ui_delegate_.ShouldAlwaysTranslate()); 181 ui_delegate_.SetAlwaysTranslate(true); 182 Translate(); 183 } 184 185 void TranslateInfoBarDelegate::NeverTranslatePageLanguage() { 186 DCHECK(!ui_delegate_.IsLanguageBlocked()); 187 ui_delegate_.SetLanguageBlocked(true); 188 infobar()->RemoveSelf(); 189 } 190 191 base::string16 TranslateInfoBarDelegate::GetMessageInfoBarText() { 192 if (step_ == translate::TRANSLATE_STEP_TRANSLATING) { 193 base::string16 target_language_name = 194 language_name_at(target_language_index()); 195 return l10n_util::GetStringFUTF16(IDS_TRANSLATE_INFOBAR_TRANSLATING_TO, 196 target_language_name); 197 } 198 199 DCHECK_EQ(translate::TRANSLATE_STEP_TRANSLATE_ERROR, step_); 200 UMA_HISTOGRAM_ENUMERATION("Translate.ShowErrorInfobar", 201 error_type_, 202 TranslateErrors::TRANSLATE_ERROR_MAX); 203 ui_delegate_.OnErrorShown(error_type_); 204 switch (error_type_) { 205 case TranslateErrors::NETWORK: 206 return l10n_util::GetStringUTF16( 207 IDS_TRANSLATE_INFOBAR_ERROR_CANT_CONNECT); 208 case TranslateErrors::INITIALIZATION_ERROR: 209 case TranslateErrors::TRANSLATION_ERROR: 210 return l10n_util::GetStringUTF16( 211 IDS_TRANSLATE_INFOBAR_ERROR_CANT_TRANSLATE); 212 case TranslateErrors::UNKNOWN_LANGUAGE: 213 return l10n_util::GetStringUTF16( 214 IDS_TRANSLATE_INFOBAR_UNKNOWN_PAGE_LANGUAGE); 215 case TranslateErrors::UNSUPPORTED_LANGUAGE: 216 return l10n_util::GetStringFUTF16( 217 IDS_TRANSLATE_INFOBAR_UNSUPPORTED_PAGE_LANGUAGE, 218 language_name_at(target_language_index())); 219 case TranslateErrors::IDENTICAL_LANGUAGES: 220 return l10n_util::GetStringFUTF16( 221 IDS_TRANSLATE_INFOBAR_ERROR_SAME_LANGUAGE, 222 language_name_at(target_language_index())); 223 default: 224 NOTREACHED(); 225 return base::string16(); 226 } 227 } 228 229 base::string16 TranslateInfoBarDelegate::GetMessageInfoBarButtonText() { 230 if (step_ != translate::TRANSLATE_STEP_TRANSLATE_ERROR) { 231 DCHECK_EQ(translate::TRANSLATE_STEP_TRANSLATING, step_); 232 } else if ((error_type_ != TranslateErrors::IDENTICAL_LANGUAGES) && 233 (error_type_ != TranslateErrors::UNKNOWN_LANGUAGE)) { 234 return l10n_util::GetStringUTF16( 235 (error_type_ == TranslateErrors::UNSUPPORTED_LANGUAGE) ? 236 IDS_TRANSLATE_INFOBAR_REVERT : IDS_TRANSLATE_INFOBAR_RETRY); 237 } 238 return base::string16(); 239 } 240 241 void TranslateInfoBarDelegate::MessageInfoBarButtonPressed() { 242 DCHECK_EQ(translate::TRANSLATE_STEP_TRANSLATE_ERROR, step_); 243 if (error_type_ == TranslateErrors::UNSUPPORTED_LANGUAGE) { 244 RevertTranslation(); 245 return; 246 } 247 // This is the "Try again..." case. 248 DCHECK(translate_manager_); 249 translate_manager_->TranslatePage( 250 original_language_code(), target_language_code(), false); 251 } 252 253 bool TranslateInfoBarDelegate::ShouldShowMessageInfoBarButton() { 254 return !GetMessageInfoBarButtonText().empty(); 255 } 256 257 bool TranslateInfoBarDelegate::ShouldShowNeverTranslateShortcut() { 258 DCHECK_EQ(translate::TRANSLATE_STEP_BEFORE_TRANSLATE, step_); 259 return !is_off_the_record_ && 260 (prefs_->GetTranslationDeniedCount(original_language_code()) >= 261 kNeverTranslateMinCount); 262 } 263 264 bool TranslateInfoBarDelegate::ShouldShowAlwaysTranslateShortcut() { 265 DCHECK_EQ(translate::TRANSLATE_STEP_BEFORE_TRANSLATE, step_); 266 return !is_off_the_record_ && 267 (prefs_->GetTranslationAcceptedCount(original_language_code()) >= 268 kAlwaysTranslateMinCount); 269 } 270 271 // static 272 void TranslateInfoBarDelegate::GetAfterTranslateStrings( 273 std::vector<base::string16>* strings, 274 bool* swap_languages, 275 bool autodetermined_source_language) { 276 DCHECK(strings); 277 278 if (autodetermined_source_language) { 279 size_t offset; 280 base::string16 text = l10n_util::GetStringFUTF16( 281 IDS_TRANSLATE_INFOBAR_AFTER_MESSAGE_AUTODETERMINED_SOURCE_LANGUAGE, 282 base::string16(), 283 &offset); 284 285 strings->push_back(text.substr(0, offset)); 286 strings->push_back(text.substr(offset)); 287 return; 288 } 289 DCHECK(swap_languages); 290 291 std::vector<size_t> offsets; 292 base::string16 text = l10n_util::GetStringFUTF16( 293 IDS_TRANSLATE_INFOBAR_AFTER_MESSAGE, base::string16(), base::string16(), 294 &offsets); 295 DCHECK_EQ(2U, offsets.size()); 296 297 *swap_languages = (offsets[0] > offsets[1]); 298 if (*swap_languages) 299 std::swap(offsets[0], offsets[1]); 300 301 strings->push_back(text.substr(0, offsets[0])); 302 strings->push_back(text.substr(offsets[0], offsets[1] - offsets[0])); 303 strings->push_back(text.substr(offsets[1])); 304 } 305 306 TranslateDriver* TranslateInfoBarDelegate::GetTranslateDriver() { 307 if (!translate_manager_) 308 return NULL; 309 310 return translate_manager_->translate_client()->GetTranslateDriver(); 311 } 312 313 TranslateInfoBarDelegate::TranslateInfoBarDelegate( 314 const base::WeakPtr<TranslateManager>& translate_manager, 315 bool is_off_the_record, 316 translate::TranslateStep step, 317 TranslateInfoBarDelegate* old_delegate, 318 const std::string& original_language, 319 const std::string& target_language, 320 TranslateErrors::Type error_type, 321 bool triggered_from_menu) 322 : infobars::InfoBarDelegate(), 323 is_off_the_record_(is_off_the_record), 324 step_(step), 325 background_animation_(NONE), 326 ui_delegate_(translate_manager, original_language, target_language), 327 translate_manager_(translate_manager), 328 error_type_(error_type), 329 prefs_(translate_manager->translate_client()->GetTranslatePrefs()), 330 triggered_from_menu_(triggered_from_menu) { 331 DCHECK_NE((step_ == translate::TRANSLATE_STEP_TRANSLATE_ERROR), 332 (error_type_ == TranslateErrors::NONE)); 333 DCHECK(translate_manager_); 334 335 if (old_delegate && (old_delegate->is_error() != is_error())) 336 background_animation_ = is_error() ? NORMAL_TO_ERROR : ERROR_TO_NORMAL; 337 } 338 339 void TranslateInfoBarDelegate::InfoBarDismissed() { 340 if (step_ != translate::TRANSLATE_STEP_BEFORE_TRANSLATE) 341 return; 342 343 // The user closed the infobar without clicking the translate button. 344 TranslationDeclined(); 345 UMA_HISTOGRAM_BOOLEAN("Translate.DeclineTranslateCloseInfobar", true); 346 } 347 348 int TranslateInfoBarDelegate::GetIconID() const { 349 return translate_manager_->translate_client()->GetInfobarIconID(); 350 } 351 352 infobars::InfoBarDelegate::Type TranslateInfoBarDelegate::GetInfoBarType() 353 const { 354 return PAGE_ACTION_TYPE; 355 } 356 357 bool TranslateInfoBarDelegate::ShouldExpire( 358 const NavigationDetails& details) const { 359 // Note: we allow closing this infobar even if the main frame navigation 360 // was programmatic and not initiated by the user - crbug.com/70261 . 361 if (!details.is_navigation_to_different_page && !details.is_main_frame) 362 return false; 363 364 return infobars::InfoBarDelegate::ShouldExpireInternal(details); 365 } 366 367 TranslateInfoBarDelegate* 368 TranslateInfoBarDelegate::AsTranslateInfoBarDelegate() { 369 return this; 370 } 371 372 } // namespace translate 373