• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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 "components/translate/core/browser/translate_infobar_delegate.h"
6 
7 #include <algorithm>
8 
9 #include "base/i18n/string_compare.h"
10 #include "base/metrics/histogram.h"
11 #include "components/infobars/core/infobar.h"
12 #include "components/infobars/core/infobar_manager.h"
13 #include "components/translate/core/browser/language_state.h"
14 #include "components/translate/core/browser/translate_accept_languages.h"
15 #include "components/translate/core/browser/translate_client.h"
16 #include "components/translate/core/browser/translate_download_manager.h"
17 #include "components/translate/core/browser/translate_driver.h"
18 #include "components/translate/core/browser/translate_manager.h"
19 #include "components/translate/core/common/translate_constants.h"
20 #include "grit/components_strings.h"
21 #include "ui/base/l10n/l10n_util.h"
22 
23 namespace {
24 
25 // Counts used to decide whether infobars should be shown.
26 // Android and iOS implementations do not offer a drop down (for space reasons),
27 // so we are more aggressive about showing the shortcut to never translate.
28 // The "Always Translate" option is always shown on iOS and Android.
29 #if defined(OS_ANDROID)
30 const int kAlwaysTranslateMinCount = 1;
31 const int kNeverTranslateMinCount = 1;
32 #elif defined(OS_IOS)
33 // The iOS implementation, like the Android implementation, shows the "Never
34 // translate" infobar after two denials. There is an offset of one because on
35 // Android the last event is not counted.
36 const int kAlwaysTranslateMinCount = 1;
37 const int kNeverTranslateMinCount = 2;
38 #else
39 const int kAlwaysTranslateMinCount = 3;
40 const int kNeverTranslateMinCount = 3;
41 #endif
42 
43 }  // namespace
44 
45 const size_t TranslateInfoBarDelegate::kNoIndex = TranslateUIDelegate::NO_INDEX;
46 
~TranslateInfoBarDelegate()47 TranslateInfoBarDelegate::~TranslateInfoBarDelegate() {
48 }
49 
50 // static
Create(bool replace_existing_infobar,const base::WeakPtr<TranslateManager> & translate_manager,infobars::InfoBarManager * infobar_manager,bool is_off_the_record,translate::TranslateStep step,const std::string & original_language,const std::string & target_language,TranslateErrors::Type error_type,bool triggered_from_menu)51 void TranslateInfoBarDelegate::Create(
52     bool replace_existing_infobar,
53     const base::WeakPtr<TranslateManager>& translate_manager,
54     infobars::InfoBarManager* infobar_manager,
55     bool is_off_the_record,
56     translate::TranslateStep step,
57     const std::string& original_language,
58     const std::string& target_language,
59     TranslateErrors::Type error_type,
60     bool triggered_from_menu) {
61   DCHECK(translate_manager);
62   DCHECK(infobar_manager);
63 
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
70       // context menu.
71       DCHECK(step == translate::TRANSLATE_STEP_TRANSLATING ||
72              step == translate::TRANSLATE_STEP_AFTER_TRANSLATE);
73       DCHECK_EQ(translate::kUnknownLanguageCode, original_language);
74     }
75   }
76 
77   // Do not create the after translate infobar if we are auto translating.
78   TranslateClient* translate_client = translate_manager->translate_client();
79   if (((step == translate::TRANSLATE_STEP_AFTER_TRANSLATE) ||
80        (step == translate::TRANSLATE_STEP_TRANSLATING)) &&
81       translate_manager->GetLanguageState().InTranslateNavigation()) {
82     return;
83   }
84 
85   // Find any existing translate infobar delegate.
86   infobars::InfoBar* old_infobar = NULL;
87   TranslateInfoBarDelegate* old_delegate = NULL;
88   for (size_t i = 0; i < infobar_manager->infobar_count(); ++i) {
89     old_infobar = infobar_manager->infobar_at(i);
90     old_delegate = old_infobar->delegate()->AsTranslateInfoBarDelegate();
91     if (old_delegate) {
92       if (!replace_existing_infobar)
93         return;
94       break;
95     }
96   }
97 
98   // Add the new delegate.
99   scoped_ptr<infobars::InfoBar> infobar(translate_client->CreateInfoBar(
100       scoped_ptr<TranslateInfoBarDelegate>(new TranslateInfoBarDelegate(
101           translate_manager, is_off_the_record, step, old_delegate,
102           original_language, target_language, error_type,
103           triggered_from_menu))));
104   if (old_delegate)
105     infobar_manager->ReplaceInfoBar(old_infobar, infobar.Pass());
106   else
107     infobar_manager->AddInfoBar(infobar.Pass());
108 }
109 
UpdateOriginalLanguageIndex(size_t language_index)110 void TranslateInfoBarDelegate::UpdateOriginalLanguageIndex(
111     size_t language_index) {
112   ui_delegate_.UpdateOriginalLanguageIndex(language_index);
113 }
114 
UpdateTargetLanguageIndex(size_t language_index)115 void TranslateInfoBarDelegate::UpdateTargetLanguageIndex(
116     size_t language_index) {
117   ui_delegate_.UpdateTargetLanguageIndex(language_index);
118 }
119 
Translate()120 void TranslateInfoBarDelegate::Translate() {
121   ui_delegate_.Translate();
122 }
123 
RevertTranslation()124 void TranslateInfoBarDelegate::RevertTranslation() {
125   ui_delegate_.RevertTranslation();
126   infobar()->RemoveSelf();
127 }
128 
ReportLanguageDetectionError()129 void TranslateInfoBarDelegate::ReportLanguageDetectionError() {
130   if (translate_manager_)
131     translate_manager_->ReportLanguageDetectionError();
132 }
133 
TranslationDeclined()134 void TranslateInfoBarDelegate::TranslationDeclined() {
135   ui_delegate_.TranslationDeclined(false);
136 }
137 
IsTranslatableLanguageByPrefs()138 bool TranslateInfoBarDelegate::IsTranslatableLanguageByPrefs() {
139   TranslateClient* client = translate_manager_->translate_client();
140   scoped_ptr<TranslatePrefs> translate_prefs(client->GetTranslatePrefs());
141   TranslateAcceptLanguages* accept_languages =
142       client->GetTranslateAcceptLanguages();
143   return translate_prefs->CanTranslateLanguage(accept_languages,
144                                                original_language_code());
145 }
146 
ToggleTranslatableLanguageByPrefs()147 void TranslateInfoBarDelegate::ToggleTranslatableLanguageByPrefs() {
148   if (ui_delegate_.IsLanguageBlocked()) {
149     ui_delegate_.SetLanguageBlocked(false);
150   } else {
151     ui_delegate_.SetLanguageBlocked(true);
152     infobar()->RemoveSelf();
153   }
154 }
155 
IsSiteBlacklisted()156 bool TranslateInfoBarDelegate::IsSiteBlacklisted() {
157   return ui_delegate_.IsSiteBlacklisted();
158 }
159 
ToggleSiteBlacklist()160 void TranslateInfoBarDelegate::ToggleSiteBlacklist() {
161   if (ui_delegate_.IsSiteBlacklisted()) {
162     ui_delegate_.SetSiteBlacklist(false);
163   } else {
164     ui_delegate_.SetSiteBlacklist(true);
165     infobar()->RemoveSelf();
166   }
167 }
168 
ShouldAlwaysTranslate()169 bool TranslateInfoBarDelegate::ShouldAlwaysTranslate() {
170   return ui_delegate_.ShouldAlwaysTranslate();
171 }
172 
ToggleAlwaysTranslate()173 void TranslateInfoBarDelegate::ToggleAlwaysTranslate() {
174   ui_delegate_.SetAlwaysTranslate(!ui_delegate_.ShouldAlwaysTranslate());
175 }
176 
AlwaysTranslatePageLanguage()177 void TranslateInfoBarDelegate::AlwaysTranslatePageLanguage() {
178   DCHECK(!ui_delegate_.ShouldAlwaysTranslate());
179   ui_delegate_.SetAlwaysTranslate(true);
180   Translate();
181 }
182 
NeverTranslatePageLanguage()183 void TranslateInfoBarDelegate::NeverTranslatePageLanguage() {
184   DCHECK(!ui_delegate_.IsLanguageBlocked());
185   ui_delegate_.SetLanguageBlocked(true);
186   infobar()->RemoveSelf();
187 }
188 
GetMessageInfoBarText()189 base::string16 TranslateInfoBarDelegate::GetMessageInfoBarText() {
190   if (step_ == translate::TRANSLATE_STEP_TRANSLATING) {
191     base::string16 target_language_name =
192         language_name_at(target_language_index());
193     return l10n_util::GetStringFUTF16(IDS_TRANSLATE_INFOBAR_TRANSLATING_TO,
194                                       target_language_name);
195   }
196 
197   DCHECK_EQ(translate::TRANSLATE_STEP_TRANSLATE_ERROR, step_);
198   UMA_HISTOGRAM_ENUMERATION("Translate.ShowErrorInfobar",
199                             error_type_,
200                             TranslateErrors::TRANSLATE_ERROR_MAX);
201   ui_delegate_.OnErrorShown(error_type_);
202   switch (error_type_) {
203     case TranslateErrors::NETWORK:
204       return l10n_util::GetStringUTF16(
205           IDS_TRANSLATE_INFOBAR_ERROR_CANT_CONNECT);
206     case TranslateErrors::INITIALIZATION_ERROR:
207     case TranslateErrors::TRANSLATION_ERROR:
208       return l10n_util::GetStringUTF16(
209           IDS_TRANSLATE_INFOBAR_ERROR_CANT_TRANSLATE);
210     case TranslateErrors::UNKNOWN_LANGUAGE:
211       return l10n_util::GetStringUTF16(
212           IDS_TRANSLATE_INFOBAR_UNKNOWN_PAGE_LANGUAGE);
213     case TranslateErrors::UNSUPPORTED_LANGUAGE:
214       return l10n_util::GetStringFUTF16(
215           IDS_TRANSLATE_INFOBAR_UNSUPPORTED_PAGE_LANGUAGE,
216           language_name_at(target_language_index()));
217     case TranslateErrors::IDENTICAL_LANGUAGES:
218       return l10n_util::GetStringFUTF16(
219           IDS_TRANSLATE_INFOBAR_ERROR_SAME_LANGUAGE,
220           language_name_at(target_language_index()));
221     default:
222       NOTREACHED();
223       return base::string16();
224   }
225 }
226 
GetMessageInfoBarButtonText()227 base::string16 TranslateInfoBarDelegate::GetMessageInfoBarButtonText() {
228   if (step_ != translate::TRANSLATE_STEP_TRANSLATE_ERROR) {
229     DCHECK_EQ(translate::TRANSLATE_STEP_TRANSLATING, step_);
230   } else if ((error_type_ != TranslateErrors::IDENTICAL_LANGUAGES) &&
231              (error_type_ != TranslateErrors::UNKNOWN_LANGUAGE)) {
232     return l10n_util::GetStringUTF16(
233         (error_type_ == TranslateErrors::UNSUPPORTED_LANGUAGE) ?
234         IDS_TRANSLATE_INFOBAR_REVERT : IDS_TRANSLATE_INFOBAR_RETRY);
235   }
236   return base::string16();
237 }
238 
MessageInfoBarButtonPressed()239 void TranslateInfoBarDelegate::MessageInfoBarButtonPressed() {
240   DCHECK_EQ(translate::TRANSLATE_STEP_TRANSLATE_ERROR, step_);
241   if (error_type_ == TranslateErrors::UNSUPPORTED_LANGUAGE) {
242     RevertTranslation();
243     return;
244   }
245   // This is the "Try again..." case.
246   DCHECK(translate_manager_);
247   translate_manager_->TranslatePage(
248       original_language_code(), target_language_code(), false);
249 }
250 
ShouldShowMessageInfoBarButton()251 bool TranslateInfoBarDelegate::ShouldShowMessageInfoBarButton() {
252   return !GetMessageInfoBarButtonText().empty();
253 }
254 
ShouldShowNeverTranslateShortcut()255 bool TranslateInfoBarDelegate::ShouldShowNeverTranslateShortcut() {
256   DCHECK_EQ(translate::TRANSLATE_STEP_BEFORE_TRANSLATE, step_);
257   return !is_off_the_record_ &&
258       (prefs_->GetTranslationDeniedCount(original_language_code()) >=
259           kNeverTranslateMinCount);
260 }
261 
ShouldShowAlwaysTranslateShortcut()262 bool TranslateInfoBarDelegate::ShouldShowAlwaysTranslateShortcut() {
263   DCHECK_EQ(translate::TRANSLATE_STEP_BEFORE_TRANSLATE, step_);
264   return !is_off_the_record_ &&
265       (prefs_->GetTranslationAcceptedCount(original_language_code()) >=
266           kAlwaysTranslateMinCount);
267 }
268 
269 // static
GetAfterTranslateStrings(std::vector<base::string16> * strings,bool * swap_languages,bool autodetermined_source_language)270 void TranslateInfoBarDelegate::GetAfterTranslateStrings(
271     std::vector<base::string16>* strings,
272     bool* swap_languages,
273     bool autodetermined_source_language) {
274   DCHECK(strings);
275 
276   if (autodetermined_source_language) {
277     size_t offset;
278     base::string16 text = l10n_util::GetStringFUTF16(
279         IDS_TRANSLATE_INFOBAR_AFTER_MESSAGE_AUTODETERMINED_SOURCE_LANGUAGE,
280         base::string16(),
281         &offset);
282 
283     strings->push_back(text.substr(0, offset));
284     strings->push_back(text.substr(offset));
285     return;
286   }
287   DCHECK(swap_languages);
288 
289   std::vector<size_t> offsets;
290   base::string16 text = l10n_util::GetStringFUTF16(
291       IDS_TRANSLATE_INFOBAR_AFTER_MESSAGE, base::string16(), base::string16(),
292       &offsets);
293   DCHECK_EQ(2U, offsets.size());
294 
295   *swap_languages = (offsets[0] > offsets[1]);
296   if (*swap_languages)
297     std::swap(offsets[0], offsets[1]);
298 
299   strings->push_back(text.substr(0, offsets[0]));
300   strings->push_back(text.substr(offsets[0], offsets[1] - offsets[0]));
301   strings->push_back(text.substr(offsets[1]));
302 }
303 
GetTranslateDriver()304 TranslateDriver* TranslateInfoBarDelegate::GetTranslateDriver() {
305   if (!translate_manager_)
306     return NULL;
307 
308   return translate_manager_->translate_client()->GetTranslateDriver();
309 }
310 
TranslateInfoBarDelegate(const base::WeakPtr<TranslateManager> & translate_manager,bool is_off_the_record,translate::TranslateStep step,TranslateInfoBarDelegate * old_delegate,const std::string & original_language,const std::string & target_language,TranslateErrors::Type error_type,bool triggered_from_menu)311 TranslateInfoBarDelegate::TranslateInfoBarDelegate(
312     const base::WeakPtr<TranslateManager>& translate_manager,
313     bool is_off_the_record,
314     translate::TranslateStep step,
315     TranslateInfoBarDelegate* old_delegate,
316     const std::string& original_language,
317     const std::string& target_language,
318     TranslateErrors::Type error_type,
319     bool triggered_from_menu)
320     : infobars::InfoBarDelegate(),
321       is_off_the_record_(is_off_the_record),
322       step_(step),
323       background_animation_(NONE),
324       ui_delegate_(translate_manager, original_language, target_language),
325       translate_manager_(translate_manager),
326       error_type_(error_type),
327       prefs_(translate_manager->translate_client()->GetTranslatePrefs()),
328       triggered_from_menu_(triggered_from_menu) {
329   DCHECK_NE((step_ == translate::TRANSLATE_STEP_TRANSLATE_ERROR),
330             (error_type_ == TranslateErrors::NONE));
331   DCHECK(translate_manager_);
332 
333   if (old_delegate && (old_delegate->is_error() != is_error()))
334     background_animation_ = is_error() ? NORMAL_TO_ERROR : ERROR_TO_NORMAL;
335 }
336 
InfoBarDismissed()337 void TranslateInfoBarDelegate::InfoBarDismissed() {
338   if (step_ != translate::TRANSLATE_STEP_BEFORE_TRANSLATE)
339     return;
340 
341   // The user closed the infobar without clicking the translate button.
342   TranslationDeclined();
343   UMA_HISTOGRAM_BOOLEAN("Translate.DeclineTranslateCloseInfobar", true);
344 }
345 
GetIconID() const346 int TranslateInfoBarDelegate::GetIconID() const {
347   return translate_manager_->translate_client()->GetInfobarIconID();
348 }
349 
GetInfoBarType() const350 infobars::InfoBarDelegate::Type TranslateInfoBarDelegate::GetInfoBarType()
351     const {
352   return PAGE_ACTION_TYPE;
353 }
354 
ShouldExpire(const NavigationDetails & details) const355 bool TranslateInfoBarDelegate::ShouldExpire(
356     const NavigationDetails& details) const {
357   // Note: we allow closing this infobar even if the main frame navigation
358   // was programmatic and not initiated by the user - crbug.com/70261 .
359   if (!details.is_navigation_to_different_page && !details.is_main_frame)
360     return false;
361 
362   return infobars::InfoBarDelegate::ShouldExpireInternal(details);
363 }
364 
365 TranslateInfoBarDelegate*
AsTranslateInfoBarDelegate()366     TranslateInfoBarDelegate::AsTranslateInfoBarDelegate() {
367   return this;
368 }
369