blob: 5c2ce6f3988106fcf08db4c0fcad2a5dfedef37d [file] [log] [blame]
// Copyright 2014 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 <stddef.h>
#include <memory>
#include <set>
#include <tuple>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/browser/infobars/infobar_service.h"
#include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
#include "chrome/browser/translate/chrome_translate_client.h"
#include "chrome/browser/translate/translate_service.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/translate/translate_bubble_factory.h"
#include "chrome/browser/ui/translate/translate_bubble_model.h"
#include "chrome/browser/ui/translate/translate_bubble_model_impl.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "components/infobars/core/infobar.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
#include "components/translate/content/browser/content_translate_driver.h"
#include "components/translate/content/common/translate.mojom.h"
#include "components/translate/core/browser/translate_accept_languages.h"
#include "components/translate/core/browser/translate_download_manager.h"
#include "components/translate/core/browser/translate_infobar_delegate.h"
#include "components/translate/core/browser/translate_language_list.h"
#include "components/translate/core/browser/translate_manager.h"
#include "components/translate/core/browser/translate_pref_names.h"
#include "components/translate/core/browser/translate_prefs.h"
#include "components/translate/core/browser/translate_script.h"
#include "components/translate/core/browser/translate_ui_delegate.h"
#include "components/translate/core/common/language_detection_details.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/browser_side_navigation_policy.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_renderer_host.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "net/base/net_errors.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_fetcher_delegate.h"
#include "net/url_request/url_request_status.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/WebKit/public/web/WebContextMenuData.h"
#include "url/gurl.h"
namespace {
class MockTranslateBubbleFactory : public TranslateBubbleFactory {
public:
MockTranslateBubbleFactory() {}
ShowTranslateBubbleResult ShowImplementation(
BrowserWindow* window,
content::WebContents* web_contents,
translate::TranslateStep step,
translate::TranslateErrors::Type error_type) override {
if (model_) {
model_->SetViewState(
TranslateBubbleModelImpl::TranslateStepToViewState(step));
return ShowTranslateBubbleResult::SUCCESS;
}
ChromeTranslateClient* chrome_translate_client =
ChromeTranslateClient::FromWebContents(web_contents);
std::string source_language =
chrome_translate_client->GetLanguageState().original_language();
std::string target_language =
translate::TranslateDownloadManager::GetLanguageCode(
g_browser_process->GetApplicationLocale());
std::unique_ptr<translate::TranslateUIDelegate> ui_delegate(
new translate::TranslateUIDelegate(
chrome_translate_client->GetTranslateManager()->GetWeakPtr(),
source_language, target_language));
model_.reset(new TranslateBubbleModelImpl(step, std::move(ui_delegate)));
return ShowTranslateBubbleResult::SUCCESS;
}
bool DismissBubble() {
if (!model_)
return false;
model_->DeclineTranslation();
model_->OnBubbleClosing();
model_.reset();
return true;
}
TranslateBubbleModel* model() { return model_.get(); }
private:
std::unique_ptr<TranslateBubbleModel> model_;
DISALLOW_COPY_AND_ASSIGN(MockTranslateBubbleFactory);
};
class FakePageImpl : public translate::mojom::Page {
public:
FakePageImpl()
: called_translate_(false),
called_revert_translation_(false),
binding_(this) {}
~FakePageImpl() override {}
translate::mojom::PagePtr BindToNewPagePtr() {
binding_.Close();
translate_callback_pending_.Reset();
translate::mojom::PagePtr page;
binding_.Bind(mojo::MakeRequest(&page));
return page;
}
// translate::mojom::Page implementation.
void Translate(const std::string& translate_script,
const std::string& source_lang,
const std::string& target_lang,
TranslateCallback callback) override {
// Ensure pending callback gets called.
if (translate_callback_pending_) {
std::move(translate_callback_pending_)
.Run(true, "", "", translate::TranslateErrors::NONE);
}
called_translate_ = true;
source_lang_ = source_lang;
target_lang_ = target_lang;
translate_callback_pending_ = std::move(callback);
}
void RevertTranslation() override { called_revert_translation_ = true; }
void PageTranslated(bool cancelled,
const std::string& source_lang,
const std::string& target_lang,
translate::TranslateErrors::Type error) {
std::move(translate_callback_pending_)
.Run(cancelled, source_lang, target_lang, error);
}
bool called_translate_;
base::Optional<std::string> source_lang_;
base::Optional<std::string> target_lang_;
bool called_revert_translation_;
private:
TranslateCallback translate_callback_pending_;
mojo::Binding<translate::mojom::Page> binding_;
DISALLOW_COPY_AND_ASSIGN(FakePageImpl);
};
} // namespace
// An observer that keeps track of whether a navigation entry was committed.
class NavEntryCommittedObserver : public content::NotificationObserver {
public:
explicit NavEntryCommittedObserver(content::WebContents* web_contents) {
registrar_.Add(this,
content::NOTIFICATION_NAV_ENTRY_COMMITTED,
content::Source<content::NavigationController>(
&web_contents->GetController()));
}
void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) override {
DCHECK(type == content::NOTIFICATION_NAV_ENTRY_COMMITTED);
details_ =
*(content::Details<content::LoadCommittedDetails>(details).ptr());
}
const content::LoadCommittedDetails& load_committed_details() const {
return details_;
}
private:
content::LoadCommittedDetails details_;
content::NotificationRegistrar registrar_;
DISALLOW_COPY_AND_ASSIGN(NavEntryCommittedObserver);
};
class TranslateManagerRenderViewHostTest
: public ChromeRenderViewHostTestHarness,
public content::NotificationObserver {
public:
TranslateManagerRenderViewHostTest()
: pref_callback_(
base::Bind(&TranslateManagerRenderViewHostTest::OnPreferenceChanged,
base::Unretained(this))) {}
#if !defined(USE_AURA)
// Ensure that we are testing under the bubble UI.
// TODO(groby): Remove once the bubble is enabled by default everywhere.
// http://crbug.com/507442
void EnableBubbleTest() {
if (TranslateService::IsTranslateBubbleEnabled()) {
bubble_factory_.reset(new MockTranslateBubbleFactory);
TranslateBubbleFactory::SetFactory(bubble_factory_.get());
}
}
bool TranslateUiVisible() {
if (bubble_factory_) {
TranslateBubbleModel* bubble = bubble_factory_->model();
return bubble != nullptr;
} else {
bool result = (GetTranslateInfoBar() != nullptr);
EXPECT_EQ(infobar_service()->infobar_count() != 0, result);
return result;
}
}
bool CloseTranslateUi() {
if (bubble_factory_) {
return bubble_factory_->DismissBubble();
} else {
return CloseTranslateInfoBar();
}
}
void SimulateTranslatePress() {
// Simulate the user translating.
if (bubble_factory_.get()) {
bubble_factory_->model()->Translate();
} else {
translate::TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != nullptr);
infobar->Translate();
}
}
translate::TranslateStep CurrentStep() {
if (bubble_factory_.get()) {
TranslateBubbleModel::ViewState view_state =
bubble_factory_->model()->GetViewState();
switch (view_state) {
case TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE:
return translate::TRANSLATE_STEP_BEFORE_TRANSLATE;
case TranslateBubbleModel::VIEW_STATE_TRANSLATING:
return translate::TRANSLATE_STEP_TRANSLATING;
case TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE:
return translate::TRANSLATE_STEP_AFTER_TRANSLATE;
case TranslateBubbleModel::VIEW_STATE_ERROR:
return translate::TRANSLATE_STEP_TRANSLATE_ERROR;
case TranslateBubbleModel::VIEW_STATE_ADVANCED:
NOTREACHED();
break;
}
NOTREACHED();
return translate::TRANSLATE_STEP_TRANSLATE_ERROR;
} else {
translate::TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
return infobar->translate_step();
}
}
#endif // defined(USE_AURA)
// Simulates navigating to a page and getting the page contents and language
// for that navigation.
void SimulateNavigation(const GURL& url,
const std::string& lang,
bool page_translatable) {
if (main_rfh()->GetLastCommittedURL() == url) {
content::NavigationSimulator::Reload(web_contents());
} else {
content::NavigationSimulator::NavigateAndCommitFromBrowser(web_contents(),
url);
}
SimulateOnTranslateLanguageDetermined(lang, page_translatable);
}
void SimulateOnTranslateLanguageDetermined(const std::string& lang,
bool page_translatable) {
translate::LanguageDetectionDetails details;
details.adopted_language = lang;
ChromeTranslateClient::FromWebContents(web_contents())
->translate_driver()
.RegisterPage(fake_page_.BindToNewPagePtr(), details,
page_translatable);
}
void SimulateOnPageTranslated(const std::string& source_lang,
const std::string& target_lang,
translate::TranslateErrors::Type error) {
// Ensure fake_page_ Translate() call gets dispatched.
base::RunLoop().RunUntilIdle();
fake_page_.PageTranslated(false, source_lang, target_lang, error);
// Ensure fake_page_ Translate() response callback gets dispatched.
base::RunLoop().RunUntilIdle();
}
void SimulateOnPageTranslated(const std::string& source_lang,
const std::string& target_lang) {
SimulateOnPageTranslated(source_lang, target_lang,
translate::TranslateErrors::NONE);
}
bool GetTranslateMessage(std::string* original_lang,
std::string* target_lang) {
base::RunLoop().RunUntilIdle();
if (!fake_page_.called_translate_)
return false;
EXPECT_TRUE(fake_page_.source_lang_);
EXPECT_TRUE(fake_page_.target_lang_);
if (original_lang)
*original_lang = *fake_page_.source_lang_;
if (target_lang)
*target_lang = *fake_page_.target_lang_;
// Reset
fake_page_.called_translate_ = false;
fake_page_.source_lang_ = base::nullopt;
fake_page_.target_lang_ = base::nullopt;
return true;
}
bool IsTranslationReverted() {
base::RunLoop().RunUntilIdle();
return fake_page_.called_revert_translation_;
}
InfoBarService* infobar_service() {
return InfoBarService::FromWebContents(web_contents());
}
// Returns the translate infobar if there is 1 infobar and it is a translate
// infobar.
translate::TranslateInfoBarDelegate* GetTranslateInfoBar() {
return (infobar_service()->infobar_count() == 1) ?
infobar_service()->infobar_at(0)->delegate()->
AsTranslateInfoBarDelegate() :
NULL;
}
#if !defined(USE_AURA)
// If there is 1 infobar and it is a translate infobar, closes it and returns
// true. Returns false otherwise.
bool CloseTranslateInfoBar() {
infobars::InfoBarDelegate* infobar = GetTranslateInfoBar();
if (!infobar)
return false;
infobar->InfoBarDismissed(); // Simulates closing the infobar.
infobar_service()->RemoveInfoBar(infobar_service()->infobar_at(0));
return true;
}
// Checks whether |infobar| has been removed and clears the removed infobar
// list.
bool CheckInfoBarRemovedAndReset(infobars::InfoBarDelegate* delegate) {
bool found = removed_infobars_.count(delegate) != 0;
removed_infobars_.clear();
return found;
}
void ExpireTranslateScriptImmediately() {
translate::TranslateDownloadManager::GetInstance()
->SetTranslateScriptExpirationDelay(0);
}
// If there is 1 infobar and it is a translate infobar, deny translation and
// returns true. Returns false otherwise.
bool DenyTranslation() {
if (bubble_factory_.get()) {
return bubble_factory_->DismissBubble();
} else {
translate::TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
if (!infobar)
return false;
infobar->TranslationDeclined();
infobar_service()->RemoveInfoBar(infobar_service()->infobar_at(0));
return true;
}
}
#endif // defined(USE_AURA)
void ReloadAndWait(bool successful_reload) {
NavEntryCommittedObserver nav_observer(web_contents());
if (successful_reload) {
content::NavigationSimulator::Reload(web_contents());
} else {
content::NavigationSimulator::ReloadAndFail(web_contents(),
net::ERR_TIMED_OUT);
}
// Ensures it is really handled a reload.
const content::LoadCommittedDetails& nav_details =
nav_observer.load_committed_details();
EXPECT_TRUE(nav_details.entry != NULL); // There was a navigation.
EXPECT_EQ(content::NAVIGATION_TYPE_EXISTING_PAGE, nav_details.type);
// The TranslateManager class processes the navigation entry committed
// notification in a posted task; process that task.
base::RunLoop().RunUntilIdle();
}
TestRenderViewContextMenu* CreateContextMenu() {
content::ContextMenuParams params;
params.media_type = blink::WebContextMenuData::kMediaTypeNone;
params.x = 0;
params.y = 0;
params.has_image_contents = true;
params.media_flags = 0;
params.spellcheck_enabled = false;
params.is_editable = false;
params.page_url =
web_contents()->GetController().GetActiveEntry()->GetURL();
#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 = blink::WebContextMenuData::kCanTranslate;
return new TestRenderViewContextMenu(web_contents()->GetMainFrame(),
params);
}
virtual void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK_EQ(chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED, type);
removed_infobars_.insert(
content::Details<infobars::InfoBar::RemovedDetails>(
details)->first->delegate());
}
MOCK_METHOD1(OnPreferenceChanged, void(const std::string&));
protected:
virtual void SetUp() {
// Setup the test environment, including the threads and message loops. This
// must be done before base::ThreadTaskRunnerHandle::Get() is called when
// setting up the net::TestURLRequestContextGetter below.
ChromeRenderViewHostTestHarness::SetUp();
// Clears the translate script so it is fetched every time and sets the
// expiration delay to a large value by default (in case it was zeroed in a
// previous test).
TranslateService::InitializeForTesting();
translate::TranslateDownloadManager* download_manager =
translate::TranslateDownloadManager::GetInstance();
download_manager->ClearTranslateScriptForTesting();
download_manager->SetTranslateScriptExpirationDelay(60 * 60 * 1000);
download_manager->set_request_context(new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get()));
InfoBarService::CreateForWebContents(web_contents());
ChromeTranslateClient::CreateForWebContents(web_contents());
ChromeTranslateClient::FromWebContents(web_contents())
->translate_driver()
.set_translate_max_reload_attempts(0);
notification_registrar_.Add(
this,
chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
content::Source<InfoBarService>(infobar_service()));
}
virtual void TearDown() {
notification_registrar_.Remove(
this,
chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_REMOVED,
content::Source<InfoBarService>(infobar_service()));
ChromeRenderViewHostTestHarness::TearDown();
TranslateService::ShutdownForTesting();
}
void SimulateTranslateScriptURLFetch(bool success) {
net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(
translate::TranslateScript::kFetcherId);
ASSERT_TRUE(fetcher);
net::Error error = success ? net::OK : net::ERR_FAILED;
fetcher->set_url(fetcher->GetOriginalURL());
fetcher->set_status(net::URLRequestStatus::FromError(error));
fetcher->set_response_code(success ? 200 : 500);
fetcher->delegate()->OnURLFetchComplete(fetcher);
}
void SimulateSupportedLanguagesURLFetch(
bool success,
const std::vector<std::string>& languages) {
net::Error error = success ? net::OK : net::ERR_FAILED;
std::string data;
if (success) {
data = base::StringPrintf(
"{\"sl\": {\"bla\": \"bla\"}, \"%s\": {",
translate::TranslateLanguageList::kTargetLanguagesKey);
const char* comma = "";
for (size_t i = 0; i < languages.size(); ++i) {
data += base::StringPrintf(
"%s\"%s\": \"UnusedFullName\"", comma, languages[i].c_str());
if (i == 0)
comma = ",";
}
data += "}}";
}
net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(
translate::TranslateLanguageList::kFetcherId);
ASSERT_TRUE(fetcher != NULL);
fetcher->set_url(fetcher->GetOriginalURL());
fetcher->set_status(net::URLRequestStatus::FromError(error));
fetcher->set_response_code(success ? 200 : 500);
fetcher->SetResponseString(data);
fetcher->delegate()->OnURLFetchComplete(fetcher);
}
void SetPrefObserverExpectation(const char* path) {
EXPECT_CALL(*this, OnPreferenceChanged(std::string(path)));
}
PrefChangeRegistrar::NamedChangeCallback pref_callback_;
private:
content::NotificationRegistrar notification_registrar_;
net::TestURLFetcherFactory url_fetcher_factory_;
// The infobars that have been removed.
// WARNING: the pointers point to deleted objects, use only for comparison.
std::set<infobars::InfoBarDelegate*> removed_infobars_;
std::unique_ptr<MockTranslateBubbleFactory> bubble_factory_;
FakePageImpl fake_page_;
DISALLOW_COPY_AND_ASSIGN(TranslateManagerRenderViewHostTest);
};
// A variant of the above test class that sets the UI language to an invalid
// code (and restores it afterwards). This is required because a significant
// amount of logic using the UI language has already occured by the time we
// enter an individual test body.
static const char* kInvalidLocale = "qbz";
class TranslateManagerRenderViewHostInvalidLocaleTest
: public TranslateManagerRenderViewHostTest {
public:
TranslateManagerRenderViewHostInvalidLocaleTest()
: original_locale_(g_browser_process->GetApplicationLocale()) {
SetApplicationLocale(kInvalidLocale);
}
~TranslateManagerRenderViewHostInvalidLocaleTest() override {
SetApplicationLocale(original_locale_);
}
private:
const std::string original_locale_;
void SetApplicationLocale(const std::string& locale) {
g_browser_process->SetApplicationLocale(locale);
translate::TranslateDownloadManager::GetInstance()->set_application_locale(
g_browser_process->GetApplicationLocale());
}
DISALLOW_COPY_AND_ASSIGN(TranslateManagerRenderViewHostInvalidLocaleTest);
};
// A list of languages to fake being returned by the translate server.
// Use only langauges for which Chrome's copy of ICU has
// display names in English locale. To save space, Chrome's copy of ICU
// does not have the display name for a language unless it's in the
// Accept-Language list.
static const char* kServerLanguageList[] = {
"ach", "ak", "af", "en-CA", "zh", "yi", "fr-FR", "tl", "iw", "in", "xx"};
// Test the fetching of languages from the translate server
TEST_F(TranslateManagerRenderViewHostTest, FetchLanguagesFromTranslateServer) {
std::vector<std::string> server_languages;
for (size_t i = 0; i < arraysize(kServerLanguageList); ++i)
server_languages.push_back(kServerLanguageList[i]);
// First, get the default languages list. Note that calling
// GetSupportedLanguages() invokes RequestLanguageList() internally.
std::vector<std::string> default_supported_languages;
translate::TranslateDownloadManager::GetSupportedLanguages(
&default_supported_languages);
// To make sure we got the defaults and don't confuse them with the mocks.
ASSERT_NE(default_supported_languages.size(), server_languages.size());
// Check that we still get the defaults until the URLFetch has completed.
std::vector<std::string> current_supported_languages;
translate::TranslateDownloadManager::GetSupportedLanguages(
&current_supported_languages);
EXPECT_EQ(default_supported_languages, current_supported_languages);
// Also check that it didn't change if we failed the URL fetch.
SimulateSupportedLanguagesURLFetch(false, std::vector<std::string>());
current_supported_languages.clear();
translate::TranslateDownloadManager::GetSupportedLanguages(
&current_supported_languages);
EXPECT_EQ(default_supported_languages, current_supported_languages);
// Now check that we got the appropriate set of languages from the server.
SimulateSupportedLanguagesURLFetch(true, server_languages);
current_supported_languages.clear();
translate::TranslateDownloadManager::GetSupportedLanguages(
&current_supported_languages);
// "xx" can't be displayed in the Translate infobar, so this is eliminated.
EXPECT_EQ(server_languages.size() - 1, current_supported_languages.size());
// Not sure we need to guarantee the order of languages, so we find them.
for (size_t i = 0; i < server_languages.size(); ++i) {
const std::string& lang = server_languages[i];
if (lang == "xx")
continue;
EXPECT_TRUE(base::ContainsValue(current_supported_languages, lang))
<< "lang=" << lang;
}
}
// The following tests depend on the translate infobar. They should be ported to
// use the translate bubble. On Aura there is no infobar so the tests are not
// compiled.
#if !defined(USE_AURA)
TEST_F(TranslateManagerRenderViewHostTest, NormalTranslate) {
// See BubbleNormalTranslate for corresponding bubble UX testing.
if (TranslateService::IsTranslateBubbleEnabled())
return;
// http://crbug.com/695624
if (content::IsBrowserSideNavigationEnabled())
return;
SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
// We should have an infobar.
translate::TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
EXPECT_EQ(translate::TRANSLATE_STEP_BEFORE_TRANSLATE,
infobar->translate_step());
// Simulate clicking translate.
infobar->Translate();
// The "Translating..." infobar should be showing.
infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
EXPECT_EQ(translate::TRANSLATE_STEP_TRANSLATING, infobar->translate_step());
// Simulate the translate script being retrieved (it only needs to be done
// once in the test as it is cached).
SimulateTranslateScriptURLFetch(true);
// Test that we sent the right message to the renderer.
std::string original_lang, target_lang;
EXPECT_TRUE(GetTranslateMessage(&original_lang, &target_lang));
EXPECT_EQ("fr", original_lang);
EXPECT_EQ("en", target_lang);
// Simulate the render notifying the translation has been done.
SimulateOnPageTranslated("fr", "en");
// The after translate infobar should be showing.
infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
EXPECT_EQ(translate::TRANSLATE_STEP_AFTER_TRANSLATE,
infobar->translate_step());
// Simulate changing the original language and translating.
std::string new_original_lang = infobar->language_code_at(0);
infobar->UpdateOriginalLanguage(new_original_lang);
infobar->Translate();
EXPECT_TRUE(GetTranslateMessage(&original_lang, &target_lang));
EXPECT_EQ(new_original_lang, original_lang);
EXPECT_EQ("en", target_lang);
// Simulate the render notifying the translation has been done.
SimulateOnPageTranslated(new_original_lang, "en");
infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
// Simulate changing the target language and translating.
std::string new_target_lang = infobar->language_code_at(1);
infobar->UpdateTargetLanguage(new_target_lang);
infobar->Translate();
EXPECT_TRUE(GetTranslateMessage(&original_lang, &target_lang));
EXPECT_EQ(new_original_lang, original_lang);
EXPECT_EQ(new_target_lang, target_lang);
// Simulate the render notifying the translation has been done.
SimulateOnPageTranslated(new_original_lang, new_target_lang);
infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
EXPECT_EQ(new_target_lang, infobar->target_language_code());
// Reloading should trigger translation iff Always Translate is on.
ReloadAndWait(true);
infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
EXPECT_EQ(translate::TRANSLATE_STEP_BEFORE_TRANSLATE,
infobar->translate_step());
infobar->UpdateTargetLanguage(new_target_lang);
infobar->ToggleAlwaysTranslate();
ReloadAndWait(true);
infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
EXPECT_EQ(translate::TRANSLATE_STEP_TRANSLATING, infobar->translate_step());
EXPECT_EQ(new_target_lang, infobar->target_language_code());
}
TEST_F(TranslateManagerRenderViewHostTest, TranslateScriptNotAvailable) {
// See BubbleTranslateScriptNotAvailable for corresponding bubble UX testing.
if (TranslateService::IsTranslateBubbleEnabled())
return;
SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
// We should have an infobar.
translate::TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
EXPECT_EQ(translate::TRANSLATE_STEP_BEFORE_TRANSLATE,
infobar->translate_step());
// Simulate clicking translate.
infobar->Translate();
SimulateTranslateScriptURLFetch(false);
// We should not have sent any message to translate to the renderer.
EXPECT_FALSE(GetTranslateMessage(NULL, NULL));
// And we should have an error infobar showing.
infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
EXPECT_EQ(translate::TRANSLATE_STEP_TRANSLATE_ERROR,
infobar->translate_step());
}
// Ensures we deal correctly with pages for which the browser does not recognize
// the language (the translate server may or not detect the language).
TEST_F(TranslateManagerRenderViewHostTest, TranslateUnknownLanguage) {
// See BubbleUnknownLanguage for corresponding bubble UX testing.
if (TranslateService::IsTranslateBubbleEnabled())
return;
// Simulate navigating to a page ("und" is the string returned by the CLD for
// languages it does not recognize).
SimulateNavigation(GURL("http://www.google.mys"), "und", true);
// We should not have an infobar as we don't know the language.
ASSERT_TRUE(GetTranslateInfoBar() == NULL);
// Translate the page anyway throught the context menu.
std::unique_ptr<TestRenderViewContextMenu> menu(CreateContextMenu());
menu->Init();
menu->ExecuteCommand(IDC_CONTENT_CONTEXT_TRANSLATE, 0);
// To test that bug #49018 if fixed, make sure we deal correctly with errors.
// Simulate a failure to fetch the translate script.
SimulateTranslateScriptURLFetch(false);
translate::TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
EXPECT_EQ(translate::TRANSLATE_STEP_TRANSLATE_ERROR,
infobar->translate_step());
EXPECT_TRUE(infobar->is_error());
infobar->MessageInfoBarButtonPressed();
SimulateTranslateScriptURLFetch(true); // This time succeed.
// Simulate the render notifying the translation has been done, the server
// having detected the page was in a known and supported language.
SimulateOnPageTranslated("fr", "en");
// The after translate infobar should be showing.
infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
EXPECT_EQ(translate::TRANSLATE_STEP_AFTER_TRANSLATE,
infobar->translate_step());
EXPECT_EQ("fr", infobar->original_language_code());
EXPECT_EQ("en", infobar->target_language_code());
// Let's run the same steps but this time the server detects the page is
// already in English.
SimulateNavigation(GURL("http://www.google.com"), "und", true);
menu.reset(CreateContextMenu());
menu->Init();
menu->ExecuteCommand(IDC_CONTENT_CONTEXT_TRANSLATE, 0);
SimulateOnPageTranslated("en", "en",
translate::TranslateErrors::IDENTICAL_LANGUAGES);
infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
EXPECT_EQ(translate::TRANSLATE_STEP_TRANSLATE_ERROR,
infobar->translate_step());
EXPECT_EQ(translate::TranslateErrors::IDENTICAL_LANGUAGES,
infobar->error_type());
// Let's run the same steps again but this time the server fails to detect the
// page's language (it returns an empty string).
SimulateNavigation(GURL("http://www.google.com"), "und", true);
menu.reset(CreateContextMenu());
menu->Init();
menu->ExecuteCommand(IDC_CONTENT_CONTEXT_TRANSLATE, 0);
SimulateOnPageTranslated(std::string(), "en",
translate::TranslateErrors::UNKNOWN_LANGUAGE);
infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
EXPECT_EQ(translate::TRANSLATE_STEP_TRANSLATE_ERROR,
infobar->translate_step());
EXPECT_EQ(translate::TranslateErrors::UNKNOWN_LANGUAGE,
infobar->error_type());
}
// Tests that we show/don't show an info-bar for the languages.
TEST_F(TranslateManagerRenderViewHostTest, TestLanguages) {
// This only makes sense for infobars, because the check for supported
// languages moved out of the Infobar into the TranslateManager.
if (TranslateService::IsTranslateBubbleEnabled())
return;
std::vector<std::string> languages;
languages.push_back("en");
languages.push_back("ja");
languages.push_back("fr");
languages.push_back("ht");
languages.push_back("xx");
languages.push_back("zh");
languages.push_back("zh-CN");
languages.push_back("und");
GURL url("http://www.google.com");
for (size_t i = 0; i < languages.size(); ++i) {
std::string lang = languages[i];
SCOPED_TRACE(::testing::Message() << "Iteration " << i
<< " language=" << lang);
// We should not have a translate infobar.
translate::TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar == NULL);
SimulateNavigation(url, lang, true);
// Verify we have/don't have an info-bar as expected.
infobar = GetTranslateInfoBar();
bool expected =
translate::TranslateDownloadManager::IsSupportedLanguage(lang) &&
lang != "en";
EXPECT_EQ(expected, infobar != NULL);
if (infobar != NULL)
EXPECT_TRUE(CloseTranslateInfoBar());
}
}
// Tests auto-translate on page.
TEST_F(TranslateManagerRenderViewHostTest, AutoTranslateOnNavigate) {
EnableBubbleTest();
SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
SimulateTranslatePress();
SimulateTranslateScriptURLFetch(true);
SimulateOnPageTranslated("fr", "en");
// Now navigate to a new page in the same language.
SimulateNavigation(GURL("http://news.google.fr"), "fr", true);
// This should have automatically triggered a translation.
std::string original_lang, target_lang;
EXPECT_TRUE(GetTranslateMessage(&original_lang, &target_lang));
EXPECT_EQ("fr", original_lang);
EXPECT_EQ("en", target_lang);
// Now navigate to a page in a different language.
SimulateNavigation(GURL("http://news.google.es"), "es", true);
// This should not have triggered a translate.
EXPECT_FALSE(GetTranslateMessage(&original_lang, &target_lang));
}
// Tests that multiple OnPageContents do not cause multiple infobars.
TEST_F(TranslateManagerRenderViewHostTest, MultipleOnPageContents) {
EnableBubbleTest();
SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
// Simulate clicking 'Nope' (don't translate).
EXPECT_TRUE(TranslateUiVisible());
EXPECT_TRUE(DenyTranslation());
EXPECT_FALSE(TranslateUiVisible());
// Send a new PageContents, we should not show the translate UI.
SimulateOnTranslateLanguageDetermined("fr", true);
EXPECT_FALSE(TranslateUiVisible());
// Do the same steps but simulate closing the infobar this time.
SimulateNavigation(GURL("http://www.youtube.de"), "de", true);
EXPECT_TRUE(TranslateUiVisible());
EXPECT_TRUE(CloseTranslateUi());
EXPECT_FALSE(TranslateUiVisible());
SimulateOnTranslateLanguageDetermined("de", true);
EXPECT_FALSE(TranslateUiVisible());
}
// Test that reloading the page brings back the infobar if the
// reload succeeded and does not bring it back the reload fails.
TEST_F(TranslateManagerRenderViewHostTest, Reload) {
EnableBubbleTest();
SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
EXPECT_TRUE(CloseTranslateUi());
// Reload should bring back the infobar if the reload succeeds.
ReloadAndWait(true);
EXPECT_TRUE(TranslateUiVisible());
EXPECT_TRUE(CloseTranslateUi());
// ...But not show it if the reload fails.
ReloadAndWait(false);
EXPECT_FALSE(TranslateUiVisible());
// If we set reload attempts to a high value, we will not see the infobar
// immediately.
ChromeTranslateClient::FromWebContents(web_contents())
->translate_driver()
.set_translate_max_reload_attempts(100);
ReloadAndWait(true);
EXPECT_FALSE(TranslateUiVisible());
}
// Test that reloading the page by way of typing again the URL in the
// location bar brings back the infobar.
TEST_F(TranslateManagerRenderViewHostTest, ReloadFromLocationBar) {
EnableBubbleTest();
GURL url("http://www.google.fr");
SimulateNavigation(url, "fr", true);
EXPECT_TRUE(CloseTranslateUi());
// 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(web_contents());
web_contents()->GetController().LoadURL(
url, content::Referrer(), ui::PAGE_TRANSITION_TYPED, std::string());
int pending_id =
web_contents()->GetController().GetPendingEntry()->GetUniqueID();
content::RenderFrameHostTester::For(web_contents()->GetMainFrame())
->SendNavigateWithTransition(pending_id, false, url,
ui::PAGE_TRANSITION_TYPED);
// Test that we are really getting a same page navigation, the test would be
// useless if it was not the case.
const content::LoadCommittedDetails& nav_details =
nav_observer.load_committed_details();
EXPECT_TRUE(nav_details.entry != NULL); // There was a navigation.
EXPECT_EQ(content::NAVIGATION_TYPE_SAME_PAGE, nav_details.type);
// The TranslateManager class processes the navigation entry committed
// notification in a posted task; process that task.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(CloseTranslateUi());
}
// Tests that a closed translate infobar does not reappear when navigating
// in-page.
TEST_F(TranslateManagerRenderViewHostTest, CloseInfoBarInPageNavigation) {
EnableBubbleTest();
SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
EXPECT_TRUE(CloseTranslateUi());
// Navigate in page, no infobar should be shown.
SimulateNavigation(GURL("http://www.google.fr/#ref1"), "fr", true);
EXPECT_FALSE(TranslateUiVisible());
// This is deliberately different behavior for bubbles - same language
// navigation does not show a bubble, ever. Blame ChromeTranslateClient.
if (!TranslateService::IsTranslateBubbleEnabled()) {
// Navigate out of page, a new infobar should show.
SimulateNavigation(GURL("http://www.google.fr/foot"), "fr", true);
EXPECT_TRUE(TranslateUiVisible());
}
}
// Tests that a closed translate infobar does not reappear when navigating
// in a subframe. (http://crbug.com/48215)
TEST_F(TranslateManagerRenderViewHostTest, CloseInfoBarInSubframeNavigation) {
EnableBubbleTest();
SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
EXPECT_TRUE(CloseTranslateUi());
content::RenderFrameHostTester* subframe_tester =
content::RenderFrameHostTester::For(
content::RenderFrameHostTester::For(main_rfh())
->AppendChild("subframe"));
// Simulate a sub-frame auto-navigating.
subframe_tester->SendNavigateWithTransition(
0, false, GURL("http://pub.com"), ui::PAGE_TRANSITION_AUTO_SUBFRAME);
EXPECT_FALSE(TranslateUiVisible());
// Simulate the user navigating in a sub-frame.
subframe_tester->SendNavigateWithTransition(
1, true, GURL("http://pub.com"), ui::PAGE_TRANSITION_MANUAL_SUBFRAME);
EXPECT_FALSE(TranslateUiVisible());
// This is deliberately different behavior for bubbles - same language
// navigation does not show a bubble, ever. Blame ChromeTranslateClient.
if (!TranslateService::IsTranslateBubbleEnabled()) {
// Navigate out of page, a new infobar should show.
SimulateNavigation(GURL("http://www.google.fr/foot"), "fr", true);
EXPECT_TRUE(TranslateUiVisible());
}
}
// Tests that denying translation is sticky when navigating in page.
TEST_F(TranslateManagerRenderViewHostTest, DenyTranslateInPageNavigation) {
EnableBubbleTest();
SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
// Simulate clicking 'Nope' (don't translate).
EXPECT_TRUE(DenyTranslation());
// Navigate in page, no infobar should be shown.
SimulateNavigation(GURL("http://www.google.fr/#ref1"), "fr", true);
EXPECT_FALSE(TranslateUiVisible());
// Navigate out of page, a new infobar should show. (Infobar only).
SimulateNavigation(GURL("http://www.google.fr/foot"), "fr", true);
EXPECT_NE(TranslateService::IsTranslateBubbleEnabled(), TranslateUiVisible());
}
// Tests that after translating and closing the infobar, the infobar does not
// return when navigating in page.
TEST_F(TranslateManagerRenderViewHostTest,
TranslateCloseInfoBarInPageNavigation) {
EnableBubbleTest();
SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
// Simulate the user translating.
SimulateTranslatePress();
// Simulate the translate script being retrieved.
SimulateTranslateScriptURLFetch(true);
SimulateOnPageTranslated("fr", "en");
EXPECT_TRUE(CloseTranslateUi());
// Navigate in page, no infobar should be shown.
SimulateNavigation(GURL("http://www.google.fr/#ref1"), "fr", true);
EXPECT_FALSE(TranslateUiVisible());
// 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 infobar would not be shown).
SimulateNavigation(GURL("http://www.google.de"), "de", true);
EXPECT_TRUE(TranslateUiVisible());
}
// Tests that the after translate the infobar still shows when navigating
// in-page.
TEST_F(TranslateManagerRenderViewHostTest, TranslateInPageNavigation) {
EnableBubbleTest();
SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
// Simulate the user translating.
SimulateTranslatePress();
SimulateTranslateScriptURLFetch(true);
SimulateOnPageTranslated("fr", "en");
// The after translate UI is showing.
EXPECT_TRUE(TranslateUiVisible());
// Remember infobar, so removal can be verified.
translate::TranslateInfoBarDelegate* infobar = nullptr;
if (!TranslateService::IsTranslateBubbleEnabled())
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"), "de", true);
// The old infobar is gone. Can't verify this for bubbles.
// Also, does not apply - existing bubbles are reused.
if (!TranslateService::IsTranslateBubbleEnabled())
EXPECT_TRUE(CheckInfoBarRemovedAndReset(infobar));
// And there is a new one.
EXPECT_TRUE(TranslateUiVisible());
}
// Tests that no translate infobar is shown when navigating to a page in an
// unsupported language.
TEST_F(TranslateManagerRenderViewHostTest, CLDReportsUnsupportedPageLanguage) {
EnableBubbleTest();
// Simulate navigating to a page and getting an unsupported language.
SimulateNavigation(GURL("http://www.google.com"), kInvalidLocale, true);
// No info-bar should be shown.
EXPECT_FALSE(TranslateUiVisible());
}
// Tests that we deal correctly with unsupported languages returned by the
// server.
// The translation server might return a language we don't support.
TEST_F(TranslateManagerRenderViewHostTest, ServerReportsUnsupportedLanguage) {
EnableBubbleTest();
SimulateNavigation(GURL("http://mail.google.fr"), "fr", true);
SimulateTranslatePress();
SimulateTranslateScriptURLFetch(true);
// Simulate the render notifying the translation has been done, but it
// reports a language we don't support.
SimulateOnPageTranslated(kInvalidLocale, "en");
// An error infobar should be showing to report that we don't support this
// language.
EXPECT_EQ(translate::TRANSLATE_STEP_TRANSLATE_ERROR, CurrentStep());
// This infobar should have a button (so the string should not be empty).
// The error string on bubbles is currently not retrievable.
// TODO(http://crbug.com/589301): OSX does not have an error view (yet).
if (!TranslateService::IsTranslateBubbleEnabled()) {
translate::TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar);
ASSERT_FALSE(infobar->GetMessageInfoBarButtonText().empty());
// Pressing the button on that infobar should revert to the original
// language.
infobar->MessageInfoBarButtonPressed();
EXPECT_TRUE(IsTranslationReverted());
// And it should have removed the infobar.
EXPECT_TRUE(GetTranslateInfoBar() == NULL);
}
}
// Tests that no translate infobar is shown and context menu is disabled, when
// Chrome is in a language that the translate server does not support.
TEST_F(TranslateManagerRenderViewHostInvalidLocaleTest, UnsupportedUILanguage) {
// TODO(port): Test corresponding bubble translate UX: http://crbug.com/383235
if (TranslateService::IsTranslateBubbleEnabled())
return;
// Make sure that the accept language list only contains unsupported languages
Profile* profile =
Profile::FromBrowserContext(web_contents()->GetBrowserContext());
PrefService* prefs = profile->GetPrefs();
prefs->SetString(prefs::kAcceptLanguages, kInvalidLocale);
// Simulate navigating to a page in a language supported by the translate
// server.
SimulateNavigation(GURL("http://www.google.com"), "en", true);
// No info-bar should be shown.
EXPECT_TRUE(GetTranslateInfoBar() == NULL);
// And the context menu option should be disabled too.
std::unique_ptr<TestRenderViewContextMenu> menu(CreateContextMenu());
menu->Init();
EXPECT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_TRANSLATE));
EXPECT_FALSE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_TRANSLATE));
}
// Tests that the first supported accept language is selected
TEST_F(TranslateManagerRenderViewHostInvalidLocaleTest,
TranslateAcceptLanguage) {
// TODO(port): Test corresponding bubble translate UX: http://crbug.com/383235
if (TranslateService::IsTranslateBubbleEnabled())
return;
// Set an invalid language and French as the only accepted languages
Profile* profile =
Profile::FromBrowserContext(web_contents()->GetBrowserContext());
PrefService* prefs = profile->GetPrefs();
prefs->SetString(prefs::kAcceptLanguages,
std::string(kInvalidLocale) + ",fr");
// Go to a German page
SimulateNavigation(GURL("http://google.de"), "de", true);
// Expect the infobar to pop up
EXPECT_TRUE(GetTranslateInfoBar() != NULL);
// Set an invalid language and English-US as the only accepted languages to
// test the country code removal code which was causing a crash as filed in
// Issue 90106, a crash caused by a language with a country code that wasn't
// recognized.
prefs->SetString(prefs::kAcceptLanguages,
std::string(kInvalidLocale) + ",en-us");
// Go to a German page
SimulateNavigation(GURL("http://google.de"), "de", true);
// Expect the infobar to pop up
EXPECT_TRUE(GetTranslateInfoBar() != NULL);
}
// Tests that the translate enabled preference is honored.
TEST_F(TranslateManagerRenderViewHostTest, TranslateEnabledPref) {
// TODO(port): Test corresponding bubble translate UX: http://crbug.com/383235
if (TranslateService::IsTranslateBubbleEnabled())
return;
// Make sure the pref allows translate.
Profile* profile =
Profile::FromBrowserContext(web_contents()->GetBrowserContext());
PrefService* prefs = profile->GetPrefs();
prefs->SetBoolean(prefs::kEnableTranslate, true);
SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
// An infobar should be shown.
translate::TranslateInfoBarDelegate* 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.
SimulateOnTranslateLanguageDetermined("fr", true);
infobar = GetTranslateInfoBar();
EXPECT_TRUE(infobar == NULL);
}
// Tests the "Never translate <language>" pref.
TEST_F(TranslateManagerRenderViewHostTest, NeverTranslateLanguagePref) {
// TODO(port): Test corresponding bubble translate UX: http://crbug.com/383235
if (TranslateService::IsTranslateBubbleEnabled())
return;
GURL url("http://www.google.fr");
SimulateNavigation(url, "fr", true);
// An infobar should be shown.
EXPECT_TRUE(GetTranslateInfoBar() != NULL);
// Select never translate this language.
Profile* profile =
Profile::FromBrowserContext(web_contents()->GetBrowserContext());
PrefService* prefs = profile->GetPrefs();
PrefChangeRegistrar registrar;
registrar.Init(prefs);
registrar.Add(translate::TranslatePrefs::kPrefTranslateBlockedLanguages,
pref_callback_);
std::unique_ptr<translate::TranslatePrefs> translate_prefs(
ChromeTranslateClient::CreateTranslatePrefs(prefs));
EXPECT_FALSE(translate_prefs->IsBlockedLanguage("fr"));
translate::TranslateAcceptLanguages* accept_languages =
ChromeTranslateClient::GetTranslateAcceptLanguages(profile);
EXPECT_TRUE(translate_prefs->CanTranslateLanguage(accept_languages, "fr"));
SetPrefObserverExpectation(
translate::TranslatePrefs::kPrefTranslateBlockedLanguages);
translate_prefs->AddToLanguageList("fr", /*force_blocked=*/true);
EXPECT_TRUE(translate_prefs->IsBlockedLanguage("fr"));
EXPECT_FALSE(translate_prefs->IsSiteBlacklisted(url.host()));
EXPECT_FALSE(translate_prefs->CanTranslateLanguage(accept_languages, "fr"));
EXPECT_TRUE(CloseTranslateInfoBar());
// Navigate to a new page also in French.
SimulateNavigation(GURL("http://wwww.youtube.fr"), "fr", true);
// There should not be a translate infobar.
EXPECT_TRUE(GetTranslateInfoBar() == NULL);
// Remove the language from the blacklist.
SetPrefObserverExpectation(
translate::TranslatePrefs::kPrefTranslateBlockedLanguages);
translate_prefs->UnblockLanguage("fr");
EXPECT_FALSE(translate_prefs->IsBlockedLanguage("fr"));
EXPECT_FALSE(translate_prefs->IsSiteBlacklisted(url.host()));
EXPECT_TRUE(translate_prefs->CanTranslateLanguage(accept_languages, "fr"));
// Navigate to a page in French.
SimulateNavigation(url, "fr", true);
// There should be a translate infobar.
EXPECT_TRUE(GetTranslateInfoBar() != NULL);
}
// Tests the "Never translate this site" pref.
TEST_F(TranslateManagerRenderViewHostTest, NeverTranslateSitePref) {
// TODO(port): Test corresponding bubble translate UX: http://crbug.com/383235
if (TranslateService::IsTranslateBubbleEnabled())
return;
GURL url("http://www.google.fr");
std::string host(url.host());
SimulateNavigation(url, "fr", true);
// An infobar should be shown.
EXPECT_TRUE(GetTranslateInfoBar() != NULL);
// Select never translate this site.
Profile* profile =
Profile::FromBrowserContext(web_contents()->GetBrowserContext());
PrefService* prefs = profile->GetPrefs();
PrefChangeRegistrar registrar;
registrar.Init(prefs);
registrar.Add(translate::TranslatePrefs::kPrefTranslateSiteBlacklist,
pref_callback_);
std::unique_ptr<translate::TranslatePrefs> translate_prefs(
ChromeTranslateClient::CreateTranslatePrefs(prefs));
EXPECT_FALSE(translate_prefs->IsSiteBlacklisted(host));
translate::TranslateAcceptLanguages* accept_languages =
ChromeTranslateClient::GetTranslateAcceptLanguages(profile);
EXPECT_TRUE(translate_prefs->CanTranslateLanguage(accept_languages, "fr"));
SetPrefObserverExpectation(
translate::TranslatePrefs::kPrefTranslateSiteBlacklist);
translate_prefs->BlacklistSite(host);
EXPECT_TRUE(translate_prefs->IsSiteBlacklisted(host));
EXPECT_TRUE(translate_prefs->CanTranslateLanguage(accept_languages, "fr"));
EXPECT_TRUE(CloseTranslateInfoBar());
// Navigate to a new page also on the same site.
SimulateNavigation(GURL("http://www.google.fr/hello"), "fr", true);
// There should not be a translate infobar.
EXPECT_TRUE(GetTranslateInfoBar() == NULL);
// Remove the site from the blacklist.
SetPrefObserverExpectation(
translate::TranslatePrefs::kPrefTranslateSiteBlacklist);
translate_prefs->RemoveSiteFromBlacklist(host);
EXPECT_FALSE(translate_prefs->IsSiteBlacklisted(host));
EXPECT_TRUE(translate_prefs->CanTranslateLanguage(accept_languages, "fr"));
// Navigate to a page in French.
SimulateNavigation(url, "fr", true);
// There should be a translate infobar.
EXPECT_TRUE(GetTranslateInfoBar() != NULL);
}
// Tests the "Always translate this language" pref.
TEST_F(TranslateManagerRenderViewHostTest, AlwaysTranslateLanguagePref) {
// TODO(port): Test corresponding bubble translate UX: http://crbug.com/383235
if (TranslateService::IsTranslateBubbleEnabled())
return;
// Select always translate French to English.
Profile* profile =
Profile::FromBrowserContext(web_contents()->GetBrowserContext());
PrefService* prefs = profile->GetPrefs();
PrefChangeRegistrar registrar;
registrar.Init(prefs);
registrar.Add(translate::TranslatePrefs::kPrefTranslateWhitelists,
pref_callback_);
std::unique_ptr<translate::TranslatePrefs> translate_prefs(
ChromeTranslateClient::CreateTranslatePrefs(prefs));
SetPrefObserverExpectation(
translate::TranslatePrefs::kPrefTranslateWhitelists);
translate_prefs->WhitelistLanguagePair("fr", "en");
// Load a page in French.
SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
// It should have triggered an automatic translation to English.
// The translating infobar should be showing.
translate::TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
EXPECT_EQ(translate::TRANSLATE_STEP_TRANSLATING, infobar->translate_step());
SimulateTranslateScriptURLFetch(true);
std::string original_lang, target_lang;
EXPECT_TRUE(GetTranslateMessage(&original_lang, &target_lang));
EXPECT_EQ("fr", original_lang);
EXPECT_EQ("en", target_lang);
// Try another language, it should not be autotranslated.
SimulateNavigation(GURL("http://www.google.es"), "es", true);
EXPECT_FALSE(GetTranslateMessage(&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*>(web_contents()->GetBrowserContext());
test_profile->ForceIncognito(true);
SimulateNavigation(GURL("http://www.youtube.fr"), "fr", true);
EXPECT_FALSE(GetTranslateMessage(&original_lang, &target_lang));
EXPECT_TRUE(GetTranslateInfoBar() != NULL);
EXPECT_TRUE(CloseTranslateInfoBar());
test_profile->ForceIncognito(false); // Get back to non incognito.
// Now revert the always translate pref and make sure we go back to expected
// behavior, which is show a "before translate" infobar.
SetPrefObserverExpectation(
translate::TranslatePrefs::kPrefTranslateWhitelists);
translate_prefs->RemoveLanguagePairFromWhitelist("fr", "en");
SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
EXPECT_FALSE(GetTranslateMessage(&original_lang, &target_lang));
infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
EXPECT_EQ(translate::TRANSLATE_STEP_BEFORE_TRANSLATE,
infobar->translate_step());
}
// Context menu.
TEST_F(TranslateManagerRenderViewHostTest, ContextMenu) {
// TODO(port): Test corresponding bubble translate UX: http://crbug.com/383235
if (TranslateService::IsTranslateBubbleEnabled())
return;
// Blacklist www.google.fr and French for translation.
GURL url("http://www.google.fr");
Profile* profile =
Profile::FromBrowserContext(web_contents()->GetBrowserContext());
std::unique_ptr<translate::TranslatePrefs> translate_prefs(
ChromeTranslateClient::CreateTranslatePrefs(profile->GetPrefs()));
translate_prefs->AddToLanguageList("fr", /*force_blocked=*/true);
translate_prefs->BlacklistSite(url.host());
EXPECT_TRUE(translate_prefs->IsBlockedLanguage("fr"));
EXPECT_TRUE(translate_prefs->IsSiteBlacklisted(url.host()));
// Simulate navigating to a page in French. The translate menu should show but
// should only be enabled when the page language has been received.
NavigateAndCommit(url);
std::unique_ptr<TestRenderViewContextMenu> menu(CreateContextMenu());
menu->Init();
EXPECT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_TRANSLATE));
EXPECT_FALSE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_TRANSLATE));
// Simulate receiving the language.
SimulateOnTranslateLanguageDetermined("fr", true);
menu.reset(CreateContextMenu());
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, 0);
// That should have triggered a translation.
// The "translating..." infobar should be showing.
translate::TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
EXPECT_EQ(translate::TRANSLATE_STEP_TRANSLATING, infobar->translate_step());
SimulateTranslateScriptURLFetch(true);
std::string original_lang, target_lang;
EXPECT_TRUE(GetTranslateMessage(&original_lang, &target_lang));
EXPECT_EQ("fr", original_lang);
EXPECT_EQ("en", target_lang);
// This should also have reverted the blacklisting of this site and language.
EXPECT_FALSE(translate_prefs->IsBlockedLanguage("fr"));
EXPECT_FALSE(translate_prefs->IsSiteBlacklisted(url.host()));
// Let's simulate the page being translated.
SimulateOnPageTranslated("fr", "en");
// The translate menu should now be disabled.
menu.reset(CreateContextMenu());
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"), "es", true);
infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
infobar->Translate();
EXPECT_TRUE(GetTranslateMessage(&original_lang, &target_lang));
menu.reset(CreateContextMenu());
menu->Init();
EXPECT_TRUE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_TRANSLATE));
menu->ExecuteCommand(IDC_CONTENT_CONTEXT_TRANSLATE, 0);
// No message expected since the translation should have been ignored.
EXPECT_FALSE(GetTranslateMessage(&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"), "de", true);
infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
infobar->Translate();
EXPECT_TRUE(GetTranslateMessage(&original_lang, &target_lang));
menu.reset(CreateContextMenu());
menu->Init();
EXPECT_TRUE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_TRANSLATE));
SimulateOnPageTranslated("de", "en");
menu->ExecuteCommand(IDC_CONTENT_CONTEXT_TRANSLATE, 0);
// No message expected since the translation should have been ignored.
EXPECT_FALSE(GetTranslateMessage(&original_lang, &target_lang));
// Test that the translate context menu is enabled when the page is in an
// unknown language.
SimulateNavigation(url, "und", true);
menu.reset(CreateContextMenu());
menu->Init();
EXPECT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_TRANSLATE));
EXPECT_TRUE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_TRANSLATE));
// Test that the translate context menu is enabled even if the page is in an
// unsupported language.
SimulateNavigation(url, kInvalidLocale, true);
menu.reset(CreateContextMenu());
menu->Init();
EXPECT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_TRANSLATE));
EXPECT_TRUE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_TRANSLATE));
}
// Tests that an extra always/never translate button is shown on the "before
// translate" infobar when the translation is accepted/declined 3 times,
// only when not in incognito mode.
TEST_F(TranslateManagerRenderViewHostTest, BeforeTranslateExtraButtons) {
// TODO(port): Test corresponding bubble translate UX: http://crbug.com/383235
if (TranslateService::IsTranslateBubbleEnabled())
return;
Profile* profile =
Profile::FromBrowserContext(web_contents()->GetBrowserContext());
std::unique_ptr<translate::TranslatePrefs> translate_prefs(
ChromeTranslateClient::CreateTranslatePrefs(profile->GetPrefs()));
translate_prefs->ResetTranslationAcceptedCount("fr");
translate_prefs->ResetTranslationDeniedCount("fr");
translate_prefs->ResetTranslationAcceptedCount("de");
translate_prefs->ResetTranslationDeniedCount("de");
// We'll do 4 times in incognito mode first to make sure the button is not
// shown in that case, then 4 times in normal mode.
translate::TranslateInfoBarDelegate* infobar;
TestingProfile* test_profile =
static_cast<TestingProfile*>(web_contents()->GetBrowserContext());
test_profile->ForceIncognito(true);
for (int i = 0; i < 8; ++i) {
SCOPED_TRACE(::testing::Message() << "Iteration " << i << " incognito mode="
<< test_profile->IsOffTheRecord());
SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
EXPECT_EQ(translate::TRANSLATE_STEP_BEFORE_TRANSLATE,
infobar->translate_step());
if (i < 7) {
EXPECT_FALSE(infobar->ShouldShowAlwaysTranslateShortcut());
infobar->Translate();
} else {
EXPECT_TRUE(infobar->ShouldShowAlwaysTranslateShortcut());
}
if (i == 3)
test_profile->ForceIncognito(false);
}
// Simulate the user pressing "Always translate French".
infobar->AlwaysTranslatePageLanguage();
EXPECT_TRUE(translate_prefs->IsLanguagePairWhitelisted("fr", "en"));
// Simulate the translate script being retrieved (it only needs to be done
// once in the test as it is cached).
SimulateTranslateScriptURLFetch(true);
// That should have triggered a page translate.
std::string original_lang, target_lang;
EXPECT_TRUE(GetTranslateMessage(&original_lang, &target_lang));
// Now test that declining the translation causes a "never translate" button
// to be shown (in non incognito mode only).
test_profile->ForceIncognito(true);
for (int i = 0; i < 8; ++i) {
SCOPED_TRACE(::testing::Message() << "Iteration " << i << " incognito mode="
<< test_profile->IsOffTheRecord());
SimulateNavigation(GURL("http://www.google.de"), "de", true);
infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
EXPECT_EQ(translate::TRANSLATE_STEP_BEFORE_TRANSLATE,
infobar->translate_step());
if (i < 7) {
EXPECT_FALSE(infobar->ShouldShowNeverTranslateShortcut());
infobar->TranslationDeclined();
} else {
EXPECT_TRUE(infobar->ShouldShowNeverTranslateShortcut());
}
if (i == 3)
test_profile->ForceIncognito(false);
}
// Simulate the user pressing "Never translate French".
infobar->NeverTranslatePageLanguage();
EXPECT_TRUE(translate_prefs->IsBlockedLanguage("de"));
// No translation should have occured and the infobar should be gone.
EXPECT_FALSE(GetTranslateMessage(&original_lang, &target_lang));
ASSERT_TRUE(GetTranslateInfoBar() == NULL);
}
// Tests that we don't show a translate infobar when a page instructs that it
// should not be translated.
TEST_F(TranslateManagerRenderViewHostTest, NonTranslatablePage) {
// TODO(port): Test corresponding bubble translate UX: http://crbug.com/383235
if (TranslateService::IsTranslateBubbleEnabled())
return;
SimulateNavigation(GURL("http://mail.google.fr"), "fr", false);
// We should not have an infobar.
EXPECT_TRUE(GetTranslateInfoBar() == NULL);
// The context menu is enabled to allow users to force translation.
std::unique_ptr<TestRenderViewContextMenu> menu(CreateContextMenu());
menu->Init();
EXPECT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_TRANSLATE));
EXPECT_TRUE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_TRANSLATE));
}
// Tests that the script is expired and refetched as expected.
TEST_F(TranslateManagerRenderViewHostTest, ScriptExpires) {
// TODO(port): Test corresponding bubble translate UX: http://crbug.com/383235
if (TranslateService::IsTranslateBubbleEnabled())
return;
ExpireTranslateScriptImmediately();
SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
translate::TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
infobar->Translate();
SimulateTranslateScriptURLFetch(true);
// The translate request should have been sent.
EXPECT_TRUE(GetTranslateMessage(NULL, NULL));
SimulateOnPageTranslated("fr", "en");
// A task should have been posted to clear the script, run it.
base::RunLoop().RunUntilIdle();
// Do another navigation and translation.
SimulateNavigation(GURL("http://www.google.es"), "es", true);
infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
infobar->Translate();
// If we don't simulate the URL fetch, the TranslateManager should be waiting
// for the script and no message should have been sent to the renderer.
EXPECT_FALSE(GetTranslateMessage(NULL, NULL));
// Now simulate the URL fetch.
SimulateTranslateScriptURLFetch(true);
// Now the message should have been sent.
std::string original_lang, target_lang;
EXPECT_TRUE(GetTranslateMessage(&original_lang, &target_lang));
EXPECT_EQ("es", original_lang);
EXPECT_EQ("en", target_lang);
}
#else
// The following tests depend on the translate bubble UI.
TEST_F(TranslateManagerRenderViewHostTest, BubbleNormalTranslate) {
// See NormalTranslate for corresponding infobar UX testing.
EXPECT_TRUE(TranslateService::IsTranslateBubbleEnabled());
MockTranslateBubbleFactory* factory = new MockTranslateBubbleFactory;
std::unique_ptr<TranslateBubbleFactory> factory_ptr(factory);
TranslateBubbleFactory::SetFactory(factory);
SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
// Check the bubble exists instead of the infobar.
translate::TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar == NULL);
TranslateBubbleModel* bubble = factory->model();
ASSERT_TRUE(bubble != NULL);
EXPECT_EQ(TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE,
bubble->GetViewState());
// Simulate clicking translate.
bubble->Translate();
// Check the bubble shows "Translating...".
bubble = factory->model();
ASSERT_TRUE(bubble != NULL);
EXPECT_EQ(TranslateBubbleModel::VIEW_STATE_TRANSLATING,
bubble->GetViewState());
// Simulate the translate script being retrieved (it only needs to be done
// once in the test as it is cached).
SimulateTranslateScriptURLFetch(true);
// Simulate the render notifying the translation has been done.
SimulateOnPageTranslated("fr", "en");
// Check the bubble shows "Translated."
bubble = factory->model();
ASSERT_TRUE(bubble != NULL);
EXPECT_EQ(TranslateBubbleModel::VIEW_STATE_AFTER_TRANSLATE,
bubble->GetViewState());
}
TEST_F(TranslateManagerRenderViewHostTest, BubbleTranslateScriptNotAvailable) {
// See TranslateScriptNotAvailable for corresponding infobar UX testing.
EXPECT_TRUE(TranslateService::IsTranslateBubbleEnabled());
MockTranslateBubbleFactory* factory = new MockTranslateBubbleFactory;
std::unique_ptr<TranslateBubbleFactory> factory_ptr(factory);
TranslateBubbleFactory::SetFactory(factory);
SimulateNavigation(GURL("http://www.google.fr"), "fr", true);
// Check the bubble exists instead of the infobar.
translate::TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar == NULL);
TranslateBubbleModel* bubble = factory->model();
ASSERT_TRUE(bubble != NULL);
EXPECT_EQ(TranslateBubbleModel::VIEW_STATE_BEFORE_TRANSLATE,
bubble->GetViewState());
// Simulate clicking translate.
bubble->Translate();
SimulateTranslateScriptURLFetch(false);
// We should not have sent any message to translate to the renderer.
EXPECT_FALSE(GetTranslateMessage(NULL, NULL));
// And we should have an error infobar showing.
bubble = factory->model();
ASSERT_TRUE(bubble != NULL);
EXPECT_EQ(TranslateBubbleModel::VIEW_STATE_ERROR, bubble->GetViewState());
}
TEST_F(TranslateManagerRenderViewHostTest, BubbleUnknownLanguage) {
// See TranslateUnknownLanguage for corresponding infobar UX testing.
EXPECT_TRUE(TranslateService::IsTranslateBubbleEnabled());
MockTranslateBubbleFactory* factory = new MockTranslateBubbleFactory;
std::unique_ptr<TranslateBubbleFactory> factory_ptr(factory);
TranslateBubbleFactory::SetFactory(factory);
// Simulate navigating to a page ("und" is the string returned by the CLD for
// languages it does not recognize).
SimulateNavigation(GURL("http://www.google.mys"), "und", true);
// We should not have a bubble as we don't know the language.
ASSERT_TRUE(factory->model() == NULL);
// Translate the page anyway throught the context menu.
std::unique_ptr<TestRenderViewContextMenu> menu(CreateContextMenu());
menu->Init();
menu->ExecuteCommand(IDC_CONTENT_CONTEXT_TRANSLATE, 0);
// Check the bubble exists instead of the infobar.
translate::TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar == NULL);
TranslateBubbleModel* bubble = factory->model();
ASSERT_TRUE(bubble != NULL);
EXPECT_EQ(TranslateBubbleModel::VIEW_STATE_TRANSLATING,
bubble->GetViewState());
}
#endif // defined(USE_AURA)