summaryrefslogtreecommitdiffstats
path: root/chrome/renderer
diff options
context:
space:
mode:
authorjcivelli@google.com <jcivelli@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-20 22:14:06 +0000
committerjcivelli@google.com <jcivelli@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-20 22:14:06 +0000
commitd64b07bf98e4f27da4c22da6c615b75d4b2e16bc (patch)
tree345939445360af7d7ec3931ac8a839d5c08aa457 /chrome/renderer
parent4bedba77d40df5fa5f1e00f5918123ee8711ca76 (diff)
downloadchromium_src-d64b07bf98e4f27da4c22da6c615b75d4b2e16bc.zip
chromium_src-d64b07bf98e4f27da4c22da6c615b75d4b2e16bc.tar.gz
chromium_src-d64b07bf98e4f27da4c22da6c615b75d4b2e16bc.tar.bz2
Translate now supports translating pages for which we
don't know the original language: if the browser specifies undefined for the source language, we use the translate element in the page to detect the language. BUG=40857 TEST=See bug. Review URL: http://codereview.chromium.org/1508030 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@45094 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer')
-rw-r--r--chrome/renderer/render_view.cc7
-rw-r--r--chrome/renderer/render_view.h3
-rw-r--r--chrome/renderer/translate_helper.cc131
-rw-r--r--chrome/renderer/translate_helper.h38
-rw-r--r--chrome/renderer/translate_helper_unittest.cc129
5 files changed, 253 insertions, 55 deletions
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc
index 530a820..1aaf0a4 100644
--- a/chrome/renderer/render_view.cc
+++ b/chrome/renderer/render_view.cc
@@ -247,8 +247,8 @@ static const char* const kUnreachableWebDataURL =
static const char* const kBackForwardNavigationScheme = "history";
-// The string returned in DetectLanguage if we failed to detect the language.
-static const char* const kUnknownLanguageCode = "und";
+// static
+const char* const RenderView::kUnknownLanguageCode = "und";
static void GetRedirectChain(WebDataSource* ds, std::vector<GURL>* result) {
WebVector<WebURL> urls;
@@ -2548,6 +2548,9 @@ void RenderView::didCommitProvisionalLoad(WebFrame* frame,
// We bump our Page ID to correspond with the new session history entry.
page_id_ = next_page_id_++;
+ // Any pending translation is now obsolete.
+ translate_helper_.CancelPendingTranslation();
+
// Advance our offset in session history, applying the length limit. There
// is now no forward history.
history_list_offset_++;
diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h
index bfdf304..837c617 100644
--- a/chrome/renderer/render_view.h
+++ b/chrome/renderer/render_view.h
@@ -514,6 +514,9 @@ class RenderView : public RenderWidget,
bool ScheduleFileChooser(const ViewHostMsg_RunFileChooser_Params& params,
WebKit::WebFileChooserCompletion* completion);
+ // The language code used when the page language is unknown.
+ static const char* const kUnknownLanguageCode;
+
protected:
// RenderWidget overrides:
virtual void Close();
diff --git a/chrome/renderer/translate_helper.cc b/chrome/renderer/translate_helper.cc
index f2a3f66..d6d8569 100644
--- a/chrome/renderer/translate_helper.cc
+++ b/chrome/renderer/translate_helper.cc
@@ -24,11 +24,16 @@ static const int kMaxTranslateInitCheckAttempts = 5;
// finished.
static const int kTranslateStatusCheckDelayMs = 400;
+// Language name passed to the Translate element for it to detect the language.
+static const char* const kAutoDetectionLanguage = "auto";
+
////////////////////////////////////////////////////////////////////////////////
// TranslateHelper, public:
//
TranslateHelper::TranslateHelper(RenderView* render_view)
: render_view_(render_view),
+ translation_pending_(false),
+ page_id_(-1),
ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
}
@@ -39,6 +44,24 @@ void TranslateHelper::TranslatePage(int page_id,
if (render_view_->page_id() != page_id)
return; // We navigated away, nothing to do.
+ if (translation_pending_ && page_id == page_id_ &&
+ target_lang_ == target_lang) {
+ // A similar translation is already under way, nothing to do.
+ return;
+ }
+
+ // Any pending translation is now irrelevant.
+ CancelPendingTranslation();
+
+ // Set our states.
+ translation_pending_ = true;
+ page_id_ = page_id;
+ // If the source language is undetermined, we'll let the translate element
+ // detect it.
+ source_lang_ = (source_lang != RenderView::kUnknownLanguageCode) ?
+ source_lang : kAutoDetectionLanguage;
+ target_lang_ = target_lang;
+
if (!IsTranslateLibAvailable()) {
// Evaluate the script to add the translation related method to the global
// context of the page.
@@ -46,11 +69,7 @@ void TranslateHelper::TranslatePage(int page_id,
DCHECK(IsTranslateLibAvailable());
}
- // Cancel any pending tasks related to a previous translation, they are now
- // obsolete.
- method_factory_.RevokeAll();
-
- TranslatePageImpl(page_id, source_lang, target_lang, 0);
+ TranslatePageImpl(0);
}
void TranslateHelper::RevertTranslation(int page_id) {
@@ -66,10 +85,20 @@ void TranslateHelper::RevertTranslation(int page_id) {
if (!main_frame)
return;
+ CancelPendingTranslation();
+
main_frame->executeScript(
WebScriptSource(ASCIIToUTF16("cr.googleTranslate.revert()")));
}
+void TranslateHelper::CancelPendingTranslation() {
+ method_factory_.RevokeAll();
+ translation_pending_ = false;
+ page_id_ = -1;
+ source_lang_.clear();
+ target_lang_.clear();
+}
+
////////////////////////////////////////////////////////////////////////////////
// TranslateHelper, protected:
//
@@ -116,11 +145,10 @@ bool TranslateHelper::HasTranslationFailed() {
return translation_failed;
}
-bool TranslateHelper::StartTranslation(const std::string& source_lang,
- const std::string& target_lang) {
+bool TranslateHelper::StartTranslation() {
bool translate_success = false;
if (!ExecuteScriptAndGetBoolResult("cr.googleTranslate.translate('" +
- source_lang + "','" + target_lang + "')",
+ source_lang_ + "','" + target_lang_ + "')",
&translate_success)) {
NOTREACHED();
return false;
@@ -128,34 +156,56 @@ bool TranslateHelper::StartTranslation(const std::string& source_lang,
return translate_success;
}
+std::string TranslateHelper::GetOriginalPageLanguage() {
+ std::string lang;
+ ExecuteScriptAndGetStringResult("cr.googleTranslate.sourceLang", &lang);
+ return lang;
+}
+
////////////////////////////////////////////////////////////////////////////////
// TranslateHelper, private:
//
-void TranslateHelper::CheckTranslateStatus(int page_id,
- const std::string& source_lang,
- const std::string& target_lang) {
- if (page_id != render_view_->page_id())
+void TranslateHelper::CheckTranslateStatus() {
+ if (page_id_ != render_view_->page_id())
return; // This is not the same page, the translation has been canceled.
// First check if there was an error.
if (HasTranslationFailed()) {
- NotifyBrowserTranslationFailed(source_lang, target_lang,
- TranslateErrors::TRANSLATION_ERROR);
+ NotifyBrowserTranslationFailed(TranslateErrors::TRANSLATION_ERROR);
return; // There was an error.
}
if (HasTranslationFinished()) {
- // Translation was successfull, notify the browser.
+ std::string actual_source_lang;
+ // Translation was successfull, if it was auto, retrieve the source
+ // language the Translate Element detected.
+ if (source_lang_ == kAutoDetectionLanguage) {
+ actual_source_lang = GetOriginalPageLanguage();
+ if (actual_source_lang.empty()) {
+ NotifyBrowserTranslationFailed(TranslateErrors::TRANSLATION_ERROR);
+ return;
+ }
+ } else {
+ actual_source_lang = source_lang_;
+ }
+
+ if (!translation_pending_) {
+ NOTREACHED();
+ return;
+ }
+
+ translation_pending_ = false;
+
+ // Notify the browser we are done.
render_view_->Send(new ViewHostMsg_PageTranslated(
render_view_->routing_id(), render_view_->page_id(),
- source_lang, target_lang, TranslateErrors::NONE));
+ actual_source_lang, target_lang_, TranslateErrors::NONE));
return;
}
// The translation is still pending, check again later.
MessageLoop::current()->PostDelayedTask(FROM_HERE,
- method_factory_.NewRunnableMethod(&TranslateHelper::CheckTranslateStatus,
- page_id, source_lang, target_lang),
+ method_factory_.NewRunnableMethod(&TranslateHelper::CheckTranslateStatus),
DontDelayTasks() ? 0 : kTranslateStatusCheckDelayMs);
}
@@ -183,48 +233,59 @@ bool TranslateHelper::ExecuteScriptAndGetBoolResult(const std::string& script,
return true;
}
-void TranslateHelper::TranslatePageImpl(int page_id,
- const std::string& source_lang,
- const std::string& target_lang,
- int count) {
+bool TranslateHelper::ExecuteScriptAndGetStringResult(const std::string& script,
+ std::string* value) {
+ DCHECK(value);
+ WebFrame* main_frame = render_view_->webview()->mainFrame();
+ if (!main_frame)
+ return false;
+
+ v8::Handle<v8::Value> v = main_frame->executeScriptAndReturnValue(
+ WebScriptSource(ASCIIToUTF16(script)));
+ if (v.IsEmpty() || !v->IsString())
+ return false;
+
+ v8::Local<v8::String> v8_str = v->ToString();
+ int length = v8_str->Utf8Length() + 1;
+ scoped_array<char> str(new char[length]);
+ v8_str->WriteUtf8(str.get(), length);
+ *value = str.get();
+ return true;
+}
+
+void TranslateHelper::TranslatePageImpl(int count) {
DCHECK_LT(count, kMaxTranslateInitCheckAttempts);
- if (page_id != render_view_->page_id())
+ if (page_id_ != render_view_->page_id())
return;
if (!IsTranslateLibReady()) {
// The library is not ready, try again later, unless we have tried several
// times unsucessfully already.
if (++count >= kMaxTranslateInitCheckAttempts) {
- NotifyBrowserTranslationFailed(source_lang, target_lang,
- TranslateErrors::INITIALIZATION_ERROR);
+ NotifyBrowserTranslationFailed(TranslateErrors::INITIALIZATION_ERROR);
return;
}
MessageLoop::current()->PostDelayedTask(FROM_HERE,
method_factory_.NewRunnableMethod(&TranslateHelper::TranslatePageImpl,
- page_id, source_lang, target_lang,
count),
DontDelayTasks() ? 0 : count * kTranslateInitCheckDelayMs);
return;
}
- if (!StartTranslation(source_lang, target_lang)) {
- NotifyBrowserTranslationFailed(source_lang, target_lang,
- TranslateErrors::TRANSLATION_ERROR);
+ if (!StartTranslation()) {
+ NotifyBrowserTranslationFailed(TranslateErrors::TRANSLATION_ERROR);
return;
}
// Check the status of the translation.
MessageLoop::current()->PostDelayedTask(FROM_HERE,
- method_factory_.NewRunnableMethod(&TranslateHelper::CheckTranslateStatus,
- page_id, source_lang, target_lang),
+ method_factory_.NewRunnableMethod(&TranslateHelper::CheckTranslateStatus),
DontDelayTasks() ? 0 : kTranslateStatusCheckDelayMs);
}
void TranslateHelper::NotifyBrowserTranslationFailed(
- const std::string& source_lang,
- const std::string& target_lang,
TranslateErrors::Type error) {
+ translation_pending_ = false;
// Notify the browser there was an error.
render_view_->Send(new ViewHostMsg_PageTranslated(
- render_view_->routing_id(), render_view_->page_id(),
- source_lang, target_lang, error));
+ render_view_->routing_id(), page_id_, source_lang_, target_lang_, error));
}
diff --git a/chrome/renderer/translate_helper.h b/chrome/renderer/translate_helper.h
index efd5a72..8b55e8a 100644
--- a/chrome/renderer/translate_helper.h
+++ b/chrome/renderer/translate_helper.h
@@ -31,6 +31,10 @@ class TranslateHelper {
// Reverts the page's text to its original contents.
void RevertTranslation(int page_id);
+ // Cancels any translation that is currently being performed. This does not
+ // revert existing translations.
+ void CancelPendingTranslation();
+
protected:
// The following methods are protected so they can be overridden in
// unit-tests.
@@ -52,8 +56,12 @@ class TranslateHelper {
// Starts the translation by calling the translate library. This method
// should only be called when the translate script has been injected in the
// page. Returns false if the call failed immediately.
- virtual bool StartTranslation(const std::string& original_lang,
- const std::string& target_lang);
+ virtual bool StartTranslation();
+
+ // Asks the Translate element in the page what the language of the page is.
+ // Can only be called if a translation has happened and was successful.
+ // Returns the language code on success, an empty string on failure.
+ virtual std::string GetOriginalPageLanguage();
// Used in unit-tests. Makes the various tasks be posted immediately so that
// the tests don't have to wait before checking states.
@@ -63,9 +71,7 @@ class TranslateHelper {
// Checks if the current running page translation is finished or errored and
// notifies the browser accordingly. If the translation has not terminated,
// posts a task to check again later.
- void CheckTranslateStatus(int page_id,
- const std::string& source_lang,
- const std::string& target_lang);
+ void CheckTranslateStatus();
// Executes the JavaScript code in |script| in the main frame of
// |render_view_host_|.
@@ -78,22 +84,30 @@ class TranslateHelper {
// a boolean, false otherwise
bool ExecuteScriptAndGetBoolResult(const std::string& script, bool* value);
+ // Executes the JavaScript code in |script| in the main frame of
+ // |render_view_host_|, and sets |value| to the string returned by the script
+ // evaluation. Returns true if the script was run successfully and returned
+ // a string, false otherwise
+ bool ExecuteScriptAndGetStringResult(const std::string& script,
+ std::string* value);
+
// Called by TranslatePage to do the actual translation. |count| is used to
// limit the number of retries.
- void TranslatePageImpl(int page_id,
- const std::string& source_lang,
- const std::string& target_lang,
- int count);
+ void TranslatePageImpl(int count);
// Sends a message to the browser to notify it that the translation failed
// with |error|.
- void NotifyBrowserTranslationFailed(const std::string& original_lang,
- const std::string& target_lang,
- TranslateErrors::Type error);
+ void NotifyBrowserTranslationFailed(TranslateErrors::Type error);
// The RenderView we are performing translations for.
RenderView* render_view_;
+ // The states associated with the current translation.
+ bool translation_pending_;
+ int page_id_;
+ std::string source_lang_;
+ std::string target_lang_;
+
// Method factory used to make calls to TranslatePageImpl.
ScopedRunnableMethodFactory<TranslateHelper> method_factory_;
diff --git a/chrome/renderer/translate_helper_unittest.cc b/chrome/renderer/translate_helper_unittest.cc
index c0f0e5e..eef25fb 100644
--- a/chrome/renderer/translate_helper_unittest.cc
+++ b/chrome/renderer/translate_helper_unittest.cc
@@ -22,8 +22,8 @@ class TestTranslateHelper : public TranslateHelper {
MOCK_METHOD0(IsTranslateLibReady, bool());
MOCK_METHOD0(HasTranslationFinished, bool());
MOCK_METHOD0(HasTranslationFailed, bool());
- MOCK_METHOD2(StartTranslation, bool(const std::string& source_lang,
- const std::string& target_lang));
+ MOCK_METHOD0(GetOriginalPageLanguage, std::string());
+ MOCK_METHOD0(StartTranslation, bool());
private:
DISALLOW_COPY_AND_ASSIGN(TestTranslateHelper);
@@ -101,8 +101,7 @@ TEST_F(TranslateHelperTest, TranslateSuccess) {
.WillOnce(Return(false))
.WillOnce(Return(true));
- EXPECT_CALL(*translate_helper_, StartTranslation("en", "fr"))
- .WillOnce(Return(true));
+ EXPECT_CALL(*translate_helper_, StartTranslation()).WillOnce(Return(true));
// Succeed after few checks.
EXPECT_CALL(*translate_helper_, HasTranslationFailed())
@@ -144,8 +143,7 @@ TEST_F(TranslateHelperTest, TranslateFailure) {
EXPECT_CALL(*translate_helper_, IsTranslateLibReady())
.WillOnce(Return(true));
- EXPECT_CALL(*translate_helper_, StartTranslation("en", "fr"))
- .WillOnce(Return(true));
+ EXPECT_CALL(*translate_helper_, StartTranslation()).WillOnce(Return(true));
// Fail after few checks.
EXPECT_CALL(*translate_helper_, HasTranslationFailed())
@@ -167,3 +165,122 @@ TEST_F(TranslateHelperTest, TranslateFailure) {
EXPECT_EQ(view_->page_id(), page_id);
EXPECT_EQ(TranslateErrors::TRANSLATION_ERROR, error);
}
+
+// Tests that when the browser translate a page for which the language is
+// undefined we query the translate element to get the language.
+TEST_F(TranslateHelperTest, UndefinedSourceLang) {
+ // We make IsTranslateLibAvailable true so we don't attempt to inject the
+ // library.
+ EXPECT_CALL(*translate_helper_, IsTranslateLibAvailable())
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return(true));
+
+ EXPECT_CALL(*translate_helper_, IsTranslateLibReady())
+ .WillOnce(Return(true));
+
+ EXPECT_CALL(*translate_helper_, GetOriginalPageLanguage())
+ .WillOnce(Return("de"));
+
+ EXPECT_CALL(*translate_helper_, StartTranslation()).WillOnce(Return(true));
+ EXPECT_CALL(*translate_helper_, HasTranslationFailed())
+ .WillOnce(Return(false));
+ EXPECT_CALL(*translate_helper_, HasTranslationFinished())
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return(true));
+
+ translate_helper_->TranslatePage(view_->page_id(),
+ RenderView::kUnknownLanguageCode, "fr",
+ std::string());
+ MessageLoop::current()->RunAllPending();
+
+ int page_id;
+ TranslateErrors::Type error;
+ std::string original_lang;
+ std::string target_lang;
+ ASSERT_TRUE(GetPageTranslatedMessage(&page_id, &original_lang, &target_lang,
+ &error));
+ EXPECT_EQ(view_->page_id(), page_id);
+ EXPECT_EQ("de", original_lang);
+ EXPECT_EQ("fr", target_lang);
+ EXPECT_EQ(TranslateErrors::NONE, error);
+}
+
+// Tests that starting a translation while a similar one is pending does not
+// break anything.
+TEST_F(TranslateHelperTest, MultipleSimilarTranslations) {
+ // We make IsTranslateLibAvailable true so we don't attempt to inject the
+ // library.
+ EXPECT_CALL(*translate_helper_, IsTranslateLibAvailable())
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return(true));
+
+ EXPECT_CALL(*translate_helper_, IsTranslateLibReady())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*translate_helper_, StartTranslation())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*translate_helper_, HasTranslationFailed())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*translate_helper_, HasTranslationFinished())
+ .WillOnce(Return(true));
+
+ std::string original_lang("en");
+ std::string target_lang("fr");
+ translate_helper_->TranslatePage(view_->page_id(), original_lang, target_lang,
+ std::string());
+ // While this is running call again TranslatePage to make sure noting bad
+ // happens.
+ translate_helper_->TranslatePage(view_->page_id(), original_lang, target_lang,
+ std::string());
+ MessageLoop::current()->RunAllPending();
+
+ int page_id;
+ std::string received_original_lang;
+ std::string received_target_lang;
+ TranslateErrors::Type error;
+ ASSERT_TRUE(GetPageTranslatedMessage(&page_id,
+ &received_original_lang,
+ &received_target_lang,
+ &error));
+ EXPECT_EQ(view_->page_id(), page_id);
+ EXPECT_EQ(original_lang, received_original_lang);
+ EXPECT_EQ(target_lang, received_target_lang);
+ EXPECT_EQ(TranslateErrors::NONE, error);
+}
+
+// Tests that starting a translation while a different one is pending works.
+TEST_F(TranslateHelperTest, MultipleDifferentTranslations) {
+ EXPECT_CALL(*translate_helper_, IsTranslateLibAvailable())
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*translate_helper_, IsTranslateLibReady())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*translate_helper_, StartTranslation())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(*translate_helper_, HasTranslationFailed())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*translate_helper_, HasTranslationFinished())
+ .WillOnce(Return(true));
+
+ std::string original_lang("en");
+ std::string target_lang("fr");
+ translate_helper_->TranslatePage(view_->page_id(), original_lang, target_lang,
+ std::string());
+ // While this is running call again TranslatePage with a new target lang.
+ std::string new_target_lang("de");
+ translate_helper_->TranslatePage(view_->page_id(), original_lang,
+ new_target_lang, std::string());
+ MessageLoop::current()->RunAllPending();
+
+ int page_id;
+ std::string received_original_lang;
+ std::string received_target_lang;
+ TranslateErrors::Type error;
+ ASSERT_TRUE(GetPageTranslatedMessage(&page_id,
+ &received_original_lang,
+ &received_target_lang,
+ &error));
+ EXPECT_EQ(view_->page_id(), page_id);
+ EXPECT_EQ(original_lang, received_original_lang);
+ EXPECT_EQ(new_target_lang, received_target_lang);
+ EXPECT_EQ(TranslateErrors::NONE, error);
+}