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
24const size_t TranslateInfoBarDelegate::kNoIndex = static_cast<size_t>(-1);
25
26// static
27TranslateInfoBarDelegate* 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
49TranslateInfoBarDelegate* 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
58TranslateInfoBarDelegate::~TranslateInfoBarDelegate() {
59}
60
61std::string TranslateInfoBarDelegate::GetLanguageCodeAt(size_t index) const {
62  DCHECK_LT(index, GetLanguageCount());
63  return languages_[index].first;
64}
65
66string16 TranslateInfoBarDelegate::GetLanguageDisplayableNameAt(
67    size_t index) const {
68  DCHECK_LT(index, GetLanguageCount());
69  return languages_[index].second;
70}
71
72std::string TranslateInfoBarDelegate::GetOriginalLanguageCode() const {
73  return (original_language_index() == kNoIndex) ?
74      chrome::kUnknownLanguageCode :
75      GetLanguageCodeAt(original_language_index());
76}
77
78std::string TranslateInfoBarDelegate::GetTargetLanguageCode() const {
79  return GetLanguageCodeAt(target_language_index());
80}
81
82void 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
91void 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
100void 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
112void TranslateInfoBarDelegate::RevertTranslation() {
113  TranslateManager::GetInstance()->RevertTranslation(tab_contents_);
114  tab_contents_->RemoveInfoBar(this);
115}
116
117void TranslateInfoBarDelegate::ReportLanguageDetectionError() {
118  TranslateManager::GetInstance()->ReportLanguageDetectionError(tab_contents_);
119}
120
121void 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
138bool TranslateInfoBarDelegate::IsLanguageBlacklisted() {
139  return prefs_.IsLanguageBlacklisted(GetOriginalLanguageCode());
140}
141
142void 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
152bool TranslateInfoBarDelegate::IsSiteBlacklisted() {
153  std::string host = GetPageHost();
154  return !host.empty() && prefs_.IsSiteBlacklisted(host);
155}
156
157void 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
170bool TranslateInfoBarDelegate::ShouldAlwaysTranslate() {
171  return prefs_.IsLanguagePairWhitelisted(GetOriginalLanguageCode(),
172                                          GetTargetLanguageCode());
173}
174
175void 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
184void 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
192void 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
199string16 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
231string16 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
243void 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
254bool TranslateInfoBarDelegate::ShouldShowMessageInfoBarButton() {
255  return !GetMessageInfoBarButtonText().empty();
256}
257
258bool TranslateInfoBarDelegate::ShouldShowNeverTranslateButton() {
259  DCHECK_EQ(BEFORE_TRANSLATE, type_);
260  return !tab_contents()->profile()->IsOffTheRecord() &&
261      (prefs_.GetTranslationDeniedCount(GetOriginalLanguageCode()) >= 3);
262}
263
264bool TranslateInfoBarDelegate::ShouldShowAlwaysTranslateButton() {
265  DCHECK_EQ(BEFORE_TRANSLATE, type_);
266  return !tab_contents()->profile()->IsOffTheRecord() &&
267      (prefs_.GetTranslationAcceptedCount(GetOriginalLanguageCode()) >= 3);
268}
269
270void 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
279string16 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
286void 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
306TranslateInfoBarDelegate::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
353void 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
362void TranslateInfoBarDelegate::InfoBarClosed() {
363  delete this;
364}
365
366SkBitmap* TranslateInfoBarDelegate::GetIcon() const {
367  return ResourceBundle::GetSharedInstance().GetBitmapNamed(
368      IDR_INFOBAR_TRANSLATE);
369}
370
371InfoBarDelegate::Type TranslateInfoBarDelegate::GetInfoBarType() const {
372  return PAGE_ACTION_TYPE;
373}
374
375TranslateInfoBarDelegate*
376    TranslateInfoBarDelegate::AsTranslateInfoBarDelegate() {
377  return this;
378}
379
380std::string TranslateInfoBarDelegate::GetPageHost() {
381  NavigationEntry* entry = tab_contents_->controller().GetActiveEntry();
382  return entry ? entry->url().HostNoBrackets() : std::string();
383}
384