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