diff options
author | jcivelli@google.com <jcivelli@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-20 22:14:06 +0000 |
---|---|---|
committer | jcivelli@google.com <jcivelli@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-20 22:14:06 +0000 |
commit | d64b07bf98e4f27da4c22da6c615b75d4b2e16bc (patch) | |
tree | 345939445360af7d7ec3931ac8a839d5c08aa457 /chrome/renderer | |
parent | 4bedba77d40df5fa5f1e00f5918123ee8711ca76 (diff) | |
download | chromium_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.cc | 7 | ||||
-rw-r--r-- | chrome/renderer/render_view.h | 3 | ||||
-rw-r--r-- | chrome/renderer/translate_helper.cc | 131 | ||||
-rw-r--r-- | chrome/renderer/translate_helper.h | 38 | ||||
-rw-r--r-- | chrome/renderer/translate_helper_unittest.cc | 129 |
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); +} |