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.
5 #include "chrome/browser/translate/translate_infobar_delegate.h"
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_tab_helper.h"
16 #include "components/infobars/core/infobar.h"
17 #include "components/translate/core/browser/translate_accept_languages.h"
18 #include "components/translate/core/browser/translate_download_manager.h"
19 #include "components/translate/core/browser/translate_manager.h"
20 #include "components/translate/core/common/translate_constants.h"
21 #include "content/public/browser/navigation_entry.h"
22 #include "content/public/browser/web_contents.h"
23 #include "grit/generated_resources.h"
24 #include "grit/theme_resources.h"
25 #include "third_party/icu/source/i18n/unicode/coll.h"
26 #include "ui/base/l10n/l10n_util.h"
30 // Counts used to decide whether infobars should be shown.
31 // Android and iOS implementations do not offer a drop down (for space reasons),
32 // so we are more aggressive about showing the shortcut to never translate.
33 // The "Always Translate" option is always shown on iOS and Android.
34 #if defined(OS_ANDROID)
35 const int kAlwaysTranslateMinCount
= 1;
36 const int kNeverTranslateMinCount
= 1;
38 // The iOS implementation, like the Android implementation, shows the "Never
39 // translate" infobar after two denials. There is an offset of one because on
40 // Android the last event is not counted.
41 const int kAlwaysTranslateMinCount
= 1;
42 const int kNeverTranslateMinCount
= 2;
44 const int kAlwaysTranslateMinCount
= 3;
45 const int kNeverTranslateMinCount
= 3;
50 const size_t TranslateInfoBarDelegate::kNoIndex
= TranslateUIDelegate::NO_INDEX
;
52 TranslateInfoBarDelegate::~TranslateInfoBarDelegate() {
56 void TranslateInfoBarDelegate::Create(bool replace_existing_infobar
,
57 content::WebContents
* web_contents
,
58 translate::TranslateStep step
,
59 const std::string
& original_language
,
60 const std::string
& target_language
,
61 TranslateErrors::Type error_type
,
63 bool triggered_from_menu
) {
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
71 DCHECK(step
== translate::TRANSLATE_STEP_TRANSLATING
||
72 step
== translate::TRANSLATE_STEP_AFTER_TRANSLATE
);
73 DCHECK_EQ(translate::kUnknownLanguageCode
, original_language
);
77 // Do not create the after translate infobar if we are auto translating.
78 if ((step
== translate::TRANSLATE_STEP_AFTER_TRANSLATE
) ||
79 (step
== translate::TRANSLATE_STEP_TRANSLATING
)) {
80 TranslateTabHelper
* translate_tab_helper
=
81 TranslateTabHelper::FromWebContents(web_contents
);
82 if (!translate_tab_helper
||
83 translate_tab_helper
->GetLanguageState().InTranslateNavigation())
87 // Find any existing translate infobar delegate.
88 infobars::InfoBar
* old_infobar
= NULL
;
89 InfoBarService
* infobar_service
=
90 InfoBarService::FromWebContents(web_contents
);
91 TranslateInfoBarDelegate
* old_delegate
= NULL
;
92 for (size_t i
= 0; i
< infobar_service
->infobar_count(); ++i
) {
93 old_infobar
= infobar_service
->infobar_at(i
);
94 old_delegate
= old_infobar
->delegate()->AsTranslateInfoBarDelegate();
96 if (!replace_existing_infobar
)
102 // Add the new delegate.
103 scoped_ptr
<infobars::InfoBar
> infobar(CreateInfoBar(
104 scoped_ptr
<TranslateInfoBarDelegate
>(new TranslateInfoBarDelegate(
105 web_contents
, step
, old_delegate
, original_language
,
106 target_language
, error_type
, prefs
,
107 triggered_from_menu
))));
109 infobar_service
->ReplaceInfoBar(old_infobar
, infobar
.Pass());
111 infobar_service
->AddInfoBar(infobar
.Pass());
115 void TranslateInfoBarDelegate::UpdateOriginalLanguageIndex(
116 size_t language_index
) {
117 ui_delegate_
.UpdateOriginalLanguageIndex(language_index
);
120 void TranslateInfoBarDelegate::UpdateTargetLanguageIndex(
121 size_t language_index
) {
122 ui_delegate_
.UpdateTargetLanguageIndex(language_index
);
125 void TranslateInfoBarDelegate::Translate() {
126 ui_delegate_
.Translate();
129 void TranslateInfoBarDelegate::RevertTranslation() {
130 ui_delegate_
.RevertTranslation();
131 infobar()->RemoveSelf();
134 void TranslateInfoBarDelegate::ReportLanguageDetectionError() {
135 TranslateManager
* manager
=
136 TranslateTabHelper::GetManagerFromWebContents(GetWebContents());
139 manager
->ReportLanguageDetectionError();
142 void TranslateInfoBarDelegate::TranslationDeclined() {
143 ui_delegate_
.TranslationDeclined(false);
146 bool TranslateInfoBarDelegate::IsTranslatableLanguageByPrefs() {
148 Profile::FromBrowserContext(GetWebContents()->GetBrowserContext());
149 Profile
* original_profile
= profile
->GetOriginalProfile();
150 scoped_ptr
<TranslatePrefs
> translate_prefs(
151 TranslateTabHelper::CreateTranslatePrefs(original_profile
->GetPrefs()));
152 TranslateAcceptLanguages
* accept_languages
=
153 TranslateTabHelper::GetTranslateAcceptLanguages(original_profile
);
154 return translate_prefs
->CanTranslateLanguage(accept_languages
,
155 original_language_code());
158 void TranslateInfoBarDelegate::ToggleTranslatableLanguageByPrefs() {
159 if (ui_delegate_
.IsLanguageBlocked()) {
160 ui_delegate_
.SetLanguageBlocked(false);
162 ui_delegate_
.SetLanguageBlocked(true);
163 infobar()->RemoveSelf();
167 bool TranslateInfoBarDelegate::IsSiteBlacklisted() {
168 return ui_delegate_
.IsSiteBlacklisted();
171 void TranslateInfoBarDelegate::ToggleSiteBlacklist() {
172 if (ui_delegate_
.IsSiteBlacklisted()) {
173 ui_delegate_
.SetSiteBlacklist(false);
175 ui_delegate_
.SetSiteBlacklist(true);
176 infobar()->RemoveSelf();
180 bool TranslateInfoBarDelegate::ShouldAlwaysTranslate() {
181 return ui_delegate_
.ShouldAlwaysTranslate();
184 void TranslateInfoBarDelegate::ToggleAlwaysTranslate() {
185 ui_delegate_
.SetAlwaysTranslate(!ui_delegate_
.ShouldAlwaysTranslate());
188 void TranslateInfoBarDelegate::AlwaysTranslatePageLanguage() {
189 DCHECK(!ui_delegate_
.ShouldAlwaysTranslate());
190 ui_delegate_
.SetAlwaysTranslate(true);
194 void TranslateInfoBarDelegate::NeverTranslatePageLanguage() {
195 DCHECK(!ui_delegate_
.IsLanguageBlocked());
196 ui_delegate_
.SetLanguageBlocked(true);
197 infobar()->RemoveSelf();
200 base::string16
TranslateInfoBarDelegate::GetMessageInfoBarText() {
201 if (step_
== translate::TRANSLATE_STEP_TRANSLATING
) {
202 base::string16 target_language_name
=
203 language_name_at(target_language_index());
204 return l10n_util::GetStringFUTF16(IDS_TRANSLATE_INFOBAR_TRANSLATING_TO
,
205 target_language_name
);
208 DCHECK_EQ(translate::TRANSLATE_STEP_TRANSLATE_ERROR
, step_
);
209 UMA_HISTOGRAM_ENUMERATION("Translate.ShowErrorInfobar",
211 TranslateErrors::TRANSLATE_ERROR_MAX
);
212 ui_delegate_
.OnErrorShown(error_type_
);
213 switch (error_type_
) {
214 case TranslateErrors::NETWORK
:
215 return l10n_util::GetStringUTF16(
216 IDS_TRANSLATE_INFOBAR_ERROR_CANT_CONNECT
);
217 case TranslateErrors::INITIALIZATION_ERROR
:
218 case TranslateErrors::TRANSLATION_ERROR
:
219 return l10n_util::GetStringUTF16(
220 IDS_TRANSLATE_INFOBAR_ERROR_CANT_TRANSLATE
);
221 case TranslateErrors::UNKNOWN_LANGUAGE
:
222 return l10n_util::GetStringUTF16(
223 IDS_TRANSLATE_INFOBAR_UNKNOWN_PAGE_LANGUAGE
);
224 case TranslateErrors::UNSUPPORTED_LANGUAGE
:
225 return l10n_util::GetStringFUTF16(
226 IDS_TRANSLATE_INFOBAR_UNSUPPORTED_PAGE_LANGUAGE
,
227 language_name_at(target_language_index()));
228 case TranslateErrors::IDENTICAL_LANGUAGES
:
229 return l10n_util::GetStringFUTF16(
230 IDS_TRANSLATE_INFOBAR_ERROR_SAME_LANGUAGE
,
231 language_name_at(target_language_index()));
234 return base::string16();
238 base::string16
TranslateInfoBarDelegate::GetMessageInfoBarButtonText() {
239 if (step_
!= translate::TRANSLATE_STEP_TRANSLATE_ERROR
) {
240 DCHECK_EQ(translate::TRANSLATE_STEP_TRANSLATING
, step_
);
241 } else if ((error_type_
!= TranslateErrors::IDENTICAL_LANGUAGES
) &&
242 (error_type_
!= TranslateErrors::UNKNOWN_LANGUAGE
)) {
243 return l10n_util::GetStringUTF16(
244 (error_type_
== TranslateErrors::UNSUPPORTED_LANGUAGE
) ?
245 IDS_TRANSLATE_INFOBAR_REVERT
: IDS_TRANSLATE_INFOBAR_RETRY
);
247 return base::string16();
250 void TranslateInfoBarDelegate::MessageInfoBarButtonPressed() {
251 DCHECK_EQ(translate::TRANSLATE_STEP_TRANSLATE_ERROR
, step_
);
252 if (error_type_
== TranslateErrors::UNSUPPORTED_LANGUAGE
) {
256 // This is the "Try again..." case.
257 TranslateManager
* manager
=
258 TranslateTabHelper::GetManagerFromWebContents(GetWebContents());
260 manager
->TranslatePage(
261 original_language_code(), target_language_code(), false);
264 bool TranslateInfoBarDelegate::ShouldShowMessageInfoBarButton() {
265 return !GetMessageInfoBarButtonText().empty();
268 bool TranslateInfoBarDelegate::ShouldShowNeverTranslateShortcut() {
269 DCHECK_EQ(translate::TRANSLATE_STEP_BEFORE_TRANSLATE
, step_
);
270 return !GetWebContents()->GetBrowserContext()->IsOffTheRecord() &&
271 (prefs_
->GetTranslationDeniedCount(original_language_code()) >=
272 kNeverTranslateMinCount
);
275 bool TranslateInfoBarDelegate::ShouldShowAlwaysTranslateShortcut() {
276 DCHECK_EQ(translate::TRANSLATE_STEP_BEFORE_TRANSLATE
, step_
);
277 return !GetWebContents()->GetBrowserContext()->IsOffTheRecord() &&
278 (prefs_
->GetTranslationAcceptedCount(original_language_code()) >=
279 kAlwaysTranslateMinCount
);
282 content::WebContents
* TranslateInfoBarDelegate::GetWebContents() {
283 return InfoBarService::WebContentsFromInfoBar(infobar());
287 base::string16
TranslateInfoBarDelegate::GetLanguageDisplayableName(
288 const std::string
& language_code
) {
289 return l10n_util::GetDisplayNameForLocale(
290 language_code
, g_browser_process
->GetApplicationLocale(), true);
294 void TranslateInfoBarDelegate::GetAfterTranslateStrings(
295 std::vector
<base::string16
>* strings
,
296 bool* swap_languages
,
297 bool autodetermined_source_language
) {
300 if (autodetermined_source_language
) {
302 base::string16 text
= l10n_util::GetStringFUTF16(
303 IDS_TRANSLATE_INFOBAR_AFTER_MESSAGE_AUTODETERMINED_SOURCE_LANGUAGE
,
307 strings
->push_back(text
.substr(0, offset
));
308 strings
->push_back(text
.substr(offset
));
311 DCHECK(swap_languages
);
313 std::vector
<size_t> offsets
;
314 base::string16 text
= l10n_util::GetStringFUTF16(
315 IDS_TRANSLATE_INFOBAR_AFTER_MESSAGE
, base::string16(), base::string16(),
317 DCHECK_EQ(2U, offsets
.size());
319 *swap_languages
= (offsets
[0] > offsets
[1]);
321 std::swap(offsets
[0], offsets
[1]);
323 strings
->push_back(text
.substr(0, offsets
[0]));
324 strings
->push_back(text
.substr(offsets
[0], offsets
[1] - offsets
[0]));
325 strings
->push_back(text
.substr(offsets
[1]));
328 TranslateInfoBarDelegate::TranslateInfoBarDelegate(
329 content::WebContents
* web_contents
,
330 translate::TranslateStep step
,
331 TranslateInfoBarDelegate
* old_delegate
,
332 const std::string
& original_language
,
333 const std::string
& target_language
,
334 TranslateErrors::Type error_type
,
336 bool triggered_from_menu
)
337 : infobars::InfoBarDelegate(),
339 background_animation_(NONE
),
340 ui_delegate_(TranslateTabHelper::FromWebContents(web_contents
),
341 TranslateTabHelper::GetManagerFromWebContents(web_contents
),
344 error_type_(error_type
),
345 prefs_(TranslateTabHelper::CreateTranslatePrefs(prefs
)),
346 triggered_from_menu_(triggered_from_menu
) {
347 DCHECK_NE((step_
== translate::TRANSLATE_STEP_TRANSLATE_ERROR
),
348 (error_type_
== TranslateErrors::NONE
));
350 if (old_delegate
&& (old_delegate
->is_error() != is_error()))
351 background_animation_
= is_error() ? NORMAL_TO_ERROR
: ERROR_TO_NORMAL
;
354 // TranslateInfoBarDelegate::CreateInfoBar() is implemented in platform-specific
357 void TranslateInfoBarDelegate::InfoBarDismissed() {
358 if (step_
!= translate::TRANSLATE_STEP_BEFORE_TRANSLATE
)
361 // The user closed the infobar without clicking the translate button.
362 TranslationDeclined();
363 UMA_HISTOGRAM_BOOLEAN("Translate.DeclineTranslateCloseInfobar", true);
366 int TranslateInfoBarDelegate::GetIconID() const {
367 return IDR_INFOBAR_TRANSLATE
;
370 infobars::InfoBarDelegate::Type
TranslateInfoBarDelegate::GetInfoBarType()
372 return PAGE_ACTION_TYPE
;
375 bool TranslateInfoBarDelegate::ShouldExpire(
376 const NavigationDetails
& details
) const {
377 // Note: we allow closing this infobar even if the main frame navigation
378 // was programmatic and not initiated by the user - crbug.com/70261 .
379 if (!details
.is_navigation_to_different_page
&& !details
.is_main_frame
)
382 return infobars::InfoBarDelegate::ShouldExpireInternal(details
);
385 TranslateInfoBarDelegate
*
386 TranslateInfoBarDelegate::AsTranslateInfoBarDelegate() {