Home | History | Annotate | Download | only in translate
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/time/time.h"
      6 #include "chrome/renderer/isolated_world_ids.h"
      7 #include "chrome/test/base/chrome_render_view_test.h"
      8 #include "components/translate/content/common/translate_messages.h"
      9 #include "components/translate/content/renderer/translate_helper.h"
     10 #include "components/translate/core/common/translate_constants.h"
     11 #include "content/public/renderer/render_view.h"
     12 #include "extensions/common/constants.h"
     13 #include "extensions/renderer/extension_groups.h"
     14 #include "testing/gmock/include/gmock/gmock.h"
     15 #include "testing/gtest/include/gtest/gtest.h"
     16 #include "third_party/WebKit/public/web/WebLocalFrame.h"
     17 
     18 using testing::AtLeast;
     19 using testing::Return;
     20 using testing::_;
     21 
     22 class TestTranslateHelper : public translate::TranslateHelper {
     23  public:
     24   explicit TestTranslateHelper(content::RenderView* render_view)
     25       : translate::TranslateHelper(
     26             render_view,
     27             chrome::ISOLATED_WORLD_ID_TRANSLATE,
     28             extensions::EXTENSION_GROUP_INTERNAL_TRANSLATE_SCRIPTS,
     29             extensions::kExtensionScheme) {}
     30 
     31   virtual base::TimeDelta AdjustDelay(int delayInMs) OVERRIDE {
     32     // Just returns base::TimeDelta() which has initial value 0.
     33     // Tasks doesn't need to be delayed in tests.
     34     return base::TimeDelta();
     35   }
     36 
     37   void TranslatePage(const std::string& source_lang,
     38                      const std::string& target_lang,
     39                      const std::string& translate_script) {
     40     OnTranslatePage(0, translate_script, source_lang, target_lang);
     41   }
     42 
     43   MOCK_METHOD0(IsTranslateLibAvailable, bool());
     44   MOCK_METHOD0(IsTranslateLibReady, bool());
     45   MOCK_METHOD0(HasTranslationFinished, bool());
     46   MOCK_METHOD0(HasTranslationFailed, bool());
     47   MOCK_METHOD0(GetOriginalPageLanguage, std::string());
     48   MOCK_METHOD0(StartTranslation, bool());
     49   MOCK_METHOD1(ExecuteScript, void(const std::string&));
     50   MOCK_METHOD2(ExecuteScriptAndGetBoolResult, bool(const std::string&, bool));
     51   MOCK_METHOD1(ExecuteScriptAndGetStringResult,
     52                std::string(const std::string&));
     53   MOCK_METHOD1(ExecuteScriptAndGetDoubleResult, double(const std::string&));
     54 
     55  private:
     56   DISALLOW_COPY_AND_ASSIGN(TestTranslateHelper);
     57 };
     58 
     59 class TranslateHelperBrowserTest : public ChromeRenderViewTest {
     60  public:
     61   TranslateHelperBrowserTest() : translate_helper_(NULL) {}
     62 
     63  protected:
     64   virtual void SetUp() OVERRIDE {
     65     ChromeRenderViewTest::SetUp();
     66     translate_helper_ = new TestTranslateHelper(view_);
     67   }
     68 
     69   virtual void TearDown() OVERRIDE {
     70     delete translate_helper_;
     71     ChromeRenderViewTest::TearDown();
     72   }
     73 
     74   bool GetPageTranslatedMessage(std::string* original_lang,
     75                                 std::string* target_lang,
     76                                 translate::TranslateErrors::Type* error) {
     77     const IPC::Message* message = render_thread_->sink().
     78         GetUniqueMessageMatching(ChromeViewHostMsg_PageTranslated::ID);
     79     if (!message)
     80       return false;
     81     Tuple3<std::string, std::string, translate::TranslateErrors::Type>
     82         translate_param;
     83     ChromeViewHostMsg_PageTranslated::Read(message, &translate_param);
     84     if (original_lang)
     85       *original_lang = translate_param.a;
     86     if (target_lang)
     87       *target_lang = translate_param.b;
     88     if (error)
     89       *error = translate_param.c;
     90     return true;
     91   }
     92 
     93   TestTranslateHelper* translate_helper_;
     94 
     95  private:
     96   DISALLOW_COPY_AND_ASSIGN(TranslateHelperBrowserTest);
     97 };
     98 
     99 // Tests that the browser gets notified of the translation failure if the
    100 // translate library fails/times-out during initialization.
    101 TEST_F(TranslateHelperBrowserTest, TranslateLibNeverReady) {
    102   // We make IsTranslateLibAvailable true so we don't attempt to inject the
    103   // library.
    104   EXPECT_CALL(*translate_helper_, IsTranslateLibAvailable())
    105       .Times(AtLeast(1))
    106       .WillRepeatedly(Return(true));
    107 
    108   EXPECT_CALL(*translate_helper_, IsTranslateLibReady())
    109       .Times(AtLeast(5))  // See kMaxTranslateInitCheckAttempts in
    110                           // translate_helper.cc
    111       .WillRepeatedly(Return(false));
    112 
    113   translate_helper_->TranslatePage("en", "fr", std::string());
    114   base::MessageLoop::current()->RunUntilIdle();
    115 
    116   translate::TranslateErrors::Type error;
    117   ASSERT_TRUE(GetPageTranslatedMessage(NULL, NULL, &error));
    118   EXPECT_EQ(translate::TranslateErrors::INITIALIZATION_ERROR, error);
    119 }
    120 
    121 // Tests that the browser gets notified of the translation success when the
    122 // translation succeeds.
    123 TEST_F(TranslateHelperBrowserTest, TranslateSuccess) {
    124   // We make IsTranslateLibAvailable true so we don't attempt to inject the
    125   // library.
    126   EXPECT_CALL(*translate_helper_, IsTranslateLibAvailable())
    127       .Times(AtLeast(1))
    128       .WillRepeatedly(Return(true));
    129 
    130   EXPECT_CALL(*translate_helper_, IsTranslateLibReady())
    131       .WillOnce(Return(false))
    132       .WillOnce(Return(true));
    133 
    134   EXPECT_CALL(*translate_helper_, StartTranslation()).WillOnce(Return(true));
    135 
    136   // Succeed after few checks.
    137   EXPECT_CALL(*translate_helper_, HasTranslationFailed())
    138       .WillRepeatedly(Return(false));
    139   EXPECT_CALL(*translate_helper_, HasTranslationFinished())
    140       .WillOnce(Return(false))
    141       .WillOnce(Return(false))
    142       .WillOnce(Return(true));
    143 
    144   // V8 call for performance monitoring should be ignored.
    145   EXPECT_CALL(*translate_helper_,
    146               ExecuteScriptAndGetDoubleResult(_)).Times(3);
    147 
    148   std::string original_lang("en");
    149   std::string target_lang("fr");
    150   translate_helper_->TranslatePage(original_lang, target_lang, std::string());
    151   base::MessageLoop::current()->RunUntilIdle();
    152 
    153   std::string received_original_lang;
    154   std::string received_target_lang;
    155   translate::TranslateErrors::Type error;
    156   ASSERT_TRUE(GetPageTranslatedMessage(&received_original_lang,
    157                                        &received_target_lang,
    158                                        &error));
    159   EXPECT_EQ(original_lang, received_original_lang);
    160   EXPECT_EQ(target_lang, received_target_lang);
    161   EXPECT_EQ(translate::TranslateErrors::NONE, error);
    162 }
    163 
    164 // Tests that the browser gets notified of the translation failure when the
    165 // translation fails.
    166 TEST_F(TranslateHelperBrowserTest, TranslateFailure) {
    167   // We make IsTranslateLibAvailable true so we don't attempt to inject the
    168   // library.
    169   EXPECT_CALL(*translate_helper_, IsTranslateLibAvailable())
    170       .Times(AtLeast(1))
    171       .WillRepeatedly(Return(true));
    172 
    173   EXPECT_CALL(*translate_helper_, IsTranslateLibReady())
    174       .WillOnce(Return(true));
    175 
    176   EXPECT_CALL(*translate_helper_, StartTranslation()).WillOnce(Return(true));
    177 
    178   // Fail after few checks.
    179   EXPECT_CALL(*translate_helper_, HasTranslationFailed())
    180       .WillOnce(Return(false))
    181       .WillOnce(Return(false))
    182       .WillOnce(Return(false))
    183       .WillOnce(Return(true));
    184 
    185   EXPECT_CALL(*translate_helper_, HasTranslationFinished())
    186       .Times(AtLeast(1))
    187       .WillRepeatedly(Return(false));
    188 
    189   // V8 call for performance monitoring should be ignored.
    190   EXPECT_CALL(*translate_helper_,
    191               ExecuteScriptAndGetDoubleResult(_)).Times(2);
    192 
    193   translate_helper_->TranslatePage("en", "fr", std::string());
    194   base::MessageLoop::current()->RunUntilIdle();
    195 
    196   translate::TranslateErrors::Type error;
    197   ASSERT_TRUE(GetPageTranslatedMessage(NULL, NULL, &error));
    198   EXPECT_EQ(translate::TranslateErrors::TRANSLATION_ERROR, error);
    199 }
    200 
    201 // Tests that when the browser translate a page for which the language is
    202 // undefined we query the translate element to get the language.
    203 TEST_F(TranslateHelperBrowserTest, UndefinedSourceLang) {
    204   // We make IsTranslateLibAvailable true so we don't attempt to inject the
    205   // library.
    206   EXPECT_CALL(*translate_helper_, IsTranslateLibAvailable())
    207       .Times(AtLeast(1))
    208       .WillRepeatedly(Return(true));
    209 
    210   EXPECT_CALL(*translate_helper_, IsTranslateLibReady())
    211       .WillOnce(Return(true));
    212 
    213   EXPECT_CALL(*translate_helper_, GetOriginalPageLanguage())
    214       .WillOnce(Return("de"));
    215 
    216   EXPECT_CALL(*translate_helper_, StartTranslation()).WillOnce(Return(true));
    217   EXPECT_CALL(*translate_helper_, HasTranslationFailed())
    218       .WillOnce(Return(false));
    219   EXPECT_CALL(*translate_helper_, HasTranslationFinished())
    220       .Times(AtLeast(1))
    221       .WillRepeatedly(Return(true));
    222 
    223   // V8 call for performance monitoring should be ignored.
    224   EXPECT_CALL(*translate_helper_,
    225               ExecuteScriptAndGetDoubleResult(_)).Times(3);
    226 
    227   translate_helper_->TranslatePage(translate::kUnknownLanguageCode,
    228                                    "fr",
    229                                    std::string());
    230   base::MessageLoop::current()->RunUntilIdle();
    231 
    232   translate::TranslateErrors::Type error;
    233   std::string original_lang;
    234   std::string target_lang;
    235   ASSERT_TRUE(GetPageTranslatedMessage(&original_lang, &target_lang, &error));
    236   EXPECT_EQ("de", original_lang);
    237   EXPECT_EQ("fr", target_lang);
    238   EXPECT_EQ(translate::TranslateErrors::NONE, error);
    239 }
    240 
    241 // Tests that starting a translation while a similar one is pending does not
    242 // break anything.
    243 TEST_F(TranslateHelperBrowserTest, MultipleSimilarTranslations) {
    244   // We make IsTranslateLibAvailable true so we don't attempt to inject the
    245   // library.
    246   EXPECT_CALL(*translate_helper_, IsTranslateLibAvailable())
    247       .Times(AtLeast(1))
    248       .WillRepeatedly(Return(true));
    249 
    250   EXPECT_CALL(*translate_helper_, IsTranslateLibReady())
    251       .WillRepeatedly(Return(true));
    252   EXPECT_CALL(*translate_helper_, StartTranslation())
    253       .WillRepeatedly(Return(true));
    254   EXPECT_CALL(*translate_helper_, HasTranslationFailed())
    255       .WillRepeatedly(Return(false));
    256   EXPECT_CALL(*translate_helper_, HasTranslationFinished())
    257       .WillOnce(Return(true));
    258 
    259   // V8 call for performance monitoring should be ignored.
    260   EXPECT_CALL(*translate_helper_,
    261               ExecuteScriptAndGetDoubleResult(_)).Times(3);
    262 
    263   std::string original_lang("en");
    264   std::string target_lang("fr");
    265   translate_helper_->TranslatePage(original_lang, target_lang, std::string());
    266   // While this is running call again TranslatePage to make sure noting bad
    267   // happens.
    268   translate_helper_->TranslatePage(original_lang, target_lang, std::string());
    269   base::MessageLoop::current()->RunUntilIdle();
    270 
    271   std::string received_original_lang;
    272   std::string received_target_lang;
    273   translate::TranslateErrors::Type error;
    274   ASSERT_TRUE(GetPageTranslatedMessage(&received_original_lang,
    275                                        &received_target_lang,
    276                                        &error));
    277   EXPECT_EQ(original_lang, received_original_lang);
    278   EXPECT_EQ(target_lang, received_target_lang);
    279   EXPECT_EQ(translate::TranslateErrors::NONE, error);
    280 }
    281 
    282 // Tests that starting a translation while a different one is pending works.
    283 TEST_F(TranslateHelperBrowserTest, MultipleDifferentTranslations) {
    284   EXPECT_CALL(*translate_helper_, IsTranslateLibAvailable())
    285       .Times(AtLeast(1))
    286       .WillRepeatedly(Return(true));
    287   EXPECT_CALL(*translate_helper_, IsTranslateLibReady())
    288       .WillRepeatedly(Return(true));
    289   EXPECT_CALL(*translate_helper_, StartTranslation())
    290       .WillRepeatedly(Return(true));
    291   EXPECT_CALL(*translate_helper_, HasTranslationFailed())
    292       .WillRepeatedly(Return(false));
    293   EXPECT_CALL(*translate_helper_, HasTranslationFinished())
    294       .WillOnce(Return(true));
    295 
    296   // V8 call for performance monitoring should be ignored.
    297   EXPECT_CALL(*translate_helper_,
    298               ExecuteScriptAndGetDoubleResult(_)).Times(5);
    299 
    300   std::string original_lang("en");
    301   std::string target_lang("fr");
    302   translate_helper_->TranslatePage(original_lang, target_lang, std::string());
    303   // While this is running call again TranslatePage with a new target lang.
    304   std::string new_target_lang("de");
    305   translate_helper_->TranslatePage(
    306       original_lang, new_target_lang, std::string());
    307   base::MessageLoop::current()->RunUntilIdle();
    308 
    309   std::string received_original_lang;
    310   std::string received_target_lang;
    311   translate::TranslateErrors::Type error;
    312   ASSERT_TRUE(GetPageTranslatedMessage(&received_original_lang,
    313                                        &received_target_lang,
    314                                        &error));
    315   EXPECT_EQ(original_lang, received_original_lang);
    316   EXPECT_EQ(new_target_lang, received_target_lang);
    317   EXPECT_EQ(translate::TranslateErrors::NONE, error);
    318 }
    319 
    320 // Tests that we send the right translate language message for a page and that
    321 // we respect the "no translate" meta-tag.
    322 TEST_F(ChromeRenderViewTest, TranslatablePage) {
    323   // Suppress the normal delay that occurs when the page is loaded before which
    324   // the renderer sends the page contents to the browser.
    325   SendContentStateImmediately();
    326 
    327   LoadHTML("<html><body>A random page with random content.</body></html>");
    328   const IPC::Message* message = render_thread_->sink().GetUniqueMessageMatching(
    329       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    330   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    331   ChromeViewHostMsg_TranslateLanguageDetermined::Param params;
    332   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    333   EXPECT_TRUE(params.b) << "Page should be translatable.";
    334   render_thread_->sink().ClearMessages();
    335 
    336   // Now the page specifies the META tag to prevent translation.
    337   LoadHTML("<html><head><meta name=\"google\" value=\"notranslate\"></head>"
    338            "<body>A random page with random content.</body></html>");
    339   message = render_thread_->sink().GetUniqueMessageMatching(
    340       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    341   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    342   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    343   EXPECT_FALSE(params.b) << "Page should not be translatable.";
    344   render_thread_->sink().ClearMessages();
    345 
    346   // Try the alternate version of the META tag (content instead of value).
    347   LoadHTML("<html><head><meta name=\"google\" content=\"notranslate\"></head>"
    348            "<body>A random page with random content.</body></html>");
    349   message = render_thread_->sink().GetUniqueMessageMatching(
    350       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    351   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    352   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    353   EXPECT_FALSE(params.b) << "Page should not be translatable.";
    354 }
    355 
    356 // Tests that the language meta tag takes precedence over the CLD when reporting
    357 // the page's language.
    358 TEST_F(ChromeRenderViewTest, LanguageMetaTag) {
    359   // Suppress the normal delay that occurs when the page is loaded before which
    360   // the renderer sends the page contents to the browser.
    361   SendContentStateImmediately();
    362 
    363   LoadHTML("<html><head><meta http-equiv=\"content-language\" content=\"es\">"
    364            "</head><body>A random page with random content.</body></html>");
    365   const IPC::Message* message = render_thread_->sink().GetUniqueMessageMatching(
    366       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    367   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    368   ChromeViewHostMsg_TranslateLanguageDetermined::Param params;
    369   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    370   EXPECT_EQ("es", params.a.adopted_language);
    371   render_thread_->sink().ClearMessages();
    372 
    373   // Makes sure we support multiple languages specified.
    374   LoadHTML("<html><head><meta http-equiv=\"content-language\" "
    375            "content=\" fr , es,en \">"
    376            "</head><body>A random page with random content.</body></html>");
    377   message = render_thread_->sink().GetUniqueMessageMatching(
    378       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    379   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    380   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    381   EXPECT_EQ("fr", params.a.adopted_language);
    382 }
    383 
    384 // Tests that the language meta tag works even with non-all-lower-case.
    385 // http://code.google.com/p/chromium/issues/detail?id=145689
    386 TEST_F(ChromeRenderViewTest, LanguageMetaTagCase) {
    387   // Suppress the normal delay that occurs when the page is loaded before which
    388   // the renderer sends the page contents to the browser.
    389   SendContentStateImmediately();
    390 
    391   LoadHTML("<html><head><meta http-equiv=\"Content-Language\" content=\"es\">"
    392            "</head><body>A random page with random content.</body></html>");
    393   const IPC::Message* message = render_thread_->sink().GetUniqueMessageMatching(
    394       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    395   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    396   ChromeViewHostMsg_TranslateLanguageDetermined::Param params;
    397   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    398   EXPECT_EQ("es", params.a.adopted_language);
    399   render_thread_->sink().ClearMessages();
    400 
    401   // Makes sure we support multiple languages specified.
    402   LoadHTML("<html><head><meta http-equiv=\"Content-Language\" "
    403            "content=\" fr , es,en \">"
    404            "</head><body>A random page with random content.</body></html>");
    405   message = render_thread_->sink().GetUniqueMessageMatching(
    406       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    407   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    408   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    409   EXPECT_EQ("fr", params.a.adopted_language);
    410 }
    411 
    412 // Tests that the language meta tag is converted to Chrome standard of dashes
    413 // instead of underscores and proper capitalization.
    414 // http://code.google.com/p/chromium/issues/detail?id=159487
    415 TEST_F(ChromeRenderViewTest, LanguageCommonMistakesAreCorrected) {
    416   // Suppress the normal delay that occurs when the page is loaded before which
    417   // the renderer sends the page contents to the browser.
    418   SendContentStateImmediately();
    419 
    420   LoadHTML("<html><head><meta http-equiv='Content-Language' content='EN_us'>"
    421            "</head><body>A random page with random content.</body></html>");
    422   const IPC::Message* message = render_thread_->sink().GetUniqueMessageMatching(
    423       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    424   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    425   ChromeViewHostMsg_TranslateLanguageDetermined::Param params;
    426   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    427   EXPECT_EQ("en-US", params.a.adopted_language);
    428   render_thread_->sink().ClearMessages();
    429 }
    430 
    431 // Tests that a back navigation gets a translate language message.
    432 TEST_F(ChromeRenderViewTest, BackToTranslatablePage) {
    433   SendContentStateImmediately();
    434   LoadHTML("<html><head><meta http-equiv=\"content-language\" content=\"zh\">"
    435            "</head><body>This page is in Chinese.</body></html>");
    436   const IPC::Message* message = render_thread_->sink().GetUniqueMessageMatching(
    437       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    438   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    439   ChromeViewHostMsg_TranslateLanguageDetermined::Param params;
    440   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    441   EXPECT_EQ("zh", params.a.adopted_language);
    442   render_thread_->sink().ClearMessages();
    443 
    444   content::PageState back_state = GetCurrentPageState();
    445 
    446   LoadHTML("<html><head><meta http-equiv=\"content-language\" content=\"fr\">"
    447            "</head><body>This page is in French.</body></html>");
    448   message = render_thread_->sink().GetUniqueMessageMatching(
    449       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    450   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    451   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    452   EXPECT_EQ("fr", params.a.adopted_language);
    453   render_thread_->sink().ClearMessages();
    454 
    455   GoBack(back_state);
    456 
    457   message = render_thread_->sink().GetUniqueMessageMatching(
    458       ChromeViewHostMsg_TranslateLanguageDetermined::ID);
    459   ASSERT_NE(static_cast<IPC::Message*>(NULL), message);
    460   ChromeViewHostMsg_TranslateLanguageDetermined::Read(message, &params);
    461   EXPECT_EQ("zh", params.a.adopted_language);
    462   render_thread_->sink().ClearMessages();
    463 }
    464