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, ¶ms); 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, ¶ms); 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, ¶ms); 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, ¶ms); 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, ¶ms); 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, ¶ms); 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, ¶ms); 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, ¶ms); 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, ¶ms); 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, ¶ms); 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, ¶ms); 461 EXPECT_EQ("zh", params.a.adopted_language); 462 render_thread_->sink().ClearMessages(); 463 } 464