1 // Copyright (c) 2011 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/metrics/histogram.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/translate/translate_infobar_view.h"
13 #include "chrome/browser/translate/translate_manager.h"
14 #include "chrome/browser/translate/translate_tab_helper.h"
15 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
16 #include "chrome/common/chrome_constants.h"
17 #include "content/browser/tab_contents/tab_contents.h"
18 #include "grit/generated_resources.h"
19 #include "grit/theme_resources.h"
20 #include "ui/base/l10n/l10n_util.h"
21 #include "ui/base/resource/resource_bundle.h"
22
23 // static
24 const size_t TranslateInfoBarDelegate::kNoIndex = static_cast<size_t>(-1);
25
26 // static
CreateDelegate(Type type,TabContents * tab_contents,const std::string & original_language,const std::string & target_language)27 TranslateInfoBarDelegate* TranslateInfoBarDelegate::CreateDelegate(
28 Type type,
29 TabContents* tab_contents,
30 const std::string& original_language,
31 const std::string& target_language) {
32 DCHECK_NE(TRANSLATION_ERROR, type);
33 // The original language can only be "unknown" for the "translating"
34 // infobar, which is the case when the user started a translation from the
35 // context menu.
36 DCHECK(type == TRANSLATING ||
37 original_language != chrome::kUnknownLanguageCode);
38 if ((original_language != chrome::kUnknownLanguageCode &&
39 !TranslateManager::IsSupportedLanguage(original_language)) ||
40 !TranslateManager::IsSupportedLanguage(target_language))
41 return NULL;
42 TranslateInfoBarDelegate* delegate =
43 new TranslateInfoBarDelegate(type, TranslateErrors::NONE, tab_contents,
44 original_language, target_language);
45 DCHECK_NE(kNoIndex, delegate->target_language_index());
46 return delegate;
47 }
48
CreateErrorDelegate(TranslateErrors::Type error,TabContents * tab_contents,const std::string & original_language,const std::string & target_language)49 TranslateInfoBarDelegate* TranslateInfoBarDelegate::CreateErrorDelegate(
50 TranslateErrors::Type error,
51 TabContents* tab_contents,
52 const std::string& original_language,
53 const std::string& target_language) {
54 return new TranslateInfoBarDelegate(TRANSLATION_ERROR, error, tab_contents,
55 original_language, target_language);
56 }
57
~TranslateInfoBarDelegate()58 TranslateInfoBarDelegate::~TranslateInfoBarDelegate() {
59 }
60
GetLanguageCodeAt(size_t index) const61 std::string TranslateInfoBarDelegate::GetLanguageCodeAt(size_t index) const {
62 DCHECK_LT(index, GetLanguageCount());
63 return languages_[index].first;
64 }
65
GetLanguageDisplayableNameAt(size_t index) const66 string16 TranslateInfoBarDelegate::GetLanguageDisplayableNameAt(
67 size_t index) const {
68 DCHECK_LT(index, GetLanguageCount());
69 return languages_[index].second;
70 }
71
GetOriginalLanguageCode() const72 std::string TranslateInfoBarDelegate::GetOriginalLanguageCode() const {
73 return (original_language_index() == kNoIndex) ?
74 chrome::kUnknownLanguageCode :
75 GetLanguageCodeAt(original_language_index());
76 }
77
GetTargetLanguageCode() const78 std::string TranslateInfoBarDelegate::GetTargetLanguageCode() const {
79 return GetLanguageCodeAt(target_language_index());
80 }
81
SetOriginalLanguage(size_t language_index)82 void TranslateInfoBarDelegate::SetOriginalLanguage(size_t language_index) {
83 DCHECK_LT(language_index, GetLanguageCount());
84 original_language_index_ = language_index;
85 if (infobar_view_)
86 infobar_view_->OriginalLanguageChanged();
87 if (type_ == AFTER_TRANSLATE)
88 Translate();
89 }
90
SetTargetLanguage(size_t language_index)91 void TranslateInfoBarDelegate::SetTargetLanguage(size_t language_index) {
92 DCHECK_LT(language_index, GetLanguageCount());
93 target_language_index_ = language_index;
94 if (infobar_view_)
95 infobar_view_->TargetLanguageChanged();
96 if (type_ == AFTER_TRANSLATE)
97 Translate();
98 }
99
Translate()100 void TranslateInfoBarDelegate::Translate() {
101 const std::string& original_language_code = GetOriginalLanguageCode();
102 if (!tab_contents()->profile()->IsOffTheRecord()) {
103 prefs_.ResetTranslationDeniedCount(original_language_code);
104 prefs_.IncrementTranslationAcceptedCount(original_language_code);
105 }
106
107 TranslateManager::GetInstance()->TranslatePage(tab_contents_,
108 GetLanguageCodeAt(original_language_index()),
109 GetLanguageCodeAt(target_language_index()));
110 }
111
RevertTranslation()112 void TranslateInfoBarDelegate::RevertTranslation() {
113 TranslateManager::GetInstance()->RevertTranslation(tab_contents_);
114 tab_contents_->RemoveInfoBar(this);
115 }
116
ReportLanguageDetectionError()117 void TranslateInfoBarDelegate::ReportLanguageDetectionError() {
118 TranslateManager::GetInstance()->ReportLanguageDetectionError(tab_contents_);
119 }
120
TranslationDeclined()121 void TranslateInfoBarDelegate::TranslationDeclined() {
122 const std::string& original_language_code = GetOriginalLanguageCode();
123 if (!tab_contents()->profile()->IsOffTheRecord()) {
124 prefs_.ResetTranslationAcceptedCount(original_language_code);
125 prefs_.IncrementTranslationDeniedCount(original_language_code);
126 }
127
128 // Remember that the user declined the translation so as to prevent showing a
129 // translate infobar for that page again. (TranslateManager initiates
130 // translations when getting a LANGUAGE_DETERMINED from the page, which
131 // happens when a load stops. That could happen multiple times, including
132 // after the user already declined the translation.)
133 TranslateTabHelper* helper = TabContentsWrapper::GetCurrentWrapperForContents(
134 tab_contents_)->translate_tab_helper();
135 helper->language_state().set_translation_declined(true);
136 }
137
IsLanguageBlacklisted()138 bool TranslateInfoBarDelegate::IsLanguageBlacklisted() {
139 return prefs_.IsLanguageBlacklisted(GetOriginalLanguageCode());
140 }
141
ToggleLanguageBlacklist()142 void TranslateInfoBarDelegate::ToggleLanguageBlacklist() {
143 const std::string& original_lang = GetOriginalLanguageCode();
144 if (prefs_.IsLanguageBlacklisted(original_lang)) {
145 prefs_.RemoveLanguageFromBlacklist(original_lang);
146 } else {
147 prefs_.BlacklistLanguage(original_lang);
148 tab_contents_->RemoveInfoBar(this);
149 }
150 }
151
IsSiteBlacklisted()152 bool TranslateInfoBarDelegate::IsSiteBlacklisted() {
153 std::string host = GetPageHost();
154 return !host.empty() && prefs_.IsSiteBlacklisted(host);
155 }
156
ToggleSiteBlacklist()157 void TranslateInfoBarDelegate::ToggleSiteBlacklist() {
158 std::string host = GetPageHost();
159 if (host.empty())
160 return;
161
162 if (prefs_.IsSiteBlacklisted(host)) {
163 prefs_.RemoveSiteFromBlacklist(host);
164 } else {
165 prefs_.BlacklistSite(host);
166 tab_contents_->RemoveInfoBar(this);
167 }
168 }
169
ShouldAlwaysTranslate()170 bool TranslateInfoBarDelegate::ShouldAlwaysTranslate() {
171 return prefs_.IsLanguagePairWhitelisted(GetOriginalLanguageCode(),
172 GetTargetLanguageCode());
173 }
174
ToggleAlwaysTranslate()175 void TranslateInfoBarDelegate::ToggleAlwaysTranslate() {
176 const std::string& original_lang = GetOriginalLanguageCode();
177 const std::string& target_lang = GetTargetLanguageCode();
178 if (prefs_.IsLanguagePairWhitelisted(original_lang, target_lang))
179 prefs_.RemoveLanguagePairFromWhitelist(original_lang, target_lang);
180 else
181 prefs_.WhitelistLanguagePair(original_lang, target_lang);
182 }
183
AlwaysTranslatePageLanguage()184 void TranslateInfoBarDelegate::AlwaysTranslatePageLanguage() {
185 const std::string& original_lang = GetOriginalLanguageCode();
186 const std::string& target_lang = GetTargetLanguageCode();
187 DCHECK(!prefs_.IsLanguagePairWhitelisted(original_lang, target_lang));
188 prefs_.WhitelistLanguagePair(original_lang, target_lang);
189 Translate();
190 }
191
NeverTranslatePageLanguage()192 void TranslateInfoBarDelegate::NeverTranslatePageLanguage() {
193 std::string original_lang = GetOriginalLanguageCode();
194 DCHECK(!prefs_.IsLanguageBlacklisted(original_lang));
195 prefs_.BlacklistLanguage(original_lang);
196 tab_contents_->RemoveInfoBar(this);
197 }
198
GetMessageInfoBarText()199 string16 TranslateInfoBarDelegate::GetMessageInfoBarText() {
200 if (type_ == TRANSLATING) {
201 return l10n_util::GetStringFUTF16(IDS_TRANSLATE_INFOBAR_TRANSLATING_TO,
202 GetLanguageDisplayableNameAt(target_language_index_));
203 }
204
205 DCHECK_EQ(TRANSLATION_ERROR, type_);
206 switch (error_) {
207 case TranslateErrors::NETWORK:
208 return l10n_util::GetStringUTF16(
209 IDS_TRANSLATE_INFOBAR_ERROR_CANT_CONNECT);
210 case TranslateErrors::INITIALIZATION_ERROR:
211 case TranslateErrors::TRANSLATION_ERROR:
212 return l10n_util::GetStringUTF16(
213 IDS_TRANSLATE_INFOBAR_ERROR_CANT_TRANSLATE);
214 case TranslateErrors::UNKNOWN_LANGUAGE:
215 return l10n_util::GetStringUTF16(
216 IDS_TRANSLATE_INFOBAR_UNKNOWN_PAGE_LANGUAGE);
217 case TranslateErrors::UNSUPPORTED_LANGUAGE:
218 return l10n_util::GetStringFUTF16(
219 IDS_TRANSLATE_INFOBAR_UNSUPPORTED_PAGE_LANGUAGE,
220 GetLanguageDisplayableNameAt(target_language_index_));
221 case TranslateErrors::IDENTICAL_LANGUAGES:
222 return l10n_util::GetStringFUTF16(
223 IDS_TRANSLATE_INFOBAR_ERROR_SAME_LANGUAGE,
224 GetLanguageDisplayableNameAt(target_language_index_));
225 default:
226 NOTREACHED();
227 return string16();
228 }
229 }
230
GetMessageInfoBarButtonText()231 string16 TranslateInfoBarDelegate::GetMessageInfoBarButtonText() {
232 if (type_ != TRANSLATION_ERROR) {
233 DCHECK_EQ(TRANSLATING, type_);
234 } else if ((error_ != TranslateErrors::IDENTICAL_LANGUAGES) &&
235 (error_ != TranslateErrors::UNKNOWN_LANGUAGE)) {
236 return l10n_util::GetStringUTF16(
237 (error_ == TranslateErrors::UNSUPPORTED_LANGUAGE) ?
238 IDS_TRANSLATE_INFOBAR_REVERT : IDS_TRANSLATE_INFOBAR_RETRY);
239 }
240 return string16();
241 }
242
MessageInfoBarButtonPressed()243 void TranslateInfoBarDelegate::MessageInfoBarButtonPressed() {
244 DCHECK_EQ(TRANSLATION_ERROR, type_);
245 if (error_ == TranslateErrors::UNSUPPORTED_LANGUAGE) {
246 RevertTranslation();
247 return;
248 }
249 // This is the "Try again..." case.
250 TranslateManager::GetInstance()->TranslatePage(tab_contents_,
251 GetOriginalLanguageCode(), GetTargetLanguageCode());
252 }
253
ShouldShowMessageInfoBarButton()254 bool TranslateInfoBarDelegate::ShouldShowMessageInfoBarButton() {
255 return !GetMessageInfoBarButtonText().empty();
256 }
257
ShouldShowNeverTranslateButton()258 bool TranslateInfoBarDelegate::ShouldShowNeverTranslateButton() {
259 DCHECK_EQ(BEFORE_TRANSLATE, type_);
260 return !tab_contents()->profile()->IsOffTheRecord() &&
261 (prefs_.GetTranslationDeniedCount(GetOriginalLanguageCode()) >= 3);
262 }
263
ShouldShowAlwaysTranslateButton()264 bool TranslateInfoBarDelegate::ShouldShowAlwaysTranslateButton() {
265 DCHECK_EQ(BEFORE_TRANSLATE, type_);
266 return !tab_contents()->profile()->IsOffTheRecord() &&
267 (prefs_.GetTranslationAcceptedCount(GetOriginalLanguageCode()) >= 3);
268 }
269
UpdateBackgroundAnimation(TranslateInfoBarDelegate * previous_infobar)270 void TranslateInfoBarDelegate::UpdateBackgroundAnimation(
271 TranslateInfoBarDelegate* previous_infobar) {
272 if (!previous_infobar || previous_infobar->IsError() == IsError())
273 background_animation_ = NONE;
274 else
275 background_animation_ = IsError() ? NORMAL_TO_ERROR : ERROR_TO_NORMAL;
276 }
277
278 // static
GetLanguageDisplayableName(const std::string & language_code)279 string16 TranslateInfoBarDelegate::GetLanguageDisplayableName(
280 const std::string& language_code) {
281 return l10n_util::GetDisplayNameForLocale(
282 language_code, g_browser_process->GetApplicationLocale(), true);
283 }
284
285 // static
GetAfterTranslateStrings(std::vector<string16> * strings,bool * swap_languages)286 void TranslateInfoBarDelegate::GetAfterTranslateStrings(
287 std::vector<string16>* strings, bool* swap_languages) {
288 DCHECK(strings);
289 DCHECK(swap_languages);
290
291 std::vector<size_t> offsets;
292 string16 text =
293 l10n_util::GetStringFUTF16(IDS_TRANSLATE_INFOBAR_AFTER_MESSAGE,
294 string16(), string16(), &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
TranslateInfoBarDelegate(Type type,TranslateErrors::Type error,TabContents * tab_contents,const std::string & original_language,const std::string & target_language)306 TranslateInfoBarDelegate::TranslateInfoBarDelegate(
307 Type type,
308 TranslateErrors::Type error,
309 TabContents* tab_contents,
310 const std::string& original_language,
311 const std::string& target_language)
312 : InfoBarDelegate(tab_contents),
313 type_(type),
314 background_animation_(NONE),
315 tab_contents_(tab_contents),
316 original_language_index_(kNoIndex),
317 initial_original_language_index_(kNoIndex),
318 target_language_index_(kNoIndex),
319 error_(error),
320 infobar_view_(NULL),
321 prefs_(tab_contents_->profile()->GetPrefs()) {
322 DCHECK_NE((type_ == TRANSLATION_ERROR), (error == TranslateErrors::NONE));
323
324 std::vector<std::string> language_codes;
325 TranslateManager::GetSupportedLanguages(&language_codes);
326
327 languages_.reserve(language_codes.size());
328 for (std::vector<std::string>::const_iterator iter = language_codes.begin();
329 iter != language_codes.end(); ++iter) {
330 std::string language_code = *iter;
331
332 string16 language_name = GetLanguageDisplayableName(language_code);
333 // Insert the language in languages_ in alphabetical order.
334 std::vector<LanguageNamePair>::iterator iter2;
335 for (iter2 = languages_.begin(); iter2 != languages_.end(); ++iter2) {
336 if (language_name.compare(iter2->second) < 0)
337 break;
338 }
339 languages_.insert(iter2, LanguageNamePair(language_code, language_name));
340 }
341 for (std::vector<LanguageNamePair>::const_iterator iter = languages_.begin();
342 iter != languages_.end(); ++iter) {
343 std::string language_code = iter->first;
344 if (language_code == original_language) {
345 original_language_index_ = iter - languages_.begin();
346 initial_original_language_index_ = original_language_index_;
347 }
348 if (language_code == target_language)
349 target_language_index_ = iter - languages_.begin();
350 }
351 }
352
InfoBarDismissed()353 void TranslateInfoBarDelegate::InfoBarDismissed() {
354 if (type_ != BEFORE_TRANSLATE)
355 return;
356
357 // The user closed the infobar without clicking the translate button.
358 TranslationDeclined();
359 UMA_HISTOGRAM_COUNTS("Translate.DeclineTranslateCloseInfobar", 1);
360 }
361
InfoBarClosed()362 void TranslateInfoBarDelegate::InfoBarClosed() {
363 delete this;
364 }
365
GetIcon() const366 SkBitmap* TranslateInfoBarDelegate::GetIcon() const {
367 return ResourceBundle::GetSharedInstance().GetBitmapNamed(
368 IDR_INFOBAR_TRANSLATE);
369 }
370
GetInfoBarType() const371 InfoBarDelegate::Type TranslateInfoBarDelegate::GetInfoBarType() const {
372 return PAGE_ACTION_TYPE;
373 }
374
375 TranslateInfoBarDelegate*
AsTranslateInfoBarDelegate()376 TranslateInfoBarDelegate::AsTranslateInfoBarDelegate() {
377 return this;
378 }
379
GetPageHost()380 std::string TranslateInfoBarDelegate::GetPageHost() {
381 NavigationEntry* entry = tab_contents_->controller().GetActiveEntry();
382 return entry ? entry->url().HostNoBrackets() : std::string();
383 }
384