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