// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/translate/translate_infobar_delegate.h"
#include <algorithm>
#include "base/metrics/histogram.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/translate/translate_infobar_view.h"
#include "chrome/browser/translate/translate_manager.h"
#include "chrome/browser/translate/translate_tab_helper.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/common/chrome_constants.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
// static
const size_t TranslateInfoBarDelegate::kNoIndex = static_cast<size_t>(-1);
// static
TranslateInfoBarDelegate* TranslateInfoBarDelegate::CreateDelegate(
Type type,
TabContents* tab_contents,
const std::string& original_language,
const std::string& target_language) {
DCHECK_NE(TRANSLATION_ERROR, type);
// The original language can only be "unknown" for the "translating"
// infobar, which is the case when the user started a translation from the
// context menu.
DCHECK(type == TRANSLATING ||
original_language != chrome::kUnknownLanguageCode);
if ((original_language != chrome::kUnknownLanguageCode &&
!TranslateManager::IsSupportedLanguage(original_language)) ||
!TranslateManager::IsSupportedLanguage(target_language))
return NULL;
TranslateInfoBarDelegate* delegate =
new TranslateInfoBarDelegate(type, TranslateErrors::NONE, tab_contents,
original_language, target_language);
DCHECK_NE(kNoIndex, delegate->target_language_index());
return delegate;
}
TranslateInfoBarDelegate* TranslateInfoBarDelegate::CreateErrorDelegate(
TranslateErrors::Type error,
TabContents* tab_contents,
const std::string& original_language,
const std::string& target_language) {
return new TranslateInfoBarDelegate(TRANSLATION_ERROR, error, tab_contents,
original_language, target_language);
}
TranslateInfoBarDelegate::~TranslateInfoBarDelegate() {
}
std::string TranslateInfoBarDelegate::GetLanguageCodeAt(size_t index) const {
DCHECK_LT(index, GetLanguageCount());
return languages_[index].first;
}
string16 TranslateInfoBarDelegate::GetLanguageDisplayableNameAt(
size_t index) const {
DCHECK_LT(index, GetLanguageCount());
return languages_[index].second;
}
std::string TranslateInfoBarDelegate::GetOriginalLanguageCode() const {
return (original_language_index() == kNoIndex) ?
chrome::kUnknownLanguageCode :
GetLanguageCodeAt(original_language_index());
}
std::string TranslateInfoBarDelegate::GetTargetLanguageCode() const {
return GetLanguageCodeAt(target_language_index());
}
void TranslateInfoBarDelegate::SetOriginalLanguage(size_t language_index) {
DCHECK_LT(language_index, GetLanguageCount());
original_language_index_ = language_index;
if (infobar_view_)
infobar_view_->OriginalLanguageChanged();
if (type_ == AFTER_TRANSLATE)
Translate();
}
void TranslateInfoBarDelegate::SetTargetLanguage(size_t language_index) {
DCHECK_LT(language_index, GetLanguageCount());
target_language_index_ = language_index;
if (infobar_view_)
infobar_view_->TargetLanguageChanged();
if (type_ == AFTER_TRANSLATE)
Translate();
}
void TranslateInfoBarDelegate::Translate() {
const std::string& original_language_code = GetOriginalLanguageCode();
if (!tab_contents()->profile()->IsOffTheRecord()) {
prefs_.ResetTranslationDeniedCount(original_language_code);
prefs_.IncrementTranslationAcceptedCount(original_language_code);
}
TranslateManager::GetInstance()->TranslatePage(tab_contents_,
GetLanguageCodeAt(original_language_index()),
GetLanguageCodeAt(target_language_index()));
}
void TranslateInfoBarDelegate::RevertTranslation() {
TranslateManager::GetInstance()->RevertTranslation(tab_contents_);
tab_contents_->RemoveInfoBar(this);
}
void TranslateInfoBarDelegate::ReportLanguageDetectionError() {
TranslateManager::GetInstance()->ReportLanguageDetectionError(tab_contents_);
}
void TranslateInfoBarDelegate::TranslationDeclined() {
const std::string& original_language_code = GetOriginalLanguageCode();
if (!tab_contents()->profile()->IsOffTheRecord()) {
prefs_.ResetTranslationAcceptedCount(original_language_code);
prefs_.IncrementTranslationDeniedCount(original_language_code);
}
// Remember that the user declined the translation so as to prevent showing a
// translate infobar for that page again. (TranslateManager initiates
// translations when getting a LANGUAGE_DETERMINED from the page, which
// happens when a load stops. That could happen multiple times, including
// after the user already declined the translation.)
TranslateTabHelper* helper = TabContentsWrapper::GetCurrentWrapperForContents(
tab_contents_)->translate_tab_helper();
helper->language_state().set_translation_declined(true);
}
bool TranslateInfoBarDelegate::IsLanguageBlacklisted() {
return prefs_.IsLanguageBlacklisted(GetOriginalLanguageCode());
}
void TranslateInfoBarDelegate::ToggleLanguageBlacklist() {
const std::string& original_lang = GetOriginalLanguageCode();
if (prefs_.IsLanguageBlacklisted(original_lang)) {
prefs_.RemoveLanguageFromBlacklist(original_lang);
} else {
prefs_.BlacklistLanguage(original_lang);
tab_contents_->RemoveInfoBar(this);
}
}
bool TranslateInfoBarDelegate::IsSiteBlacklisted() {
std::string host = GetPageHost();
return !host.empty() && prefs_.IsSiteBlacklisted(host);
}
void TranslateInfoBarDelegate::ToggleSiteBlacklist() {
std::string host = GetPageHost();
if (host.empty())
return;
if (prefs_.IsSiteBlacklisted(host)) {
prefs_.RemoveSiteFromBlacklist(host);
} else {
prefs_.BlacklistSite(host);
tab_contents_->RemoveInfoBar(this);
}
}
bool TranslateInfoBarDelegate::ShouldAlwaysTranslate() {
return prefs_.IsLanguagePairWhitelisted(GetOriginalLanguageCode(),
GetTargetLanguageCode());
}
void TranslateInfoBarDelegate::ToggleAlwaysTranslate() {
const std::string& original_lang = GetOriginalLanguageCode();
const std::string& target_lang = GetTargetLanguageCode();
if (prefs_.IsLanguagePairWhitelisted(original_lang, target_lang))
prefs_.RemoveLanguagePairFromWhitelist(original_lang, target_lang);
else
prefs_.WhitelistLanguagePair(original_lang, target_lang);
}
void TranslateInfoBarDelegate::AlwaysTranslatePageLanguage() {
const std::string& original_lang = GetOriginalLanguageCode();
const std::string& target_lang = GetTargetLanguageCode();
DCHECK(!prefs_.IsLanguagePairWhitelisted(original_lang, target_lang));
prefs_.WhitelistLanguagePair(original_lang, target_lang);
Translate();
}
void TranslateInfoBarDelegate::NeverTranslatePageLanguage() {
std::string original_lang = GetOriginalLanguageCode();
DCHECK(!prefs_.IsLanguageBlacklisted(original_lang));
prefs_.BlacklistLanguage(original_lang);
tab_contents_->RemoveInfoBar(this);
}
string16 TranslateInfoBarDelegate::GetMessageInfoBarText() {
if (type_ == TRANSLATING) {
return l10n_util::GetStringFUTF16(IDS_TRANSLATE_INFOBAR_TRANSLATING_TO,
GetLanguageDisplayableNameAt(target_language_index_));
}
DCHECK_EQ(TRANSLATION_ERROR, type_);
switch (error_) {
case TranslateErrors::NETWORK:
return l10n_util::GetStringUTF16(
IDS_TRANSLATE_INFOBAR_ERROR_CANT_CONNECT);
case TranslateErrors::INITIALIZATION_ERROR:
case TranslateErrors::TRANSLATION_ERROR:
return l10n_util::GetStringUTF16(
IDS_TRANSLATE_INFOBAR_ERROR_CANT_TRANSLATE);
case TranslateErrors::UNKNOWN_LANGUAGE:
return l10n_util::GetStringUTF16(
IDS_TRANSLATE_INFOBAR_UNKNOWN_PAGE_LANGUAGE);
case TranslateErrors::UNSUPPORTED_LANGUAGE:
return l10n_util::GetStringFUTF16(
IDS_TRANSLATE_INFOBAR_UNSUPPORTED_PAGE_LANGUAGE,
GetLanguageDisplayableNameAt(target_language_index_));
case TranslateErrors::IDENTICAL_LANGUAGES:
return l10n_util::GetStringFUTF16(
IDS_TRANSLATE_INFOBAR_ERROR_SAME_LANGUAGE,
GetLanguageDisplayableNameAt(target_language_index_));
default:
NOTREACHED();
return string16();
}
}
string16 TranslateInfoBarDelegate::GetMessageInfoBarButtonText() {
if (type_ != TRANSLATION_ERROR) {
DCHECK_EQ(TRANSLATING, type_);
} else if ((error_ != TranslateErrors::IDENTICAL_LANGUAGES) &&
(error_ != TranslateErrors::UNKNOWN_LANGUAGE)) {
return l10n_util::GetStringUTF16(
(error_ == TranslateErrors::UNSUPPORTED_LANGUAGE) ?
IDS_TRANSLATE_INFOBAR_REVERT : IDS_TRANSLATE_INFOBAR_RETRY);
}
return string16();
}
void TranslateInfoBarDelegate::MessageInfoBarButtonPressed() {
DCHECK_EQ(TRANSLATION_ERROR, type_);
if (error_ == TranslateErrors::UNSUPPORTED_LANGUAGE) {
RevertTranslation();
return;
}
// This is the "Try again..." case.
TranslateManager::GetInstance()->TranslatePage(tab_contents_,
GetOriginalLanguageCode(), GetTargetLanguageCode());
}
bool TranslateInfoBarDelegate::ShouldShowMessageInfoBarButton() {
return !GetMessageInfoBarButtonText().empty();
}
bool TranslateInfoBarDelegate::ShouldShowNeverTranslateButton() {
DCHECK_EQ(BEFORE_TRANSLATE, type_);
return !tab_contents()->profile()->IsOffTheRecord() &&
(prefs_.GetTranslationDeniedCount(GetOriginalLanguageCode()) >= 3);
}
bool TranslateInfoBarDelegate::ShouldShowAlwaysTranslateButton() {
DCHECK_EQ(BEFORE_TRANSLATE, type_);
return !tab_contents()->profile()->IsOffTheRecord() &&
(prefs_.GetTranslationAcceptedCount(GetOriginalLanguageCode()) >= 3);
}
void TranslateInfoBarDelegate::UpdateBackgroundAnimation(
TranslateInfoBarDelegate* previous_infobar) {
if (!previous_infobar || previous_infobar->IsError() == IsError())
background_animation_ = NONE;
else
background_animation_ = IsError() ? NORMAL_TO_ERROR : ERROR_TO_NORMAL;
}
// static
string16 TranslateInfoBarDelegate::GetLanguageDisplayableName(
const std::string& language_code) {
return l10n_util::GetDisplayNameForLocale(
language_code, g_browser_process->GetApplicationLocale(), true);
}
// static
void TranslateInfoBarDelegate::GetAfterTranslateStrings(
std::vector<string16>* strings, bool* swap_languages) {
DCHECK(strings);
DCHECK(swap_languages);
std::vector<size_t> offsets;
string16 text =
l10n_util::GetStringFUTF16(IDS_TRANSLATE_INFOBAR_AFTER_MESSAGE,
string16(), string16(), &offsets);
DCHECK_EQ(2U, offsets.size());
*swap_languages = (offsets[0] > offsets[1]);
if (*swap_languages)
std::swap(offsets[0], offsets[1]);
strings->push_back(text.substr(0, offsets[0]));
strings->push_back(text.substr(offsets[0], offsets[1] - offsets[0]));
strings->push_back(text.substr(offsets[1]));
}
TranslateInfoBarDelegate::TranslateInfoBarDelegate(
Type type,
TranslateErrors::Type error,
TabContents* tab_contents,
const std::string& original_language,
const std::string& target_language)
: InfoBarDelegate(tab_contents),
type_(type),
background_animation_(NONE),
tab_contents_(tab_contents),
original_language_index_(kNoIndex),
initial_original_language_index_(kNoIndex),
target_language_index_(kNoIndex),
error_(error),
infobar_view_(NULL),
prefs_(tab_contents_->profile()->GetPrefs()) {
DCHECK_NE((type_ == TRANSLATION_ERROR), (error == TranslateErrors::NONE));
std::vector<std::string> language_codes;
TranslateManager::GetSupportedLanguages(&language_codes);
languages_.reserve(language_codes.size());
for (std::vector<std::string>::const_iterator iter = language_codes.begin();
iter != language_codes.end(); ++iter) {
std::string language_code = *iter;
string16 language_name = GetLanguageDisplayableName(language_code);
// Insert the language in languages_ in alphabetical order.
std::vector<LanguageNamePair>::iterator iter2;
for (iter2 = languages_.begin(); iter2 != languages_.end(); ++iter2) {
if (language_name.compare(iter2->second) < 0)
break;
}
languages_.insert(iter2, LanguageNamePair(language_code, language_name));
}
for (std::vector<LanguageNamePair>::const_iterator iter = languages_.begin();
iter != languages_.end(); ++iter) {
std::string language_code = iter->first;
if (language_code == original_language) {
original_language_index_ = iter - languages_.begin();
initial_original_language_index_ = original_language_index_;
}
if (language_code == target_language)
target_language_index_ = iter - languages_.begin();
}
}
void TranslateInfoBarDelegate::InfoBarDismissed() {
if (type_ != BEFORE_TRANSLATE)
return;
// The user closed the infobar without clicking the translate button.
TranslationDeclined();
UMA_HISTOGRAM_COUNTS("Translate.DeclineTranslateCloseInfobar", 1);
}
void TranslateInfoBarDelegate::InfoBarClosed() {
delete this;
}
SkBitmap* TranslateInfoBarDelegate::GetIcon() const {
return ResourceBundle::GetSharedInstance().GetBitmapNamed(
IDR_INFOBAR_TRANSLATE);
}
InfoBarDelegate::Type TranslateInfoBarDelegate::GetInfoBarType() const {
return PAGE_ACTION_TYPE;
}
TranslateInfoBarDelegate*
TranslateInfoBarDelegate::AsTranslateInfoBarDelegate() {
return this;
}
std::string TranslateInfoBarDelegate::GetPageHost() {
NavigationEntry* entry = tab_contents_->controller().GetActiveEntry();
return entry ? entry->url().HostNoBrackets() : std::string();
}