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.
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"
18 using testing::AtLeast
;
19 using testing::Return
;
22 class TestTranslateHelper
: public translate::TranslateHelper
{
24 explicit TestTranslateHelper(content::RenderView
* render_view
)
25 : translate::TranslateHelper(
27 chrome::ISOLATED_WORLD_ID_TRANSLATE
,
28 extensions::EXTENSION_GROUP_INTERNAL_TRANSLATE_SCRIPTS
,
29 extensions::kExtensionScheme
) {}
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();
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
);
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
&));
56 DISALLOW_COPY_AND_ASSIGN(TestTranslateHelper
);
59 class TranslateHelperBrowserTest
: public ChromeRenderViewTest
{
61 TranslateHelperBrowserTest() : translate_helper_(NULL
) {}
64 void SetUp() override
{
65 ChromeRenderViewTest::SetUp();
66 translate_helper_
= new TestTranslateHelper(view_
);
69 void TearDown() override
{
70 delete translate_helper_
;
71 ChromeRenderViewTest::TearDown();
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
);
81 Tuple
<std::string
, std::string
, translate::TranslateErrors::Type
>
83 ChromeViewHostMsg_PageTranslated::Read(message
, &translate_param
);
85 *original_lang
= get
<0>(translate_param
);
87 *target_lang
= get
<1>(translate_param
);
89 *error
= get
<2>(translate_param
);
93 TestTranslateHelper
* translate_helper_
;
96 DISALLOW_COPY_AND_ASSIGN(TranslateHelperBrowserTest
);
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
104 EXPECT_CALL(*translate_helper_
, IsTranslateLibAvailable())
106 .WillRepeatedly(Return(true));
108 EXPECT_CALL(*translate_helper_
, IsTranslateLibReady())
109 .Times(AtLeast(5)) // See kMaxTranslateInitCheckAttempts in
110 // translate_helper.cc
111 .WillRepeatedly(Return(false));
113 translate_helper_
->TranslatePage("en", "fr", std::string());
114 base::MessageLoop::current()->RunUntilIdle();
116 translate::TranslateErrors::Type error
;
117 ASSERT_TRUE(GetPageTranslatedMessage(NULL
, NULL
, &error
));
118 EXPECT_EQ(translate::TranslateErrors::INITIALIZATION_ERROR
, error
);
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
126 EXPECT_CALL(*translate_helper_
, IsTranslateLibAvailable())
128 .WillRepeatedly(Return(true));
130 EXPECT_CALL(*translate_helper_
, IsTranslateLibReady())
131 .WillOnce(Return(false))
132 .WillOnce(Return(true));
134 EXPECT_CALL(*translate_helper_
, StartTranslation()).WillOnce(Return(true));
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));
144 // V8 call for performance monitoring should be ignored.
145 EXPECT_CALL(*translate_helper_
,
146 ExecuteScriptAndGetDoubleResult(_
)).Times(3);
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();
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
,
159 EXPECT_EQ(original_lang
, received_original_lang
);
160 EXPECT_EQ(target_lang
, received_target_lang
);
161 EXPECT_EQ(translate::TranslateErrors::NONE
, error
);
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
169 EXPECT_CALL(*translate_helper_
, IsTranslateLibAvailable())
171 .WillRepeatedly(Return(true));
173 EXPECT_CALL(*translate_helper_
, IsTranslateLibReady())
174 .WillOnce(Return(true));
176 EXPECT_CALL(*translate_helper_
, StartTranslation()).WillOnce(Return(true));
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));
185 EXPECT_CALL(*translate_helper_
, HasTranslationFinished())
187 .WillRepeatedly(Return(false));
189 // V8 call for performance monitoring should be ignored.
190 EXPECT_CALL(*translate_helper_
,
191 ExecuteScriptAndGetDoubleResult(_
)).Times(2);
193 translate_helper_
->TranslatePage("en", "fr", std::string());
194 base::MessageLoop::current()->RunUntilIdle();
196 translate::TranslateErrors::Type error
;
197 ASSERT_TRUE(GetPageTranslatedMessage(NULL
, NULL
, &error
));
198 EXPECT_EQ(translate::TranslateErrors::TRANSLATION_ERROR
, error
);
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
206 EXPECT_CALL(*translate_helper_
, IsTranslateLibAvailable())
208 .WillRepeatedly(Return(true));
210 EXPECT_CALL(*translate_helper_
, IsTranslateLibReady())
211 .WillOnce(Return(true));
213 EXPECT_CALL(*translate_helper_
, GetOriginalPageLanguage())
214 .WillOnce(Return("de"));
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())
221 .WillRepeatedly(Return(true));
223 // V8 call for performance monitoring should be ignored.
224 EXPECT_CALL(*translate_helper_
,
225 ExecuteScriptAndGetDoubleResult(_
)).Times(3);
227 translate_helper_
->TranslatePage(translate::kUnknownLanguageCode
,
230 base::MessageLoop::current()->RunUntilIdle();
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
);
241 // Tests that starting a translation while a similar one is pending does not
243 TEST_F(TranslateHelperBrowserTest
, MultipleSimilarTranslations
) {
244 // We make IsTranslateLibAvailable true so we don't attempt to inject the
246 EXPECT_CALL(*translate_helper_
, IsTranslateLibAvailable())
248 .WillRepeatedly(Return(true));
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));
259 // V8 call for performance monitoring should be ignored.
260 EXPECT_CALL(*translate_helper_
,
261 ExecuteScriptAndGetDoubleResult(_
)).Times(3);
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
268 translate_helper_
->TranslatePage(original_lang
, target_lang
, std::string());
269 base::MessageLoop::current()->RunUntilIdle();
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
,
277 EXPECT_EQ(original_lang
, received_original_lang
);
278 EXPECT_EQ(target_lang
, received_target_lang
);
279 EXPECT_EQ(translate::TranslateErrors::NONE
, error
);
282 // Tests that starting a translation while a different one is pending works.
283 TEST_F(TranslateHelperBrowserTest
, MultipleDifferentTranslations
) {
284 EXPECT_CALL(*translate_helper_
, IsTranslateLibAvailable())
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));
296 // V8 call for performance monitoring should be ignored.
297 EXPECT_CALL(*translate_helper_
,
298 ExecuteScriptAndGetDoubleResult(_
)).Times(5);
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();
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
,
315 EXPECT_EQ(original_lang
, received_original_lang
);
316 EXPECT_EQ(new_target_lang
, received_target_lang
);
317 EXPECT_EQ(translate::TranslateErrors::NONE
, error
);
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();
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(get
<1>(params
)) << "Page should be translatable.";
334 render_thread_
->sink().ClearMessages();
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(get
<1>(params
)) << "Page should not be translatable.";
344 render_thread_
->sink().ClearMessages();
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(get
<1>(params
)) << "Page should not be translatable.";
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();
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", get
<0>(params
).adopted_language
);
371 render_thread_
->sink().ClearMessages();
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", get
<0>(params
).adopted_language
);
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();
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", get
<0>(params
).adopted_language
);
399 render_thread_
->sink().ClearMessages();
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", get
<0>(params
).adopted_language
);
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();
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", get
<0>(params
).adopted_language
);
428 render_thread_
->sink().ClearMessages();
430 LoadHTML("<html><head><meta http-equiv='Content-Language' content='ZH_tw'>"
431 "</head><body>A random page with random content.</body></html>");
432 message
= render_thread_
->sink().GetUniqueMessageMatching(
433 ChromeViewHostMsg_TranslateLanguageDetermined::ID
);
434 ASSERT_NE(static_cast<IPC::Message
*>(NULL
), message
);
435 ChromeViewHostMsg_TranslateLanguageDetermined::Read(message
, ¶ms
);
436 EXPECT_EQ("zh-TW", get
<0>(params
).adopted_language
);
437 render_thread_
->sink().ClearMessages();
440 // Tests that a back navigation gets a translate language message.
441 TEST_F(ChromeRenderViewTest
, BackToTranslatablePage
) {
442 SendContentStateImmediately();
443 LoadHTML("<html><head><meta http-equiv=\"content-language\" content=\"zh\">"
444 "</head><body>This page is in Chinese.</body></html>");
445 const IPC::Message
* message
= render_thread_
->sink().GetUniqueMessageMatching(
446 ChromeViewHostMsg_TranslateLanguageDetermined::ID
);
447 ASSERT_NE(static_cast<IPC::Message
*>(NULL
), message
);
448 ChromeViewHostMsg_TranslateLanguageDetermined::Param params
;
449 ChromeViewHostMsg_TranslateLanguageDetermined::Read(message
, ¶ms
);
450 EXPECT_EQ("zh", get
<0>(params
).adopted_language
);
451 render_thread_
->sink().ClearMessages();
453 content::PageState back_state
= GetCurrentPageState();
455 LoadHTML("<html><head><meta http-equiv=\"content-language\" content=\"fr\">"
456 "</head><body>This page is in French.</body></html>");
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("fr", get
<0>(params
).adopted_language
);
462 render_thread_
->sink().ClearMessages();
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("zh", get
<0>(params
).adopted_language
);
471 render_thread_
->sink().ClearMessages();