Home | History | Annotate | Download | only in browser
      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