Home | History | Annotate | Download | only in translate
      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_service.h"
     14 #include "chrome/browser/profiles/profile.h"
     15 #include "chrome/browser/translate/translate_accept_languages.h"
     16 #include "chrome/browser/translate/translate_manager.h"
     17 #include "chrome/browser/translate/translate_tab_helper.h"
     18 #include "content/public/browser/navigation_details.h"
     19 #include "content/public/browser/navigation_entry.h"
     20 #include "content/public/browser/web_contents.h"
     21 #include "grit/generated_resources.h"
     22 #include "grit/theme_resources.h"
     23 #include "third_party/icu/source/i18n/unicode/coll.h"
     24 #include "ui/base/l10n/l10n_util.h"
     25 
     26 using content::NavigationEntry;
     27 
     28 namespace {
     29 
     30 const char kDeclineTranslate[] = "Translate.DeclineTranslate";
     31 const char kCloseInfobar[] = "Translate.DeclineTranslateCloseInfobar";
     32 const char kRevertTranslation[] = "Translate.RevertTranslation";
     33 const char kShowErrorInfobar[] = "Translate.ShowErrorInfobar";
     34 const char kPerformTranslate[] = "Translate.Translate";
     35 const char kNeverTranslateLang[] = "Translate.NeverTranslateLang";
     36 const char kNeverTranslateSite[] = "Translate.NeverTranslateSite";
     37 const char kAlwaysTranslateLang[] = "Translate.AlwaysTranslateLang";
     38 
     39 }  // namespace
     40 
     41 // static
     42 const size_t TranslateInfoBarDelegate::kNoIndex = static_cast<size_t>(-1);
     43 
     44 TranslateInfoBarDelegate::~TranslateInfoBarDelegate() {
     45 }
     46 
     47 // static
     48 void TranslateInfoBarDelegate::Create(
     49     bool replace_existing_infobar,
     50     InfoBarService* infobar_service,
     51     Type infobar_type,
     52     const std::string& original_language,
     53     const std::string& target_language,
     54     TranslateErrors::Type error_type,
     55     PrefService* prefs,
     56     const ShortcutConfiguration& shortcut_config) {
     57   // Check preconditions.
     58   if (infobar_type != TRANSLATION_ERROR) {
     59     DCHECK(TranslateManager::IsSupportedLanguage(target_language));
     60     if (!TranslateManager::IsSupportedLanguage(original_language)) {
     61       // The original language can only be "unknown" for the "translating"
     62       // infobar, which is the case when the user started a translation from the
     63       // context menu.
     64       DCHECK(infobar_type == TRANSLATING || infobar_type == AFTER_TRANSLATE);
     65       DCHECK_EQ(chrome::kUnknownLanguageCode, original_language);
     66     }
     67   }
     68 
     69   // Find any existing translate infobar delegate.
     70   TranslateInfoBarDelegate* old_delegate = NULL;
     71   for (size_t i = 0; i < infobar_service->infobar_count(); ++i) {
     72     old_delegate = infobar_service->infobar_at(i)->AsTranslateInfoBarDelegate();
     73     if (old_delegate) {
     74       if (!replace_existing_infobar)
     75         return;
     76       break;
     77     }
     78   }
     79 
     80   // Create the new delegate.
     81   scoped_ptr<TranslateInfoBarDelegate> infobar(
     82       new TranslateInfoBarDelegate(infobar_service, infobar_type, old_delegate,
     83                                    original_language, target_language,
     84                                    error_type, prefs, shortcut_config));
     85 
     86   // Do not create the after translate infobar if we are auto translating.
     87   if ((infobar_type == TranslateInfoBarDelegate::AFTER_TRANSLATE) ||
     88       (infobar_type == TranslateInfoBarDelegate::TRANSLATING)) {
     89     TranslateTabHelper* translate_tab_helper =
     90       TranslateTabHelper::FromWebContents(infobar_service->web_contents());
     91     if (!translate_tab_helper ||
     92          translate_tab_helper->language_state().InTranslateNavigation())
     93       return;
     94   }
     95 
     96   // Add the new delegate if necessary.
     97   if (!old_delegate) {
     98     infobar_service->AddInfoBar(infobar.PassAs<InfoBarDelegate>());
     99   } else {
    100     DCHECK(replace_existing_infobar);
    101     infobar_service->ReplaceInfoBar(old_delegate,
    102                                     infobar.PassAs<InfoBarDelegate>());
    103   }
    104 }
    105 
    106 void TranslateInfoBarDelegate::Translate() {
    107   if (!web_contents()->GetBrowserContext()->IsOffTheRecord()) {
    108     prefs_.ResetTranslationDeniedCount(original_language_code());
    109     prefs_.IncrementTranslationAcceptedCount(original_language_code());
    110   }
    111 
    112   TranslateManager::GetInstance()->TranslatePage(web_contents(),
    113                                                  original_language_code(),
    114                                                  target_language_code());
    115 
    116   UMA_HISTOGRAM_BOOLEAN(kPerformTranslate, true);
    117 }
    118 
    119 void TranslateInfoBarDelegate::RevertTranslation() {
    120   TranslateManager::GetInstance()->RevertTranslation(web_contents());
    121   RemoveSelf();
    122 
    123   UMA_HISTOGRAM_BOOLEAN(kRevertTranslation, true);
    124 }
    125 
    126 void TranslateInfoBarDelegate::ReportLanguageDetectionError() {
    127   TranslateManager::GetInstance()->ReportLanguageDetectionError(
    128       web_contents());
    129 }
    130 
    131 void TranslateInfoBarDelegate::TranslationDeclined() {
    132   if (!web_contents()->GetBrowserContext()->IsOffTheRecord()) {
    133     prefs_.ResetTranslationAcceptedCount(original_language_code());
    134     prefs_.IncrementTranslationDeniedCount(original_language_code());
    135   }
    136 
    137   // Remember that the user declined the translation so as to prevent showing a
    138   // translate infobar for that page again.  (TranslateManager initiates
    139   // translations when getting a LANGUAGE_DETERMINED from the page, which
    140   // happens when a load stops. That could happen multiple times, including
    141   // after the user already declined the translation.)
    142   TranslateTabHelper::FromWebContents(web_contents())->
    143       language_state().set_translation_declined(true);
    144 
    145   UMA_HISTOGRAM_BOOLEAN(kDeclineTranslate, true);
    146 }
    147 
    148 bool TranslateInfoBarDelegate::IsTranslatableLanguageByPrefs() {
    149   Profile* profile =
    150       Profile::FromBrowserContext(web_contents()->GetBrowserContext());
    151   Profile* original_profile = profile->GetOriginalProfile();
    152   return TranslatePrefs::CanTranslateLanguage(original_profile,
    153                                               original_language_code());
    154 }
    155 
    156 void TranslateInfoBarDelegate::ToggleTranslatableLanguageByPrefs() {
    157   const std::string& original_lang = original_language_code();
    158   if (prefs_.IsBlockedLanguage(original_lang)) {
    159     prefs_.UnblockLanguage(original_lang);
    160   } else {
    161     prefs_.BlockLanguage(original_lang);
    162     RemoveSelf();
    163   }
    164 
    165   UMA_HISTOGRAM_BOOLEAN(kNeverTranslateLang, true);
    166 }
    167 
    168 bool TranslateInfoBarDelegate::IsSiteBlacklisted() {
    169   std::string host = GetPageHost();
    170   return !host.empty() && prefs_.IsSiteBlacklisted(host);
    171 }
    172 
    173 void TranslateInfoBarDelegate::ToggleSiteBlacklist() {
    174   std::string host = GetPageHost();
    175   if (host.empty())
    176     return;
    177 
    178   if (prefs_.IsSiteBlacklisted(host)) {
    179     prefs_.RemoveSiteFromBlacklist(host);
    180   } else {
    181     prefs_.BlacklistSite(host);
    182     RemoveSelf();
    183   }
    184 
    185   UMA_HISTOGRAM_BOOLEAN(kNeverTranslateSite, true);
    186 }
    187 
    188 bool TranslateInfoBarDelegate::ShouldAlwaysTranslate() {
    189   return prefs_.IsLanguagePairWhitelisted(original_language_code(),
    190                                           target_language_code());
    191 }
    192 
    193 void TranslateInfoBarDelegate::ToggleAlwaysTranslate() {
    194   const std::string& original_lang = original_language_code();
    195   const std::string& target_lang = target_language_code();
    196   if (prefs_.IsLanguagePairWhitelisted(original_lang, target_lang))
    197     prefs_.RemoveLanguagePairFromWhitelist(original_lang, target_lang);
    198   else
    199     prefs_.WhitelistLanguagePair(original_lang, target_lang);
    200 
    201   UMA_HISTOGRAM_BOOLEAN(kAlwaysTranslateLang, true);
    202 }
    203 
    204 void TranslateInfoBarDelegate::AlwaysTranslatePageLanguage() {
    205   const std::string& original_lang = original_language_code();
    206   const std::string& target_lang = target_language_code();
    207   DCHECK(!prefs_.IsLanguagePairWhitelisted(original_lang, target_lang));
    208   prefs_.WhitelistLanguagePair(original_lang, target_lang);
    209   Translate();
    210 }
    211 
    212 void TranslateInfoBarDelegate::NeverTranslatePageLanguage() {
    213   std::string original_lang = original_language_code();
    214   DCHECK(!prefs_.IsBlockedLanguage(original_lang));
    215   prefs_.BlockLanguage(original_lang);
    216   RemoveSelf();
    217 }
    218 
    219 string16 TranslateInfoBarDelegate::GetMessageInfoBarText() {
    220   if (infobar_type_ == TRANSLATING) {
    221     return l10n_util::GetStringFUTF16(IDS_TRANSLATE_INFOBAR_TRANSLATING_TO,
    222                                       language_name_at(target_language_index_));
    223   }
    224 
    225   DCHECK_EQ(TRANSLATION_ERROR, infobar_type_);
    226   UMA_HISTOGRAM_ENUMERATION(kShowErrorInfobar,
    227                             error_type_,
    228                             TranslateErrors::TRANSLATE_ERROR_MAX);
    229   switch (error_type_) {
    230     case TranslateErrors::NETWORK:
    231       return l10n_util::GetStringUTF16(
    232           IDS_TRANSLATE_INFOBAR_ERROR_CANT_CONNECT);
    233     case TranslateErrors::INITIALIZATION_ERROR:
    234     case TranslateErrors::TRANSLATION_ERROR:
    235       return l10n_util::GetStringUTF16(
    236           IDS_TRANSLATE_INFOBAR_ERROR_CANT_TRANSLATE);
    237     case TranslateErrors::UNKNOWN_LANGUAGE:
    238       return l10n_util::GetStringUTF16(
    239           IDS_TRANSLATE_INFOBAR_UNKNOWN_PAGE_LANGUAGE);
    240     case TranslateErrors::UNSUPPORTED_LANGUAGE:
    241       return l10n_util::GetStringFUTF16(
    242           IDS_TRANSLATE_INFOBAR_UNSUPPORTED_PAGE_LANGUAGE,
    243           language_name_at(target_language_index_));
    244     case TranslateErrors::IDENTICAL_LANGUAGES:
    245       return l10n_util::GetStringFUTF16(
    246           IDS_TRANSLATE_INFOBAR_ERROR_SAME_LANGUAGE,
    247           language_name_at(target_language_index_));
    248     default:
    249       NOTREACHED();
    250       return string16();
    251   }
    252 }
    253 
    254 string16 TranslateInfoBarDelegate::GetMessageInfoBarButtonText() {
    255   if (infobar_type_ != TRANSLATION_ERROR) {
    256     DCHECK_EQ(TRANSLATING, infobar_type_);
    257   } else if ((error_type_ != TranslateErrors::IDENTICAL_LANGUAGES) &&
    258              (error_type_ != TranslateErrors::UNKNOWN_LANGUAGE)) {
    259     return l10n_util::GetStringUTF16(
    260         (error_type_ == TranslateErrors::UNSUPPORTED_LANGUAGE) ?
    261         IDS_TRANSLATE_INFOBAR_REVERT : IDS_TRANSLATE_INFOBAR_RETRY);
    262   }
    263   return string16();
    264 }
    265 
    266 void TranslateInfoBarDelegate::MessageInfoBarButtonPressed() {
    267   DCHECK_EQ(TRANSLATION_ERROR, infobar_type_);
    268   if (error_type_ == TranslateErrors::UNSUPPORTED_LANGUAGE) {
    269     RevertTranslation();
    270     return;
    271   }
    272   // This is the "Try again..." case.
    273   TranslateManager::GetInstance()->TranslatePage(
    274       web_contents(), original_language_code(), target_language_code());
    275 }
    276 
    277 bool TranslateInfoBarDelegate::ShouldShowMessageInfoBarButton() {
    278   return !GetMessageInfoBarButtonText().empty();
    279 }
    280 
    281 bool TranslateInfoBarDelegate::ShouldShowNeverTranslateShortcut() {
    282   DCHECK_EQ(BEFORE_TRANSLATE, infobar_type_);
    283   return !web_contents()->GetBrowserContext()->IsOffTheRecord() &&
    284       (prefs_.GetTranslationDeniedCount(original_language_code()) >=
    285           shortcut_config_.never_translate_min_count);
    286 }
    287 
    288 bool TranslateInfoBarDelegate::ShouldShowAlwaysTranslateShortcut() {
    289   DCHECK_EQ(BEFORE_TRANSLATE, infobar_type_);
    290   return !web_contents()->GetBrowserContext()->IsOffTheRecord() &&
    291       (prefs_.GetTranslationAcceptedCount(original_language_code()) >=
    292           shortcut_config_.always_translate_min_count);
    293 }
    294 
    295 // static
    296 string16 TranslateInfoBarDelegate::GetLanguageDisplayableName(
    297     const std::string& language_code) {
    298   return l10n_util::GetDisplayNameForLocale(
    299       language_code, g_browser_process->GetApplicationLocale(), true);
    300 }
    301 
    302 // static
    303 void TranslateInfoBarDelegate::GetAfterTranslateStrings(
    304     std::vector<string16>* strings,
    305     bool* swap_languages,
    306     bool autodetermined_source_language) {
    307   DCHECK(strings);
    308 
    309   if (autodetermined_source_language) {
    310     size_t offset;
    311     string16 text = l10n_util::GetStringFUTF16(
    312         IDS_TRANSLATE_INFOBAR_AFTER_MESSAGE_AUTODETERMINED_SOURCE_LANGUAGE,
    313         string16(),
    314         &offset);
    315 
    316     strings->push_back(text.substr(0, offset));
    317     strings->push_back(text.substr(offset));
    318     return;
    319   }
    320   DCHECK(swap_languages);
    321 
    322   std::vector<size_t> offsets;
    323   string16 text = l10n_util::GetStringFUTF16(
    324       IDS_TRANSLATE_INFOBAR_AFTER_MESSAGE, string16(), string16(), &offsets);
    325   DCHECK_EQ(2U, offsets.size());
    326 
    327   *swap_languages = (offsets[0] > offsets[1]);
    328   if (*swap_languages)
    329     std::swap(offsets[0], offsets[1]);
    330 
    331   strings->push_back(text.substr(0, offsets[0]));
    332   strings->push_back(text.substr(offsets[0], offsets[1] - offsets[0]));
    333   strings->push_back(text.substr(offsets[1]));
    334 }
    335 
    336 TranslateInfoBarDelegate::TranslateInfoBarDelegate(
    337     InfoBarService* infobar_service,
    338     Type infobar_type,
    339     TranslateInfoBarDelegate* old_delegate,
    340     const std::string& original_language,
    341     const std::string& target_language,
    342     TranslateErrors::Type error_type,
    343     PrefService* prefs,
    344     ShortcutConfiguration shortcut_config)
    345     : InfoBarDelegate(infobar_service),
    346       infobar_type_(infobar_type),
    347       background_animation_(NONE),
    348       original_language_index_(kNoIndex),
    349       initial_original_language_index_(kNoIndex),
    350       target_language_index_(kNoIndex),
    351       error_type_(error_type),
    352       prefs_(prefs),
    353       shortcut_config_(shortcut_config) {
    354   DCHECK_NE((infobar_type_ == TRANSLATION_ERROR),
    355             (error_type_ == TranslateErrors::NONE));
    356 
    357   if (old_delegate && (old_delegate->is_error() != is_error()))
    358     background_animation_ = is_error() ? NORMAL_TO_ERROR : ERROR_TO_NORMAL;
    359 
    360   std::vector<std::string> language_codes;
    361   TranslateManager::GetSupportedLanguages(&language_codes);
    362 
    363   // Preparing for the alphabetical order in the locale.
    364   UErrorCode error = U_ZERO_ERROR;
    365   std::string locale = g_browser_process->GetApplicationLocale();
    366   icu::Locale loc(locale.c_str());
    367   scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(loc, error));
    368   collator->setStrength(icu::Collator::PRIMARY);
    369 
    370   languages_.reserve(language_codes.size());
    371   for (std::vector<std::string>::const_iterator iter = language_codes.begin();
    372        iter != language_codes.end(); ++iter) {
    373     std::string language_code = *iter;
    374 
    375     string16 language_name = GetLanguageDisplayableName(language_code);
    376     // Insert the language in languages_ in alphabetical order.
    377     std::vector<LanguageNamePair>::iterator iter2;
    378     for (iter2 = languages_.begin(); iter2 != languages_.end(); ++iter2) {
    379       if (base::i18n::CompareString16WithCollator(collator.get(),
    380           language_name, iter2->second) == UCOL_LESS) {
    381         break;
    382       }
    383     }
    384     languages_.insert(iter2, LanguageNamePair(language_code, language_name));
    385   }
    386   for (std::vector<LanguageNamePair>::const_iterator iter = languages_.begin();
    387        iter != languages_.end(); ++iter) {
    388     std::string language_code = iter->first;
    389     if (language_code == original_language) {
    390       original_language_index_ = iter - languages_.begin();
    391       initial_original_language_index_ = original_language_index_;
    392     }
    393     if (language_code == target_language)
    394       target_language_index_ = iter - languages_.begin();
    395   }
    396   DCHECK_NE(kNoIndex, target_language_index_);
    397 }
    398 
    399 void TranslateInfoBarDelegate::InfoBarDismissed() {
    400   if (infobar_type_ != BEFORE_TRANSLATE)
    401     return;
    402 
    403   // The user closed the infobar without clicking the translate button.
    404   TranslationDeclined();
    405   UMA_HISTOGRAM_BOOLEAN(kCloseInfobar, true);
    406 }
    407 
    408 int TranslateInfoBarDelegate::GetIconID() const {
    409   return IDR_INFOBAR_TRANSLATE;
    410 }
    411 
    412 InfoBarDelegate::Type TranslateInfoBarDelegate::GetInfoBarType() const {
    413   return PAGE_ACTION_TYPE;
    414 }
    415 
    416 bool TranslateInfoBarDelegate::ShouldExpire(
    417     const content::LoadCommittedDetails& details) const {
    418   // Note: we allow closing this infobar even if the main frame navigation
    419   // was programmatic and not initiated by the user - crbug.com/70261 .
    420   if (!details.is_navigation_to_different_page() && !details.is_main_frame)
    421     return false;
    422 
    423   return InfoBarDelegate::ShouldExpireInternal(details);
    424 }
    425 
    426 TranslateInfoBarDelegate*
    427     TranslateInfoBarDelegate::AsTranslateInfoBarDelegate() {
    428   return this;
    429 }
    430 
    431 std::string TranslateInfoBarDelegate::GetPageHost() {
    432   NavigationEntry* entry = web_contents()->GetController().GetActiveEntry();
    433   return entry ? entry->GetURL().HostNoBrackets() : std::string();
    434 }
    435