diff options
author | jcivelli@chromium.org <jcivelli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-09 20:23:15 +0000 |
---|---|---|
committer | jcivelli@chromium.org <jcivelli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-09 20:23:15 +0000 |
commit | e88079e395bf606c55e746bd618414abbcc6bc31 (patch) | |
tree | 83088d953b0c9cf512ebb62f89ffcbb605cc2416 | |
parent | 76b2ad7e89d95b6b3934563cf625dfc493fca4c5 (diff) | |
download | chromium_src-e88079e395bf606c55e746bd618414abbcc6bc31.zip chromium_src-e88079e395bf606c55e746bd618414abbcc6bc31.tar.gz chromium_src-e88079e395bf606c55e746bd618414abbcc6bc31.tar.bz2 |
Refactored the translate infobars.
Since some work is needed on Linux and Mac to use the new translate infobar delegate, I created a new version of the refactored classes instead of replacing them. Once Linux and Mac use the new classes, we can make the swicth.
The TranslateInfoBarDelegate now contains all states, so there is no more logic on the infobar classes.
I broke down the single infobar class into multiple ones (there is now an infobar for each state: before translate, translating, after translate, error): it makes the code simpler.
I had to fix ReplaceInfoBar on Windows as it was not working properly.
BUG=40828
TEST=Test thoroughly the translate feature.
Review URL: http://codereview.chromium.org/2602003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@49307 0039d316-1c4b-4281-b951-d872f2087c98
35 files changed, 3651 insertions, 217 deletions
diff --git a/app/l10n_util.cc b/app/l10n_util.cc index d1c53f1..e50bbb8 100644 --- a/app/l10n_util.cc +++ b/app/l10n_util.cc @@ -648,6 +648,15 @@ std::wstring GetStringF(int message_id, string16(), string16(), offsets)); } +string16 GetStringFUTF16(int message_id, const string16& a, size_t* offset) { + DCHECK(offset); + std::vector<size_t> offsets; + string16 result = GetStringFUTF16(message_id, a, string16(), &offsets); + DCHECK(offsets.size() == 1); + *offset = offsets[0]; + return result; +} + string16 GetStringFUTF16(int message_id, const string16& a, const string16& b, diff --git a/app/l10n_util.h b/app/l10n_util.h index e0100c0..df820ba 100644 --- a/app/l10n_util.h +++ b/app/l10n_util.h @@ -149,6 +149,9 @@ std::wstring GetStringF(int message_id, const std::wstring& b, std::vector<size_t>* offsets); string16 GetStringFUTF16(int message_id, + const string16& a, + size_t* offset); +string16 GetStringFUTF16(int message_id, const string16& a, const string16& b, std::vector<size_t>* offsets); diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index e58e07e..b8618b9 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -6921,6 +6921,9 @@ Keep your key file in a safe place. You will need it to create new versions of y <message name="IDS_TRANSLATE_INFOBAR_TRANSLATING" desc="Text shown when page is currently being translated by servers"> Translating... </message> + <message name="IDS_TRANSLATE_INFOBAR_TRANSLATING_TO" desc="Text shown when page is currently being translated by servers"> + Translating page to <ph name="language">$1<ex>English</ex></ph>... + </message> <message name="IDS_TRANSLATE_INFOBAR_AFTER_MESSAGE" desc="Message that page has been translated from one language to another"> This page has been translated from<ph name="original_language">$1<ex>French</ex></ph>to<ph name="language_language">$2<ex>German</ex></ph> </message> diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc index fa58807..4df35b4 100644 --- a/chrome/browser/browser_main.cc +++ b/chrome/browser/browser_main.cc @@ -56,7 +56,11 @@ #include "chrome/browser/search_engines/template_url_model.h" #include "chrome/browser/search_engines/template_url_prepopulate_data.h" #include "chrome/browser/shell_integration.h" +#if !defined(OS_WIN) #include "chrome/browser/translate/translate_manager.h" +#else +#include "chrome/browser/translate/translate_manager2.h" +#endif #include "chrome/browser/user_data_manager.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" @@ -1129,7 +1133,11 @@ int BrowserMain(const MainFunctionParams& parameters) { return ResultCodes::MACHINE_LEVEL_INSTALL_EXISTS; // Create the TranslateManager singleton. +#if defined(OS_WIN) + Singleton<TranslateManager2>::get(); +#else Singleton<TranslateManager>::get(); +#endif #if defined(OS_MACOSX) if (!parsed_command_line.HasSwitch(switches::kNoFirstRun)) { diff --git a/chrome/browser/tab_contents/infobar_delegate.h b/chrome/browser/tab_contents/infobar_delegate.h index 3c8b7145..fc18cab 100644 --- a/chrome/browser/tab_contents/infobar_delegate.h +++ b/chrome/browser/tab_contents/infobar_delegate.h @@ -16,6 +16,7 @@ class ConfirmInfoBarDelegate; class CrashedExtensionInfoBarDelegate; class ExtensionInfoBarDelegate; class TranslateInfoBarDelegate; +class TranslateInfoBarDelegate2; class InfoBar; class LinkInfoBarDelegate; class SkBitmap; @@ -111,6 +112,9 @@ class InfoBarDelegate { virtual TranslateInfoBarDelegate* AsTranslateInfoBarDelegate() { return NULL; } + virtual TranslateInfoBarDelegate2* AsTranslateInfoBarDelegate2() { + return NULL; + } // Returns a pointer to the ExtensionInfoBarDelegate interface, if // implemented. diff --git a/chrome/browser/tab_contents/render_view_context_menu.cc b/chrome/browser/tab_contents/render_view_context_menu.cc index d3ea7d6..df97268 100644 --- a/chrome/browser/tab_contents/render_view_context_menu.cc +++ b/chrome/browser/tab_contents/render_view_context_menu.cc @@ -35,6 +35,9 @@ #include "chrome/browser/tab_contents/navigation_entry.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/translate/translate_manager.h" +#if defined(OS_WIN) +#include "chrome/browser/translate/translate_manager2.h" +#endif #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" @@ -1135,8 +1138,13 @@ void RenderViewContextMenu::ExecuteCommand(int id) { TranslatePrefs prefs(profile_->GetPrefs()); prefs.RemoveLanguageFromBlacklist(original_lang); prefs.RemoveSiteFromBlacklist(params_.page_url.HostNoBrackets()); +#if defined(OS_WIN) + Singleton<TranslateManager2>::get()->TranslatePage( + source_tab_contents_, original_lang, target_lang); +#else Singleton<TranslateManager>::get()->TranslatePage( source_tab_contents_, original_lang, target_lang); +#endif break; } diff --git a/chrome/browser/translate/languages_menu_model2.cc b/chrome/browser/translate/languages_menu_model2.cc new file mode 100644 index 0000000..453c27d --- /dev/null +++ b/chrome/browser/translate/languages_menu_model2.cc @@ -0,0 +1,49 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/translate/languages_menu_model2.h" + +#include "base/histogram.h" +#include "chrome/browser/translate/translate_infobar_delegate2.h" + +LanguagesMenuModel2::LanguagesMenuModel2( + TranslateInfoBarDelegate2* translate_delegate, + LanguageType language_type) + : ALLOW_THIS_IN_INITIALIZER_LIST(menus::SimpleMenuModel(this)), + translate_infobar_delegate_(translate_delegate), + language_type_(language_type) { + for (int i = 0; i < translate_delegate->GetLanguageCount(); ++i) + AddCheckItem(i, translate_delegate->GetLanguageDisplayableNameAt(i)); +} + +LanguagesMenuModel2::~LanguagesMenuModel2() { +} + +bool LanguagesMenuModel2::IsCommandIdChecked(int command_id) const { + if (language_type_ == ORIGINAL) + return command_id == translate_infobar_delegate_->original_language_index(); + return command_id == translate_infobar_delegate_->target_language_index(); +} + +bool LanguagesMenuModel2::IsCommandIdEnabled(int command_id) const { + // Prevent from having the same language selectable in original and target. + if (language_type_ == ORIGINAL) + return true; + return command_id != translate_infobar_delegate_->original_language_index(); +} + +bool LanguagesMenuModel2::GetAcceleratorForCommandId( + int command_id, menus::Accelerator* accelerator) { + return false; +} + +void LanguagesMenuModel2::ExecuteCommand(int command_id) { + if (language_type_ == ORIGINAL) { + UMA_HISTOGRAM_COUNTS("Translate.ModifyOriginalLang", 1); + translate_infobar_delegate_->SetOriginalLanguage(command_id); + return; + } + UMA_HISTOGRAM_COUNTS("Translate.ModifyTargetLang", 1); + translate_infobar_delegate_->SetTargetLanguage(command_id); +} diff --git a/chrome/browser/translate/languages_menu_model2.h b/chrome/browser/translate/languages_menu_model2.h new file mode 100644 index 0000000..84860d0 --- /dev/null +++ b/chrome/browser/translate/languages_menu_model2.h @@ -0,0 +1,40 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_TRANSLATE_LANGUAGES_MENU_MODEL2_H_ +#define CHROME_BROWSER_TRANSLATE_LANGUAGES_MENU_MODEL2_H_ + +#include "app/menus/simple_menu_model.h" + +class TranslateInfoBarDelegate2; +class String16; + +// A menu model that builds the contents of the language menus in the translate +// infobar. This menu has only one level (no submenus). +class LanguagesMenuModel2 : public menus::SimpleMenuModel, + public menus::SimpleMenuModel::Delegate { + public: + enum LanguageType { + ORIGINAL, + TARGET + }; + LanguagesMenuModel2(TranslateInfoBarDelegate2* translate_delegate, + LanguageType language_type); + virtual ~LanguagesMenuModel2(); + + // menus::SimpleMenuModel::Delegate implementation: + virtual bool IsCommandIdChecked(int command_id) const; + virtual bool IsCommandIdEnabled(int command_id) const; + virtual bool GetAcceleratorForCommandId(int command_id, + menus::Accelerator* accelerator); + virtual void ExecuteCommand(int command_id); + + private: + TranslateInfoBarDelegate2* translate_infobar_delegate_; + LanguageType language_type_; + + DISALLOW_COPY_AND_ASSIGN(LanguagesMenuModel2); +}; + +#endif // CHROME_BROWSER_TRANSLATE_LANGUAGES_MENU_MODEL2_H_ diff --git a/chrome/browser/translate/options_menu_model.cc b/chrome/browser/translate/options_menu_model.cc index 5f0dd66..5eadbbf 100644 --- a/chrome/browser/translate/options_menu_model.cc +++ b/chrome/browser/translate/options_menu_model.cc @@ -6,6 +6,8 @@ #include "app/l10n_util.h" #include "chrome/app/chrome_dll_resource.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/translate/translate_infobar_delegate2.h" #include "chrome/browser/translate/translate_infobars_delegates.h" #include "grit/generated_resources.h" @@ -16,6 +18,21 @@ OptionsMenuModel::OptionsMenuModel(menus::SimpleMenuModel::Delegate* delegate, translate_delegate->original_lang_code()); string16 target_language = translate_delegate->GetDisplayNameForLocale( translate_delegate->target_lang_code()); + Init(original_language, target_language); +} + +OptionsMenuModel::OptionsMenuModel(menus::SimpleMenuModel::Delegate* delegate, + TranslateInfoBarDelegate2* translate_delegate) + : menus::SimpleMenuModel(delegate) { + string16 original_language = translate_delegate->GetLanguageDisplayableNameAt( + translate_delegate->original_language_index()); + string16 target_language = translate_delegate->GetLanguageDisplayableNameAt( + translate_delegate->target_language_index()); + Init(original_language, target_language); +} + +void OptionsMenuModel::Init(const string16& original_language, + const string16& target_language) { AddCheckItem(IDC_TRANSLATE_OPTIONS_ALWAYS, l10n_util::GetStringFUTF16(IDS_TRANSLATE_INFOBAR_OPTIONS_ALWAYS, original_language, target_language)); @@ -32,4 +49,3 @@ OptionsMenuModel::OptionsMenuModel(menus::SimpleMenuModel::Delegate* delegate, OptionsMenuModel::~OptionsMenuModel() { } - diff --git a/chrome/browser/translate/options_menu_model.h b/chrome/browser/translate/options_menu_model.h index c6de2f3..b4a5a21 100644 --- a/chrome/browser/translate/options_menu_model.h +++ b/chrome/browser/translate/options_menu_model.h @@ -8,16 +8,24 @@ #include "app/menus/simple_menu_model.h" class TranslateInfoBarDelegate; +class TranslateInfoBarDelegate2; +class String16; // A menu model that builds the contents of the options menu in the translate // infobar. This menu has only one level (no submenus). class OptionsMenuModel : public menus::SimpleMenuModel { public: - explicit OptionsMenuModel(menus::SimpleMenuModel::Delegate* menu_delegate, - TranslateInfoBarDelegate* translate_delegate); + // TODO(jcivelli): remove this constructor once we have migrated to the new + // translate infobars. + OptionsMenuModel(menus::SimpleMenuModel::Delegate* menu_delegate, + TranslateInfoBarDelegate* translate_delegate); + OptionsMenuModel(menus::SimpleMenuModel::Delegate* menu_delegate, + TranslateInfoBarDelegate2* translate_delegate); virtual ~OptionsMenuModel(); private: + void Init(const string16& original_language, const string16& target_language); + DISALLOW_COPY_AND_ASSIGN(OptionsMenuModel); }; diff --git a/chrome/browser/translate/options_menu_model2.cc b/chrome/browser/translate/options_menu_model2.cc new file mode 100644 index 0000000..5fb225f --- /dev/null +++ b/chrome/browser/translate/options_menu_model2.cc @@ -0,0 +1,114 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/translate/options_menu_model2.h" + +#include "app/l10n_util.h" +#include "base/histogram.h" +#include "chrome/app/chrome_dll_resource.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/translate/translate_infobar_delegate2.h" +#include "grit/generated_resources.h" +#include "grit/locale_settings.h" + +OptionsMenuModel2::OptionsMenuModel2( + TranslateInfoBarDelegate2* translate_delegate) + : ALLOW_THIS_IN_INITIALIZER_LIST(menus::SimpleMenuModel(this)), + translate_infobar_delegate_(translate_delegate) { + string16 original_language = translate_delegate->GetLanguageDisplayableNameAt( + translate_delegate->original_language_index()); + string16 target_language = translate_delegate->GetLanguageDisplayableNameAt( + translate_delegate->target_language_index()); + + // Populate the menu. + AddCheckItem(IDC_TRANSLATE_OPTIONS_ALWAYS, + l10n_util::GetStringFUTF16(IDS_TRANSLATE_INFOBAR_OPTIONS_ALWAYS, + original_language, target_language)); + AddCheckItem(IDC_TRANSLATE_OPTIONS_NEVER_TRANSLATE_LANG, + l10n_util::GetStringFUTF16( + IDS_TRANSLATE_INFOBAR_OPTIONS_NEVER_TRANSLATE_LANG, + original_language)); + AddCheckItem(IDC_TRANSLATE_OPTIONS_NEVER_TRANSLATE_SITE, + l10n_util::GetStringUTF16( + IDS_TRANSLATE_INFOBAR_OPTIONS_NEVER_TRANSLATE_SITE)); + AddItemWithStringId(IDC_TRANSLATE_OPTIONS_ABOUT, + IDS_TRANSLATE_INFOBAR_OPTIONS_ABOUT); +} + +OptionsMenuModel2::~OptionsMenuModel2() { +} + +bool OptionsMenuModel2::IsCommandIdChecked(int command_id) const { + switch (command_id) { + case IDC_TRANSLATE_OPTIONS_NEVER_TRANSLATE_LANG: + return translate_infobar_delegate_->IsLanguageBlacklisted(); + + case IDC_TRANSLATE_OPTIONS_NEVER_TRANSLATE_SITE: + return translate_infobar_delegate_->IsSiteBlacklisted(); + + case IDC_TRANSLATE_OPTIONS_ALWAYS: + return translate_infobar_delegate_->ShouldAlwaysTranslate(); + + default: + NOTREACHED() << "Invalid command_id from menu"; + break; + } + return false; +} + +bool OptionsMenuModel2::IsCommandIdEnabled(int command_id) const { + switch (command_id) { + case IDC_TRANSLATE_OPTIONS_NEVER_TRANSLATE_LANG : + case IDC_TRANSLATE_OPTIONS_NEVER_TRANSLATE_SITE : + return !translate_infobar_delegate_->ShouldAlwaysTranslate(); + + case IDC_TRANSLATE_OPTIONS_ALWAYS : + return (!translate_infobar_delegate_->IsLanguageBlacklisted() && + !translate_infobar_delegate_->IsSiteBlacklisted()); + + default: + break; + } + return true; +} + +bool OptionsMenuModel2::GetAcceleratorForCommandId( + int command_id, menus::Accelerator* accelerator) { + return false; +} + +void OptionsMenuModel2::ExecuteCommand(int command_id) { + switch (command_id) { + case IDC_TRANSLATE_OPTIONS_NEVER_TRANSLATE_LANG: + UMA_HISTOGRAM_COUNTS("Translate.NeverTranslateLang", 1); + translate_infobar_delegate_->ToggleLanguageBlacklist(); + break; + + case IDC_TRANSLATE_OPTIONS_NEVER_TRANSLATE_SITE: + UMA_HISTOGRAM_COUNTS("Translate.NeverTranslateSite", 1); + translate_infobar_delegate_->ToggleSiteBlacklist(); + break; + + case IDC_TRANSLATE_OPTIONS_ALWAYS: + UMA_HISTOGRAM_COUNTS("Translate.AlwaysTranslateLang", 1); + translate_infobar_delegate_->ToggleAlwaysTranslate(); + break; + + case IDC_TRANSLATE_OPTIONS_ABOUT: { + TabContents* tab_contents = translate_infobar_delegate_->tab_contents(); + if (tab_contents) { + string16 url = l10n_util::GetStringUTF16( + IDS_ABOUT_GOOGLE_TRANSLATE_URL); + tab_contents->OpenURL(GURL(url), GURL(), NEW_FOREGROUND_TAB, + PageTransition::LINK); + } + break; + } + + default: + NOTREACHED() << "Invalid command id from menu."; + break; + } +} diff --git a/chrome/browser/translate/options_menu_model2.h b/chrome/browser/translate/options_menu_model2.h new file mode 100644 index 0000000..35c8df6 --- /dev/null +++ b/chrome/browser/translate/options_menu_model2.h @@ -0,0 +1,36 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_TRANSLATE_OPTIONS_MENU_MODEL2_H_ +#define CHROME_BROWSER_TRANSLATE_OPTIONS_MENU_MODEL2_H_ + +#include "app/menus/simple_menu_model.h" + +class TranslateInfoBarDelegate2; +class String16; + +// A menu model that builds the contents of the options menu in the translate +// infobar. This menu has only one level (no submenus). +class OptionsMenuModel2 : public menus::SimpleMenuModel, + public menus::SimpleMenuModel::Delegate { + public: + explicit OptionsMenuModel2(TranslateInfoBarDelegate2* translate_delegate); + virtual ~OptionsMenuModel2(); + + // menus::SimpleMenuModel::Delegate implementation: + virtual bool IsCommandIdChecked(int command_id) const; + virtual bool IsCommandIdEnabled(int command_id) const; + virtual bool GetAcceleratorForCommandId(int command_id, + menus::Accelerator* accelerator); + virtual void ExecuteCommand(int command_id); + + private: + TranslateInfoBarDelegate2* translate_infobar_delegate_; + + DISALLOW_COPY_AND_ASSIGN(OptionsMenuModel2); +}; + +#endif // CHROME_BROWSER_TRANSLATE_OPTIONS_MENU_MODEL2_H_ + + diff --git a/chrome/browser/translate/translate_infobar_delegate2.cc b/chrome/browser/translate/translate_infobar_delegate2.cc new file mode 100644 index 0000000..9b5395b --- /dev/null +++ b/chrome/browser/translate/translate_infobar_delegate2.cc @@ -0,0 +1,305 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/translate/translate_infobar_delegate2.h" + +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/translate/translate_infobar_view.h" +#include "chrome/browser/translate/translate_manager2.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" + +// static +TranslateInfoBarDelegate2* TranslateInfoBarDelegate2::CreateInstance( + Type type, + TranslateErrors::Type error, + TabContents* tab_contents, + const std::string& original_language, + const std::string& target_language) { + if (!TranslateManager2::IsSupportedLanguage(original_language) || + !TranslateManager2::IsSupportedLanguage(target_language)) { + return NULL; + } + return new TranslateInfoBarDelegate2(type, error, tab_contents, + original_language, target_language); +} + +TranslateInfoBarDelegate2::TranslateInfoBarDelegate2( + Type type, + TranslateErrors::Type error, + TabContents* tab_contents, + const std::string& original_language, + const std::string& target_language) + : InfoBarDelegate(tab_contents), + type_(type), + background_animation_(NONE), + tab_contents_(tab_contents), + original_language_index_(-1), + target_language_index_(-1), + error_(error), + infobar_view_(NULL), + prefs_(tab_contents_->profile()->GetPrefs()) { + DCHECK((type_ != TRANSLATION_ERROR && error == TranslateErrors::NONE) || + (type_ == TRANSLATION_ERROR && error != TranslateErrors::NONE)); + + std::vector<std::string> language_codes; + TranslateManager2::GetSupportedLanguages(&language_codes); + + languages_.reserve(language_codes.size()); + for (std::vector<std::string>::const_iterator iter = language_codes.begin(); + iter != language_codes.end(); ++iter) { + std::string language_code = *iter; + + if (language_code == original_language) + original_language_index_ = iter - language_codes.begin(); + else if (language_code == target_language) + target_language_index_ = iter - language_codes.begin(); + + string16 language_name = GetLanguageDisplayableName(language_code); + // Insert the language in languages_ in alphabetical order. + std::vector<LanguageNamePair>::iterator iter2; + for (iter2 = languages_.begin(); iter2 != languages_.end(); ++iter2) { + if (language_name.compare(iter2->second) < 0) + break; + } + languages_.insert(iter2, LanguageNamePair(language_code, language_name)); + } + DCHECK(original_language_index_ != -1); + DCHECK(target_language_index_ != -1); +} + +int TranslateInfoBarDelegate2::GetLanguageCount() const { + return static_cast<int>(languages_.size()); +} + +const std::string& TranslateInfoBarDelegate2::GetLanguageCodeAt( + int index) const { + DCHECK(index >=0 && index < GetLanguageCount()); + return languages_[index].first; +} + +const string16& TranslateInfoBarDelegate2::GetLanguageDisplayableNameAt( + int index) const { + DCHECK(index >=0 && index < GetLanguageCount()); + return languages_[index].second; +} + +const std::string& TranslateInfoBarDelegate2::GetOriginalLanguageCode() const { + return GetLanguageCodeAt(original_language_index()); +} + +const std::string& TranslateInfoBarDelegate2::GetTargetLanguageCode() const { + return GetLanguageCodeAt(target_language_index()); +} + +void TranslateInfoBarDelegate2::SetOriginalLanguage(int language_index) { + DCHECK(language_index < static_cast<int>(languages_.size())); + original_language_index_ = language_index; + if (infobar_view_) + infobar_view_->OriginalLanguageChanged(); + if (type_ == AFTER_TRANSLATE) + Translate(); +} + +void TranslateInfoBarDelegate2::SetTargetLanguage(int language_index) { + DCHECK(language_index < static_cast<int>(languages_.size())); + target_language_index_ = language_index; + if (infobar_view_) + infobar_view_->TargetLanguageChanged(); + if (type_ == AFTER_TRANSLATE) + Translate(); +} + +bool TranslateInfoBarDelegate2::IsError() { + return type_ == TRANSLATION_ERROR; +} + +void TranslateInfoBarDelegate2::Translate() { + Singleton<TranslateManager2>::get()->TranslatePage( + tab_contents_, + GetLanguageCodeAt(original_language_index()), + GetLanguageCodeAt(target_language_index())); +} + +void TranslateInfoBarDelegate2::RevertTranslation() { + Singleton<TranslateManager2>::get()->RevertTranslation(tab_contents_); + tab_contents_->RemoveInfoBar(this); +} + +void TranslateInfoBarDelegate2::TranslationDeclined() { + // Remember that the user declined the translation so as to prevent showing a + // translate infobar for that page again. (TranslateManager initiates + // translations when getting a LANGUAGE_DETERMINED from the page, which + // happens when a load stops. That could happen multiple times, including + // after the user already declined the translation.) + tab_contents_->language_state().set_translation_declined(true); +} + +void TranslateInfoBarDelegate2::InfoBarDismissed() { + if (type_ != BEFORE_TRANSLATE) + return; + + // The user closed the infobar without clicking the translate button. + TranslationDeclined(); + UMA_HISTOGRAM_COUNTS("Translate.DeclineTranslateCloseInfobar", 1); +} + +SkBitmap* TranslateInfoBarDelegate2::GetIcon() const { + return ResourceBundle::GetSharedInstance().GetBitmapNamed( + IDR_INFOBAR_TRANSLATE); +} + +InfoBarDelegate::Type TranslateInfoBarDelegate2::GetInfoBarType() { + return InfoBarDelegate::PAGE_ACTION_TYPE; +} + +bool TranslateInfoBarDelegate2::IsLanguageBlacklisted() { + const std::string& original_lang = + GetLanguageCodeAt(original_language_index()); + return prefs_.IsLanguageBlacklisted(original_lang); +} + +void TranslateInfoBarDelegate2::ToggleLanguageBlacklist() { + const std::string& original_lang = + GetLanguageCodeAt(original_language_index()); + if (prefs_.IsLanguageBlacklisted(original_lang)) + prefs_.RemoveLanguageFromBlacklist(original_lang); + else + prefs_.BlacklistLanguage(original_lang); +} + +bool TranslateInfoBarDelegate2::IsSiteBlacklisted() { + std::string host = GetPageHost(); + return !host.empty() && prefs_.IsSiteBlacklisted(host); +} + +void TranslateInfoBarDelegate2::ToggleSiteBlacklist() { + std::string host = GetPageHost(); + if (host.empty()) + return; + + if (prefs_.IsSiteBlacklisted(host)) + prefs_.RemoveSiteFromBlacklist(host); + else + prefs_.BlacklistSite(host); +} + +bool TranslateInfoBarDelegate2::ShouldAlwaysTranslate() { + return prefs_.IsLanguagePairWhitelisted(GetOriginalLanguageCode(), + GetTargetLanguageCode()); +} + +void TranslateInfoBarDelegate2::ToggleAlwaysTranslate() { + std::string original_lang = GetOriginalLanguageCode(); + std::string target_lang = GetTargetLanguageCode(); + if (prefs_.IsLanguagePairWhitelisted(original_lang, target_lang)) + prefs_.RemoveLanguagePairFromWhitelist(original_lang, target_lang); + else + prefs_.WhitelistLanguagePair(original_lang, target_lang); +} + +string16 TranslateInfoBarDelegate2::GetMessageInfoBarText() { + switch (type_) { + case TRANSLATING: + return l10n_util::GetStringFUTF16( + IDS_TRANSLATE_INFOBAR_TRANSLATING_TO, + GetLanguageDisplayableNameAt(target_language_index_)); + case TRANSLATION_ERROR: + switch (error_) { + case TranslateErrors::NETWORK: + return l10n_util::GetStringUTF16( + IDS_TRANSLATE_INFOBAR_ERROR_CANT_CONNECT); + case TranslateErrors::INITIALIZATION_ERROR: + case TranslateErrors::TRANSLATION_ERROR: + return l10n_util::GetStringUTF16( + IDS_TRANSLATE_INFOBAR_ERROR_CANT_TRANSLATE); + default: + NOTREACHED(); + return string16(); + } + default: + NOTREACHED(); + return string16(); + } +} + +string16 TranslateInfoBarDelegate2::GetMessageInfoBarButtonText() { + switch (type_) { + case TRANSLATING: + return string16(); + case TRANSLATION_ERROR: + return l10n_util::GetStringUTF16(IDS_TRANSLATE_INFOBAR_RETRY); + default: + NOTREACHED(); + return string16(); + } +} + +void TranslateInfoBarDelegate2::MessageInfoBarButtonPressed() { + DCHECK(type_ == TRANSLATION_ERROR); + Singleton<TranslateManager2>::get()->TranslatePage( + tab_contents_, + GetLanguageCodeAt(original_language_index()), + GetLanguageCodeAt(target_language_index())); +} + +void TranslateInfoBarDelegate2::UpdateBackgroundAnimation( + TranslateInfoBarDelegate2* previous_infobar) { + if (!previous_infobar || previous_infobar->IsError() == IsError()) { + background_animation_ = NONE; + return; + } + background_animation_ = IsError() ? NORMAL_TO_ERROR : ERROR_TO_NORMAL; +} + +std::string TranslateInfoBarDelegate2::GetPageHost() { + NavigationEntry* entry = tab_contents_->controller().GetActiveEntry(); + return entry ? entry->url().HostNoBrackets() : std::string(); +} + +// static +string16 TranslateInfoBarDelegate2::GetLanguageDisplayableName( + const std::string& language_code) { + return l10n_util::GetDisplayNameForLocale( + language_code, g_browser_process->GetApplicationLocale(), true); +} + +// static +void TranslateInfoBarDelegate2::GetAfterTranslateStrings( + std::vector<string16>* strings, bool* swap_languages) { + DCHECK(strings); + DCHECK(swap_languages); + + std::vector<size_t> offsets; + string16 text = + l10n_util::GetStringFUTF16(IDS_TRANSLATE_INFOBAR_AFTER_MESSAGE, + string16(), string16(), &offsets); + DCHECK(offsets.size() == 2U); + + if (offsets[0] > offsets[1]) { + // Target language comes before source. + int tmp = offsets[0]; + offsets[0] = offsets[0]; + offsets[1] = tmp; + *swap_languages = true; + } else { + *swap_languages = false; + } + + strings->push_back(text.substr(0, offsets[0])); + strings->push_back(text.substr(offsets[0], offsets[1])); + strings->push_back(text.substr(offsets[1])); +} + +#if !defined(OS_WIN) && !defined(OS_CHROMEOS) +// Necessary so we link OK on Mac and Linux while the new translate infobars +// are being ported to these platforms. +InfoBar* TranslateInfoBarDelegate2::CreateInfoBar() { + return NULL; +} +#endif diff --git a/chrome/browser/translate/translate_infobar_delegate2.h b/chrome/browser/translate/translate_infobar_delegate2.h new file mode 100644 index 0000000..2129776 --- /dev/null +++ b/chrome/browser/translate/translate_infobar_delegate2.h @@ -0,0 +1,178 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_TRANSLATE_TRANSLATE_INFOBAR_DELEGATE2_H +#define CHROME_BROWSER_TRANSLATE_TRANSLATE_INFOBAR_DELEGATE2_H + +#include <string> +#include <vector> + +#include "chrome/browser/tab_contents/infobar_delegate.h" +#include "chrome/browser/translate/translate_prefs.h" +#include "chrome/common/translate_errors.h" + +class SkBitmap; +class TranslateInfoBarView; + +class TranslateInfoBarDelegate2 : public InfoBarDelegate { + public: + // The different types of infobars that can be shown for translation. + enum Type { + BEFORE_TRANSLATE, + TRANSLATING, + AFTER_TRANSLATE, + TRANSLATION_ERROR + }; + + // The types of background color animations. + enum BackgroundAnimationType { + NONE, + NORMAL_TO_ERROR, + ERROR_TO_NORMAL + }; + + // Factory method. + // The original and target language specified are the ASCII language codes + // (ex: en, fr...). + // Returns NULL if it failed, typically if |original_language| or + // |target_language| is not a supported language. + // |error| should be set to NONE if |infobar_type| is not TRANSLATION_ERROR. + static TranslateInfoBarDelegate2* CreateInstance( + Type infobar_type, + TranslateErrors::Type error, + TabContents* tab_contents, + const std::string& original_language, + const std::string& target_language); + + // Returns the number of languages supported. + int GetLanguageCount() const; + + // Returns the ISO code for the language at |index|. + const std::string& GetLanguageCodeAt(int index) const; + + // Returns the displayable name for the language at |index|. + const string16& GetLanguageDisplayableNameAt(int index) const; + + TabContents* tab_contents() const { return tab_contents_; } + + Type type() const { return type_; } + + int original_language_index() const { return original_language_index_; } + int target_language_index() const { return target_language_index_; } + + // Convenience methods. + const std::string& GetOriginalLanguageCode() const; + const std::string& GetTargetLanguageCode() const; + + // Called by the InfoBar to notify that the original/target language has + // changed and is now the language at |language_index|. + virtual void SetOriginalLanguage(int language_index); + virtual void SetTargetLanguage(int language_index); + + // Returns true if the current infobar indicates an error (in which case it + // should get a yellow background instead of a blue one). + bool IsError(); + + // Returns what kind of background fading effect the infobar should use when + // its is shown. + BackgroundAnimationType background_animation_type() const { + return background_animation_; + } + + virtual void Translate(); + virtual void RevertTranslation(); + + // Called when the user declines to translate a page, by either closing the + // infobar or pressing the "Don't translate" button. + void TranslationDeclined(); + + // InfoBarDelegate implementation: + virtual InfoBar* CreateInfoBar(); + void InfoBarDismissed(); + virtual SkBitmap* GetIcon() const; + virtual InfoBarDelegate::Type GetInfoBarType(); + virtual TranslateInfoBarDelegate2* AsTranslateInfoBarDelegate2() { + return this; + } + + // Methods called by the Options menu delegate. + virtual bool IsLanguageBlacklisted(); + virtual void ToggleLanguageBlacklist(); + virtual bool IsSiteBlacklisted(); + virtual void ToggleSiteBlacklist(); + virtual bool ShouldAlwaysTranslate(); + virtual void ToggleAlwaysTranslate(); + + // The following methods are called by the infobar that displays the status + // while translating and also the one displaying the error message. + string16 GetMessageInfoBarText(); + string16 GetMessageInfoBarButtonText(); + void MessageInfoBarButtonPressed(); + + // Sets this infobar background animation based on the previous infobar shown. + // A fading background effect is used when transitioning from a normal state + // to an error state (and vice-versa). + void UpdateBackgroundAnimation(TranslateInfoBarDelegate2* previous_infobar); + + // Convenience method that returns the displayable language name for + // |language_code| in the current application locale. + static string16 GetLanguageDisplayableName(const std::string& language_code); + + // Adds the strings that should be displayed in the after translate infobar to + // |strings|. The text in that infobar is: + // "The page has been translated from <lang1> to <lang2>." + // Because <lang1> and <lang2> are displayed in menu buttons, the text is + // split in 3 chunks. |swap_languages| is set to true if <lang1> and <lang2> + // should be inverted (some languages express the sentense as "The page has + // been translate to <lang2> from <lang1>."). + static void GetAfterTranslateStrings(std::vector<string16>* strings, + bool* swap_languages); + + private: + typedef std::pair<std::string, string16> LanguageNamePair; + + // Gets the host of the page being translated, or an empty string if no URL is + // associated with the current page. + std::string GetPageHost(); + + TranslateInfoBarDelegate2(Type infobar_type, + TranslateErrors::Type error, + TabContents* tab_contents, + const std::string& original_language, + const std::string& target_language); + + Type type_; + + // The type of fading animation if any that should be used when showing this + // infobar. + BackgroundAnimationType background_animation_; + + TabContents* tab_contents_; + + // The list supported languages for translation. + // The pair first string is the language ISO code (ex: en, fr...), the second + // string is the displayable name on the current locale. + // The languages are sorted alphabetically based on the displayable name. + std::vector<LanguageNamePair> languages_; + + // The index for language the page is originally in. + int original_language_index_; + + // The index for language the page should be translated to. + int target_language_index_; + + // The error that occurred when trying to translate (NONE if no error). + TranslateErrors::Type error_; + + // The current infobar view. + TranslateInfoBarView* infobar_view_; + + // The translation related preferences. + TranslatePrefs prefs_; + + DISALLOW_COPY_AND_ASSIGN(TranslateInfoBarDelegate2); +}; + +#endif // CHROME_BROWSER_TRANSLATE_TRANSLATE_INFOBAR_DELEGATE2_H + diff --git a/chrome/browser/translate/translate_infobar_view.h b/chrome/browser/translate/translate_infobar_view.h new file mode 100644 index 0000000..c1898e72 --- /dev/null +++ b/chrome/browser/translate/translate_infobar_view.h @@ -0,0 +1,19 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_TRANSLATE_TRANSLATE_INFOBAR_VIEW_H_ +#define CHROME_BROWSER_TRANSLATE_TRANSLATE_INFOBAR_VIEW_H_ + +// This interface should be implemented by classes that are the view (in the MVC +// sense) of the TranslateInfoBarDelegate (which is the model). +class TranslateInfoBarView { + public: + virtual void OriginalLanguageChanged() = 0; + virtual void TargetLanguageChanged() = 0; + + protected: + virtual ~TranslateInfoBarView() {} +}; + +#endif // CHROME_BROWSER_TRANSLATE_TRANSLATE_INFOBAR_VIEW_H_ diff --git a/chrome/browser/translate/translate_manager2.cc b/chrome/browser/translate/translate_manager2.cc new file mode 100644 index 0000000..24cc2a6c --- /dev/null +++ b/chrome/browser/translate/translate_manager2.cc @@ -0,0 +1,549 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/translate/translate_manager2.h" + +#include "app/resource_bundle.h" +#include "base/compiler_specific.h" +#include "base/string_util.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/pref_service.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/renderer_host/render_process_host.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/tab_contents/language_state.h" +#include "chrome/browser/tab_contents/navigation_controller.h" +#include "chrome/browser/tab_contents/navigation_entry.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tab_contents/tab_util.h" +#include "chrome/browser/translate/page_translated_details.h" +#include "chrome/browser/translate/translate_infobar_delegate2.h" +#include "chrome/browser/translate/translate_prefs.h" +#include "chrome/common/notification_details.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_source.h" +#include "chrome/common/notification_type.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/translate_errors.h" +#include "grit/browser_resources.h" +#include "net/url_request/url_request_status.h" + +namespace { + +// Mapping from a locale name to a language code name. +// Locale names not included are translated as is. +struct LocaleToCLDLanguage { + const char* locale_language; // Language Chrome locale is in. + const char* cld_language; // Language the CLD reports. +}; +LocaleToCLDLanguage kLocaleToCLDLanguages[] = { + { "en-GB", "en" }, + { "en-US", "en" }, + { "es-419", "es" }, + { "pt-BR", "pt" }, + { "pt-PT", "pt" }, +}; + +// The list of languages the Google translation server supports. +// For information, here is the list of languages that Chrome can be run in +// but that the translation server does not support: +// am Amharic +// bn Bengali +// gu Gujarati +// kn Kannada +// ml Malayalam +// mr Marathi +// ta Tamil +// te Telugu +const char* kSupportedLanguages[] = { + "af", // Afrikaans + "sq", // Albanian + "ar", // Arabic + "be", // Belarusian + "bg", // Bulgarian + "ca", // Catalan + "zh-CN", // Chinese (Simplified) + "zh-TW", // Chinese (Traditional) + "hr", // Croatian + "cs", // Czech + "da", // Danish + "nl", // Dutch + "en", // English + "et", // Estonian + "fi", // Finnish + "fil", // Filipino + "fr", // French + "gl", // Galician + "de", // German + "el", // Greek + "he", // Hebrew + "hi", // Hindi + "hu", // Hungarian + "is", // Icelandic + "id", // Indonesian + "it", // Italian + "ga", // Irish + "ja", // Japanese + "ko", // Korean + "lv", // Latvian + "lt", // Lithuanian + "mk", // Macedonian + "ms", // Malay + "mt", // Maltese + "nb", // Norwegian + "fa", // Persian + "pl", // Polish + "pt", // Portuguese + "ro", // Romanian + "ru", // Russian + "sr", // Serbian + "sk", // Slovak + "sl", // Slovenian + "es", // Spanish + "sw", // Swahili + "sv", // Swedish + "th", // Thai + "tr", // Turkish + "uk", // Ukrainian + "vi", // Vietnamese + "cy", // Welsh + "yi", // Yiddish +}; + +const char* const kTranslateScriptURL = + "http://translate.google.com/translate_a/element.js?" + "cb=cr.googleTranslate.onTranslateElementLoad"; +const char* const kTranslateScriptHeader = + "Google-Translate-Element-Mode: library"; + +} // namespace + +// static +base::LazyInstance<std::set<std::string> > + TranslateManager2::supported_languages_(base::LINKER_INITIALIZED); + +TranslateManager2::~TranslateManager2() { +} + +// static +bool TranslateManager2::IsTranslatableURL(const GURL& url) { + return !url.SchemeIs("chrome"); +} + +// static +void TranslateManager2::GetSupportedLanguages( + std::vector<std::string>* languages) { + DCHECK(languages && languages->empty()); + for (size_t i = 0; i < arraysize(kSupportedLanguages); ++i) + languages->push_back(kSupportedLanguages[i]); +} + +// static +std::string TranslateManager2::GetLanguageCode( + const std::string& chrome_locale) { + for (size_t i = 0; i < arraysize(kLocaleToCLDLanguages); ++i) { + if (chrome_locale == kLocaleToCLDLanguages[i].locale_language) + return kLocaleToCLDLanguages[i].cld_language; + } + return chrome_locale; +} + +// static +bool TranslateManager2::IsSupportedLanguage(const std::string& page_language) { + if (supported_languages_.Pointer()->empty()) { + for (size_t i = 0; i < arraysize(kSupportedLanguages); ++i) + supported_languages_.Pointer()->insert(kSupportedLanguages[i]); + } + return supported_languages_.Pointer()->find(page_language) != + supported_languages_.Pointer()->end(); +} + +void TranslateManager2::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::NAV_ENTRY_COMMITTED: { + NavigationController* controller = + Source<NavigationController>(source).ptr(); + NavigationController::LoadCommittedDetails* load_details = + Details<NavigationController::LoadCommittedDetails>(details).ptr(); + NavigationEntry* entry = controller->GetActiveEntry(); + if (!entry) { + NOTREACHED(); + return; + } + if (entry->transition_type() != PageTransition::RELOAD && + load_details->type != NavigationType::SAME_PAGE) { + return; + } + // When doing a page reload, we don't get a TAB_LANGUAGE_DETERMINED + // notification. So we need to explictly initiate the translation. + // Note that we delay it as the TranslateManager2 gets this notification + // before the TabContents and the TabContents processing might remove the + // current infobars. Since InitTranslation might add an infobar, it must + // be done after that. + MessageLoop::current()->PostTask(FROM_HERE, + method_factory_.NewRunnableMethod( + &TranslateManager2::InitiateTranslationPosted, + controller->tab_contents()->render_view_host()->process()->id(), + controller->tab_contents()->render_view_host()->routing_id(), + controller->tab_contents()->language_state(). + original_language())); + break; + } + case NotificationType::TAB_LANGUAGE_DETERMINED: { + TabContents* tab = Source<TabContents>(source).ptr(); + // We may get this notifications multiple times. Make sure to translate + // only once. + LanguageState& language_state = tab->language_state(); + if (!language_state.translation_pending() && + !language_state.translation_declined() && + !language_state.IsPageTranslated()) { + std::string language = *(Details<std::string>(details).ptr()); + InitiateTranslation(tab, language); + } + break; + } + case NotificationType::PAGE_TRANSLATED: { + // Only add translate infobar if it doesn't exist; if it already exists, + // just update the state, the actual infobar would have received the same + // notification and update the visual display accordingly. + TabContents* tab = Source<TabContents>(source).ptr(); + PageTranslatedDetails* page_translated_details = + Details<PageTranslatedDetails>(details).ptr(); + PageTranslated(tab, page_translated_details); + break; + } + case NotificationType::PROFILE_DESTROYED: { + Profile* profile = Source<Profile>(source).ptr(); + notification_registrar_.Remove(this, NotificationType::PROFILE_DESTROYED, + source); + size_t count = accept_languages_.erase(profile->GetPrefs()); + // We should know about this profile since we are listening for + // notifications on it. + DCHECK(count > 0); + profile->GetPrefs()->RemovePrefObserver(prefs::kAcceptLanguages, this); + break; + } + case NotificationType::PREF_CHANGED: { + DCHECK(*Details<std::wstring>(details).ptr() == prefs::kAcceptLanguages); + PrefService* prefs = Source<PrefService>(source).ptr(); + InitAcceptLanguages(prefs); + break; + } + default: + NOTREACHED(); + } +} + +void TranslateManager2::OnURLFetchComplete(const URLFetcher* source, + const GURL& url, + const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data) { + scoped_ptr<const URLFetcher> delete_ptr(source); + DCHECK(translate_script_request_pending_); + translate_script_request_pending_ = false; + bool error = + (status.status() != URLRequestStatus::SUCCESS || response_code != 200); + + if (!error) { + base::StringPiece str = ResourceBundle::GetSharedInstance(). + GetRawDataResource(IDR_TRANSLATE_JS); + DCHECK(translate_script_.empty()); + str.CopyToString(&translate_script_); + translate_script_ += "\n" + data; + } + + // Process any pending requests. + std::vector<PendingRequest>::const_iterator iter; + for (iter = pending_requests_.begin(); iter != pending_requests_.end(); + ++iter) { + const PendingRequest& request = *iter; + TabContents* tab = tab_util::GetTabContentsByID(request.render_process_id, + request.render_view_id); + if (!tab) { + // The tab went away while we were retrieving the script. + continue; + } + NavigationEntry* entry = tab->controller().GetActiveEntry(); + if (!entry || entry->page_id() != request.page_id) { + // We navigated away from the page the translation was triggered on. + continue; + } + + if (error) { + ShowInfoBar(tab, + TranslateInfoBarDelegate2::CreateInstance( + TranslateInfoBarDelegate2::TRANSLATION_ERROR, + TranslateErrors::NETWORK, + tab, request.source_lang, request.target_lang)); + } else { + // Translate the page. + DoTranslatePage(tab, translate_script_, + request.source_lang, request.target_lang); + } + } + pending_requests_.clear(); +} + +// static +bool TranslateManager2::IsShowingTranslateInfobar(TabContents* tab) { + return GetTranslateInfoBarDelegate2(tab) != NULL; +} + +TranslateManager2::TranslateManager2() + : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), + translate_script_request_pending_(false) { + notification_registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED, + NotificationService::AllSources()); + notification_registrar_.Add(this, NotificationType::TAB_LANGUAGE_DETERMINED, + NotificationService::AllSources()); + notification_registrar_.Add(this, NotificationType::PAGE_TRANSLATED, + NotificationService::AllSources()); +} + +void TranslateManager2::InitiateTranslation(TabContents* tab, + const std::string& page_lang) { + PrefService* prefs = tab->profile()->GetPrefs(); + if (!prefs->GetBoolean(prefs::kEnableTranslate)) + return; + + NavigationEntry* entry = tab->controller().GetActiveEntry(); + if (!entry) { + // This can happen for popups created with window.open(""). + return; + } + + // If there is already a translate infobar showing, don't show another one. + if (GetTranslateInfoBarDelegate2(tab)) + return; + + std::string target_lang = GetTargetLanguage(); + // Nothing to do if either the language Chrome is in or the language of the + // page is not supported by the translation server. + if (target_lang.empty() || !IsSupportedLanguage(page_lang)) { + return; + } + + // We don't want to translate: + // - any Chrome specific page (New Tab Page, Download, History... pages). + // - similar languages (ex: en-US to en). + // - any user black-listed URLs or user selected language combination. + // - any language the user configured as accepted languages. + if (!IsTranslatableURL(entry->url()) || page_lang == target_lang || + !TranslatePrefs::CanTranslate(prefs, page_lang, entry->url()) || + IsAcceptLanguage(tab, page_lang)) { + return; + } + + // If the user has previously selected "always translate" for this language we + // automatically translate. Note that in incognito mode we disable that + // feature; the user will get an infobar, so they can control whether the + // page's text is sent to the translate server. + std::string auto_target_lang; + if (!tab->profile()->IsOffTheRecord() && + TranslatePrefs::ShouldAutoTranslate(prefs, page_lang, + &auto_target_lang)) { + TranslatePage(tab, page_lang, auto_target_lang); + return; + } + + std::string auto_translate_to = tab->language_state().AutoTranslateTo(); + if (!auto_translate_to.empty()) { + // This page was navigated through a click from a translated page. + TranslatePage(tab, page_lang, auto_translate_to); + return; + } + + // Prompts the user if he/she wants the page translated. + tab->AddInfoBar(TranslateInfoBarDelegate2::CreateInstance( + TranslateInfoBarDelegate2::BEFORE_TRANSLATE, + TranslateErrors::NONE, tab, page_lang, target_lang)); +} + +void TranslateManager2::InitiateTranslationPosted( + int process_id, int render_id, const std::string& page_lang) { + // The tab might have been closed. + TabContents* tab = tab_util::GetTabContentsByID(process_id, render_id); + if (!tab || tab->language_state().translation_pending()) + return; + + InitiateTranslation(tab, page_lang); +} + +void TranslateManager2::TranslatePage(TabContents* tab_contents, + const std::string& source_lang, + const std::string& target_lang) { + NavigationEntry* entry = tab_contents->controller().GetActiveEntry(); + if (!entry) { + NOTREACHED(); + return; + } + if (!translate_script_.empty()) { + DoTranslatePage(tab_contents, translate_script_, source_lang, target_lang); + return; + } + + // The script is not available yet. Queue that request and query for the + // script. Once it is downloaded we'll do the translate. + RenderViewHost* rvh = tab_contents->render_view_host(); + PendingRequest request; + request.render_process_id = rvh->process()->id(); + request.render_view_id = rvh->routing_id(); + request.page_id = entry->page_id(); + request.source_lang = source_lang; + request.target_lang = target_lang; + pending_requests_.push_back(request); + RequestTranslateScript(); +} + +void TranslateManager2::RevertTranslation(TabContents* tab_contents) { + NavigationEntry* entry = tab_contents->controller().GetActiveEntry(); + if (!entry) { + NOTREACHED(); + return; + } + tab_contents->render_view_host()->RevertTranslation(entry->page_id()); + tab_contents->language_state().set_current_language( + tab_contents->language_state().original_language()); +} + +void TranslateManager2::DoTranslatePage(TabContents* tab, + const std::string& translate_script, + const std::string& source_lang, + const std::string& target_lang) { + NavigationEntry* entry = tab->controller().GetActiveEntry(); + if (!entry) { + NOTREACHED(); + return; + } + + TranslateInfoBarDelegate2* infobar = GetTranslateInfoBarDelegate2(tab); + if (infobar) { + // We don't show the translating infobar if no translate infobar is already + // showing (that is the case when the translation was triggered by the + // "always translate" for example). + infobar = TranslateInfoBarDelegate2::CreateInstance( + TranslateInfoBarDelegate2::TRANSLATING, TranslateErrors::NONE, + tab, source_lang, target_lang); + ShowInfoBar(tab, infobar); + } + tab->language_state().set_translation_pending(true); + tab->render_view_host()->TranslatePage(entry->page_id(), translate_script, + source_lang, target_lang); +} + +void TranslateManager2::PageTranslated(TabContents* tab, + PageTranslatedDetails* details) { + // Create the new infobar to display. + TranslateInfoBarDelegate2* infobar; + if (details->error_type != TranslateErrors::NONE) { + infobar = TranslateInfoBarDelegate2::CreateInstance( + TranslateInfoBarDelegate2::TRANSLATION_ERROR, details->error_type, + tab, details->source_language, details->target_language); + } else { + infobar = TranslateInfoBarDelegate2::CreateInstance( + TranslateInfoBarDelegate2::AFTER_TRANSLATE, TranslateErrors::NONE, + tab, details->source_language, details->target_language); + } + ShowInfoBar(tab, infobar); +} + +bool TranslateManager2::IsAcceptLanguage(TabContents* tab, + const std::string& language) { + PrefService* pref_service = tab->profile()->GetPrefs(); + PrefServiceLanguagesMap::const_iterator iter = + accept_languages_.find(pref_service); + if (iter == accept_languages_.end()) { + InitAcceptLanguages(pref_service); + // Listen for this profile going away, in which case we would need to clear + // the accepted languages for the profile. + notification_registrar_.Add(this, NotificationType::PROFILE_DESTROYED, + Source<Profile>(tab->profile())); + // Also start listening for changes in the accept languages. + tab->profile()->GetPrefs()->AddPrefObserver(prefs::kAcceptLanguages, this); + + iter = accept_languages_.find(pref_service); + } + + return iter->second.count(language) != 0; +} + +void TranslateManager2::InitAcceptLanguages(PrefService* prefs) { + // We have been asked for this profile, build the languages. + std::wstring accept_langs_str = prefs->GetString(prefs::kAcceptLanguages); + std::vector<std::string> accept_langs_list; + LanguageSet accept_langs_set; + SplitString(WideToASCII(accept_langs_str), ',', &accept_langs_list); + std::vector<std::string>::const_iterator iter; + std::string ui_lang = + GetLanguageCode(g_browser_process->GetApplicationLocale()); + bool is_ui_english = StartsWithASCII(ui_lang, "en-", false); + for (iter = accept_langs_list.begin(); + iter != accept_langs_list.end(); ++iter) { + // Get rid of the locale extension if any (ex: en-US -> en), but for Chinese + // for which the CLD reports zh-CN and zh-TW. + std::string accept_lang(*iter); + size_t index = iter->find("-"); + if (index != std::string::npos && *iter != "zh-CN" && *iter != "zh-TW") + accept_lang = iter->substr(0, index); + // Special-case English until we resolve bug 36182 properly. + // Add English only if the UI language is not English. This will annoy + // users of non-English Chrome who can comprehend English until English is + // black-listed. + // TODO(jungshik): Once we determine that it's safe to remove English from + // the default Accept-Language values for most locales, remove this + // special-casing. + if (accept_lang != "en" || is_ui_english) + accept_langs_set.insert(accept_lang); + } + accept_languages_[prefs] = accept_langs_set; +} + +void TranslateManager2::RequestTranslateScript() { + if (translate_script_request_pending_) + return; + + translate_script_request_pending_ = true; + URLFetcher* fetcher = URLFetcher::Create(0, GURL(kTranslateScriptURL), + URLFetcher::GET, this); + fetcher->set_request_context(Profile::GetDefaultRequestContext()); + fetcher->set_extra_request_headers(kTranslateScriptHeader); + fetcher->Start(); +} + +void TranslateManager2::ShowInfoBar(TabContents* tab, + TranslateInfoBarDelegate2* infobar) { + TranslateInfoBarDelegate2* old_infobar = GetTranslateInfoBarDelegate2(tab); + infobar->UpdateBackgroundAnimation(old_infobar); + if (old_infobar) { + // There already is a translate infobar, simply replace it. + tab->ReplaceInfoBar(old_infobar, infobar); + } else { + tab->AddInfoBar(infobar); + } +} + +// static +std::string TranslateManager2::GetTargetLanguage() { + std::string target_lang = + GetLanguageCode(g_browser_process->GetApplicationLocale()); + if (IsSupportedLanguage(target_lang)) + return target_lang; + return std::string(); +} + +// static +TranslateInfoBarDelegate2* TranslateManager2::GetTranslateInfoBarDelegate2( + TabContents* tab) { + for (int i = 0; i < tab->infobar_delegate_count(); ++i) { + TranslateInfoBarDelegate2* delegate = + tab->GetInfoBarDelegateAt(i)->AsTranslateInfoBarDelegate2(); + if (delegate) + return delegate; + } + return NULL; +} diff --git a/chrome/browser/translate/translate_manager2.h b/chrome/browser/translate/translate_manager2.h new file mode 100644 index 0000000..5e28070 --- /dev/null +++ b/chrome/browser/translate/translate_manager2.h @@ -0,0 +1,170 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_TRANSLATE_TRANSLATE_MANAGER2_H_ +#define CHROME_BROWSER_TRANSLATE_TRANSLATE_MANAGER2_H_ + +#include <map> +#include <set> +#include <string> +#include <vector> + +#include "base/lazy_instance.h" +#include "base/singleton.h" +#include "base/task.h" +#include "chrome/common/net/url_fetcher.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" +#include "chrome/common/translate_errors.h" + +class GURL; +struct PageTranslatedDetails; +class PrefService; +class TabContents; +class TranslateInfoBarDelegate2; + +// The TranslateManager class is responsible for showing an info-bar when a page +// in a language different than the user language is loaded. It triggers the +// page translation the user requests. +// It is a singleton. + +class TranslateManager2 : public NotificationObserver, + public URLFetcher::Delegate { + public: + virtual ~TranslateManager2(); + + // Translates the page contents from |source_lang| to |target_lang|. + // The actual translation might be performed asynchronously if the translate + // script is not yet available. + void TranslatePage(TabContents* tab_contents, + const std::string& source_lang, + const std::string& target_lang); + + // Reverts the contents of the page in |tab_contents| to its original + // language. + void RevertTranslation(TabContents* tab_contents); + + // Clears the translate script, so it will be fetched next time we translate. + // Currently used by unit-tests. + void ClearTranslateScript() { translate_script_.clear(); } + + // NotificationObserver implementation: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // URLFetcher::Delegate implementation: + virtual void OnURLFetchComplete(const URLFetcher* source, + const GURL& url, + const URLRequestStatus& status, + int response_code, + const ResponseCookies& cookies, + const std::string& data); + + // Convenience method to know if a tab is showing a translate infobar. + static bool IsShowingTranslateInfobar(TabContents* tab); + + // Returns true if the URL can be translated, if it is not an internal URL + // (chrome:// and others). + static bool IsTranslatableURL(const GURL& url); + + // Fills |languages| with the list of languages that the translate server can + // translate to and from. + static void GetSupportedLanguages(std::vector<std::string>* languages); + + // Returns the language code that can be used with the Translate method for a + // specified |chrome_locale|. + static std::string GetLanguageCode(const std::string& chrome_locale); + + // Returns true if |language| is supported by the translation server. + static bool IsSupportedLanguage(const std::string& language); + + protected: + TranslateManager2(); + + private: + friend struct DefaultSingletonTraits<TranslateManager2>; + + // Structure that describes a translate request. + // Translation may be deferred while the translate script is being retrieved + // from the translate server. + struct PendingRequest { + int render_process_id; + int render_view_id; + int page_id; + std::string source_lang; + std::string target_lang; + }; + + // Starts the translation process on |tab| containing the page in the + // |page_lang| language. + void InitiateTranslation(TabContents* tab, const std::string& page_lang); + + // If the tab identified by |process_id| and |render_id| has been closed, this + // does nothing, otherwise it calls InitiateTranslation. + void InitiateTranslationPosted(int process_id, + int render_id, + const std::string& page_lang); + + // Sends a translation request to the RenderView of |tab_contents|. + void DoTranslatePage(TabContents* tab_contents, + const std::string& translate_script, + const std::string& source_lang, + const std::string& target_lang); + + // Shows the after translate or error infobar depending on the details. + void PageTranslated(TabContents* tab, PageTranslatedDetails* details); + + // Returns true if the passed language has been configured by the user as an + // accept language. + bool IsAcceptLanguage(TabContents* tab, const std::string& language); + + // Initializes the |accept_languages_| language table based on the associated + // preference in |prefs|. + void InitAcceptLanguages(PrefService* prefs); + + // Fetches the JS translate script (the script that is injected in the page + // to translate it). + void RequestTranslateScript(); + + // Shows the specified translate |infobar| in the given |tab|. If a current + // translate infobar is showing, it just replaces it with the new one. + void ShowInfoBar(TabContents* tab, TranslateInfoBarDelegate2* infobar); + + // Returns the language to translate to, which is the language the UI is + // configured in. Returns an empty string if that language is not supported + // by the translation service. + static std::string GetTargetLanguage(); + + // Returns the translate info bar showing in |tab| or NULL if none is showing. + static TranslateInfoBarDelegate2* GetTranslateInfoBarDelegate2( + TabContents* tab); + + NotificationRegistrar notification_registrar_; + + // A map that associates a profile with its parsed "accept languages". + typedef std::set<std::string> LanguageSet; + typedef std::map<PrefService*, LanguageSet> PrefServiceLanguagesMap; + PrefServiceLanguagesMap accept_languages_; + + ScopedRunnableMethodFactory<TranslateManager2> method_factory_; + + // The JS injected in the page to do the translation. + std::string translate_script_; + + // Whether the translate JS is currently being retrieved. + bool translate_script_request_pending_; + + // The list of pending translate requests. Translate requests are queued when + // the translate script is not ready and has to be fetched from the translate + // server. + std::vector<PendingRequest> pending_requests_; + + // The languages supported by the translation server. + static base::LazyInstance<std::set<std::string> > supported_languages_; + + DISALLOW_COPY_AND_ASSIGN(TranslateManager2); +}; + +#endif // CHROME_BROWSER_TRANSLATE_TRANSLATE_MANAGER2_H_ diff --git a/chrome/browser/translate/translate_manager2_unittest.cc b/chrome/browser/translate/translate_manager2_unittest.cc new file mode 100644 index 0000000..6ad168d --- /dev/null +++ b/chrome/browser/translate/translate_manager2_unittest.cc @@ -0,0 +1,999 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/renderer_host/test/test_render_view_host.h" + +#include "chrome/app/chrome_dll_resource.h" +#include "chrome/browser/renderer_host/mock_render_process_host.h" +#include "chrome/browser/tab_contents/render_view_context_menu.h" +#include "chrome/browser/translate/translate_infobar_delegate2.h" +#include "chrome/browser/translate/translate_manager2.h" +#include "chrome/browser/translate/translate_prefs.h" +#include "chrome/common/ipc_test_sink.h" +#include "chrome/common/notification_details.h" +#include "chrome/common/notification_observer_mock.h" +#include "chrome/common/notification_registrar.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_type.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/render_messages.h" +#include "chrome/common/net/test_url_fetcher_factory.h" +#include "chrome/test/testing_browser_process.h" +#include "grit/generated_resources.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "third_party/cld/languages/public/languages.h" + +using testing::_; +using testing::Pointee; +using testing::Property; + +class TestTranslateManager2 : public TranslateManager2 { + public: + TestTranslateManager2() {} +}; + +class TranslateManager2Test : public RenderViewHostTestHarness, + public NotificationObserver { + public: + TranslateManager2Test() {} + + // Simluates navigating to a page and getting the page contents and language + // for that navigation. + void SimulateNavigation(const GURL& url, int page_id, + const std::wstring& contents, + const std::string& lang) { + NavigateAndCommit(url); + SimulateOnPageContents(url, page_id, contents, lang); + } + + void SimulateOnPageContents(const GURL& url, int page_id, + const std::wstring& contents, + const std::string& lang) { + rvh()->TestOnMessageReceived(ViewHostMsg_PageContents(0, url, page_id, + contents, lang)); + } + + bool GetTranslateMessage(int* page_id, + std::string* original_lang, + std::string* target_lang) { + const IPC::Message* message = + process()->sink().GetFirstMessageMatching(ViewMsg_TranslatePage::ID); + if (!message) + return false; + Tuple4<int, std::string, std::string, std::string> translate_param; + ViewMsg_TranslatePage::Read(message, &translate_param); + if (page_id) + *page_id = translate_param.a; + // Ignore translate_param.b which is the script injected in the page. + if (original_lang) + *original_lang = translate_param.c; + if (target_lang) + *target_lang = translate_param.d; + return true; + } + + // Returns the translate infobar if there is 1 infobar and it is a translate + // infobar. + TranslateInfoBarDelegate2* GetTranslateInfoBar() { + if (contents()->infobar_delegate_count() != 1) + return NULL; + return contents()->GetInfoBarDelegateAt(0)->AsTranslateInfoBarDelegate2(); + } + + // If there is 1 infobar and it is a translate infobar, closes it and returns + // true. Returns false otherwise. + bool CloseTranslateInfoBar() { + TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar(); + if (!infobar) + return false; + infobar->InfoBarDismissed(); // Simulates closing the infobar. + contents()->RemoveInfoBar(infobar); + return true; + } + + // Checks whether |infobar| has been removed and clears the removed infobar + // list. + bool CheckInfoBarRemovedAndReset(InfoBarDelegate* infobar) { + bool found = std::find(removed_infobars_.begin(), removed_infobars_.end(), + infobar) != removed_infobars_.end(); + removed_infobars_.clear(); + return found; + } + + // Returns true if at least one infobar was closed. + bool InfoBarRemoved() { + return !removed_infobars_.empty(); + } + + // Clears the list of stored removed infobars. + void ClearRemovedInfoBars() { + removed_infobars_.clear(); + } + + // If there is 1 infobar and it is a translate infobar, deny translation and + // returns true. Returns false otherwise. + bool DenyTranslation() { + TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar(); + if (!infobar) + return false; + infobar->TranslationDeclined(); + contents()->RemoveInfoBar(infobar); + return true; + } + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NotificationType::TAB_CONTENTS_INFOBAR_REMOVED); + removed_infobars_.push_back(Details<InfoBarDelegate>(details).ptr()); + } + + protected: + virtual void SetUp() { + URLFetcher::set_factory(&url_fetcher_factory_); + + // Access the TranslateManager singleton so it is created before we call + // RenderViewHostTestHarness::SetUp() to match what's done in Chrome, where + // the TranslateManager is created before the TabContents. This matters as + // they both register for similar events and we want the notifications to + // happen in the same sequence (TranslateManager first, TabContents second). + // Also clears the translate script so it is fetched everytime. + Singleton<TranslateManager2>::get()->ClearTranslateScript(); + + RenderViewHostTestHarness::SetUp(); + + notification_registrar_.Add( + this, + NotificationType::TAB_CONTENTS_INFOBAR_REMOVED, + Source<TabContents>(contents())); + } + + virtual void TearDown() { + notification_registrar_.Remove( + this, + NotificationType::TAB_CONTENTS_INFOBAR_REMOVED, + Source<TabContents>(contents())); + + RenderViewHostTestHarness::TearDown(); + + URLFetcher::set_factory(NULL); + } + + void SimulateURLFetch(bool success) { + TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0); + ASSERT_TRUE(fetcher); + URLRequestStatus status; + status.set_status(success ? URLRequestStatus::SUCCESS : + URLRequestStatus::FAILED); + fetcher->delegate()->OnURLFetchComplete(fetcher, fetcher->original_url(), + status, success ? 200 : 500, + ResponseCookies(), + std::string()); + } + + void SetPrefObserverExpectation(const wchar_t* path) { + EXPECT_CALL( + pref_observer_, + Observe(NotificationType(NotificationType::PREF_CHANGED), + _, + Property(&Details<std::wstring>::ptr, Pointee(path)))); + } + + NotificationObserverMock pref_observer_; + + private: + NotificationRegistrar notification_registrar_; + scoped_ptr<TestTranslateManager2> translate_manager_; + TestURLFetcherFactory url_fetcher_factory_; + + // The list of infobars that have been removed. + // WARNING: the pointers points to deleted objects, use only for comparison. + std::vector<InfoBarDelegate*> removed_infobars_; + + DISALLOW_COPY_AND_ASSIGN(TranslateManager2Test); +}; + +// An observer that keeps track of whether a navigation entry was committed. +class NavEntryCommittedObserver : public NotificationObserver { + public: + explicit NavEntryCommittedObserver(TabContents* tab_contents) { + registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED, + Source<NavigationController>(&tab_contents->controller())); + } + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NotificationType::NAV_ENTRY_COMMITTED); + details_ = + *(Details<NavigationController::LoadCommittedDetails>(details).ptr()); + } + + const NavigationController::LoadCommittedDetails& + get_load_commited_details() const { + return details_; + } + + private: + NavigationController::LoadCommittedDetails details_; + NotificationRegistrar registrar_; + + DISALLOW_COPY_AND_ASSIGN(NavEntryCommittedObserver); +}; + +class TestRenderViewContextMenu : public RenderViewContextMenu { + public: + static TestRenderViewContextMenu* CreateContextMenu( + TabContents* tab_contents) { + ContextMenuParams params; + params.media_type = WebKit::WebContextMenuData::MediaTypeNone; + params.x = 0; + params.y = 0; + params.is_image_blocked = false; + params.media_flags = 0; + params.spellcheck_enabled = false;; + params.is_editable = false; + params.page_url = tab_contents->controller().GetActiveEntry()->url(); +#if defined(OS_MACOSX) + params.writing_direction_default = 0; + params.writing_direction_left_to_right = 0; + params.writing_direction_right_to_left = 0; +#endif // OS_MACOSX + params.edit_flags = 0; + return new TestRenderViewContextMenu(tab_contents, params); + } + + bool IsItemPresent(int id) { + return menu_model_.GetIndexOfCommandId(id) != -1; + } + + virtual void PlatformInit() { } + virtual bool GetAcceleratorForCommandId( + int command_id, + menus::Accelerator* accelerator) { return false; } + + private: + TestRenderViewContextMenu(TabContents* tab_contents, + const ContextMenuParams& params) + : RenderViewContextMenu(tab_contents, params) { + } + + DISALLOW_COPY_AND_ASSIGN(TestRenderViewContextMenu); +}; + +TEST_F(TranslateManager2Test, NormalTranslate) { + // Simulate navigating to a page. + SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr"); + + // We should have an infobar. + TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar(); + ASSERT_TRUE(infobar != NULL); + EXPECT_EQ(TranslateInfoBarDelegate2::BEFORE_TRANSLATE, infobar->type()); + + // Simulate clicking translate. + process()->sink().ClearMessages(); + infobar->Translate(); + + // Simulate the translate script being retrieved (it only needs to be done + // once in the test as it is cached). + SimulateURLFetch(true); + + // Test that we sent the right message to the renderer. + int page_id = 0; + std::string original_lang, target_lang; + EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); + EXPECT_EQ(0, page_id); + EXPECT_EQ("fr", original_lang); + EXPECT_EQ("en", target_lang); + + // The "Translating..." infobar should be showing. + infobar = GetTranslateInfoBar(); + ASSERT_TRUE(infobar != NULL); + EXPECT_EQ(TranslateInfoBarDelegate2::TRANSLATING, infobar->type()); + + // Simulate the render notifying the translation has been done. + rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "fr", "en", + TranslateErrors::NONE)); + + // The after translate infobar should be showing. + infobar = GetTranslateInfoBar(); + ASSERT_TRUE(infobar != NULL); + EXPECT_EQ(TranslateInfoBarDelegate2::AFTER_TRANSLATE, infobar->type()); + + // Simulate translating again from there but with 2 different languages. + infobar->SetOriginalLanguage(0); + infobar->SetTargetLanguage(1); + std::string new_original_lang = infobar->GetOriginalLanguageCode(); + std::string new_target_lang = infobar->GetTargetLanguageCode(); + process()->sink().ClearMessages(); + infobar->Translate(); + + // Test that we sent the right message to the renderer. + EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); + EXPECT_EQ(0, page_id); + EXPECT_EQ(new_original_lang, original_lang); + EXPECT_EQ(new_target_lang, target_lang); +} + +TEST_F(TranslateManager2Test, TranslateScriptNotAvailable) { + // Simulate navigating to a page. + SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr"); + + // We should have an infobar. + TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar(); + ASSERT_TRUE(infobar != NULL); + EXPECT_EQ(TranslateInfoBarDelegate2::BEFORE_TRANSLATE, infobar->type()); + + // Simulate clicking translate. + process()->sink().ClearMessages(); + infobar->Translate(); + // Simulate a failure retrieving the translate script. + SimulateURLFetch(false); + + // We should not have sent any message to translate to the renderer. + EXPECT_FALSE(GetTranslateMessage(NULL, NULL, NULL)); + + // And we should have an error infobar showing. + infobar = GetTranslateInfoBar(); + ASSERT_TRUE(infobar != NULL); + EXPECT_EQ(TranslateInfoBarDelegate2::TRANSLATION_ERROR, infobar->type()); +} + +// Tests that we show/don't show an info-bar for all languages the CLD can +// report. +TEST_F(TranslateManager2Test, TestAllLanguages) { + // The index in kExpectation are the Language enum (see languages.pb.h). + // true if we expect a translate infobar for that language. + // Note the supported languages are in translation_service.cc, see + // kSupportedLanguages. + bool kExpectations[] = { + // 0-9 + false, true, true, true, true, true, true, true, true, true, + // 10-19 + true, true, true, true, true, true, true, true, true, true, + // 20-29 + true, true, true, true, true, false, false, true, true, true, + // 30-39 + true, true, true, true, true, true, true, false, true, false, + // 40-49 + true, false, true, false, false, true, false, true, false, false, + // 50-59 + false, false, false, true, true, true, false, false, false, false, + // 60-69 + false, false, true, true, false, true, true, false, true, true, + // 70-79 + false, false, false, false, false, false, false, true, false, false, + // 80-89 + false, false, false, false, false, false, false, false, false, false, + // 90-99 + false, true, false, false, false, false, false, false, false, false, + // 100-109 + false, true, false, false, false, false, false, false, false, false, + // 110-119 + false, false, false, false, false, false, false, false, false, false, + // 120-129 + false, false, false, false, false, false, false, false, false, false, + // 130-139 + false, false, false, false, false, false, false, false, false, false, + // 140-149 + false, false, false, false, false, false, false, false, false, false, + // 150-159 + false, false, false, false, false, false, false, false, false, false, + // 160 + false + }; + + GURL url("http://www.google.com"); + for (size_t i = 0; i < arraysize(kExpectations); ++i) { + ASSERT_LT(i, static_cast<size_t>(NUM_LANGUAGES)); + + std::string lang = LanguageCodeWithDialects(static_cast<Language>(i)); + SCOPED_TRACE(::testing::Message::Message() << "Iteration " << i << + " language=" << lang); + + // We should not have a translate infobar. + TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar(); + ASSERT_TRUE(infobar == NULL); + + // Simulate navigating to a page. + NavigateAndCommit(url); + SimulateOnPageContents(url, i, L"", lang); + + // Verify we have/don't have an info-bar as expected. + infobar = GetTranslateInfoBar(); + EXPECT_EQ(kExpectations[i], infobar != NULL); + + // Close the info-bar if applicable. + if (infobar != NULL) + EXPECT_TRUE(CloseTranslateInfoBar()); + } +} + +// Tests auto-translate on page. +TEST_F(TranslateManager2Test, AutoTranslateOnNavigate) { + // Simulate navigating to a page and getting its language. + SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr"); + + // Simulate the user translating. + TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar(); + ASSERT_TRUE(infobar != NULL); + infobar->Translate(); + SimulateURLFetch(true); // Simulate the translate script being retrieved. + + rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "fr", "en", + TranslateErrors::NONE)); + + // Now navigate to a new page in the same language. + process()->sink().ClearMessages(); + SimulateNavigation(GURL("http://news.google.fr"), 1, L"Les news", "fr"); + + // This should have automatically triggered a translation. + int page_id = 0; + std::string original_lang, target_lang; + EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); + EXPECT_EQ(1, page_id); + EXPECT_EQ("fr", original_lang); + EXPECT_EQ("en", target_lang); + + // Now navigate to a page in a different language. + process()->sink().ClearMessages(); + SimulateNavigation(GURL("http://news.google.es"), 1, L"Las news", "es"); + + // This should not have triggered a translate. + EXPECT_FALSE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); +} + +// Tests that multiple OnPageContents do not cause multiple infobars. +TEST_F(TranslateManager2Test, MultipleOnPageContents) { + // Simulate navigating to a page and getting its language. + SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr"); + + // Simulate clicking 'Nope' (don't translate). + EXPECT_TRUE(DenyTranslation()); + EXPECT_EQ(0, contents()->infobar_delegate_count()); + + // Send a new PageContents, we should not show an infobar. + SimulateOnPageContents(GURL("http://www.google.fr"), 0, L"Le Google", "fr"); + EXPECT_EQ(0, contents()->infobar_delegate_count()); + + // Do the same steps but simulate closing the infobar this time. + SimulateNavigation(GURL("http://www.youtube.fr"), 1, L"Le YouTube", "fr"); + EXPECT_TRUE(CloseTranslateInfoBar()); + EXPECT_EQ(0, contents()->infobar_delegate_count()); + SimulateOnPageContents(GURL("http://www.youtube.fr"), 1, L"Le YouTube", "fr"); + EXPECT_EQ(0, contents()->infobar_delegate_count()); +} + +// Test that reloading the page brings back the infobar. +TEST_F(TranslateManager2Test, Reload) { + // Simulate navigating to a page and getting its language. + SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr"); + + // Close the infobar. + EXPECT_TRUE(CloseTranslateInfoBar()); + + // Reload should bring back the infobar. + NavEntryCommittedObserver nav_observer(contents()); + Reload(); + + // Ensures it is really handled a reload. + const NavigationController::LoadCommittedDetails& nav_details = + nav_observer.get_load_commited_details(); + EXPECT_TRUE(nav_details.entry != NULL); // There was a navigation. + EXPECT_EQ(NavigationType::EXISTING_PAGE, nav_details.type); + + // The TranslateManager class processes the navigation entry committed + // notification in a posted task; process that task. + MessageLoop::current()->RunAllPending(); + EXPECT_TRUE(GetTranslateInfoBar() != NULL); +} + +// Test that reloading the page by way of typing again the URL in the +// location bar brings back the infobar. +TEST_F(TranslateManager2Test, ReloadFromLocationBar) { + GURL url("http://www.google.fr"); + + // Simulate navigating to a page and getting its language. + SimulateNavigation(url, 0, L"Le Google", "fr"); + + // Close the infobar. + EXPECT_TRUE(CloseTranslateInfoBar()); + + // Create a pending navigation and simulate a page load. That should be the + // equivalent of typing the URL again in the location bar. + NavEntryCommittedObserver nav_observer(contents()); + contents()->controller().LoadURL(url, GURL(), PageTransition::TYPED); + rvh()->SendNavigate(0, url); + + // Test that we are really getting a same page navigation, the test would be + // useless if it was not the case. + const NavigationController::LoadCommittedDetails& nav_details = + nav_observer.get_load_commited_details(); + EXPECT_TRUE(nav_details.entry != NULL); // There was a navigation. + EXPECT_EQ(NavigationType::SAME_PAGE, nav_details.type); + + // The TranslateManager class processes the navigation entry committed + // notification in a posted task; process that task. + MessageLoop::current()->RunAllPending(); + EXPECT_TRUE(GetTranslateInfoBar() != NULL); +} + +// Tests that a close translate infobar does not reappear when navigating +// in-page. +TEST_F(TranslateManager2Test, CloseInfoBarInPageNavigation) { + // Simulate navigating to a page and getting its language. + SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr"); + + // Close the infobar. + EXPECT_TRUE(CloseTranslateInfoBar()); + + // Navigate in page, no infobar should be shown. + SimulateNavigation(GURL("http://www.google.fr/#ref1"), 0, L"Le Google", "fr"); + EXPECT_TRUE(GetTranslateInfoBar() == NULL); + + // Navigate out of page, a new infobar should show. + SimulateNavigation(GURL("http://www.google.fr/foot"), 0, L"Le Google", "fr"); + EXPECT_TRUE(GetTranslateInfoBar() != NULL); +} + +// Tests that denying translation is sticky when navigating in page. +TEST_F(TranslateManager2Test, DenyTranslateInPageNavigation) { + // Simulate navigating to a page and getting its language. + SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr"); + + // Simulate clicking 'Nope' (don't translate). + EXPECT_TRUE(DenyTranslation()); + + // Navigate in page, no infobar should be shown. + SimulateNavigation(GURL("http://www.google.fr/#ref1"), 0, L"Le Google", "fr"); + EXPECT_TRUE(GetTranslateInfoBar() == NULL); + + // Navigate out of page, a new infobar should show. + SimulateNavigation(GURL("http://www.google.fr/foot"), 0, L"Le Google", "fr"); + EXPECT_TRUE(GetTranslateInfoBar() != NULL); +} + +// Tests that after translating and closing the infobar, the infobar does not +// return when navigating in page. +TEST_F(TranslateManager2Test, TranslateCloseInfoBarInPageNavigation) { + // Simulate navigating to a page and getting its language. + SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr"); + + // Simulate the user translating. + TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar(); + ASSERT_TRUE(infobar != NULL); + infobar->Translate(); + SimulateURLFetch(true); // Simulate the translate script being retrieved. + rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "fr", "en", + TranslateErrors::NONE)); + + // Close the infobar. + EXPECT_TRUE(CloseTranslateInfoBar()); + + // Navigate in page, no infobar should be shown. + SimulateNavigation(GURL("http://www.google.fr/#ref1"), 0, L"Le Google", "fr"); + EXPECT_TRUE(GetTranslateInfoBar() == NULL); + + // Navigate out of page, a new infobar should show. + // Note that we navigate to a page in a different language so we don't trigger + // the auto-translate feature (it would translate the page automatically and + // the before translate inforbar would not be shown). + SimulateNavigation(GURL("http://www.google.de"), 0, L"Das Google", "de"); + EXPECT_TRUE(GetTranslateInfoBar() != NULL); +} + +// Tests that the after translate the infobar still shows when navigating +// in-page. +TEST_F(TranslateManager2Test, TranslateInPageNavigation) { + // Simulate navigating to a page and getting its language. + SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr"); + + // Simulate the user translating. + TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar(); + ASSERT_TRUE(infobar != NULL); + infobar->Translate(); + SimulateURLFetch(true); // Simulate the translate script being retrieved. + rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "fr", "en", + TranslateErrors::NONE)); + // The after translate infobar is showing. + infobar = GetTranslateInfoBar(); + ASSERT_TRUE(infobar != NULL); + + // Navigate in page, the same infobar should still be shown. + ClearRemovedInfoBars(); + SimulateNavigation(GURL("http://www.google.fr/#ref1"), 0, L"Le Google", "fr"); + EXPECT_FALSE(InfoBarRemoved()); + EXPECT_EQ(infobar, GetTranslateInfoBar()); + + // Navigate out of page, a new infobar should show. + // See note in TranslateCloseInfoBarInPageNavigation test on why it is + // important to navigate to a page in a different language for this test. + SimulateNavigation(GURL("http://www.google.de"), 0, L"Das Google", "de"); + // The old infobar is gone. + EXPECT_TRUE(CheckInfoBarRemovedAndReset(infobar)); + // And there is a new one. + EXPECT_TRUE(GetTranslateInfoBar() != NULL); +} + +// Tests that no translate infobar is shown when navigating to a page in an +// unsupported language. +TEST_F(TranslateManager2Test, UnsupportedPageLanguage) { + // Simulate navigating to a page and getting an unsupported language. + SimulateNavigation(GURL("http://www.google.com"), 0, L"Google", "qbz"); + + // No info-bar should be shown. + EXPECT_TRUE(GetTranslateInfoBar() == NULL); +} + +// Tests that no translate infobar is shown when Chrome is in a language that +// the translate server does not support. +TEST_F(TranslateManager2Test, UnsupportedUILanguage) { + TestingBrowserProcess* browser_process = + static_cast<TestingBrowserProcess*>(g_browser_process); + std::string original_lang = browser_process->GetApplicationLocale(); + browser_process->SetApplicationLocale("qbz"); + + // Simulate navigating to a page in a language supported by the translate + // server. + SimulateNavigation(GURL("http://www.google.com"), 0, L"Google", "en"); + + // No info-bar should be shown. + EXPECT_TRUE(GetTranslateInfoBar() == NULL); + + browser_process->SetApplicationLocale(original_lang); +} + +// Tests that the translate enabled preference is honored. +TEST_F(TranslateManager2Test, TranslateEnabledPref) { + // Make sure the pref allows translate. + PrefService* prefs = contents()->profile()->GetPrefs(); + prefs->SetBoolean(prefs::kEnableTranslate, true); + + // Simulate navigating to a page and getting its language. + SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr"); + + // An infobar should be shown. + TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar(); + EXPECT_TRUE(infobar != NULL); + + // Disable translate. + prefs->SetBoolean(prefs::kEnableTranslate, false); + + // Navigate to a new page, that should close the previous infobar. + GURL url("http://www.youtube.fr"); + NavigateAndCommit(url); + infobar = GetTranslateInfoBar(); + EXPECT_TRUE(infobar == NULL); + + // Simulate getting the page contents and language, that should not trigger + // a translate infobar. + SimulateOnPageContents(url, 1, L"Le YouTube", "fr"); + infobar = GetTranslateInfoBar(); + EXPECT_TRUE(infobar == NULL); +} + +// Tests the "Never translate <language>" pref. +TEST_F(TranslateManager2Test, NeverTranslateLanguagePref) { + // Simulate navigating to a page and getting its language. + GURL url("http://www.google.fr"); + SimulateNavigation(url, 0, L"Le Google", "fr"); + + // An infobar should be shown. + EXPECT_TRUE(GetTranslateInfoBar() != NULL); + + // Select never translate this language. + PrefService* prefs = contents()->profile()->GetPrefs(); + prefs->AddPrefObserver(TranslatePrefs::kPrefTranslateLanguageBlacklist, + &pref_observer_); + TranslatePrefs translate_prefs(prefs); + EXPECT_FALSE(translate_prefs.IsLanguageBlacklisted("fr")); + EXPECT_TRUE(translate_prefs.CanTranslate(prefs, "fr", url)); + SetPrefObserverExpectation(TranslatePrefs::kPrefTranslateLanguageBlacklist); + translate_prefs.BlacklistLanguage("fr"); + EXPECT_TRUE(translate_prefs.IsLanguageBlacklisted("fr")); + EXPECT_FALSE(translate_prefs.CanTranslate(prefs, "fr", url)); + + // Close the infobar. + EXPECT_TRUE(CloseTranslateInfoBar()); + + // Navigate to a new page also in French. + SimulateNavigation(GURL("http://wwww.youtube.fr"), 1, L"Le YouTube", "fr"); + + // There should not be a translate infobar. + EXPECT_TRUE(GetTranslateInfoBar() == NULL); + + // Remove the language from the blacklist. + SetPrefObserverExpectation(TranslatePrefs::kPrefTranslateLanguageBlacklist); + translate_prefs.RemoveLanguageFromBlacklist("fr"); + EXPECT_FALSE(translate_prefs.IsLanguageBlacklisted("fr")); + EXPECT_TRUE(translate_prefs.CanTranslate(prefs, "fr", url)); + + // Navigate to a page in French. + SimulateNavigation(url, 2, L"Le Google", "fr"); + + // There should be a translate infobar. + EXPECT_TRUE(GetTranslateInfoBar() != NULL); + prefs->RemovePrefObserver(TranslatePrefs::kPrefTranslateLanguageBlacklist, + &pref_observer_); +} + +// Tests the "Never translate this site" pref. +TEST_F(TranslateManager2Test, NeverTranslateSitePref) { + // Simulate navigating to a page and getting its language. + GURL url("http://www.google.fr"); + std::string host(url.host()); + SimulateNavigation(url, 0, L"Le Google", "fr"); + + // An infobar should be shown. + EXPECT_TRUE(GetTranslateInfoBar() != NULL); + + // Select never translate this site. + PrefService* prefs = contents()->profile()->GetPrefs(); + prefs->AddPrefObserver(TranslatePrefs::kPrefTranslateSiteBlacklist, + &pref_observer_); + TranslatePrefs translate_prefs(prefs); + EXPECT_FALSE(translate_prefs.IsSiteBlacklisted(host)); + EXPECT_TRUE(translate_prefs.CanTranslate(prefs, "fr", url)); + SetPrefObserverExpectation(TranslatePrefs::kPrefTranslateSiteBlacklist); + translate_prefs.BlacklistSite(host); + EXPECT_TRUE(translate_prefs.IsSiteBlacklisted(host)); + EXPECT_FALSE(translate_prefs.CanTranslate(prefs, "fr", url)); + + // Close the infobar. + EXPECT_TRUE(CloseTranslateInfoBar()); + + // Navigate to a new page also on the same site. + SimulateNavigation(GURL("http://www.google.fr/hello"), 1, L"Bonjour", "fr"); + + // There should not be a translate infobar. + EXPECT_TRUE(GetTranslateInfoBar() == NULL); + + // Remove the site from the blacklist. + SetPrefObserverExpectation(TranslatePrefs::kPrefTranslateSiteBlacklist); + translate_prefs.RemoveSiteFromBlacklist(host); + EXPECT_FALSE(translate_prefs.IsSiteBlacklisted(host)); + EXPECT_TRUE(translate_prefs.CanTranslate(prefs, "fr", url)); + + // Navigate to a page in French. + SimulateNavigation(url, 0, L"Le Google", "fr"); + + // There should be a translate infobar. + EXPECT_TRUE(GetTranslateInfoBar() != NULL); + prefs->RemovePrefObserver(TranslatePrefs::kPrefTranslateSiteBlacklist, + &pref_observer_); +} + +// Tests the "Always translate this language" pref. +TEST_F(TranslateManager2Test, AlwaysTranslateLanguagePref) { + // Select always translate French to English. + PrefService* prefs = contents()->profile()->GetPrefs(); + prefs->AddPrefObserver(TranslatePrefs::kPrefTranslateWhitelists, + &pref_observer_); + TranslatePrefs translate_prefs(prefs); + SetPrefObserverExpectation(TranslatePrefs::kPrefTranslateWhitelists); + translate_prefs.WhitelistLanguagePair("fr", "en"); + + // Load a page in French. + SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr"); + + // It should have triggered an automatic translation to English. + SimulateURLFetch(true); // Simulate the translate script being retrieved. + int page_id = 0; + std::string original_lang, target_lang; + EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); + EXPECT_EQ(0, page_id); + EXPECT_EQ("fr", original_lang); + EXPECT_EQ("en", target_lang); + process()->sink().ClearMessages(); + // And we should have no infobar (since we don't send the page translated + // notification, the after translate infobar is not shown). + EXPECT_TRUE(GetTranslateInfoBar() == NULL); + + // Try another language, it should not be autotranslated. + SimulateNavigation(GURL("http://www.google.es"), 1, L"El Google", "es"); + EXPECT_FALSE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); + EXPECT_TRUE(GetTranslateInfoBar() != NULL); + EXPECT_TRUE(CloseTranslateInfoBar()); + + // Let's switch to incognito mode, it should not be autotranslated in that + // case either. + TestingProfile* test_profile = + static_cast<TestingProfile*>(contents()->profile()); + test_profile->set_off_the_record(true); + SimulateNavigation(GURL("http://www.youtube.fr"), 2, L"Le YouTube", "fr"); + EXPECT_FALSE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); + EXPECT_TRUE(GetTranslateInfoBar() != NULL); + EXPECT_TRUE(CloseTranslateInfoBar()); + test_profile->set_off_the_record(false); // Get back to non incognito. + + // Now revert the always translate pref and make sure we go back to expected + // behavior, which is show an infobar. + SetPrefObserverExpectation(TranslatePrefs::kPrefTranslateWhitelists); + translate_prefs.RemoveLanguagePairFromWhitelist("fr", "en"); + SimulateNavigation(GURL("http://www.google.fr"), 3, L"Le Google", "fr"); + EXPECT_FALSE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); + EXPECT_TRUE(GetTranslateInfoBar() != NULL); + prefs->RemovePrefObserver(TranslatePrefs::kPrefTranslateWhitelists, + &pref_observer_); +} +/* + +// Context menu. +TEST_F(TranslateManager2Test, ContextMenu) { + // Blacklist www.google.fr and French for translation. + GURL url("http://www.google.fr"); + TranslatePrefs translate_prefs(contents()->profile()->GetPrefs()); + translate_prefs.BlacklistLanguage("fr"); + translate_prefs.BlacklistSite(url.host()); + EXPECT_TRUE(translate_prefs.IsLanguageBlacklisted("fr")); + EXPECT_TRUE(translate_prefs.IsSiteBlacklisted(url.host())); + + // Simulate navigating to a page in French. The translate menu should show. + SimulateNavigation(url, 0, L"Le Google", "fr"); + scoped_ptr<TestRenderViewContextMenu> menu( + TestRenderViewContextMenu::CreateContextMenu(contents())); + menu->Init(); + EXPECT_TRUE(menu->IsItemPresent(IDS_CONTENT_CONTEXT_TRANSLATE)); + EXPECT_TRUE(menu->TestIsCommandIdEnabled(IDS_CONTENT_CONTEXT_TRANSLATE)); + + // Use the menu to translate the page. + menu->TestExecuteItemCommand(IDS_CONTENT_CONTEXT_TRANSLATE); + + // That should have triggered a translation. + SimulateURLFetch(true); // Simulate the translate script being retrieved. + int page_id = 0; + std::string original_lang, target_lang; + EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); + EXPECT_EQ(0, page_id); + EXPECT_EQ("fr", original_lang); + EXPECT_EQ("en", target_lang); + process()->sink().ClearMessages(); + + // This should also have reverted the blacklisting of this site and language. + EXPECT_FALSE(translate_prefs.IsLanguageBlacklisted("fr")); + EXPECT_FALSE(translate_prefs.IsSiteBlacklisted(url.host())); + + // Let's simulate the page being translated. + rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "fr", "en", + TranslateErrors::NONE)); + + // The translate menu should now be disabled. + menu.reset(TestRenderViewContextMenu::CreateContextMenu(contents())); + menu->Init(); + EXPECT_TRUE(menu->IsItemPresent(IDS_CONTENT_CONTEXT_TRANSLATE)); + EXPECT_FALSE(menu->TestIsCommandIdEnabled(IDS_CONTENT_CONTEXT_TRANSLATE)); + + // Test that selecting translate in the context menu WHILE the page is being + // translated does nothing (this could happen if autotranslate kicks-in and + // the user selects the menu while the translation is being performed). + SimulateNavigation(GURL("http://www.google.es"), 1, L"El Google", "es"); + TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar(); + ASSERT_TRUE(infobar != NULL); + infobar->Translate(); + EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); + EXPECT_EQ(1, page_id); + process()->sink().ClearMessages(); + menu.reset(TestRenderViewContextMenu::CreateContextMenu(contents())); + menu->Init(); + EXPECT_TRUE(menu->TestIsCommandIdEnabled(IDS_CONTENT_CONTEXT_TRANSLATE)); + menu->TestExecuteItemCommand(IDS_CONTENT_CONTEXT_TRANSLATE); + // No message expected since the translation should have been ignored. + EXPECT_FALSE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); + + // Now test that selecting translate in the context menu AFTER the page has + // been translated does nothing. + SimulateNavigation(GURL("http://www.google.de"), 2, L"Das Google", "de"); + infobar = GetTranslateInfoBar(); + ASSERT_TRUE(infobar != NULL); + infobar->Translate(); + EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); + EXPECT_EQ(2, page_id); + process()->sink().ClearMessages(); + menu.reset(TestRenderViewContextMenu::CreateContextMenu(contents())); + menu->Init(); + EXPECT_TRUE(menu->TestIsCommandIdEnabled(IDS_CONTENT_CONTEXT_TRANSLATE)); + rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "de", "en", + TranslateErrors::NONE)); + menu->TestExecuteItemCommand(IDS_CONTENT_CONTEXT_TRANSLATE); + // No message expected since the translation should have been ignored. + EXPECT_FALSE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); + + // Test that the translate context menu is disabled when the page is in the + // same language as the UI. + SimulateNavigation(url, 0, L"Google", "en"); + menu.reset(TestRenderViewContextMenu::CreateContextMenu(contents())); + menu->Init(); + EXPECT_TRUE(menu->IsItemPresent(IDS_CONTENT_CONTEXT_TRANSLATE)); + EXPECT_FALSE(menu->TestIsCommandIdEnabled(IDS_CONTENT_CONTEXT_TRANSLATE)); +} +*/ + +// Context menu. +TEST_F(TranslateManager2Test, ContextMenu) { + // Blacklist www.google.fr and French for translation. + GURL url("http://www.google.fr"); + TranslatePrefs translate_prefs(contents()->profile()->GetPrefs()); + translate_prefs.BlacklistLanguage("fr"); + translate_prefs.BlacklistSite(url.host()); + EXPECT_TRUE(translate_prefs.IsLanguageBlacklisted("fr")); + EXPECT_TRUE(translate_prefs.IsSiteBlacklisted(url.host())); + + // Simulate navigating to a page in French. The translate menu should show. + SimulateNavigation(url, 0, L"Le Google", "fr"); + scoped_ptr<TestRenderViewContextMenu> menu( + TestRenderViewContextMenu::CreateContextMenu(contents())); + menu->Init(); + EXPECT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_TRANSLATE)); + EXPECT_TRUE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_TRANSLATE)); + + // Use the menu to translate the page. + menu->ExecuteCommand(IDC_CONTENT_CONTEXT_TRANSLATE); + + // That should have triggered a translation. + SimulateURLFetch(true); // Simulate the translate script being retrieved. + int page_id = 0; + std::string original_lang, target_lang; + EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); + EXPECT_EQ(0, page_id); + EXPECT_EQ("fr", original_lang); + EXPECT_EQ("en", target_lang); + process()->sink().ClearMessages(); + + // This should also have reverted the blacklisting of this site and language. + EXPECT_FALSE(translate_prefs.IsLanguageBlacklisted("fr")); + EXPECT_FALSE(translate_prefs.IsSiteBlacklisted(url.host())); + + // Let's simulate the page being translated. + rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "fr", "en", + TranslateErrors::NONE)); + + // The translate menu should now be disabled. + menu.reset(TestRenderViewContextMenu::CreateContextMenu(contents())); + menu->Init(); + EXPECT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_TRANSLATE)); + EXPECT_FALSE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_TRANSLATE)); + + // Test that selecting translate in the context menu WHILE the page is being + // translated does nothing (this could happen if autotranslate kicks-in and + // the user selects the menu while the translation is being performed). + SimulateNavigation(GURL("http://www.google.es"), 1, L"El Google", "es"); + TranslateInfoBarDelegate2* infobar = GetTranslateInfoBar(); + ASSERT_TRUE(infobar != NULL); + infobar->Translate(); + EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); + EXPECT_EQ(1, page_id); + process()->sink().ClearMessages(); + menu.reset(TestRenderViewContextMenu::CreateContextMenu(contents())); + menu->Init(); + EXPECT_TRUE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_TRANSLATE)); + menu->ExecuteCommand(IDC_CONTENT_CONTEXT_TRANSLATE); + // No message expected since the translation should have been ignored. + EXPECT_FALSE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); + + // Now test that selecting translate in the context menu AFTER the page has + // been translated does nothing. + SimulateNavigation(GURL("http://www.google.de"), 2, L"Das Google", "de"); + infobar = GetTranslateInfoBar(); + ASSERT_TRUE(infobar != NULL); + infobar->Translate(); + EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); + EXPECT_EQ(2, page_id); + process()->sink().ClearMessages(); + menu.reset(TestRenderViewContextMenu::CreateContextMenu(contents())); + menu->Init(); + EXPECT_TRUE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_TRANSLATE)); + rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "de", "en", + TranslateErrors::NONE)); + menu->ExecuteCommand(IDC_CONTENT_CONTEXT_TRANSLATE); + // No message expected since the translation should have been ignored. + EXPECT_FALSE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); + + // Test that the translate context menu is disabled when the page is in the + // same language as the UI. + SimulateNavigation(url, 0, L"Google", "en"); + menu.reset(TestRenderViewContextMenu::CreateContextMenu(contents())); + menu->Init(); + EXPECT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_TRANSLATE)); + EXPECT_FALSE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_TRANSLATE)); +} diff --git a/chrome/browser/views/infobars/after_translate_infobar.cc b/chrome/browser/views/infobars/after_translate_infobar.cc new file mode 100644 index 0000000..c5c5691 --- /dev/null +++ b/chrome/browser/views/infobars/after_translate_infobar.cc @@ -0,0 +1,170 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/views/infobars/after_translate_infobar.h" + +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/translate/options_menu_model.h" +#include "chrome/browser/translate/translate_infobar_delegate2.h" +#include "chrome/browser/views/infobars/infobar_button_border.h" +#include "chrome/browser/views/infobars/infobar_text_button.h" +#include "grit/app_resources.h" +#include "grit/generated_resources.h" +#include "views/controls/button/menu_button.h" +#include "views/controls/button/text_button.h" +#include "views/controls/image_view.h" +#include "views/controls/label.h" +#include "views/controls/menu/menu_2.h" + +AfterTranslateInfoBar::AfterTranslateInfoBar( + TranslateInfoBarDelegate2* delegate) + : TranslateInfoBarBase(delegate), + original_language_menu_model_(delegate, LanguagesMenuModel2::ORIGINAL), + target_language_menu_model_(delegate, LanguagesMenuModel2::TARGET), + options_menu_model_(delegate), + swapped_language_buttons_(false) { + std::vector<string16> strings; + TranslateInfoBarDelegate2::GetAfterTranslateStrings( + &strings, &swapped_language_buttons_); + DCHECK(strings.size() == 3U); + + label_1_ = CreateLabel(strings[0]); + AddChildView(label_1_); + + label_2_ = CreateLabel(strings[1]); + AddChildView(label_2_); + + label_3_ = CreateLabel(strings[2]); + AddChildView(label_3_); + + original_language_menu_button_ = CreateMenuButton(string16(), true, this); + AddChildView(original_language_menu_button_); + + target_language_menu_button_ = CreateMenuButton(string16(), true, this); + AddChildView(target_language_menu_button_); + + options_menu_button_ = + CreateMenuButton(l10n_util::GetStringUTF16(IDS_TRANSLATE_INFOBAR_OPTIONS), + false, this); + AddChildView(options_menu_button_); + + revert_button_ = InfoBarTextButton::Create(this, + l10n_util::GetStringUTF16(IDS_TRANSLATE_INFOBAR_REVERT)); + AddChildView(revert_button_); + + UpdateLanguageButtonText(LanguagesMenuModel2::ORIGINAL); + UpdateLanguageButtonText(LanguagesMenuModel2::TARGET); +} + +AfterTranslateInfoBar::~AfterTranslateInfoBar() { +} + +// Overridden from views::View: +void AfterTranslateInfoBar::Layout() { + // Layout the icon and close button. + TranslateInfoBarBase::Layout(); + + // Layout the options menu button on right of bar. + int available_width = InfoBar::GetAvailableWidth(); + gfx::Size pref_size = options_menu_button_->GetPreferredSize(); + options_menu_button_->SetBounds(available_width - pref_size.width(), + OffsetY(this, pref_size), pref_size.width(), pref_size.height()); + + views::MenuButton* left_button = swapped_language_buttons_ ? + target_language_menu_button_ : original_language_menu_button_; + views::MenuButton* right_button = swapped_language_buttons_ ? + original_language_menu_button_ : target_language_menu_button_; + + pref_size = label_1_->GetPreferredSize(); + label_1_->SetBounds(icon_->bounds().right() + InfoBar::kIconLabelSpacing, + InfoBar::OffsetY(this, pref_size), pref_size.width(), pref_size.height()); + + pref_size = left_button->GetPreferredSize(); + left_button->SetBounds(label_1_->bounds().right() + + InfoBar::kButtonInLabelSpacing, OffsetY(this, pref_size), + pref_size.width(), pref_size.height()); + + pref_size = label_2_->GetPreferredSize(); + label_2_->SetBounds(left_button->bounds().right() + + InfoBar::kButtonInLabelSpacing, InfoBar::OffsetY(this, pref_size), + pref_size.width(), pref_size.height()); + + pref_size = right_button->GetPreferredSize(); + right_button->SetBounds(label_2_->bounds().right() + + InfoBar::kButtonInLabelSpacing, OffsetY(this, pref_size), + pref_size.width(), pref_size.height()); + + pref_size = label_3_->GetPreferredSize(); + label_3_->SetBounds(right_button->bounds().right() + + InfoBar::kButtonInLabelSpacing, InfoBar::OffsetY(this, pref_size), + pref_size.width(), pref_size.height()); + + pref_size = revert_button_->GetPreferredSize(); + revert_button_->SetBounds(label_3_->bounds().right() + + InfoBar::kButtonInLabelSpacing, InfoBar::OffsetY(this, pref_size), + pref_size.width(), pref_size.height()); +} + +void AfterTranslateInfoBar::OriginalLanguageChanged() { + UpdateLanguageButtonText(LanguagesMenuModel2::ORIGINAL); +} + +void AfterTranslateInfoBar::TargetLanguageChanged() { + UpdateLanguageButtonText(LanguagesMenuModel2::TARGET); +} + +void AfterTranslateInfoBar::ButtonPressed(views::Button* sender, + const views::Event& event) { + if (sender == revert_button_) { + GetDelegate()->RevertTranslation(); + return; + } + TranslateInfoBarBase::ButtonPressed(sender, event); +} + +void AfterTranslateInfoBar::RunMenu(views::View* source, + const gfx::Point& pt) { + if (source == original_language_menu_button_) { + if (!original_language_menu_.get()) { + original_language_menu_.reset( + new views::Menu2(&original_language_menu_model_)); + } + original_language_menu_->RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT); + } else if (source == target_language_menu_button_) { + if (!target_language_menu_.get()) { + target_language_menu_.reset( + new views::Menu2(&target_language_menu_model_)); + } + target_language_menu_->RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT); + } else if (source == options_menu_button_) { + if (!options_menu_.get()) + options_menu_.reset(new views::Menu2(&options_menu_model_)); + options_menu_->RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT); + } else { + NOTREACHED(); + } +} + +void AfterTranslateInfoBar::UpdateLanguageButtonText( + LanguagesMenuModel2::LanguageType language_type) { + int language_index; + views::MenuButton* language_button; + if (language_type == LanguagesMenuModel2::ORIGINAL) { + language_index = GetDelegate()->original_language_index(); + language_button = original_language_menu_button_; + } else { + language_index = GetDelegate()->target_language_index(); + language_button = target_language_menu_button_; + } + string16 language = + GetDelegate()->GetLanguageDisplayableNameAt(language_index); + language_button->SetText(UTF16ToWideHack(language)); + // The following line is necessary for the preferred size to be recomputed. + language_button->ClearMaxTextSize(); + // The button may have to grow to show the new text. + Layout(); + SchedulePaint(); +} diff --git a/chrome/browser/views/infobars/after_translate_infobar.h b/chrome/browser/views/infobars/after_translate_infobar.h new file mode 100644 index 0000000..1717239 --- /dev/null +++ b/chrome/browser/views/infobars/after_translate_infobar.h @@ -0,0 +1,82 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_VIEWS_INFOBARS_AFTER_TRANSLATE_INFOBAR_H_ +#define CHROME_BROWSER_VIEWS_INFOBARS_AFTER_TRANSLATE_INFOBAR_H_ + +#include <string> + +#include "app/menus/simple_menu_model.h" +#include "chrome/browser/translate/languages_menu_model2.h" +#include "chrome/browser/translate/options_menu_model2.h" +#include "chrome/browser/translate/translate_infobar_view.h" +#include "chrome/browser/views/infobars/translate_infobar_base.h" +#include "views/controls/button/button.h" +#include "views/controls/link.h" +#include "views/controls/menu/view_menu_delegate.h" + +class InfoBarTextButton; +class TranslateInfoBarDelegate2; + +namespace views { +class Menu2; +class MenuButton; +} + +class AfterTranslateInfoBar : + public TranslateInfoBarBase, + public views::ViewMenuDelegate { + public: + explicit AfterTranslateInfoBar(TranslateInfoBarDelegate2* delegate); + virtual ~AfterTranslateInfoBar(); + + // Overridden from views::View: + virtual void Layout(); + + // Overridden from TranslateInfoBarView: + virtual void OriginalLanguageChanged(); + virtual void TargetLanguageChanged(); + + // Overridden from views::ButtonListener: + virtual void ButtonPressed(views::Button* sender, const views::Event& event); + + protected: + // Overridden from views::ViewMenuDelegate: + virtual void RunMenu(View* source, const gfx::Point& pt); + + private: + // Sets the text of the original or target language menu buttons to reflect + // the current value from the delegate. + void UpdateLanguageButtonText(LanguagesMenuModel2::LanguageType language); + + // The text displayed in the infobar is something like: + // "Translated from <lang1> to <lang2>" + // Where <lang1> and <lang2> are displayed in a combobox. + // So the text is split in 3 chunks, each one displayed in one of the label + // below. + views::Label* label_1_; + views::Label* label_2_; + views::Label* label_3_; + + views::MenuButton* original_language_menu_button_; + views::MenuButton* target_language_menu_button_; + views::MenuButton* options_menu_button_; + InfoBarTextButton* revert_button_; + + scoped_ptr<views::Menu2> original_language_menu_; + LanguagesMenuModel2 original_language_menu_model_; + + scoped_ptr<views::Menu2> target_language_menu_; + LanguagesMenuModel2 target_language_menu_model_; + + scoped_ptr<views::Menu2> options_menu_; + OptionsMenuModel2 options_menu_model_; + + // True if the target language comes before the original one. + bool swapped_language_buttons_; + + DISALLOW_COPY_AND_ASSIGN(AfterTranslateInfoBar); +}; + +#endif // CHROME_BROWSER_VIEWS_INFOBARS_AFTER_TRANSLATE_INFOBAR_H_ diff --git a/chrome/browser/views/infobars/before_translate_infobar.cc b/chrome/browser/views/infobars/before_translate_infobar.cc new file mode 100644 index 0000000..ccc4751 --- /dev/null +++ b/chrome/browser/views/infobars/before_translate_infobar.cc @@ -0,0 +1,137 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/views/infobars/before_translate_infobar.h" + +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/translate/options_menu_model.h" +#include "chrome/browser/translate/translate_infobar_delegate2.h" +#include "chrome/browser/views/infobars/infobar_text_button.h" +#include "grit/generated_resources.h" +#include "views/controls/button/menu_button.h" +#include "views/controls/image_view.h" +#include "views/controls/menu/menu_2.h" + +BeforeTranslateInfoBar::BeforeTranslateInfoBar( + TranslateInfoBarDelegate2* delegate) + : TranslateInfoBarBase(delegate), + languages_menu_model_(delegate, LanguagesMenuModel2::ORIGINAL), + options_menu_model_(delegate) { + size_t offset = 0; + string16 text = + l10n_util::GetStringFUTF16(IDS_TRANSLATE_INFOBAR_BEFORE_MESSAGE, + string16(), &offset); + + label_1_ = CreateLabel(text.substr(0, offset)); + AddChildView(label_1_); + + label_2_ = CreateLabel(text.substr(offset)); + AddChildView(label_2_); + + accept_button_ = + InfoBarTextButton::CreateWithMessageID(this, + IDS_TRANSLATE_INFOBAR_ACCEPT); + AddChildView(accept_button_); + + deny_button_ = + InfoBarTextButton::CreateWithMessageID(this, + IDS_TRANSLATE_INFOBAR_DENY); + AddChildView(deny_button_); + + language_menu_button_ = CreateMenuButton(string16(), true, this); + AddChildView(language_menu_button_); + + options_menu_button_ = + CreateMenuButton(l10n_util::GetStringUTF16(IDS_TRANSLATE_INFOBAR_OPTIONS), + false, this); + AddChildView(options_menu_button_); + + UpdateOriginalButtonText(); +} + +BeforeTranslateInfoBar::~BeforeTranslateInfoBar() { +} + +// Overridden from views::View: +void BeforeTranslateInfoBar::Layout() { + // Layout the icon and close button. + TranslateInfoBarBase::Layout(); + + // Layout the options menu button on right of bar. + int available_width = InfoBar::GetAvailableWidth(); + gfx::Size pref_size = options_menu_button_->GetPreferredSize(); + options_menu_button_->SetBounds(available_width - pref_size.width(), + OffsetY(this, pref_size), pref_size.width(), pref_size.height()); + + pref_size = label_1_->GetPreferredSize(); + label_1_->SetBounds(icon_->bounds().right() + InfoBar::kIconLabelSpacing, + InfoBar::OffsetY(this, pref_size), pref_size.width(), pref_size.height()); + + pref_size = language_menu_button_->GetPreferredSize(); + language_menu_button_->SetBounds(label_1_->bounds().right() + + InfoBar::kButtonInLabelSpacing, OffsetY(this, pref_size), + pref_size.width(), pref_size.height()); + + pref_size = label_2_->GetPreferredSize(); + label_2_->SetBounds(language_menu_button_->bounds().right() + + InfoBar::kButtonInLabelSpacing , InfoBar::OffsetY(this, pref_size), + pref_size.width(), pref_size.height()); + + pref_size = accept_button_->GetPreferredSize(); + accept_button_->SetBounds( + label_2_->bounds().right() + InfoBar::kEndOfLabelSpacing, + OffsetY(this, pref_size), pref_size.width(), pref_size.height()); + + pref_size = deny_button_->GetPreferredSize(); + deny_button_->SetBounds( + accept_button_->bounds().right() + InfoBar::kButtonButtonSpacing, + OffsetY(this, pref_size), pref_size.width(), pref_size.height()); +} + +void BeforeTranslateInfoBar::ButtonPressed(views::Button* sender, + const views::Event& event) { + if (sender == accept_button_) { + GetDelegate()->Translate(); + } else if (sender == deny_button_) { + Close(); + } else { + TranslateInfoBarBase::ButtonPressed(sender, event); + } +} + +void BeforeTranslateInfoBar::OriginalLanguageChanged() { + UpdateOriginalButtonText(); +} + +void BeforeTranslateInfoBar::TargetLanguageChanged() { + NOTREACHED(); +} + +void BeforeTranslateInfoBar::RunMenu(views::View* source, + const gfx::Point& pt) { + if (source == language_menu_button_) { + if (!languages_menu_.get()) + languages_menu_.reset(new views::Menu2(&languages_menu_model_)); + languages_menu_->RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT); + } else if (source == options_menu_button_) { + if (!options_menu_.get()) + options_menu_.reset(new views::Menu2(&options_menu_model_)); + options_menu_->RunMenuAt(pt, views::Menu2::ALIGN_TOPRIGHT); + } else { + NOTREACHED(); + } +} + +void BeforeTranslateInfoBar::UpdateOriginalButtonText() { + string16 language = GetDelegate()->GetLanguageDisplayableNameAt( + GetDelegate()->original_language_index()); + language_menu_button_->SetText(UTF16ToWideHack(language)); + // The following line is necessary for the preferred size to be recomputed. + language_menu_button_->ClearMaxTextSize(); + // The button may have to grow to show the new text. + Layout(); + SchedulePaint(); +} diff --git a/chrome/browser/views/infobars/before_translate_infobar.h b/chrome/browser/views/infobars/before_translate_infobar.h new file mode 100644 index 0000000..610fa0e --- /dev/null +++ b/chrome/browser/views/infobars/before_translate_infobar.h @@ -0,0 +1,76 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_VIEWS_INFOBARS_BEFORE_TRANSLATE_INFOBAR_H_ +#define CHROME_BROWSER_VIEWS_INFOBARS_BEFORE_TRANSLATE_INFOBAR_H_ + +#include <string> + +#include "app/menus/simple_menu_model.h" +#include "chrome/browser/translate/languages_menu_model2.h" +#include "chrome/browser/translate/options_menu_model2.h" +#include "chrome/browser/translate/translate_infobar_view.h" +#include "chrome/browser/views/infobars/infobars.h" +#include "chrome/browser/views/infobars/translate_infobar_base.h" +#include "views/controls/button/button.h" +#include "views/controls/link.h" +#include "views/controls/menu/view_menu_delegate.h" + +class InfoBarTextButton; +class TranslateInfoBarDelegate2; + +namespace views { +class Menu2; +class MenuButton; +} + +class BeforeTranslateInfoBar + : public TranslateInfoBarBase, + public views::ViewMenuDelegate { + public: + explicit BeforeTranslateInfoBar(TranslateInfoBarDelegate2* delegate); + virtual ~BeforeTranslateInfoBar(); + + // Overridden from views::View: + virtual void Layout(); + + // Overridden from views::ButtonListener: + virtual void ButtonPressed(views::Button* sender, const views::Event& event); + + // Overridden from TranslateInfoBarView: + virtual void OriginalLanguageChanged(); + virtual void TargetLanguageChanged(); + + protected: + // Overridden from views::ViewMenuDelegate: + virtual void RunMenu(View* source, const gfx::Point& pt); + + private: + // Sets the text of the original language menu button to reflect the current + // value from the delegate. + void UpdateOriginalButtonText(); + + // The text displayed in the infobar is something like: + // "The page is in <lang>. Would you like to translate it?" + // Where <lang> is displayed in a combobox. + // So the text is split in 2 chunks, each one displayed in one of the label + // below. + views::Label* label_1_; + views::Label* label_2_; + + views::MenuButton* language_menu_button_; + views::MenuButton* options_menu_button_; + InfoBarTextButton* accept_button_; + InfoBarTextButton* deny_button_; + + scoped_ptr<views::Menu2> languages_menu_; + LanguagesMenuModel2 languages_menu_model_; + + scoped_ptr<views::Menu2> options_menu_; + OptionsMenuModel2 options_menu_model_; + + DISALLOW_COPY_AND_ASSIGN(BeforeTranslateInfoBar); +}; + +#endif // CHROME_BROWSER_VIEWS_INFOBARS_BEFORE_TRANSLATE_INFOBAR_H_ diff --git a/chrome/browser/views/infobars/infobar_button_border.cc b/chrome/browser/views/infobars/infobar_button_border.cc new file mode 100644 index 0000000..c52a2bb --- /dev/null +++ b/chrome/browser/views/infobars/infobar_button_border.cc @@ -0,0 +1,142 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/views/infobars/infobar_button_border.h" + +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "gfx/canvas.h" +#include "grit/app_resources.h" +#include "views/controls/button/text_button.h" + +// Preferred padding between text and edge +static const int kPreferredPaddingHorizontal = 6; +static const int kPreferredPaddingVertical = 5; + +// InfoBarButtonBorder, public: ---------------------------------------------- + +InfoBarButtonBorder::InfoBarButtonBorder() { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + + normal_set_.top_left = rb.GetBitmapNamed(IDR_INFOBARBUTTON_TOP_LEFT_N); + normal_set_.top = rb.GetBitmapNamed(IDR_INFOBARBUTTON_TOP_N); + normal_set_.top_right = rb.GetBitmapNamed(IDR_INFOBARBUTTON_TOP_RIGHT_N); + normal_set_.left = rb.GetBitmapNamed(IDR_INFOBARBUTTON_LEFT_N); + normal_set_.center = rb.GetBitmapNamed(IDR_INFOBARBUTTON_CENTER_N); + normal_set_.right = rb.GetBitmapNamed(IDR_INFOBARBUTTON_RIGHT_N); + normal_set_.bottom_left = rb.GetBitmapNamed(IDR_INFOBARBUTTON_BOTTOM_LEFT_N); + normal_set_.bottom = rb.GetBitmapNamed(IDR_INFOBARBUTTON_BOTTOM_N); + normal_set_.bottom_right = + rb.GetBitmapNamed(IDR_INFOBARBUTTON_BOTTOM_RIGHT_N); + + hot_set_.top_left = rb.GetBitmapNamed(IDR_INFOBARBUTTON_TOP_LEFT_H); + hot_set_.top = rb.GetBitmapNamed(IDR_INFOBARBUTTON_TOP_H); + hot_set_.top_right = rb.GetBitmapNamed(IDR_INFOBARBUTTON_TOP_RIGHT_H); + hot_set_.left = rb.GetBitmapNamed(IDR_INFOBARBUTTON_LEFT_H); + hot_set_.center = rb.GetBitmapNamed(IDR_INFOBARBUTTON_CENTER_H); + hot_set_.right = rb.GetBitmapNamed(IDR_INFOBARBUTTON_RIGHT_H); + hot_set_.bottom_left = rb.GetBitmapNamed(IDR_INFOBARBUTTON_BOTTOM_LEFT_H); + hot_set_.bottom = rb.GetBitmapNamed(IDR_INFOBARBUTTON_BOTTOM_H); + hot_set_.bottom_right = rb.GetBitmapNamed(IDR_INFOBARBUTTON_BOTTOM_RIGHT_H); + + pushed_set_.top_left = rb.GetBitmapNamed(IDR_INFOBARBUTTON_TOP_LEFT_P); + pushed_set_.top = rb.GetBitmapNamed(IDR_INFOBARBUTTON_TOP_P); + pushed_set_.top_right = rb.GetBitmapNamed(IDR_INFOBARBUTTON_TOP_RIGHT_P); + pushed_set_.left = rb.GetBitmapNamed(IDR_INFOBARBUTTON_LEFT_P); + pushed_set_.center = rb.GetBitmapNamed(IDR_INFOBARBUTTON_CENTER_P); + pushed_set_.right = rb.GetBitmapNamed(IDR_INFOBARBUTTON_RIGHT_P); + pushed_set_.bottom_left = rb.GetBitmapNamed(IDR_INFOBARBUTTON_BOTTOM_LEFT_P); + pushed_set_.bottom = rb.GetBitmapNamed(IDR_INFOBARBUTTON_BOTTOM_P); + pushed_set_.bottom_right = + rb.GetBitmapNamed(IDR_INFOBARBUTTON_BOTTOM_RIGHT_P); +} + +InfoBarButtonBorder::~InfoBarButtonBorder() { +} + +// InfoBarButtonBorder, Border overrides: ------------------------------------ + +void InfoBarButtonBorder::GetInsets(gfx::Insets* insets) const { + insets->Set(kPreferredPaddingVertical, kPreferredPaddingHorizontal, + kPreferredPaddingVertical, kPreferredPaddingHorizontal); +} + +void InfoBarButtonBorder::Paint(const views::View& view, + gfx::Canvas* canvas) const { + const views::TextButton* mb = static_cast<const views::TextButton*>(&view); + int state = mb->state(); + + // TextButton takes care of deciding when to call Paint. + const MBBImageSet* set = &normal_set_; + if (state == views::TextButton::BS_HOT) + set = &hot_set_; + else if (state == views::TextButton::BS_PUSHED) + set = &pushed_set_; + + gfx::Rect bounds = view.bounds(); + + // Draw top left image. + canvas->DrawBitmapInt(*set->top_left, 0, 0); + + // Stretch top image. + canvas->DrawBitmapInt( + *set->top, + 0, 0, set->top->width(), set->top->height(), + set->top_left->width(), + 0, + bounds.width() - set->top_right->width() - set->top_left->width(), + set->top->height(), false); + + // Draw top right image. + canvas->DrawBitmapInt(*set->top_right, + bounds.width() - set->top_right->width(), 0); + + // Stretch left image. + canvas->DrawBitmapInt( + *set->left, + 0, 0, set->left->width(), set->left->height(), + 0, + set->top_left->height(), + set->top_left->width(), + bounds.height() - set->top->height() - set->bottom_left->height(), false); + + // Stretch center image. + canvas->DrawBitmapInt( + *set->center, + 0, 0, set->center->width(), set->center->height(), + set->left->width(), + set->top->height(), + bounds.width() - set->right->width() - set->left->width(), + bounds.height() - set->bottom->height() - set->top->height(), false); + + // Stretch right image. + canvas->DrawBitmapInt( + *set->right, + 0, 0, set->right->width(), set->right->height(), + bounds.width() - set->right->width(), + set->top_right->height(), + set->right->width(), + bounds.height() - set->bottom_right->height() - + set->top_right->height(), false); + + // Draw bottom left image. + canvas->DrawBitmapInt(*set->bottom_left, + 0, + bounds.height() - set->bottom_left->height()); + + // Stretch bottom image. + canvas->DrawBitmapInt( + *set->bottom, + 0, 0, set->bottom->width(), set->bottom->height(), + set->bottom_left->width(), + bounds.height() - set->bottom->height(), + bounds.width() - set->bottom_right->width() - + set->bottom_left->width(), + set->bottom->height(), false); + + // Draw bottom right image. + canvas->DrawBitmapInt(*set->bottom_right, + bounds.width() - set->bottom_right->width(), + bounds.height() - set->bottom_right->height()); +} diff --git a/chrome/browser/views/infobars/infobar_button_border.h b/chrome/browser/views/infobars/infobar_button_border.h new file mode 100644 index 0000000..303fc4b --- /dev/null +++ b/chrome/browser/views/infobars/infobar_button_border.h @@ -0,0 +1,52 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_VIEWS_INFOBARS_INFOBAR_BUTTON_BORDER_H_ +#define CHROME_BROWSER_VIEWS_INFOBARS_INFOBAR_BUTTON_BORDER_H_ + +#include "views/border.h" + +#include "third_party/skia/include/core/SkBitmap.h" + +namespace gfx { +class Canvas; +} +namespace views { +class View; +} + +// A TextButtonBorder that is dark and also paints the button frame in the +// normal state. + +class InfoBarButtonBorder : public views::Border { + public: + InfoBarButtonBorder(); + virtual ~InfoBarButtonBorder(); + + // Overriden from Border: + virtual void GetInsets(gfx::Insets* insets) const; + virtual void Paint(const views::View& view, gfx::Canvas* canvas) const; + + private: + // Images + struct MBBImageSet { + SkBitmap* top_left; + SkBitmap* top; + SkBitmap* top_right; + SkBitmap* left; + SkBitmap* center; + SkBitmap* right; + SkBitmap* bottom_left; + SkBitmap* bottom; + SkBitmap* bottom_right; + }; + + MBBImageSet normal_set_; + MBBImageSet hot_set_; + MBBImageSet pushed_set_; + + DISALLOW_COPY_AND_ASSIGN(InfoBarButtonBorder); +}; + +#endif // CHROME_BROWSER_VIEWS_INFOBARS_INFOBAR_BUTTON_BORDER_H_ diff --git a/chrome/browser/views/infobars/infobar_text_button.cc b/chrome/browser/views/infobars/infobar_text_button.cc new file mode 100644 index 0000000..35726d0 --- /dev/null +++ b/chrome/browser/views/infobars/infobar_text_button.cc @@ -0,0 +1,50 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/views/infobars/infobar_text_button.h" + +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/views/infobars/infobar_button_border.h" + +// static +InfoBarTextButton* InfoBarTextButton::Create(views::ButtonListener* listener, + const string16& text) { + return new InfoBarTextButton(listener, text); +} + +// static +InfoBarTextButton* InfoBarTextButton::CreateWithMessageID( + views::ButtonListener* listener, int message_id) { + return new InfoBarTextButton(listener, + l10n_util::GetStringUTF16(message_id)); +} + +InfoBarTextButton::~InfoBarTextButton() { +} + +bool InfoBarTextButton::OnMousePressed(const views::MouseEvent& e) { + return views::CustomButton::OnMousePressed(e); +} + +InfoBarTextButton::InfoBarTextButton(views::ButtonListener* listener, + const string16& text) + // Don't use text to construct TextButton because we need to set font + // before setting text so that the button will resize to fit entire text. + : TextButton(listener, std::wstring()) { + set_border(new InfoBarButtonBorder); + SetNormalHasBorder(true); // Normal button state has border. + SetAnimationDuration(0); // Disable animation during state change. + // Set font colors for different states. + SetEnabledColor(SK_ColorBLACK); + SetHighlightColor(SK_ColorBLACK); + SetHoverColor(SK_ColorBLACK); + // Set font then text, then size button to fit text. + SetFont( + ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::MediumFont)); + SetText(UTF16ToWideHack(text)); + ClearMaxTextSize(); + SizeToPreferredSize(); +} diff --git a/chrome/browser/views/infobars/infobar_text_button.h b/chrome/browser/views/infobars/infobar_text_button.h new file mode 100644 index 0000000..698d2a4 --- /dev/null +++ b/chrome/browser/views/infobars/infobar_text_button.h @@ -0,0 +1,35 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_VIEWS_INFOBARS_INFOBAR_TEXT_BUTTON_H_ +#define CHROME_BROWSER_VIEWS_INFOBARS_INFOBAR_TEXT_BUTTON_H_ + +#include "views/controls/button/text_button.h" + +// A TextButton subclass that overrides OnMousePressed to default to +// CustomButton so as to create pressed state effect. + +class InfoBarTextButton : public views::TextButton { + public: + // Creates a button with the specified |text|. + static InfoBarTextButton* Create(views::ButtonListener* listener, + const string16& text); + // Creates a button which text is the resource string identified by + // |message_id|. + static InfoBarTextButton* CreateWithMessageID(views::ButtonListener* listener, + int message_id); + + virtual ~InfoBarTextButton(); + + protected: + InfoBarTextButton(views::ButtonListener* listener, const string16& text); + + // Overriden from TextButton: + virtual bool OnMousePressed(const views::MouseEvent& e); + + private: + DISALLOW_COPY_AND_ASSIGN(InfoBarTextButton); +}; + +#endif // CHROME_BROWSER_VIEWS_INFOBARS_INFOBAR_TEXT_BUTTON_H_ diff --git a/chrome/browser/views/infobars/infobars.cc b/chrome/browser/views/infobars/infobars.cc index ead3d97..55e7442 100644 --- a/chrome/browser/views/infobars/infobars.cc +++ b/chrome/browser/views/infobars/infobars.cc @@ -147,8 +147,11 @@ void InfoBar::AnimateOpen() { } void InfoBar::Open() { + // Set the animation value to 1.0 so that GetPreferredSize() returns the right + // size. animation_->Reset(1.0); - animation_->Show(); + if (container_) + container_->InfoBarAnimated(false); } void InfoBar::AnimateClose() { diff --git a/chrome/browser/views/infobars/translate_infobar_base.cc b/chrome/browser/views/infobars/translate_infobar_base.cc new file mode 100644 index 0000000..aa89609 --- /dev/null +++ b/chrome/browser/views/infobars/translate_infobar_base.cc @@ -0,0 +1,179 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/views/infobars/translate_infobar_base.h" + +#include "app/resource_bundle.h" +#include "app/slide_animation.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/translate/translate_infobar_delegate2.h" +#include "chrome/browser/views/infobars/after_translate_infobar.h" +#include "chrome/browser/views/infobars/before_translate_infobar.h" +#include "chrome/browser/views/infobars/translate_message_infobar.h" +#include "chrome/browser/views/infobars/infobar_button_border.h" +#include "gfx/canvas.h" +#include "grit/app_resources.h" +#include "views/controls/button/menu_button.h" +#include "views/controls/image_view.h" + +TranslateInfoBarBase::TranslateInfoBarBase( + TranslateInfoBarDelegate2* delegate) + : InfoBar(delegate), + normal_background_(InfoBarDelegate::PAGE_ACTION_TYPE), + error_background_(InfoBarDelegate::ERROR_TYPE) { + icon_ = new views::ImageView; + SkBitmap* image = delegate->GetIcon(); + if (image) + icon_->SetImage(image); + AddChildView(icon_); + + TranslateInfoBarDelegate2::BackgroundAnimationType animation = + delegate->background_animation_type(); + if (animation != TranslateInfoBarDelegate2::NONE) { + background_color_animation_.reset(new SlideAnimation(this)); + background_color_animation_->SetTweenType(Tween::LINEAR); + background_color_animation_->SetSlideDuration(500); + if (animation == TranslateInfoBarDelegate2::NORMAL_TO_ERROR) { + background_color_animation_->Show(); + } else { + DCHECK_EQ(TranslateInfoBarDelegate2::ERROR_TO_NORMAL, animation); + // Hide() runs the animation in reverse. + background_color_animation_->Reset(1.0); + background_color_animation_->Hide(); + } + } +} + +TranslateInfoBarBase::~TranslateInfoBarBase() { +} + +// Overridden from views::View: +void TranslateInfoBarBase::Layout() { + // Layout the close button. + InfoBar::Layout(); + + // Layout the icon on left of bar. + gfx::Size icon_ps = icon_->GetPreferredSize(); + icon_->SetBounds(InfoBar::kHorizontalPadding, InfoBar::OffsetY(this, icon_ps), + icon_ps.width(), icon_ps.height()); +} + +views::Label* TranslateInfoBarBase::CreateLabel(const string16& text) { + views::Label* label = new views::Label(UTF16ToWideHack(text), + ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::MediumFont)); + label->SetColor(SK_ColorBLACK); + label->SetHorizontalAlignment(views::Label::ALIGN_LEFT); + return label; +} + +void TranslateInfoBarBase::PaintBackground(gfx::Canvas* canvas) { + // If we're not animating, simply paint the background for the current state. + if (background_color_animation_ == NULL || + !background_color_animation_->is_animating()) { + GetBackground().Paint(canvas, this); + return; + } + + FadeBackground(canvas, 1.0 - background_color_animation_->GetCurrentValue(), + normal_background_); + FadeBackground(canvas, background_color_animation_->GetCurrentValue(), + error_background_); +} + +void TranslateInfoBarBase::AnimationProgressed(const Animation* animation) { + if (background_color_animation_.get() == animation) + SchedulePaint(); // That'll trigger a PaintBackgroud. + else + InfoBar::AnimationProgressed(animation); +} + +views::MenuButton* TranslateInfoBarBase::CreateMenuButton( + const string16& text, + bool normal_has_border, + views::ViewMenuDelegate* menu_delegate) { + // Don't use text to instantiate MenuButton because we need to set font before + // setting text so that the button will resize to fit the entire text. + views::MenuButton* menu_button = + new views::MenuButton(NULL, std::wstring(), menu_delegate, true); + menu_button->set_border(new InfoBarButtonBorder); + menu_button->set_menu_marker(ResourceBundle::GetSharedInstance(). + GetBitmapNamed(IDR_INFOBARBUTTON_MENU_DROPARROW)); + if (normal_has_border) { + menu_button->SetNormalHasBorder(true); // Normal button state has border. + // Disable animation during state change. + menu_button->SetAnimationDuration(0); + } + // Set font colors for different states. + menu_button->SetEnabledColor(SK_ColorBLACK); + menu_button->SetHighlightColor(SK_ColorBLACK); + menu_button->SetHoverColor(SK_ColorBLACK); + + // Set font then text, then size button to fit text. + menu_button->SetFont(ResourceBundle::GetSharedInstance().GetFont( + ResourceBundle::MediumFont)); + menu_button->SetText(UTF16ToWideHack(text)); + menu_button->ClearMaxTextSize(); + menu_button->SizeToPreferredSize(); + return menu_button; +} + +gfx::Point TranslateInfoBarBase::DetermineMenuPosition( + views::MenuButton* menu_button) { + gfx::Rect lb = menu_button->GetLocalBounds(true); + gfx::Point menu_position(lb.origin()); + menu_position.Offset(2, lb.height() - 3); + if (base::i18n::IsRTL()) + menu_position.Offset(lb.width() - 4, 0); + + View::ConvertPointToScreen(menu_button, &menu_position); +#if defined(OS_WIN) + int left_bound = GetSystemMetrics(SM_XVIRTUALSCREEN); + if (menu_position.x() < left_bound) + menu_position.set_x(left_bound); +#endif + return menu_position; +} + +TranslateInfoBarDelegate2* TranslateInfoBarBase::GetDelegate() const { + return static_cast<TranslateInfoBarDelegate2*>(delegate()); +} + +const InfoBarBackground& TranslateInfoBarBase::GetBackground() const { + return GetDelegate()->IsError() ? error_background_ : normal_background_; +} + +void TranslateInfoBarBase::FadeBackground(gfx::Canvas* canvas, + double animation_value, + const InfoBarBackground& background) { + // Draw the background into an offscreen buffer with alpha value per animation + // value, then blend it back into the current canvas. + canvas->saveLayerAlpha(NULL, static_cast<int>(animation_value * 255), + SkCanvas::kARGB_NoClipLayer_SaveFlag); + canvas->drawARGB(0, 255, 255, 255, SkXfermode::kClear_Mode); + background.Paint(canvas, this); + canvas->restore(); +} + +// TranslateInfoBarDelegate views specific method: +InfoBar* TranslateInfoBarDelegate2::CreateInfoBar() { + TranslateInfoBarBase* infobar = NULL; + switch (type_) { + case BEFORE_TRANSLATE: + infobar = new BeforeTranslateInfoBar(this); + break; + case AFTER_TRANSLATE: + infobar = new AfterTranslateInfoBar(this); + break; + case TRANSLATING: + case TRANSLATION_ERROR: + infobar = new TranslateMessageInfoBar(this); + break; + default: + NOTREACHED(); + } + // Set |infobar_view_| so that the model can notify the infobar when it + // changes. + infobar_view_ = infobar; + return infobar; +} diff --git a/chrome/browser/views/infobars/translate_infobar_base.h b/chrome/browser/views/infobars/translate_infobar_base.h new file mode 100644 index 0000000..758d59e --- /dev/null +++ b/chrome/browser/views/infobars/translate_infobar_base.h @@ -0,0 +1,76 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_VIEWS_INFOBARS_TRANSLATE_INFOBAR_BASE_H_ +#define CHROME_BROWSER_VIEWS_INFOBARS_TRANSLATE_INFOBAR_BASE_H_ + +#include "chrome/browser/translate/translate_infobar_view.h" +#include "chrome/browser/views/infobars/infobars.h" + +class TranslateInfoBarDelegate2; + +namespace views { +class MenuButton; +class ViewMenuDelegate; +} + +// This class contains some of the base functionality that translate infobars +// use. +class TranslateInfoBarBase : public TranslateInfoBarView, + public InfoBar { + public: + explicit TranslateInfoBarBase(TranslateInfoBarDelegate2* delegate); + virtual ~TranslateInfoBarBase(); + + // TranslateInfoBarView implementation: + virtual void OriginalLanguageChanged() {} + virtual void TargetLanguageChanged() {} + + // Overridden from views::View: + virtual void Layout(); + virtual void PaintBackground(gfx::Canvas* canvas); + + protected: + // Overridden from AnimationDelegate: + virtual void AnimationProgressed(const Animation* animation); + + // Creates a label with the appropriate font and color for the translate + // infobars. + views::Label* CreateLabel(const string16& text); + + // Creates a menu-button with a custom appearance for the translate infobars. + views::MenuButton* CreateMenuButton(const string16& text, + bool normal_has_border, + views::ViewMenuDelegate* menu_delegate); + + // Returns the location at which the menu triggered by |menu_button| should be + // positioned. + gfx::Point DetermineMenuPosition(views::MenuButton* menu_button); + + // Convenience to retrieve the TranslateInfoBarDelegate2 for this infobar. + TranslateInfoBarDelegate2* GetDelegate() const; + + // The translate icon. + views::ImageView* icon_; + + InfoBarBackground normal_background_; + InfoBarBackground error_background_; + scoped_ptr<SlideAnimation> background_color_animation_; + + private: + // Returns the background that should be displayed when not animating. + const InfoBarBackground& GetBackground() const; + + // Paints |background| to |canvas| with the opacity level based on + // |animation_value|. + void FadeBackground(gfx::Canvas* canvas, + double animation_value, + const InfoBarBackground& background); + + DISALLOW_COPY_AND_ASSIGN(TranslateInfoBarBase); +}; + +#endif // CHROME_BROWSER_VIEWS_INFOBARS_TRANSLATE_INFOBAR_BASE_H_ + + diff --git a/chrome/browser/views/infobars/translate_infobars.cc b/chrome/browser/views/infobars/translate_infobars.cc index 4ae395f..08d4a0f 100644 --- a/chrome/browser/views/infobars/translate_infobars.cc +++ b/chrome/browser/views/infobars/translate_infobars.cc @@ -17,6 +17,8 @@ #include "chrome/browser/translate/languages_menu_model.h" #include "chrome/browser/translate/options_menu_model.h" #include "chrome/browser/translate/page_translated_details.h" +#include "chrome/browser/views/infobars/infobar_button_border.h" +#include "chrome/browser/views/infobars/infobar_text_button.h" #include "gfx/canvas.h" #include "grit/app_resources.h" #include "grit/generated_resources.h" @@ -28,210 +30,13 @@ #include "views/controls/image_view.h" #include "views/controls/label.h" +#include "chrome/browser/views/infobars/before_translate_infobar.h" + // IDs for various menus. static const int kMenuIDOptions = 1000; static const int kMenuIDOriginalLanguage = 1001; static const int kMenuIDTargetLanguage = 1002; -//////////////////////////////////////////////////////////////////////////////// -// -// TranslateButtonBorder -// -// A TextButtonBorder subclass that adds to also paint button frame in normal -// state, and changes the images used. -// -//////////////////////////////////////////////////////////////////////////////// - -class TranslateButtonBorder : public views::TextButtonBorder { - public: - TranslateButtonBorder(); - virtual ~TranslateButtonBorder(); - - // Overriden from Border - virtual void Paint(const views::View& view, gfx::Canvas* canvas) const; - - private: - MBBImageSet normal_set_; - - private: - DISALLOW_COPY_AND_ASSIGN(TranslateButtonBorder); -}; - -// TranslateButtonBorder, public: ---------------------------------------------- - -TranslateButtonBorder::TranslateButtonBorder() { - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - - normal_set_.top_left = rb.GetBitmapNamed(IDR_INFOBARBUTTON_TOP_LEFT_N); - normal_set_.top = rb.GetBitmapNamed(IDR_INFOBARBUTTON_TOP_N); - normal_set_.top_right = rb.GetBitmapNamed(IDR_INFOBARBUTTON_TOP_RIGHT_N); - normal_set_.left = rb.GetBitmapNamed(IDR_INFOBARBUTTON_LEFT_N); - normal_set_.center = rb.GetBitmapNamed(IDR_INFOBARBUTTON_CENTER_N); - normal_set_.right = rb.GetBitmapNamed(IDR_INFOBARBUTTON_RIGHT_N); - normal_set_.bottom_left = rb.GetBitmapNamed(IDR_INFOBARBUTTON_BOTTOM_LEFT_N); - normal_set_.bottom = rb.GetBitmapNamed(IDR_INFOBARBUTTON_BOTTOM_N); - normal_set_.bottom_right = rb.GetBitmapNamed( - IDR_INFOBARBUTTON_BOTTOM_RIGHT_N); - - hot_set_.top_left = rb.GetBitmapNamed(IDR_INFOBARBUTTON_TOP_LEFT_H); - hot_set_.top = rb.GetBitmapNamed(IDR_INFOBARBUTTON_TOP_H); - hot_set_.top_right = rb.GetBitmapNamed(IDR_INFOBARBUTTON_TOP_RIGHT_H); - hot_set_.left = rb.GetBitmapNamed(IDR_INFOBARBUTTON_LEFT_H); - hot_set_.center = rb.GetBitmapNamed(IDR_INFOBARBUTTON_CENTER_H); - hot_set_.right = rb.GetBitmapNamed(IDR_INFOBARBUTTON_RIGHT_H); - hot_set_.bottom_left = rb.GetBitmapNamed(IDR_INFOBARBUTTON_BOTTOM_LEFT_H); - hot_set_.bottom = rb.GetBitmapNamed(IDR_INFOBARBUTTON_BOTTOM_H); - hot_set_.bottom_right = rb.GetBitmapNamed(IDR_INFOBARBUTTON_BOTTOM_RIGHT_H); - - pushed_set_.top_left = rb.GetBitmapNamed(IDR_INFOBARBUTTON_TOP_LEFT_P); - pushed_set_.top = rb.GetBitmapNamed(IDR_INFOBARBUTTON_TOP_P); - pushed_set_.top_right = rb.GetBitmapNamed(IDR_INFOBARBUTTON_TOP_RIGHT_P); - pushed_set_.left = rb.GetBitmapNamed(IDR_INFOBARBUTTON_LEFT_P); - pushed_set_.center = rb.GetBitmapNamed(IDR_INFOBARBUTTON_CENTER_P); - pushed_set_.right = rb.GetBitmapNamed(IDR_INFOBARBUTTON_RIGHT_P); - pushed_set_.bottom_left = rb.GetBitmapNamed(IDR_INFOBARBUTTON_BOTTOM_LEFT_P); - pushed_set_.bottom = rb.GetBitmapNamed(IDR_INFOBARBUTTON_BOTTOM_P); - pushed_set_.bottom_right = rb.GetBitmapNamed( - IDR_INFOBARBUTTON_BOTTOM_RIGHT_P); -} - -TranslateButtonBorder::~TranslateButtonBorder() { -} - -// TranslateButtonBorder, Border overrides: ------------------------------------ - -void TranslateButtonBorder::Paint(const views::View& view, gfx::Canvas* canvas) - const { - const views::TextButton* mb = static_cast<const views::TextButton*>(&view); - int state = mb->state(); - - // TextButton takes care of deciding when to call Paint. - const MBBImageSet* set = &normal_set_; - if (state == views::TextButton::BS_HOT) - set = &hot_set_; - else if (state == views::TextButton::BS_PUSHED) - set = &pushed_set_; - - gfx::Rect bounds = view.bounds(); - - // Draw top left image. - canvas->DrawBitmapInt(*set->top_left, 0, 0); - - // Stretch top image. - canvas->DrawBitmapInt( - *set->top, - 0, 0, set->top->width(), set->top->height(), - set->top_left->width(), - 0, - bounds.width() - set->top_right->width() - set->top_left->width(), - set->top->height(), false); - - // Draw top right image. - canvas->DrawBitmapInt(*set->top_right, - bounds.width() - set->top_right->width(), 0); - - // Stretch left image. - canvas->DrawBitmapInt( - *set->left, - 0, 0, set->left->width(), set->left->height(), - 0, - set->top_left->height(), - set->top_left->width(), - bounds.height() - set->top->height() - set->bottom_left->height(), false); - - // Stretch center image. - canvas->DrawBitmapInt( - *set->center, - 0, 0, set->center->width(), set->center->height(), - set->left->width(), - set->top->height(), - bounds.width() - set->right->width() - set->left->width(), - bounds.height() - set->bottom->height() - set->top->height(), false); - - // Stretch right image. - canvas->DrawBitmapInt( - *set->right, - 0, 0, set->right->width(), set->right->height(), - bounds.width() - set->right->width(), - set->top_right->height(), - set->right->width(), - bounds.height() - set->bottom_right->height() - - set->top_right->height(), false); - - // Draw bottom left image. - canvas->DrawBitmapInt(*set->bottom_left, - 0, - bounds.height() - set->bottom_left->height()); - - // Stretch bottom image. - canvas->DrawBitmapInt( - *set->bottom, - 0, 0, set->bottom->width(), set->bottom->height(), - set->bottom_left->width(), - bounds.height() - set->bottom->height(), - bounds.width() - set->bottom_right->width() - - set->bottom_left->width(), - set->bottom->height(), false); - - // Draw bottom right image. - canvas->DrawBitmapInt(*set->bottom_right, - bounds.width() - set->bottom_right->width(), - bounds.height() - set->bottom_right->height()); -} - -//////////////////////////////////////////////////////////////////////////////// -// -// TranslateTextButton -// -// A TextButton subclass that overrides OnMousePressed to default to -// CustomButton so as to create pressed state effect. -// -//////////////////////////////////////////////////////////////////////////////// - -class TranslateTextButton : public views::TextButton { - public: - TranslateTextButton(views::ButtonListener* listener, int label_id); - virtual ~TranslateTextButton(); - - protected: - // Overriden from TextButton: - virtual bool OnMousePressed(const views::MouseEvent& e); - - private: - DISALLOW_COPY_AND_ASSIGN(TranslateTextButton); -}; - -// TranslateButtonBorder, public: ---------------------------------------------- - -TranslateTextButton::TranslateTextButton(views::ButtonListener* listener, - int label_id) - // Don't use text to construct TextButton because we need to set font - // before setting text so that the button will resize to fit entire text. - : TextButton(listener, std::wstring()) { - set_border(new TranslateButtonBorder); - SetNormalHasBorder(true); // Normal button state has border. - SetAnimationDuration(0); // Disable animation during state change. - // Set font colors for different states. - SetEnabledColor(SK_ColorBLACK); - SetHighlightColor(SK_ColorBLACK); - SetHoverColor(SK_ColorBLACK); - // Set font then text, then size button to fit text. - SetFont(ResourceBundle::GetSharedInstance().GetFont( - ResourceBundle::MediumFont)); - SetText(l10n_util::GetString(label_id)); - ClearMaxTextSize(); - SizeToPreferredSize(); -} - -TranslateTextButton::~TranslateTextButton() { -} - -// TranslateTextButton, protected: --------------------------------------------- - -bool TranslateTextButton::OnMousePressed(const views::MouseEvent& e) { - return views::CustomButton::OnMousePressed(e); -} - // TranslateInfoBar, public: --------------------------------------------------- TranslateInfoBar::TranslateInfoBar(TranslateInfoBarDelegate* delegate) @@ -321,7 +126,7 @@ void TranslateInfoBar::UpdateState( AddChildView(target_language_menu_button_); } if (!revert_button_) { - revert_button_ = new TranslateTextButton(this, + revert_button_ = InfoBarTextButton::CreateWithMessageID(this, IDS_TRANSLATE_INFOBAR_REVERT); AddChildView(revert_button_); } @@ -331,12 +136,12 @@ void TranslateInfoBar::UpdateState( if (!label_1_) CreateLabels(); if (!accept_button_) { - accept_button_ = new TranslateTextButton(this, + accept_button_ = InfoBarTextButton::CreateWithMessageID(this, IDS_TRANSLATE_INFOBAR_ACCEPT); AddChildView(accept_button_); } if (!deny_button_) { - deny_button_ = new TranslateTextButton(this, + deny_button_ = InfoBarTextButton::CreateWithMessageID(this, IDS_TRANSLATE_INFOBAR_DENY); AddChildView(deny_button_); } @@ -352,7 +157,7 @@ void TranslateInfoBar::UpdateState( AddChildView(error_label_); } if (!retry_button_) { - retry_button_ = new TranslateTextButton(this, + retry_button_ = InfoBarTextButton::CreateWithMessageID(this, IDS_TRANSLATE_INFOBAR_RETRY); AddChildView(retry_button_); } @@ -875,7 +680,7 @@ views::MenuButton* TranslateInfoBar::CreateMenuButton(int menu_id, views::MenuButton* menu_button = new views::MenuButton(NULL, std::wstring(), this, true); menu_button->SetID(menu_id); - menu_button->set_border(new TranslateButtonBorder); + menu_button->set_border(new InfoBarButtonBorder); menu_button->set_menu_marker(ResourceBundle::GetSharedInstance(). GetBitmapNamed(IDR_INFOBARBUTTON_MENU_DROPARROW)); if (normal_has_border) { diff --git a/chrome/browser/views/infobars/translate_infobars.h b/chrome/browser/views/infobars/translate_infobars.h index 60e3dcb..3c0ac944 100644 --- a/chrome/browser/views/infobars/translate_infobars.h +++ b/chrome/browser/views/infobars/translate_infobars.h @@ -19,9 +19,9 @@ class ImageView; class Label; class MenuButton; } +class InfoBarTextButton; class LanguagesMenuModel; class OptionsMenuModel; -class TranslateTextButton; // This file contains implementations for infobars for the Translate feature. @@ -95,13 +95,13 @@ class TranslateInfoBar : public InfoBar, views::Label* label_3_; views::Label* translating_label_; views::Label* error_label_; - TranslateTextButton* accept_button_; - TranslateTextButton* deny_button_; + InfoBarTextButton* accept_button_; + InfoBarTextButton* deny_button_; views::MenuButton* original_language_menu_button_; views::MenuButton* target_language_menu_button_; - TranslateTextButton* revert_button_; + InfoBarTextButton* revert_button_; views::MenuButton* options_menu_button_; - TranslateTextButton* retry_button_; + InfoBarTextButton* retry_button_; scoped_ptr<LanguagesMenuModel> original_language_menu_model_; scoped_ptr<LanguagesMenuModel> target_language_menu_model_; diff --git a/chrome/browser/views/infobars/translate_message_infobar.cc b/chrome/browser/views/infobars/translate_message_infobar.cc new file mode 100644 index 0000000..88e99f9 --- /dev/null +++ b/chrome/browser/views/infobars/translate_message_infobar.cc @@ -0,0 +1,57 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/views/infobars/translate_message_infobar.h" + +#include "chrome/browser/translate/translate_infobar_delegate2.h" +#include "chrome/browser/views/infobars/infobar_text_button.h" +#include "views/controls/image_view.h" + +TranslateMessageInfoBar::TranslateMessageInfoBar( + TranslateInfoBarDelegate2* delegate) + : TranslateInfoBarBase(delegate) { + label_ = CreateLabel(delegate->GetMessageInfoBarText()); + AddChildView(label_); + + string16 button_text = delegate->GetMessageInfoBarButtonText(); + if (button_text.empty()) { + button_ = NULL; + } else { + button_ = InfoBarTextButton::Create(this, button_text); + AddChildView(button_); + } +} + +void TranslateMessageInfoBar::Layout() { + TranslateInfoBarBase::Layout(); + + int x = icon_->bounds().right() + InfoBar::kIconLabelSpacing; + gfx::Size label_pref_size = label_->GetPreferredSize(); + int available_width = GetAvailableWidth() - x; + gfx::Size button_pref_size; + if (button_) { + button_pref_size = button_->GetPreferredSize(); + available_width -= + (button_pref_size.width() + InfoBar::kButtonInLabelSpacing); + } + label_->SetBounds(x, InfoBar::OffsetY(this, label_pref_size), + std::min(label_pref_size.width(), available_width), + label_pref_size.height()); + + if (button_) { + button_->SetBounds(label_->bounds().right() + + InfoBar::kButtonInLabelSpacing, + InfoBar::OffsetY(this, button_pref_size), + button_pref_size.width(), button_pref_size.height()); + } +} + +void TranslateMessageInfoBar::ButtonPressed(views::Button* sender, + const views::Event& event) { + if (sender == button_) { + GetDelegate()->MessageInfoBarButtonPressed(); + return; + } + TranslateInfoBarBase::ButtonPressed(sender, event); +} diff --git a/chrome/browser/views/infobars/translate_message_infobar.h b/chrome/browser/views/infobars/translate_message_infobar.h new file mode 100644 index 0000000..b91068b --- /dev/null +++ b/chrome/browser/views/infobars/translate_message_infobar.h @@ -0,0 +1,28 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_VIEWS_INFOBARS_TRANSLATE_MESSAGE_INFOBAR_H_ +#define CHROME_BROWSER_VIEWS_INFOBARS_TRANSLATE_MESSAGE_INFOBAR_H_ + +#include "chrome/browser/views/infobars/translate_infobar_base.h" + +class InfoBarTextButton; + +class TranslateMessageInfoBar : public TranslateInfoBarBase { + public: + explicit TranslateMessageInfoBar(TranslateInfoBarDelegate2* delegate); + + virtual void Layout(); + + // views::ButtonListener implementation: + virtual void ButtonPressed(views::Button* sender, const views::Event& event); + + private: + views::Label* label_; + InfoBarTextButton* button_; + + DISALLOW_COPY_AND_ASSIGN(TranslateMessageInfoBar); +}; + +#endif // CHROME_BROWSER_VIEWS_INFOBARS_TRANSLATE_MESSAGE_INFOBAR_H_ diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 6fec0d5..916a008 100644..100755 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -2228,12 +2228,21 @@ 'browser/toolbar_model.h', 'browser/translate/languages_menu_model.cc', 'browser/translate/languages_menu_model.h', + 'browser/translate/languages_menu_model2.cc', + 'browser/translate/languages_menu_model2.h', 'browser/translate/options_menu_model.cc', 'browser/translate/options_menu_model.h', + 'browser/translate/options_menu_model2.cc', + 'browser/translate/options_menu_model2.h', + 'browser/translate/translate_infobar_view.h', 'browser/translate/translate_infobars_delegates.cc', 'browser/translate/translate_infobars_delegates.h', + 'browser/translate/translate_infobar_delegate2.cc', + 'browser/translate/translate_infobar_delegate2.h', 'browser/translate/translate_manager.cc', 'browser/translate/translate_manager.h', + 'browser/translate/translate_manager2.cc', + 'browser/translate/translate_manager2.h', 'browser/translate/translate_prefs.cc', 'browser/translate/translate_prefs.h', 'browser/upgrade_detector.cc', @@ -2408,14 +2417,26 @@ 'browser/views/importing_progress_view.h', 'browser/views/info_bubble.cc', 'browser/views/info_bubble.h', + 'browser/views/infobars/after_translate_infobar.cc', + 'browser/views/infobars/after_translate_infobar.h', + 'browser/views/infobars/before_translate_infobar.cc', + 'browser/views/infobars/before_translate_infobar.h', 'browser/views/infobars/extension_infobar.cc', 'browser/views/infobars/extension_infobar.h', + 'browser/views/infobars/infobar_button_border.cc', + 'browser/views/infobars/infobar_button_border.h', 'browser/views/infobars/infobar_container.cc', 'browser/views/infobars/infobar_container.h', + 'browser/views/infobars/infobar_text_button.cc', + 'browser/views/infobars/infobar_text_button.h', 'browser/views/infobars/infobars.cc', 'browser/views/infobars/infobars.h', 'browser/views/infobars/translate_infobars.cc', 'browser/views/infobars/translate_infobars.h', + 'browser/views/infobars/translate_infobar_base.cc', + 'browser/views/infobars/translate_infobar_base.h', + 'browser/views/infobars/translate_message_infobar.cc', + 'browser/views/infobars/translate_message_infobar.h', 'browser/views/jsmessage_box_dialog.cc', 'browser/views/jsmessage_box_dialog.h', 'browser/views/keyword_editor_view.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 6e13539..32f857e 100755 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -946,6 +946,7 @@ 'browser/theme_resources_util_unittest.cc', 'browser/thumbnail_store_unittest.cc', 'browser/translate/translate_manager_unittest.cc', + 'browser/translate/translate_manager2_unittest.cc', 'browser/user_style_sheet_watcher_unittest.cc', 'browser/views/accessibility_event_router_views_unittest.cc', 'browser/views/bookmark_context_menu_test.cc', @@ -1203,8 +1204,9 @@ '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_chromium_resources.rc', '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_resources.rc', ], - 'sources!': [ - 'browser/gtk/tabs/tab_renderer_gtk_unittest.cc', + 'sources/': [ + ['exclude', 'browser/gtk/tabs/tab_renderer_gtk_unittest\\.cc$'], + ['exclude', 'browser/translate/translate_manager_unittest\\.cc$'], ], 'link_settings': { 'libraries': [ @@ -1238,6 +1240,7 @@ 'browser/rlz/rlz_unittest.cc', 'browser/safe_browsing/safe_browsing_blocking_page_unittest.cc', 'browser/search_engines/template_url_scraper_unittest.cc', + 'browser/translate/translate_manager2_unittest.cc', 'browser/views/bookmark_editor_view_unittest.cc', 'browser/views/extensions/browser_action_drag_data_unittest.cc', 'browser/views/find_bar_host_unittest.cc', @@ -1458,7 +1461,7 @@ 'browser/chromeos/options/wifi_config_view_browsertest.cc', 'browser/chromeos/panels/panel_browsertest.cc', 'browser/chromeos/status/clock_menu_button_browsertest.cc', - 'browser/chromeos/status/language_menu_button_browsertest.cc', + 'browser/chromeos/status/language_menu_button_browsertest.cc', 'browser/chromeos/status/power_menu_button_browsertest.cc', ], }], @@ -2435,7 +2438,7 @@ # If a test bundle is added to this coverage_build target it # necessarily means this file (chrome_tests.gypi) is changed, # so the action is run (coverage_bundles.py is generated). - # Exceptions to that rule are theoretically possible + # Exceptions to that rule are theoretically possible # (e.g. re-gyp with a GYP_DEFINES set). # Else it's the same list of bundles as last time. They are # built (since on the deps list) but the action may not run. |