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.h"
14 #include "chrome/browser/infobars/infobar_service.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/translate/translate_manager.h"
17 #include "chrome/browser/translate/translate_tab_helper.h"
18 #include "components/translate/core/browser/translate_accept_languages.h"
19 #include "components/translate/core/browser/translate_download_manager.h"
20 #include "components/translate/core/common/translate_constants.h"
21 #include "content/public/browser/navigation_details.h"
22 #include "content/public/browser/navigation_entry.h"
23 #include "content/public/browser/web_contents.h"
24 #include "grit/generated_resources.h"
25 #include "grit/theme_resources.h"
26 #include "third_party/icu/source/i18n/unicode/coll.h"
27 #include "ui/base/l10n/l10n_util.h"
31 // Counts used to decide whether infobars should be shown.
32 // Android and iOS implementations do not offer a drop down (for space reasons),
33 // so we are more aggressive about showing the shortcut to never translate.
34 // The "Always Translate" option is always shown on iOS and Android.
35 #if defined(OS_ANDROID)
36 const int kAlwaysTranslateMinCount
= 1;
37 const int kNeverTranslateMinCount
= 1;
39 // The iOS implementation, like the Android implementation, shows the "Never
40 // translate" infobar after two denials. There is an offset of one because on
41 // Android the last event is not counted.
42 const int kAlwaysTranslateMinCount
= 1;
43 const int kNeverTranslateMinCount
= 2;
45 const int kAlwaysTranslateMinCount
= 3;
46 const int kNeverTranslateMinCount
= 3;
51 const size_t TranslateInfoBarDelegate::kNoIndex
= TranslateUIDelegate::NO_INDEX
;
53 TranslateInfoBarDelegate::~TranslateInfoBarDelegate() {
57 void TranslateInfoBarDelegate::Create(bool replace_existing_infobar
,
58 content::WebContents
* web_contents
,
59 TranslateTabHelper::TranslateStep step
,
60 const std::string
& original_language
,
61 const std::string
& target_language
,
62 TranslateErrors::Type error_type
,
64 // Check preconditions.
65 if (step
!= TranslateTabHelper::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
== TranslateTabHelper::TRANSLATING
||
72 step
== TranslateTabHelper::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
== TranslateTabHelper::AFTER_TRANSLATE
) ||
79 (step
== TranslateTabHelper::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 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
<InfoBar
> infobar(CreateInfoBar(
104 scoped_ptr
<TranslateInfoBarDelegate
>(new TranslateInfoBarDelegate(
105 web_contents
, step
, old_delegate
, original_language
,
106 target_language
, error_type
, prefs
))));
108 infobar_service
->ReplaceInfoBar(old_infobar
, infobar
.Pass());
110 infobar_service
->AddInfoBar(infobar
.Pass());
114 void TranslateInfoBarDelegate::UpdateOriginalLanguageIndex(
115 size_t language_index
) {
116 ui_delegate_
.UpdateOriginalLanguageIndex(language_index
);
119 void TranslateInfoBarDelegate::UpdateTargetLanguageIndex(
120 size_t language_index
) {
121 ui_delegate_
.UpdateTargetLanguageIndex(language_index
);
124 void TranslateInfoBarDelegate::Translate() {
125 ui_delegate_
.Translate();
128 void TranslateInfoBarDelegate::RevertTranslation() {
129 ui_delegate_
.RevertTranslation();
130 infobar()->RemoveSelf();
133 void TranslateInfoBarDelegate::ReportLanguageDetectionError() {
134 TranslateManager::GetInstance()->ReportLanguageDetectionError(
138 void TranslateInfoBarDelegate::TranslationDeclined() {
139 ui_delegate_
.TranslationDeclined(false);
142 bool TranslateInfoBarDelegate::IsTranslatableLanguageByPrefs() {
144 Profile::FromBrowserContext(web_contents()->GetBrowserContext());
145 Profile
* original_profile
= profile
->GetOriginalProfile();
146 scoped_ptr
<TranslatePrefs
> translate_prefs(
147 TranslateTabHelper::CreateTranslatePrefs(original_profile
->GetPrefs()));
148 TranslateAcceptLanguages
* accept_languages
=
149 TranslateTabHelper::GetTranslateAcceptLanguages(original_profile
);
150 return translate_prefs
->CanTranslateLanguage(accept_languages
,
151 original_language_code());
154 void TranslateInfoBarDelegate::ToggleTranslatableLanguageByPrefs() {
155 if (ui_delegate_
.IsLanguageBlocked()) {
156 ui_delegate_
.SetLanguageBlocked(false);
158 ui_delegate_
.SetLanguageBlocked(true);
159 infobar()->RemoveSelf();
163 bool TranslateInfoBarDelegate::IsSiteBlacklisted() {
164 return ui_delegate_
.IsSiteBlacklisted();
167 void TranslateInfoBarDelegate::ToggleSiteBlacklist() {
168 if (ui_delegate_
.IsSiteBlacklisted()) {
169 ui_delegate_
.SetSiteBlacklist(false);
171 ui_delegate_
.SetSiteBlacklist(true);
172 infobar()->RemoveSelf();
176 bool TranslateInfoBarDelegate::ShouldAlwaysTranslate() {
177 return ui_delegate_
.ShouldAlwaysTranslate();
180 void TranslateInfoBarDelegate::ToggleAlwaysTranslate() {
181 ui_delegate_
.SetAlwaysTranslate(!ui_delegate_
.ShouldAlwaysTranslate());
184 void TranslateInfoBarDelegate::AlwaysTranslatePageLanguage() {
185 DCHECK(!ui_delegate_
.ShouldAlwaysTranslate());
186 ui_delegate_
.SetAlwaysTranslate(true);
190 void TranslateInfoBarDelegate::NeverTranslatePageLanguage() {
191 DCHECK(!ui_delegate_
.IsLanguageBlocked());
192 ui_delegate_
.SetLanguageBlocked(true);
193 infobar()->RemoveSelf();
196 base::string16
TranslateInfoBarDelegate::GetMessageInfoBarText() {
197 if (step_
== TranslateTabHelper::TRANSLATING
) {
198 base::string16 target_language_name
=
199 language_name_at(target_language_index());
200 return l10n_util::GetStringFUTF16(IDS_TRANSLATE_INFOBAR_TRANSLATING_TO
,
201 target_language_name
);
204 DCHECK_EQ(TranslateTabHelper::TRANSLATE_ERROR
, step_
);
205 UMA_HISTOGRAM_ENUMERATION("Translate.ShowErrorInfobar",
207 TranslateErrors::TRANSLATE_ERROR_MAX
);
208 ui_delegate_
.OnErrorShown(error_type_
);
209 switch (error_type_
) {
210 case TranslateErrors::NETWORK
:
211 return l10n_util::GetStringUTF16(
212 IDS_TRANSLATE_INFOBAR_ERROR_CANT_CONNECT
);
213 case TranslateErrors::INITIALIZATION_ERROR
:
214 case TranslateErrors::TRANSLATION_ERROR
:
215 return l10n_util::GetStringUTF16(
216 IDS_TRANSLATE_INFOBAR_ERROR_CANT_TRANSLATE
);
217 case TranslateErrors::UNKNOWN_LANGUAGE
:
218 return l10n_util::GetStringUTF16(
219 IDS_TRANSLATE_INFOBAR_UNKNOWN_PAGE_LANGUAGE
);
220 case TranslateErrors::UNSUPPORTED_LANGUAGE
:
221 return l10n_util::GetStringFUTF16(
222 IDS_TRANSLATE_INFOBAR_UNSUPPORTED_PAGE_LANGUAGE
,
223 language_name_at(target_language_index()));
224 case TranslateErrors::IDENTICAL_LANGUAGES
:
225 return l10n_util::GetStringFUTF16(
226 IDS_TRANSLATE_INFOBAR_ERROR_SAME_LANGUAGE
,
227 language_name_at(target_language_index()));
230 return base::string16();
234 base::string16
TranslateInfoBarDelegate::GetMessageInfoBarButtonText() {
235 if (step_
!= TranslateTabHelper::TRANSLATE_ERROR
) {
236 DCHECK_EQ(TranslateTabHelper::TRANSLATING
, step_
);
237 } else if ((error_type_
!= TranslateErrors::IDENTICAL_LANGUAGES
) &&
238 (error_type_
!= TranslateErrors::UNKNOWN_LANGUAGE
)) {
239 return l10n_util::GetStringUTF16(
240 (error_type_
== TranslateErrors::UNSUPPORTED_LANGUAGE
) ?
241 IDS_TRANSLATE_INFOBAR_REVERT
: IDS_TRANSLATE_INFOBAR_RETRY
);
243 return base::string16();
246 void TranslateInfoBarDelegate::MessageInfoBarButtonPressed() {
247 DCHECK_EQ(TranslateTabHelper::TRANSLATE_ERROR
, step_
);
248 if (error_type_
== TranslateErrors::UNSUPPORTED_LANGUAGE
) {
252 // This is the "Try again..." case.
253 TranslateManager::GetInstance()->TranslatePage(
254 web_contents(), original_language_code(), target_language_code());
257 bool TranslateInfoBarDelegate::ShouldShowMessageInfoBarButton() {
258 return !GetMessageInfoBarButtonText().empty();
261 bool TranslateInfoBarDelegate::ShouldShowNeverTranslateShortcut() {
262 DCHECK_EQ(TranslateTabHelper::BEFORE_TRANSLATE
, step_
);
263 return !web_contents()->GetBrowserContext()->IsOffTheRecord() &&
264 (prefs_
->GetTranslationDeniedCount(original_language_code()) >=
265 kNeverTranslateMinCount
);
268 bool TranslateInfoBarDelegate::ShouldShowAlwaysTranslateShortcut() {
269 DCHECK_EQ(TranslateTabHelper::BEFORE_TRANSLATE
, step_
);
270 return !web_contents()->GetBrowserContext()->IsOffTheRecord() &&
271 (prefs_
->GetTranslationAcceptedCount(original_language_code()) >=
272 kAlwaysTranslateMinCount
);
276 base::string16
TranslateInfoBarDelegate::GetLanguageDisplayableName(
277 const std::string
& language_code
) {
278 return l10n_util::GetDisplayNameForLocale(
279 language_code
, g_browser_process
->GetApplicationLocale(), true);
283 void TranslateInfoBarDelegate::GetAfterTranslateStrings(
284 std::vector
<base::string16
>* strings
,
285 bool* swap_languages
,
286 bool autodetermined_source_language
) {
289 if (autodetermined_source_language
) {
291 base::string16 text
= l10n_util::GetStringFUTF16(
292 IDS_TRANSLATE_INFOBAR_AFTER_MESSAGE_AUTODETERMINED_SOURCE_LANGUAGE
,
296 strings
->push_back(text
.substr(0, offset
));
297 strings
->push_back(text
.substr(offset
));
300 DCHECK(swap_languages
);
302 std::vector
<size_t> offsets
;
303 base::string16 text
= l10n_util::GetStringFUTF16(
304 IDS_TRANSLATE_INFOBAR_AFTER_MESSAGE
, base::string16(), base::string16(),
306 DCHECK_EQ(2U, offsets
.size());
308 *swap_languages
= (offsets
[0] > offsets
[1]);
310 std::swap(offsets
[0], offsets
[1]);
312 strings
->push_back(text
.substr(0, offsets
[0]));
313 strings
->push_back(text
.substr(offsets
[0], offsets
[1] - offsets
[0]));
314 strings
->push_back(text
.substr(offsets
[1]));
317 TranslateInfoBarDelegate::TranslateInfoBarDelegate(
318 content::WebContents
* web_contents
,
319 TranslateTabHelper::TranslateStep step
,
320 TranslateInfoBarDelegate
* old_delegate
,
321 const std::string
& original_language
,
322 const std::string
& target_language
,
323 TranslateErrors::Type error_type
,
327 background_animation_(NONE
),
328 ui_delegate_(web_contents
, original_language
, target_language
),
329 error_type_(error_type
),
330 prefs_(TranslateTabHelper::CreateTranslatePrefs(prefs
)) {
331 DCHECK_NE((step_
== TranslateTabHelper::TRANSLATE_ERROR
),
332 (error_type_
== TranslateErrors::NONE
));
334 if (old_delegate
&& (old_delegate
->is_error() != is_error()))
335 background_animation_
= is_error() ? NORMAL_TO_ERROR
: ERROR_TO_NORMAL
;
338 // TranslateInfoBarDelegate::CreateInfoBar() is implemented in platform-specific
341 void TranslateInfoBarDelegate::InfoBarDismissed() {
342 if (step_
!= TranslateTabHelper::BEFORE_TRANSLATE
)
345 // The user closed the infobar without clicking the translate button.
346 TranslationDeclined();
347 UMA_HISTOGRAM_BOOLEAN("Translate.DeclineTranslateCloseInfobar", true);
350 int TranslateInfoBarDelegate::GetIconID() const {
351 return IDR_INFOBAR_TRANSLATE
;
354 InfoBarDelegate::Type
TranslateInfoBarDelegate::GetInfoBarType() const {
355 return PAGE_ACTION_TYPE
;
358 bool TranslateInfoBarDelegate::ShouldExpire(
359 const content::LoadCommittedDetails
& details
) const {
360 // Note: we allow closing this infobar even if the main frame navigation
361 // was programmatic and not initiated by the user - crbug.com/70261 .
362 if (!details
.is_navigation_to_different_page() && !details
.is_main_frame
)
365 return InfoBarDelegate::ShouldExpireInternal(details
);
368 TranslateInfoBarDelegate
*
369 TranslateInfoBarDelegate::AsTranslateInfoBarDelegate() {