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 {
     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