// Copyright (c) 2011 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 <set> #include "base/utf_string_conversions.h" #include "chrome/app/chrome_command_ids.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/prefs/pref_change_registrar.h" #include "chrome/browser/tab_contents/render_view_context_menu.h" #include "chrome/browser/translate/translate_infobar_delegate.h" #include "chrome/browser/translate/translate_manager.h" #include "chrome/browser/translate/translate_prefs.h" #include "chrome/browser/ui/tab_contents/test_tab_contents_wrapper.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 "chrome/test/testing_profile.h" #include "content/browser/browser_thread.h" #include "content/browser/renderer_host/mock_render_process_host.h" #include "content/browser/renderer_host/test_render_view_host.h" #include "content/browser/tab_contents/navigation_controller.h" #include "content/browser/tab_contents/test_tab_contents.h" #include "content/common/notification_details.h" #include "content/common/notification_observer_mock.h" #include "content/common/notification_registrar.h" #include "content/common/notification_type.h" #include "content/common/view_messages.h" #include "grit/generated_resources.h" #include "ipc/ipc_test_sink.h" #include "testing/gmock/include/gmock/gmock.h" #include "third_party/cld/languages/public/languages.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebContextMenuData.h" using testing::_; using testing::Pointee; using testing::Property; using WebKit::WebContextMenuData; class TranslateManagerTest : public TabContentsWrapperTestHarness, public NotificationObserver { public: TranslateManagerTest() : ui_thread_(BrowserThread::UI, &message_loop_) { } // Simluates 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) { NavigateAndCommit(url); SimulateOnTranslateLanguageDetermined(lang, page_translatable); } void SimulateOnTranslateLanguageDetermined(const std::string& lang, bool page_translatable) { rvh()->TestOnMessageReceived(ViewHostMsg_TranslateLanguageDetermined( 0, lang, page_translatable)); } 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. TranslateInfoBarDelegate* GetTranslateInfoBar() { return (contents()->infobar_count() == 1) ? contents()->GetInfoBarDelegateAt(0)->AsTranslateInfoBarDelegate() : NULL; } // If there is 1 infobar and it is a translate infobar, closes it and returns // true. Returns false otherwise. bool CloseTranslateInfoBar() { InfoBarDelegate* 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* delegate) { bool found = removed_infobars_.count(delegate) != 0; 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(); } void ExpireTranslateScriptImmediately() { TranslateManager::GetInstance()->set_translate_script_expiration_delay(0); } // If there is 1 infobar and it is a translate infobar, deny translation and // returns true. Returns false otherwise. bool DenyTranslation() { TranslateInfoBarDelegate* 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_EQ(NotificationType::TAB_CONTENTS_INFOBAR_REMOVED, type.value); removed_infobars_.insert(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 and sets the // expiration delay to a large value by default (in case it was zeroed in // a previous test). TranslateManager::GetInstance()->ClearTranslateScript(); TranslateManager::GetInstance()-> set_translate_script_expiration_delay(60 * 60 * 1000); TabContentsWrapperTestHarness::SetUp(); notification_registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_REMOVED, Source<TabContents>(contents())); } virtual void TearDown() { process()->sink().ClearMessages(); notification_registrar_.Remove(this, NotificationType::TAB_CONTENTS_INFOBAR_REMOVED, Source<TabContents>(contents())); TabContentsWrapperTestHarness::TearDown(); URLFetcher::set_factory(NULL); } void SimulateURLFetch(bool success) { TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0); ASSERT_TRUE(fetcher); net::URLRequestStatus status; status.set_status(success ? net::URLRequestStatus::SUCCESS : net::URLRequestStatus::FAILED); fetcher->delegate()->OnURLFetchComplete(fetcher, fetcher->original_url(), status, success ? 200 : 500, ResponseCookies(), std::string()); } void SetPrefObserverExpectation(const char* path) { EXPECT_CALL( pref_observer_, Observe(NotificationType(NotificationType::PREF_CHANGED), _, Property(&Details<std::string>::ptr, Pointee(path)))); } NotificationObserverMock pref_observer_; private: NotificationRegistrar notification_registrar_; TestURLFetcherFactory url_fetcher_factory_; BrowserThread ui_thread_; // The infobars that have been removed. // WARNING: the pointers point to deleted objects, use only for comparison. std::set<InfoBarDelegate*> removed_infobars_; DISALLOW_COPY_AND_ASSIGN(TranslateManagerTest); }; // 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 = WebContextMenuData::CanTranslate; 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, ui::Accelerator* accelerator) { return false; } private: TestRenderViewContextMenu(TabContents* tab_contents, const ContextMenuParams& params) : RenderViewContextMenu(tab_contents, params) { } DISALLOW_COPY_AND_ASSIGN(TestRenderViewContextMenu); }; TEST_F(TranslateManagerTest, NormalTranslate) { // Simulate navigating to a page. SimulateNavigation(GURL("http://www.google.fr"), "fr", true); // We should have an infobar. TranslateInfoBarDelegate* infobar = GetTranslateInfoBar(); ASSERT_TRUE(infobar != NULL); EXPECT_EQ(TranslateInfoBarDelegate::BEFORE_TRANSLATE, infobar->type()); // Simulate clicking translate. process()->sink().ClearMessages(); infobar->Translate(); // The "Translating..." infobar should be showing. infobar = GetTranslateInfoBar(); ASSERT_TRUE(infobar != NULL); EXPECT_EQ(TranslateInfoBarDelegate::TRANSLATING, infobar->type()); // 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("fr", original_lang); EXPECT_EQ("en", target_lang); // 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(TranslateInfoBarDelegate::AFTER_TRANSLATE, infobar->type()); // Simulate changing the original language, this should trigger a translation. process()->sink().ClearMessages(); std::string new_original_lang = infobar->GetLanguageCodeAt(0); infobar->SetOriginalLanguage(0); EXPECT_TRUE(GetTranslateMessage(&page_id, &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. rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, new_original_lang, "en", TranslateErrors::NONE)); // infobar is now invalid. TranslateInfoBarDelegate* new_infobar = GetTranslateInfoBar(); ASSERT_TRUE(new_infobar != NULL); infobar = new_infobar; // Simulate changing the target language, this should trigger a translation. process()->sink().ClearMessages(); std::string new_target_lang = infobar->GetLanguageCodeAt(1); infobar->SetTargetLanguage(1); EXPECT_TRUE(GetTranslateMessage(&page_id, &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. rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, new_original_lang, new_target_lang, TranslateErrors::NONE)); // infobar is now invalid. new_infobar = GetTranslateInfoBar(); ASSERT_TRUE(new_infobar != NULL); } TEST_F(TranslateManagerTest, TranslateScriptNotAvailable) { // Simulate navigating to a page. SimulateNavigation(GURL("http://www.google.fr"), "fr", true); // We should have an infobar. TranslateInfoBarDelegate* infobar = GetTranslateInfoBar(); ASSERT_TRUE(infobar != NULL); EXPECT_EQ(TranslateInfoBarDelegate::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(TranslateInfoBarDelegate::TRANSLATION_ERROR, infobar->type()); } // 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(TranslateManagerTest, TranslateUnknownLanguage) { // 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. scoped_ptr<TestRenderViewContextMenu> menu( TestRenderViewContextMenu::CreateContextMenu(contents())); menu->Init(); menu->ExecuteCommand(IDC_CONTENT_CONTEXT_TRANSLATE); // To test that bug #49018 if fixed, make sure we deal correctly with errors. SimulateURLFetch(false); // Simulate a failure to fetch the translate script. TranslateInfoBarDelegate* infobar = GetTranslateInfoBar(); ASSERT_TRUE(infobar != NULL); EXPECT_EQ(TranslateInfoBarDelegate::TRANSLATION_ERROR, infobar->type()); EXPECT_TRUE(infobar->IsError()); infobar->MessageInfoBarButtonPressed(); SimulateURLFetch(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. 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(TranslateInfoBarDelegate::AFTER_TRANSLATE, infobar->type()); EXPECT_EQ("fr", infobar->GetOriginalLanguageCode()); EXPECT_EQ("en", infobar->GetTargetLanguageCode()); // 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(TestRenderViewContextMenu::CreateContextMenu(contents())); menu->Init(); menu->ExecuteCommand(IDC_CONTENT_CONTEXT_TRANSLATE); rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(1, 0, "en", "en", TranslateErrors::IDENTICAL_LANGUAGES)); infobar = GetTranslateInfoBar(); ASSERT_TRUE(infobar != NULL); EXPECT_EQ(TranslateInfoBarDelegate::TRANSLATION_ERROR, infobar->type()); EXPECT_EQ(TranslateErrors::IDENTICAL_LANGUAGES, infobar->error()); // 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(TestRenderViewContextMenu::CreateContextMenu(contents())); menu->Init(); menu->ExecuteCommand(IDC_CONTENT_CONTEXT_TRANSLATE); rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(2, 0, "", "en", TranslateErrors::UNKNOWN_LANGUAGE)); infobar = GetTranslateInfoBar(); ASSERT_TRUE(infobar != NULL); EXPECT_EQ(TranslateInfoBarDelegate::TRANSLATION_ERROR, infobar->type()); EXPECT_EQ(TranslateErrors::UNKNOWN_LANGUAGE, infobar->error()); } // Tests that we show/don't show an info-bar for all languages the CLD can // report. TEST_F(TranslateManagerTest, 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_manager.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 true, false, false, true, true, true, false, true, false, false, // 60-69 false, false, true, true, false, true, true, false, true, true, // 70-79 false, false, false, false, true, true, 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, true, 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, true, // 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() << "Iteration " << i << " language=" << lang); // We should not have a translate infobar. TranslateInfoBarDelegate* infobar = GetTranslateInfoBar(); ASSERT_TRUE(infobar == NULL); // Simulate navigating to a page. NavigateAndCommit(url); SimulateOnTranslateLanguageDetermined(lang, true); // 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(TranslateManagerTest, AutoTranslateOnNavigate) { // Simulate navigating to a page and getting its language. SimulateNavigation(GURL("http://www.google.fr"), "fr", true); // Simulate the user translating. TranslateInfoBarDelegate* 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"), "fr", true); // 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"), "es", true); // 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(TranslateManagerTest, MultipleOnPageContents) { // Simulate navigating to a page and getting its language. SimulateNavigation(GURL("http://www.google.fr"), "fr", true); // Simulate clicking 'Nope' (don't translate). EXPECT_TRUE(DenyTranslation()); EXPECT_EQ(0U, contents()->infobar_count()); // Send a new PageContents, we should not show an infobar. SimulateOnTranslateLanguageDetermined("fr", true); EXPECT_EQ(0U, contents()->infobar_count()); // Do the same steps but simulate closing the infobar this time. SimulateNavigation(GURL("http://www.youtube.fr"), "fr", true); EXPECT_TRUE(CloseTranslateInfoBar()); EXPECT_EQ(0U, contents()->infobar_count()); SimulateOnTranslateLanguageDetermined("fr", true); EXPECT_EQ(0U, contents()->infobar_count()); } // Test that reloading the page brings back the infobar. TEST_F(TranslateManagerTest, Reload) { // Simulate navigating to a page and getting its language. SimulateNavigation(GURL("http://www.google.fr"), "fr", true); // 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(TranslateManagerTest, ReloadFromLocationBar) { GURL url("http://www.google.fr"); // Simulate navigating to a page and getting its language. SimulateNavigation(url, "fr", true); // 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 closed translate infobar does not reappear when navigating // in-page. TEST_F(TranslateManagerTest, CloseInfoBarInPageNavigation) { // Simulate navigating to a page and getting its language. SimulateNavigation(GURL("http://www.google.fr"), "fr", true); // Close the infobar. EXPECT_TRUE(CloseTranslateInfoBar()); // Navigate in page, no infobar should be shown. SimulateNavigation(GURL("http://www.google.fr/#ref1"), "fr", true); EXPECT_TRUE(GetTranslateInfoBar() == NULL); // Navigate out of page, a new infobar should show. SimulateNavigation(GURL("http://www.google.fr/foot"), "fr", true); EXPECT_TRUE(GetTranslateInfoBar() != NULL); } // Tests that a closed translate infobar does not reappear when navigating // in a subframe. (http://crbug.com/48215) TEST_F(TranslateManagerTest, CloseInfoBarInSubframeNavigation) { // Simulate navigating to a page and getting its language. SimulateNavigation(GURL("http://www.google.fr"), "fr", true); // Close the infobar. EXPECT_TRUE(CloseTranslateInfoBar()); // Simulate a sub-frame auto-navigating. rvh()->SendNavigateWithTransition(1, GURL("http://pub.com"), PageTransition::AUTO_SUBFRAME); EXPECT_TRUE(GetTranslateInfoBar() == NULL); // Simulate the user navigating in a sub-frame. rvh()->SendNavigateWithTransition(2, GURL("http://pub.com"), PageTransition::MANUAL_SUBFRAME); EXPECT_TRUE(GetTranslateInfoBar() == NULL); // Navigate out of page, a new infobar should show. SimulateNavigation(GURL("http://www.google.fr/foot"), "fr", true); EXPECT_TRUE(GetTranslateInfoBar() != NULL); } // Tests that denying translation is sticky when navigating in page. TEST_F(TranslateManagerTest, DenyTranslateInPageNavigation) { // Simulate navigating to a page and getting its language. 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_TRUE(GetTranslateInfoBar() == NULL); // Navigate out of page, a new infobar should show. SimulateNavigation(GURL("http://www.google.fr/foot"), "fr", true); EXPECT_TRUE(GetTranslateInfoBar() != NULL); } // Tests that after translating and closing the infobar, the infobar does not // return when navigating in page. TEST_F(TranslateManagerTest, TranslateCloseInfoBarInPageNavigation) { // Simulate navigating to a page and getting its language. SimulateNavigation(GURL("http://www.google.fr"), "fr", true); // Simulate the user translating. TranslateInfoBarDelegate* 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"), "fr", true); 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"), "de", true); EXPECT_TRUE(GetTranslateInfoBar() != NULL); } // Tests that the after translate the infobar still shows when navigating // in-page. TEST_F(TranslateManagerTest, TranslateInPageNavigation) { // Simulate navigating to a page and getting its language. SimulateNavigation(GURL("http://www.google.fr"), "fr", true); // Simulate the user translating. TranslateInfoBarDelegate* 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"), "fr", true); 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"), "de", true); // 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(TranslateManagerTest, CLDReportsUnsupportedPageLanguage) { // Simulate navigating to a page and getting an unsupported language. SimulateNavigation(GURL("http://www.google.com"), "qbz", true); // No info-bar should be shown. EXPECT_TRUE(GetTranslateInfoBar() == NULL); } // 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(TranslateManagerTest, ServerReportsUnsupportedLanguage) { // Simulate navigating to a page and translating it. SimulateNavigation(GURL("http://mail.google.fr"), "fr", true); TranslateInfoBarDelegate* infobar = GetTranslateInfoBar(); ASSERT_TRUE(infobar != NULL); process()->sink().ClearMessages(); infobar->Translate(); SimulateURLFetch(true); // Simulate the render notifying the translation has been done, but it // reports a language we don't support. rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "qbz", "en", TranslateErrors::NONE)); // An error infobar should be showing to report that we don't support this // language. infobar = GetTranslateInfoBar(); ASSERT_TRUE(infobar != NULL); EXPECT_EQ(TranslateInfoBarDelegate::TRANSLATION_ERROR, infobar->type()); // This infobar should have a button (so the string should not be empty). ASSERT_FALSE(infobar->GetMessageInfoBarButtonText().empty()); // Pressing the button on that infobar should revert to the original language. process()->sink().ClearMessages(); infobar->MessageInfoBarButtonPressed(); const IPC::Message* message = process()->sink().GetFirstMessageMatching(ViewMsg_RevertTranslation::ID); EXPECT_TRUE(message != NULL); // And it should have removed the infobar. 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(TranslateManagerTest, 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"), "en", true); // 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(TranslateManagerTest, 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"), "fr", true); // An infobar should be shown. 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(TranslateManagerTest, NeverTranslateLanguagePref) { // Simulate navigating to a page and getting its language. GURL url("http://www.google.fr"); SimulateNavigation(url, "fr", true); // An infobar should be shown. EXPECT_TRUE(GetTranslateInfoBar() != NULL); // Select never translate this language. PrefService* prefs = contents()->profile()->GetPrefs(); PrefChangeRegistrar registrar; registrar.Init(prefs); registrar.Add(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"), "fr", true); // 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, "fr", true); // There should be a translate infobar. EXPECT_TRUE(GetTranslateInfoBar() != NULL); } // Tests the "Never translate this site" pref. TEST_F(TranslateManagerTest, NeverTranslateSitePref) { // Simulate navigating to a page and getting its language. 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. PrefService* prefs = contents()->profile()->GetPrefs(); PrefChangeRegistrar registrar; registrar.Init(prefs); registrar.Add(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"), "fr", true); // 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, "fr", true); // There should be a translate infobar. EXPECT_TRUE(GetTranslateInfoBar() != NULL); } // Tests the "Always translate this language" pref. TEST_F(TranslateManagerTest, AlwaysTranslateLanguagePref) { // Select always translate French to English. PrefService* prefs = contents()->profile()->GetPrefs(); PrefChangeRegistrar registrar; registrar.Init(prefs); registrar.Add(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"), "fr", true); // It should have triggered an automatic translation to English. // The translating infobar should be showing. TranslateInfoBarDelegate* infobar = GetTranslateInfoBar(); ASSERT_TRUE(infobar != NULL); EXPECT_EQ(TranslateInfoBarDelegate::TRANSLATING, infobar->type()); 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("fr", original_lang); EXPECT_EQ("en", target_lang); process()->sink().ClearMessages(); // Try another language, it should not be autotranslated. SimulateNavigation(GURL("http://www.google.es"), "es", true); 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_incognito(true); SimulateNavigation(GURL("http://www.youtube.fr"), "fr", true); EXPECT_FALSE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); EXPECT_TRUE(GetTranslateInfoBar() != NULL); EXPECT_TRUE(CloseTranslateInfoBar()); test_profile->set_incognito(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(TranslatePrefs::kPrefTranslateWhitelists); translate_prefs.RemoveLanguagePairFromWhitelist("fr", "en"); SimulateNavigation(GURL("http://www.google.fr"), "fr", true); EXPECT_FALSE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); infobar = GetTranslateInfoBar(); ASSERT_TRUE(infobar != NULL); EXPECT_EQ(TranslateInfoBarDelegate::BEFORE_TRANSLATE, infobar->type()); } // Context menu. TEST_F(TranslateManagerTest, 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 but // should only be enabled when the page language has been received. NavigateAndCommit(url); scoped_ptr<TestRenderViewContextMenu> menu( TestRenderViewContextMenu::CreateContextMenu(contents())); 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(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. // The "translating..." infobar should be showing. TranslateInfoBarDelegate* infobar = GetTranslateInfoBar(); ASSERT_TRUE(infobar != NULL); EXPECT_EQ(TranslateInfoBarDelegate::TRANSLATING, infobar->type()); 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("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"), "es", true); infobar = GetTranslateInfoBar(); ASSERT_TRUE(infobar != NULL); infobar->Translate(); EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); 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"), "de", true); infobar = GetTranslateInfoBar(); ASSERT_TRUE(infobar != NULL); infobar->Translate(); EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); 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 enabled when the page is in an // unknown language. SimulateNavigation(url, "und", true); menu.reset(TestRenderViewContextMenu::CreateContextMenu(contents())); 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 disabled when the page is in an // unsupported language. SimulateNavigation(url, "qbz", true); menu.reset(TestRenderViewContextMenu::CreateContextMenu(contents())); menu->Init(); EXPECT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_TRANSLATE)); EXPECT_FALSE(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(TranslateManagerTest, BeforeTranslateExtraButtons) { TranslatePrefs translate_prefs(contents()->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. TranslateInfoBarDelegate* infobar; TestingProfile* test_profile = static_cast<TestingProfile*>(contents()->profile()); test_profile->set_incognito(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(TranslateInfoBarDelegate::BEFORE_TRANSLATE, infobar->type()); if (i < 7) { EXPECT_FALSE(infobar->ShouldShowAlwaysTranslateButton()); infobar->Translate(); process()->sink().ClearMessages(); } else { EXPECT_TRUE(infobar->ShouldShowAlwaysTranslateButton()); } if (i == 3) test_profile->set_incognito(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). SimulateURLFetch(true); // That should have triggered a page translate. int page_id = 0; std::string original_lang, target_lang; EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); process()->sink().ClearMessages(); // Now test that declining the translation causes a "never translate" button // to be shown (in non incognito mode only). test_profile->set_incognito(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(TranslateInfoBarDelegate::BEFORE_TRANSLATE, infobar->type()); if (i < 7) { EXPECT_FALSE(infobar->ShouldShowNeverTranslateButton()); infobar->TranslationDeclined(); } else { EXPECT_TRUE(infobar->ShouldShowNeverTranslateButton()); } if (i == 3) test_profile->set_incognito(false); } // Simulate the user pressing "Never translate French". infobar->NeverTranslatePageLanguage(); EXPECT_TRUE(translate_prefs.IsLanguageBlacklisted("de")); // No translation should have occured and the infobar should be gone. EXPECT_FALSE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); process()->sink().ClearMessages(); 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(TranslateManagerTest, NonTranslatablePage) { // Simulate navigating to a page. SimulateNavigation(GURL("http://mail.google.fr"), "fr", false); // We should not have an infobar. EXPECT_TRUE(GetTranslateInfoBar() == NULL); // The context menu should be disabled. scoped_ptr<TestRenderViewContextMenu> menu( TestRenderViewContextMenu::CreateContextMenu(contents())); menu->Init(); EXPECT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_TRANSLATE)); EXPECT_FALSE(menu->IsCommandIdEnabled(IDC_CONTENT_CONTEXT_TRANSLATE)); } // Tests that the script is expired and refetched as expected. TEST_F(TranslateManagerTest, ScriptExpires) { ExpireTranslateScriptImmediately(); // Simulate navigating to a page and translating it. SimulateNavigation(GURL("http://www.google.fr"), "fr", true); TranslateInfoBarDelegate* infobar = GetTranslateInfoBar(); ASSERT_TRUE(infobar != NULL); process()->sink().ClearMessages(); infobar->Translate(); SimulateURLFetch(true); rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "fr", "en", TranslateErrors::NONE)); // A task should have been posted to clear the script, run it. MessageLoop::current()->RunAllPending(); // Do another navigation and translation. SimulateNavigation(GURL("http://www.google.es"), "es", true); infobar = GetTranslateInfoBar(); ASSERT_TRUE(infobar != NULL); process()->sink().ClearMessages(); 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_TRUE( process()->sink().GetFirstMessageMatching(ViewMsg_TranslatePage::ID) == NULL); // Now simulate the URL fetch. SimulateURLFetch(true); // Now the message should have been sent. int page_id = 0; std::string original_lang, target_lang; EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang)); EXPECT_EQ("es", original_lang); EXPECT_EQ("en", target_lang); }