summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjcivelli@google.com <jcivelli@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-06 22:21:02 +0000
committerjcivelli@google.com <jcivelli@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-04-06 22:21:02 +0000
commit85d252e22b6bec161873a0b5656d59c8ebe04e30 (patch)
tree937f8add8eac344f93d6a8ec6604796f371c761c
parentd41661b5e24c8d774acea07c05ef2e5896587e56 (diff)
downloadchromium_src-85d252e22b6bec161873a0b5656d59c8ebe04e30.zip
chromium_src-85d252e22b6bec161873a0b5656d59c8ebe04e30.tar.gz
chromium_src-85d252e22b6bec161873a0b5656d59c8ebe04e30.tar.bz2
Changing the translate back-end to use the Google Translate element.
When the user indicates that a page should be translated, the browser first fetches the Google Translate Element JS code. It then sends it to the renderer, which injects the script in the page, waits for the Translate element to be initialized and then calls the translate method on it. The TranslationService class previously used to translate text chunks is now unused and has been removed. Some of its static methods that are still used have been moved to the TranslateManager class. This CL also implements the "revert" translation behavior. BUG=35474,37778,35553,39375 TEST=Test the translation feature extensively. Review URL: http://codereview.chromium.org/1599016 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@43768 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/browser_resources.grd1
-rw-r--r--chrome/browser/renderer_host/render_view_host.cc7
-rw-r--r--chrome/browser/renderer_host/render_view_host.h6
-rw-r--r--chrome/browser/renderer_host/resource_message_filter.cc9
-rw-r--r--chrome/browser/renderer_host/resource_message_filter.h6
-rw-r--r--chrome/browser/renderer_host/translation_service.cc675
-rw-r--r--chrome/browser/renderer_host/translation_service.h195
-rw-r--r--chrome/browser/renderer_host/translation_service_unittest.cc505
-rw-r--r--chrome/browser/resources/translate.js111
-rw-r--r--chrome/browser/tab_contents/render_view_context_menu.cc20
-rw-r--r--chrome/browser/tab_contents/tab_contents.cc17
-rw-r--r--chrome/browser/tab_contents/tab_contents.h7
-rw-r--r--chrome/browser/translate/translate_infobars_delegates.cc20
-rw-r--r--chrome/browser/translate/translate_manager.cc253
-rw-r--r--chrome/browser/translate/translate_manager.h77
-rw-r--r--chrome/browser/translate/translate_manager_unittest.cc74
-rw-r--r--chrome/chrome_browser.gypi4
-rwxr-xr-xchrome/chrome_renderer.gypi7
-rw-r--r--chrome/chrome_tests.gypi7
-rw-r--r--chrome/common/render_messages.h70
-rw-r--r--chrome/common/render_messages_internal.h18
-rw-r--r--chrome/common/translate_errors.h6
-rw-r--r--chrome/renderer/render_view.cc40
-rw-r--r--chrome/renderer/render_view.h32
-rw-r--r--chrome/renderer/translate/page_translator.cc324
-rw-r--r--chrome/renderer/translate/page_translator.h158
-rw-r--r--chrome/renderer/translate/page_translator_unittest.cc215
-rw-r--r--chrome/renderer/translate/text_translator.h49
-rw-r--r--chrome/renderer/translate/text_translator_impl.cc41
-rw-r--r--chrome/renderer/translate/text_translator_impl.h51
-rw-r--r--chrome/renderer/translate_helper.cc230
-rw-r--r--chrome/renderer/translate_helper.h103
-rw-r--r--chrome/renderer/translate_helper_unittest.cc169
-rw-r--r--chrome/test/data/translate/basic_ORIGINAL.html32
-rw-r--r--chrome/test/data/translate/basic_TRANSLATED.html33
-rw-r--r--chrome/test/data/translate/reverse_text.py223
36 files changed, 1050 insertions, 2745 deletions
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 1aeceab..fe9f9e9 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -66,6 +66,7 @@ without changes to the corresponding grd file. fbt1 -->
<include name="IDR_NOTIFICATION_ICON_HTML" file="resources\notification_icon.html" type="BINDATA" />
<include name="IDR_NOTIFICATION_2LINE_HTML" file="resources\notification_2line.html" type="BINDATA" />
<include name="IDR_NOTIFICATION_1LINE_HTML" file="resources\notification_1line.html" type="BINDATA" />
+ <include name="IDR_TRANSLATE_JS" file="resources\translate.js" type="BINDATA" />
</includes>
</release>
</grit>
diff --git a/chrome/browser/renderer_host/render_view_host.cc b/chrome/browser/renderer_host/render_view_host.cc
index 47debf7..3697719 100644
--- a/chrome/browser/renderer_host/render_view_host.cc
+++ b/chrome/browser/renderer_host/render_view_host.cc
@@ -1789,12 +1789,17 @@ void RenderViewHost::PerformCustomContextMenuAction(unsigned action) {
}
void RenderViewHost::TranslatePage(int page_id,
+ const std::string& translate_script,
const std::string& source_lang,
const std::string& target_lang) {
- Send(new ViewMsg_TranslatePage(routing_id(), page_id,
+ Send(new ViewMsg_TranslatePage(routing_id(), page_id, translate_script,
source_lang, target_lang));
}
+void RenderViewHost::RevertTranslation(int page_id) {
+ Send(new ViewMsg_RevertTranslation(routing_id(), page_id));
+}
+
void RenderViewHost::SendContentSettings(const std::string& host,
const ContentSettings& settings) {
Send(new ViewMsg_SetContentSettingsForCurrentHost(host, settings));
diff --git a/chrome/browser/renderer_host/render_view_host.h b/chrome/browser/renderer_host/render_view_host.h
index e35a707..48300d9 100644
--- a/chrome/browser/renderer_host/render_view_host.h
+++ b/chrome/browser/renderer_host/render_view_host.h
@@ -442,10 +442,16 @@ class RenderViewHost : public RenderWidgetHost {
// Tells the renderer to translate the current page from one language to
// another. If the current page id is not |page_id|, the request is ignored.
+ // |translate_script| is the script that should be injected in the page to
+ // perform the translation.
void TranslatePage(int page_id,
+ const std::string& translate_script,
const std::string& source_lang,
const std::string& target_lang);
+ // Reverts the text of current page to its original (non-translated) contents.
+ void RevertTranslation(int page_id);
+
// Informs renderer of updated content settings.
void SendContentSettings(const std::string& host,
const ContentSettings& settings);
diff --git a/chrome/browser/renderer_host/resource_message_filter.cc b/chrome/browser/renderer_host/resource_message_filter.cc
index a335889..0657913 100644
--- a/chrome/browser/renderer_host/resource_message_filter.cc
+++ b/chrome/browser/renderer_host/resource_message_filter.cc
@@ -315,7 +315,6 @@ ResourceMessageFilter::ResourceMessageFilter(
off_the_record_(profile->IsOffTheRecord()),
next_route_id_callback_(NewCallbackWithReturnValue(
render_widget_helper, &RenderWidgetHelper::GetNextRoutingID)),
- ALLOW_THIS_IN_INITIALIZER_LIST(translation_service_(this)),
ALLOW_THIS_IN_INITIALIZER_LIST(geolocation_dispatcher_host_(
new GeolocationDispatcherHost(
this->id(), new GeolocationPermissionContext(profile)))) {
@@ -538,7 +537,6 @@ bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& msg) {
IPC_MESSAGE_HANDLER(ViewHostMsg_Keygen, OnKeygen)
IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_GetExtensionMessageBundle,
OnGetExtensionMessageBundle)
- IPC_MESSAGE_HANDLER(ViewHostMsg_TranslateText, OnTranslateText)
#if defined(USE_TCMALLOC)
IPC_MESSAGE_HANDLER(ViewHostMsg_RendererTcmalloc, OnRendererTcmalloc)
#endif
@@ -1404,13 +1402,6 @@ void ResourceMessageFilter::OnKeygen(uint32 key_size_index,
*signed_public_key = keygen_handler.GenKeyAndSignChallenge();
}
-void ResourceMessageFilter::OnTranslateText(
- ViewHostMsg_TranslateTextParam param) {
- translation_service_.Translate(param.routing_id, param.page_id, param.work_id,
- param.text_chunks, param.from_language,
- param.to_language, param.secure);
-}
-
#if defined(USE_TCMALLOC)
void ResourceMessageFilter::OnRendererTcmalloc(base::ProcessId pid,
const std::string& output) {
diff --git a/chrome/browser/renderer_host/resource_message_filter.h b/chrome/browser/renderer_host/resource_message_filter.h
index 036ac7f..20be786 100644
--- a/chrome/browser/renderer_host/resource_message_filter.h
+++ b/chrome/browser/renderer_host/resource_message_filter.h
@@ -24,7 +24,6 @@
#include "build/build_config.h"
#include "chrome/browser/net/resolve_proxy_msg_helper.h"
#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
-#include "chrome/browser/renderer_host/translation_service.h"
#include "chrome/common/nacl_types.h"
#include "chrome/common/notification_registrar.h"
#include "chrome/common/render_messages.h"
@@ -324,8 +323,6 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter,
const std::string& extension_id,
const std::string& default_locale,
IPC::Message* reply_msg);
- void OnTranslateText(ViewHostMsg_TranslateTextParam param);
-
void OnEstablishGpuChannel();
void OnSynchronizeGpu(IPC::Message* reply);
@@ -412,9 +409,6 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter,
// A callback to create a routing id for the associated renderer process.
scoped_ptr<CallbackWithReturnValue<int>::Type> next_route_id_callback_;
- // Used to translate page contents from one language to another.
- TranslationService translation_service_;
-
// Used to handle geolocation-related messages.
scoped_refptr<GeolocationDispatcherHost> geolocation_dispatcher_host_;
diff --git a/chrome/browser/renderer_host/translation_service.cc b/chrome/browser/renderer_host/translation_service.cc
deleted file mode 100644
index 153352b..0000000
--- a/chrome/browser/renderer_host/translation_service.cc
+++ /dev/null
@@ -1,675 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/renderer_host/translation_service.h"
-
-#include "base/json/json_reader.h"
-#include "base/stl_util-inl.h"
-#include "chrome/browser/profile.h"
-#include "chrome/common/render_messages.h"
-#include "net/base/escape.h"
-
-#if defined(GOOGLE_CHROME_BUILD)
-#include "chrome/browser/renderer_host/translate/translate_internal.h"
-#else
-// Defining dummy URLs for unit-tests to pass.
-#define TRANSLATE_SERVER_URL "http://disabled"
-#define TRANSLATE_SERVER_SECURE_URL "https://disabled"
-#endif
-
-namespace {
-
-// The URLs we send translation requests to.
-const char kServiceURL[] = TRANSLATE_SERVER_URL;
-const char kSecureServiceURL[] = TRANSLATE_SERVER_SECURE_URL;
-
-// The different params used when sending requests to the translate server.
-const char kVersionParam[] = "v";
-const char kLangPairParam[] = "langpair";
-const char kTextParam[] = "q";
-const char kClientParam[] = "client";
-const char kFormatParam[] = "format";
-const char kSSLParam[] = "ssl";
-const char kTranslationCountParam[] = "tc";
-
-// Mapping from a locale name to a language code name.
-// Locale names not included are translated as is.
-struct LocaleToCLDLanguage {
- const char* locale_language; // Language Chrome locale is in.
- const char* cld_language; // Language the CLD reports.
-};
-LocaleToCLDLanguage kLocaleToCLDLanguages[] = {
- { "en-GB", "en" },
- { "en-US", "en" },
- { "es-419", "es" },
- { "pt-BR", "pt" },
- { "pt-PT", "pt" },
-};
-
-// The list of languages the Google translation server supports.
-// For information, here is the list of languages that Chrome can be run into
-// but that the translation server does not support:
-// am Amharic
-// bn Bengali
-// gu Gujarati
-// kn Kannada
-// ml Malayalam
-// mr Marathi
-// ta Tamil
-// te Telugu
-const char* kSupportedLanguages[] = {
- "af", // Afrikaans
- "sq", // Albanian
- "ar", // Arabic
- "be", // Belarusian
- "bg", // Bulgarian
- "ca", // Catalan
- "zh-CN", // Chinese (Simplified)
- "zh-TW", // Chinese (Traditional)
- "hr", // Croatian
- "cs", // Czech
- "da", // Danish
- "nl", // Dutch
- "en", // English
- "et", // Estonian
- "fi", // Finnish
- "fil", // Filipino
- "fr", // French
- "gl", // Galician
- "de", // German
- "el", // Greek
- "he", // Hebrew
- "hi", // Hindi
- "hu", // Hungarian
- "is", // Icelandic
- "id", // Indonesian
- "it", // Italian
- "ga", // Irish
- "ja", // Japanese
- "ko", // Korean
- "lv", // Latvian
- "lt", // Lithuanian
- "mk", // Macedonian
- "ms", // Malay
- "mt", // Maltese
- "nb", // Norwegian
- "fa", // Persian
- "pl", // Polish
- "pt", // Portuguese
- "ro", // Romanian
- "ru", // Russian
- "sr", // Serbian
- "sk", // Slovak
- "sl", // Slovenian
- "es", // Spanish
- "sw", // Swahili
- "sv", // Swedish
- "th", // Thai
- "tr", // Turkish
- "uk", // Ukrainian
- "vi", // Vietnamese
- "cy", // Welsh
- "yi", // Yiddish
-};
-
-// The maximum size in bytes after which the server will refuse the request.
-const size_t kTextRequestMaxSize = 1024 * 30;
-
-// Delay to wait for before sending a request to the translation server.
-const int kSendRequestDelay = 100;
-
-// Task used to send the current pending translation request for a renderer
-// after some time has elapsed with no new request from that renderer.
-// Note that this task is canceled when TranslationRequest is destroyed, which
-// happens when the TranslationService is going away. So it is OK to have it
-// have a pointer to the TranslationService.
-class SendTranslationRequestTask : public CancelableTask {
- public:
- SendTranslationRequestTask(TranslationService* translation_service,
- int renderer_id,
- bool secure);
- virtual void Run();
- virtual void Cancel();
-
- private:
- TranslationService* translation_service_;
- int renderer_id_;
- bool secure_;
- bool canceled_;
-
- DISALLOW_COPY_AND_ASSIGN(SendTranslationRequestTask);
-};
-
-} // namespace
-
-// static
-// The string is: '&' + kTextParam + '='.
-size_t TranslationService::text_param_length_ = 1 + arraysize(kTextParam) + 1;
-
-// static
-base::LazyInstance<std::set<std::string> >
- TranslationService::supported_languages_(base::LINKER_INITIALIZED);
-
-// Contains the information necessary to send a request to the translation
-// server. It is used to group several renderer queries, as to limit the
-// load sent to the translation server.
-struct TranslationService::TranslationRequest {
- TranslationRequest(int routing_id,
- int page_id,
- const std::string& source_lang,
- const std::string& target_lang,
- bool secure)
- : routing_id(routing_id),
- page_id(page_id),
- source_lang(source_lang),
- target_lang(target_lang),
- secure(secure),
- send_query_task(NULL) {
- renderer_request_info.reset(new RendererRequestInfoList());
- }
-
- ~TranslationRequest() {
- if (send_query_task)
- send_query_task->Cancel();
- }
-
- void Clear() {
- page_id = 0;
- source_lang.clear();
- target_lang.clear();
- query.clear();
- renderer_request_info->clear();
- if (send_query_task) {
- send_query_task->Cancel();
- send_query_task = NULL;
- }
- }
-
- int routing_id;
- int page_id;
- std::string source_lang;
- std::string target_lang;
- bool secure;
- std::string query;
- // renderer_request_info is a scoped_ptr so that we avoid copying the list
- // when the request is sent. At that point we only transfer ownership of that
- // list to renderer_request_infos_.
- scoped_ptr<RendererRequestInfoList> renderer_request_info;
- CancelableTask* send_query_task;
-};
-
-////////////////////////////////////////////////////////////////////////////////
-// SendTranslationRequestTask
-
-SendTranslationRequestTask::SendTranslationRequestTask(
- TranslationService* translation_service,
- int renderer_id,
- bool secure)
- : translation_service_(translation_service),
- renderer_id_(renderer_id),
- secure_(secure),
- canceled_(false) {
-}
-
-void SendTranslationRequestTask::Run() {
- if (canceled_)
- return;
- translation_service_->
- SendTranslationRequestForRenderer(renderer_id_, secure_);
-}
-
-void SendTranslationRequestTask::Cancel() {
- canceled_ = true;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// TranslationService, public:
-
-TranslationService::TranslationService(IPC::Message::Sender* message_sender)
- : message_sender_(message_sender),
- kCRAnchorTagStart(ASCIIToUTF16("<a _CR_TR_ id='")),
- kAnchorTagStart(ASCIIToUTF16("<a ")),
- kClosingAnchorTag(ASCIIToUTF16("</a>")),
- kQuote(ASCIIToUTF16("'")),
- kGreaterThan(ASCIIToUTF16(">")),
- kLessThan(ASCIIToUTF16("<")),
- kQuoteGreaterThan(ASCIIToUTF16("'>")) {
-}
-
-TranslationService::~TranslationService() {
- STLDeleteContainerPairSecondPointers(pending_translation_requests_.begin(),
- pending_translation_requests_.end());
- STLDeleteContainerPairSecondPointers(
- pending_secure_translation_requests_.begin(),
- pending_secure_translation_requests_.end());
- STLDeleteContainerPairPointers(renderer_request_infos_.begin(),
- renderer_request_infos_.end());
-}
-
-void TranslationService::Translate(int routing_id,
- int page_id,
- int work_id,
- const TextChunks& text_chunks,
- const std::string& source_lang,
- const std::string& target_lang,
- bool secure) {
- TranslationRequestMap& request_map =
- secure ? pending_secure_translation_requests_ :
- pending_translation_requests_;
- TranslationRequestMap::iterator iter = request_map.find(routing_id);
- TranslationRequest* translation_request = NULL;
-
- string16 utf16_text = MergeTextChunks(text_chunks);
- std::string text = EscapeUrlEncodedData(UTF16ToUTF8(utf16_text));
-
- if (iter != request_map.end()) {
- translation_request = iter->second;
- if (page_id != translation_request->page_id) {
- // We are getting a request from a renderer for a different page id.
- // This indicates we navigated away from the page that was being
- // translated. We should drop the current pending translations.
- translation_request->Clear();
- // Set the new states.
- translation_request->page_id = page_id;
- translation_request->source_lang = source_lang;
- translation_request->target_lang = target_lang;
- } else {
- DCHECK(translation_request->source_lang == source_lang);
- DCHECK(translation_request->target_lang == target_lang);
- // Cancel the pending tasks to send the query. We'll be posting a new one
- // after we updated the request.
- translation_request->send_query_task->Cancel();
- translation_request->send_query_task = NULL;
- if (translation_request->query.size() + text.size() +
- text_param_length_ >= kTextRequestMaxSize) {
- // The request would be too big with that last addition of text, send
- // the request now. (Single requests too big to be sent in 1 translation
- // request are dealt with below.)
- if (!translation_request->query.empty()) { // Single requests
- SendRequestToTranslationServer(translation_request);
- // The translation request has been deleted.
- translation_request = NULL;
- iter = request_map.end();
- }
- }
- }
- }
-
- if (translation_request == NULL) {
- translation_request = new TranslationRequest(routing_id, page_id,
- source_lang, target_lang,
- secure);
- request_map[routing_id] = translation_request;
- }
-
- AddTextToRequestString(&(translation_request->query), text,
- source_lang, target_lang, secure);
-
- translation_request->renderer_request_info->push_back(
- RendererRequestInfo(routing_id, work_id));
-
- if (translation_request->query.size() > kTextRequestMaxSize) {
- DCHECK(translation_request->renderer_request_info->size() == 1U);
- // This one request is too large for the translation service.
- // TODO(jcampan): we should support such requests by splitting them.
- iter = request_map.find(routing_id);
- DCHECK(iter != request_map.end());
- request_map.erase(iter);
- message_sender_->Send(
- new ViewMsg_TranslateTextReponse(routing_id, work_id, 1, TextChunks()));
- delete translation_request;
- return;
- }
-
- // Now post the new task that will ensure we'll send the request to the
- // translation server if no renderer requests are received within a
- // reasonable amount of time.
- DCHECK(!translation_request->send_query_task);
- translation_request->send_query_task =
- new SendTranslationRequestTask(this, routing_id, secure);
- MessageLoop::current()->PostDelayedTask(FROM_HERE,
- translation_request->send_query_task, GetSendRequestDelay());
-}
-
-void TranslationService::SendTranslationRequestForRenderer(int renderer_id,
- bool secure) {
- TranslationRequestMap& request_map =
- secure ? pending_secure_translation_requests_ :
- pending_translation_requests_;
- TranslationRequestMap::const_iterator iter = request_map.find(renderer_id);
- DCHECK(iter != request_map.end());
- SendRequestToTranslationServer(iter->second);
-}
-
-void TranslationService::OnURLFetchComplete(const URLFetcher* source,
- const GURL& url,
- const URLRequestStatus& status,
- int response_code,
- const ResponseCookies& cookies,
- const std::string& data) {
- if (!status.is_success() || response_code != 200 || data.empty()) {
- TranslationFailed(source);
- return;
- }
-
- // If the response is a simple string, put it in an array. (The JSONReader
- // requires an array or map at the root.)
- std::string wrapped_data;
- if (data.size() > 1U && data[0] == '"') {
- wrapped_data.append("[");
- wrapped_data.append(data);
- wrapped_data.append("]");
- }
- scoped_ptr<Value> value(base::JSONReader::Read(
- wrapped_data.empty() ? data : wrapped_data, true));
- if (!value.get()) {
- NOTREACHED() << "Translation server returned invalid JSON response.";
- TranslationFailed(source);
- return;
- }
-
- // If the request was for a single string, the response is the translated
- // string.
- TextChunksList translated_chunks_list;
- if (value->IsType(Value::TYPE_STRING)) {
- string16 translated_text;
- if (!value->GetAsUTF16(&translated_text)) {
- NOTREACHED();
- TranslationFailed(source);
- return;
- }
- TextChunks translated_text_chunks;
- translated_text_chunks.push_back(translated_text);
- translated_chunks_list.push_back(translated_text_chunks);
- } else {
- if (!value->IsType(Value::TYPE_LIST)) {
- NOTREACHED() << "Translation server returned unexpected JSON response "
- " (not a list).";
- TranslationFailed(source);
- return;
- }
- ListValue* translated_text_list = static_cast<ListValue*>(value.get());
- for (size_t i = 0; i < translated_text_list->GetSize(); ++i) {
- string16 translated_text;
- if (!translated_text_list->GetStringAsUTF16(i, &translated_text)) {
- NOTREACHED() << "Translation server returned unexpected JSON response "
- " (unexpected type in list).";
- TranslationFailed(source);
- return;
- }
- translated_text = UnescapeForHTML(translated_text);
- TranslationService::TextChunks translated_text_chunks;
- TranslationService::SplitIntoTextChunks(translated_text,
- &translated_text_chunks);
- translated_chunks_list.push_back(translated_text_chunks);
- }
- }
-
- // We have successfully extracted all the translated text chunks, send them to
- // the renderer.
- SendResponseToRenderer(source, 0, translated_chunks_list);
-}
-
-// static
-bool TranslationService::IsTranslationEnabled() {
- return GURL(kServiceURL).host() != "disabled";
-}
-
-// static
-void TranslationService::GetSupportedLanguages(
- std::vector<std::string>* languages) {
- DCHECK(languages && languages->empty());
- for (size_t i = 0; i < arraysize(kSupportedLanguages); ++i)
- languages->push_back(kSupportedLanguages[i]);
-}
-
-// static
-std::string TranslationService::GetLanguageCode(
- const std::string& chrome_locale) {
- for (size_t i = 0; i < arraysize(kLocaleToCLDLanguages); ++i) {
- if (chrome_locale == kLocaleToCLDLanguages[i].locale_language)
- return kLocaleToCLDLanguages[i].cld_language;
- }
- return chrome_locale;
-}
-
-// static
-bool TranslationService::IsSupportedLanguage(const std::string& page_language) {
- if (supported_languages_.Pointer()->empty()) {
- for (size_t i = 0; i < arraysize(kSupportedLanguages); ++i)
- supported_languages_.Pointer()->insert(kSupportedLanguages[i]);
- }
- return supported_languages_.Pointer()->find(page_language) !=
- supported_languages_.Pointer()->end();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// TranslationService, protected:
-
-int TranslationService::GetSendRequestDelay() const {
- return kSendRequestDelay;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// TranslationService, private:
-
-void TranslationService::SendRequestToTranslationServer(
- TranslationRequest* request) {
- DCHECK(!request->query.empty());
- GURL url(request->secure ? kSecureServiceURL : kServiceURL);
- URLFetcher* url_fetcher =
- URLFetcher::Create(request->routing_id /* used in tests */,
- url, URLFetcher::POST, this);
- url_fetcher->set_upload_data("application/x-www-form-urlencoded",
- request->query);
- url_fetcher->set_request_context(Profile::GetDefaultRequestContext());
- url_fetcher->Start();
-
- // renderer_request_infos_ will now own the RendererRequestInfoList.
- renderer_request_infos_[url_fetcher] =
- request->renderer_request_info.release();
-
- // Remove the request from the translation request map.
- TranslationRequestMap& translation_request_map =
- request->secure ? pending_secure_translation_requests_ :
- pending_translation_requests_;
- TranslationRequestMap::iterator iter =
- translation_request_map.find(request->routing_id);
- DCHECK(iter != translation_request_map.end());
- translation_request_map.erase(iter);
- delete request;
-}
-
-void TranslationService::SendResponseToRenderer(
- const URLFetcher* const_url_fetcher, int error_code,
- const TextChunksList& text_chunks_list) {
- scoped_ptr<const URLFetcher> url_fetcher(const_url_fetcher);
- RendererRequestInfoMap::iterator iter =
- renderer_request_infos_.find(url_fetcher.get());
- DCHECK(iter != renderer_request_infos_.end());
- scoped_ptr<RendererRequestInfoList> request_info_list(iter->second);
- DCHECK(error_code != 0 ||
- request_info_list->size() == text_chunks_list.size());
- for (size_t i = 0; i < request_info_list->size(); ++i) {
- RendererRequestInfo& request_info = request_info_list->at(i);
- message_sender_->Send(
- new ViewMsg_TranslateTextReponse(request_info.routing_id,
- request_info.work_id,
- error_code,
- error_code ? TextChunks() :
- text_chunks_list[i]));
- }
- renderer_request_infos_.erase(iter);
-}
-
-void TranslationService::TranslationFailed(const URLFetcher* url_fetcher) {
- SendResponseToRenderer(url_fetcher, 1, TranslationService::TextChunksList());
-}
-
-string16 TranslationService::MergeTextChunks(const TextChunks& text_chunks) {
- // If there is only 1 chunk, we don't need an anchor tag as there is no order
- // to preserve.
- if (text_chunks.size() == 1U)
- return text_chunks[0];
-
- string16 str;
- for (size_t i = 0; i < text_chunks.size(); ++i) {
- str.append(kCRAnchorTagStart);
- str.append(IntToString16(i));
- str.append(kQuoteGreaterThan);
- str.append(text_chunks[i]);
- str.append(kClosingAnchorTag);
- }
- return str;
-}
-
-bool TranslationService::FindOpenTagIndex(const string16& text,
- size_t start_index,
- size_t* tag_start_index,
- size_t* tag_end_index,
- int* id) {
- DCHECK(tag_start_index && tag_end_index && id);
- size_t text_length = text.length();
- if (start_index >= text_length)
- return false;
-
- *tag_start_index = text.find(kCRAnchorTagStart, start_index);
- if (*tag_start_index == std::string::npos)
- return false;
-
- size_t quote_index = *tag_start_index + kCRAnchorTagStart.length();
- size_t close_quote_index = text.find(kQuote, quote_index);
- if (close_quote_index == std::string::npos) {
- NOTREACHED();
- return false; // Not a valid anchor tag.
- }
-
- string16 id_str = text.substr(quote_index, close_quote_index - quote_index);
- // Get the id.
- if (!StringToInt(id_str, id)) {
- NOTREACHED();
- return false; // Not a valid id, give up.
- }
-
- *tag_end_index = text.find(kGreaterThan, close_quote_index);
- if (*tag_end_index == std::string::npos || *tag_end_index >= text_length)
- return false;
- return true;
-}
-
-void TranslationService::SplitIntoTextChunks(const string16& translated_text,
- TextChunks* text_chunks) {
- int id = -1;
- size_t tag_start_index = 0;
- size_t tag_end_index = 0;
- if (!FindOpenTagIndex(translated_text, 0, &tag_start_index, &tag_end_index,
- &id)) {
- // No magic anchor tag, it was a single chunk.
- text_chunks->push_back(translated_text);
- return;
- }
-
- // The server might send us some HTML with duplicated and unbalanced tags.
- // We separate from one tag begining to the next, and merge tags with
- // duplicate IDs.
- std::set<int> parsed_tags;
- string16 chunk;
- while (tag_start_index != std::string::npos) {
- int next_id = -1;
- size_t previous_tag_end_index = tag_end_index;
- if (!FindOpenTagIndex(translated_text, tag_end_index,
- &tag_start_index, &tag_end_index, &next_id)) {
- // Last tag. Just report as one chunk.
- chunk = translated_text.substr(previous_tag_end_index + 1);
- tag_start_index = std::string::npos; // So we break on next iteration.
- } else {
- // Extract the text for this tag.
- DCHECK(tag_start_index > previous_tag_end_index);
- chunk =
- translated_text.substr(previous_tag_end_index + 1,
- tag_start_index - previous_tag_end_index - 1);
- }
- chunk = RemoveTag(chunk);
- // The translation server leaves some ampersand character in the
- // translation.
- chunk = UnescapeForHTML(chunk);
- if (parsed_tags.count(id) > 0) {
- // We have already seen this tag, add it to the previous text-chunk.
- text_chunks->back().append(chunk);
- } else {
- text_chunks->push_back(chunk);
- parsed_tags.insert(id);
- }
- id = next_id;
- }
-}
-
-string16 TranslationService::RemoveTag(const string16& text) {
- // Remove any anchor tags, knowing they could be extra/unbalanced tags.
- string16 result;
- size_t start_index = text.find(kAnchorTagStart);
- if (start_index == std::string::npos) {
- result = text;
- } else {
- bool first_iter = true;
- while (true) {
- size_t stop_index = text.find(kGreaterThan, start_index);
- size_t next_tag_index = text.find(kLessThan, start_index + 1);
- // Ignore unclosed <a tag. (Ignore subsequent closing tags, they'll be
- // removed in the next loop.)
- if (stop_index == std::string::npos ||
- (next_tag_index != std::string::npos &&
- stop_index > next_tag_index)) {
- result.append(text.substr(start_index));
- break;
- }
- if (start_index > 0 && first_iter)
- result = text.substr(0, start_index);
- start_index = text.find(kAnchorTagStart, start_index + 1);
- if (start_index == std::string::npos) {
- result += text.substr(stop_index + 1);
- break;
- }
- result += text.substr(stop_index + 1, start_index - stop_index - 1);
- first_iter = false;
- }
- }
-
- // Now remove </a> tags.
- ReplaceSubstringsAfterOffset(&result, 0, kClosingAnchorTag, EmptyString16());
- return result;
-}
-
-// static
-void TranslationService::AddTextToRequestString(std::string* request,
- const std::string& text,
- const std::string& source_lang,
- const std::string& target_lang,
- bool secure) {
- if (request->empty()) {
- // First request, add required parameters.
- request->append(kVersionParam);
- request->append("=1.0&");
- request->append(kClientParam);
- request->append("=cr&"); // cr = Chrome.
- request->append(kFormatParam);
- request->append("=html&");
- request->append(kLangPairParam);
- request->append("=");
- request->append(source_lang);
- request->append("%7C"); // | URL encoded.
- request->append(target_lang);
- if (secure) {
- request->append("&");
- request->append(kSSLParam);
- request->append("=1");
- }
- }
- // IMPORTANT NOTE: if you make any change below, make sure to reflect them in
- // text_param_length_ in TranslationService constructor.
- request->append("&");
- request->append(kTextParam);
- request->append("=");
- request->append(text);
-}
diff --git a/chrome/browser/renderer_host/translation_service.h b/chrome/browser/renderer_host/translation_service.h
deleted file mode 100644
index 666265b..0000000
--- a/chrome/browser/renderer_host/translation_service.h
+++ /dev/null
@@ -1,195 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_RENDERER_HOST_TRANSLATION_SERVICE_H_
-#define CHROME_BROWSER_RENDERER_HOST_TRANSLATION_SERVICE_H_
-
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-
-#include "base/lazy_instance.h"
-#include "base/scoped_ptr.h"
-#include "base/string16.h"
-#include "chrome/browser/net/url_fetcher.h"
-#include "ipc/ipc_message.h"
-#include "testing/gtest/include/gtest/gtest_prod.h"
-
-class DictionaryValue;
-class TranslationServiceTest;
-class TranslateURLFetcherDelegate;
-class URLFetcher;
-
-// The TranslationService class is used to translate text.
-// There is one TranslationService per renderer process.
-// It receives requests to translate text from the different render views of the
-// render process, provided in lists (text chunks), where the words should be
-// translated without changing the chunks order.
-// It groups multiple such requests and sends them for translation to the Google
-// translation server. When it receives the response, it dispatches it to the
-// appropriate render view.
-
-class TranslationService : public URLFetcher::Delegate {
- public:
- explicit TranslationService(IPC::Message::Sender* message_sender);
- virtual ~TranslationService();
-
- // Sends the specified text for translation, from |source_language| to
- // |target_language|. If |secure| is true, a secure connection is used when
- // sending the text to the external translation server.
- // When the translation results have been received, it sends a
- // ViewMsg_TranslateTextReponse message on the renderer at |routing_id|.
- void Translate(int routing_id,
- int page_id,
- int work_id,
- const std::vector<string16>& text_chunks,
- const std::string& source_language,
- const std::string& target_language,
- bool secure);
-
- // Sends the pending translation request for the specified renderer to the
- // translation server.
- void SendTranslationRequestForRenderer(int renderer_id, bool secure);
-
- // URLFetcher::Delegate implementation.
- virtual void OnURLFetchComplete(const URLFetcher* source,
- const GURL& url,
- const URLRequestStatus& status,
- int response_code,
- const ResponseCookies& cookies,
- const std::string& data);
-
- // Returns true if the TranslationService is enabled.
- static bool IsTranslationEnabled();
-
- // Fills |languages| with the list of languages that the translate server can
- // translate to and from.
- static void GetSupportedLanguages(std::vector<std::string>* languages);
-
- // Returns the language code that can be used with the Translate method for a
- // specified |chrome_locale|.
- static std::string GetLanguageCode(const std::string& chrome_locale);
-
- // Returns true if |page_language| is supported by the translation server.
- static bool IsSupportedLanguage(const std::string& page_language);
-
- protected:
- // The amount of time in ms after which a pending request is sent if no other
- // translation request has been received.
- // Overriden in tests.
- virtual int GetSendRequestDelay() const;
-
- private:
- friend class TranslationServiceTest;
- friend class TranslateURLFetcherDelegate;
- FRIEND_TEST(TranslationServiceTest, MergeTestChunks);
- FRIEND_TEST(TranslationServiceTest, SplitIntoTextChunks);
- FRIEND_TEST(TranslationServiceTest, RemoveTag);
-
- struct TranslationRequest;
-
- // The information necessary to return the translated text to the renderer.
- struct RendererRequestInfo {
- RendererRequestInfo() : routing_id(0), work_id(0) {}
- RendererRequestInfo(int routing_id, int work_id)
- : routing_id(routing_id),
- work_id(work_id) {
- }
- int routing_id;
- int work_id;
- };
-
- typedef std::vector<RendererRequestInfo> RendererRequestInfoList;
-
- typedef std::vector<string16> TextChunks;
- typedef std::vector<TextChunks> TextChunksList;
- // Maps from a RenderView routing id to the pending request for the
- // translation server.
- typedef std::map<int, TranslationRequest*> TranslationRequestMap;
-
- typedef std::map<const URLFetcher*, RendererRequestInfoList*>
- RendererRequestInfoMap;
-
- // Sends the passed request to the translations server.
- // Warning: the request is deleted when this call returns.
- void SendRequestToTranslationServer(TranslationRequest* request);
-
- // Called by the URLFetcherDelegate when the translation associated with
- // |url_fetcher| has been performed. Sends the appropriate message back to
- // the renderer and deletes the URLFetcher.
- void SendResponseToRenderer(const URLFetcher* url_fetcher,
- int error_code,
- const TextChunksList& text_chunks_list);
-
- // Notifies the renderer that we failed to translate the request associated
- // with |url_fetcher|.
- void TranslationFailed(const URLFetcher* source);
-
- // Merges all text chunks to be translated into a single string that can be
- // sent to the translate server, surrounding each chunk with an anchor tag
- // to preserve chunk order in the translated version.
- string16 MergeTextChunks(const TextChunks& text_chunks);
-
- // Splits the translated text into its original text chunks, removing the
- // anchor tags wrapper that were added to preserve order.
- void SplitIntoTextChunks(const string16& translated_text,
- TextChunks* text_chunks);
-
- // Removes the HTML anchor tag surrounding |text| and returns the resulting
- // string.
- string16 RemoveTag(const string16& text);
-
- // Find the next anchor tag in |text| starting at |start_index|.
- // Sets |id| (which must be non NULL) to the id property of the tag (which is
- // expected to be an int). Sets |tag_start_index| and |tag_end_index| to the
- // index of the beginning/end of the next tag.
- // Returns true if a tag was found and it is not at the end of the string,
- // false otherwise in which case |id|, |tag_start_index| and |tag_end_index|
- // are not set.
- bool FindOpenTagIndex(const string16& text,
- size_t start_index,
- size_t* tag_start_index,
- size_t* tag_end_index,
- int* id);
-
- // Adds |text| to the string request in/out param |request|. If |request| is
- // empty, then the source, target language as well as the secure parameters
- // are also added.
- static void AddTextToRequestString(std::string* request,
- const std::string& text,
- const std::string& source_language,
- const std::string& target_language,
- bool secure);
-
- // The channel used to communicate with the renderer.
- IPC::Message::Sender* message_sender_;
-
- // Map used to retrieve the context of requests when the URLFetcher notifies
- // that it got a response.
- RendererRequestInfoMap renderer_request_infos_;
-
- TranslationRequestMap pending_translation_requests_;
- TranslationRequestMap pending_secure_translation_requests_;
-
- // Strings used for parsing.
- const string16 kCRAnchorTagStart;
- const string16 kAnchorTagStart;
- const string16 kClosingAnchorTag;
- const string16 kQuote;
- const string16 kGreaterThan;
- const string16 kLessThan;
- const string16 kQuoteGreaterThan;
-
- // The size taken by the parameters and separators needed when adding text to
- // a request string.
- static size_t text_param_length_;
-
- // The language supported by the translation server.
- static base::LazyInstance<std::set<std::string> > supported_languages_;
-
- DISALLOW_COPY_AND_ASSIGN(TranslationService);
-};
-
-#endif // CHROME_BROWSER_RENDERER_HOST_TRANSLATION_SERVICE_H_
diff --git a/chrome/browser/renderer_host/translation_service_unittest.cc b/chrome/browser/renderer_host/translation_service_unittest.cc
deleted file mode 100644
index f973502..0000000
--- a/chrome/browser/renderer_host/translation_service_unittest.cc
+++ /dev/null
@@ -1,505 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/json/json_writer.h"
-#include "base/stl_util-inl.h"
-#include "base/string_tokenizer.h"
-#include "base/string_util.h"
-#include "chrome/browser/net/test_url_fetcher_factory.h"
-#include "chrome/browser/renderer_host/translation_service.h"
-#include "chrome/common/render_messages.h"
-#include "ipc/ipc_message.h"
-#include "net/base/escape.h"
-#include "net/url_request/url_request_status.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-typedef std::vector<string16> TextChunks;
-typedef std::vector<TextChunks> TextChunksList;
-
-class TestMessageSender : public IPC::Message::Sender {
- public:
- virtual ~TestMessageSender() {
- ClearMessages();
- }
-
- virtual bool Send(IPC::Message* msg) {
- messages_.push_back(msg);
- return true;
- }
-
- size_t message_count() const { return messages_.size(); }
-
- void GetMessageParameters(size_t message_index, int* routing_id,
- int* work_id, int* error_id,
- std::vector<string16>* text_chunks) {
- ASSERT_LT(message_index, messages_.size());
- *routing_id = messages_.at(message_index)->routing_id();
- ViewMsg_TranslateTextReponse::Read(messages_.at(message_index),
- work_id, error_id, text_chunks);
- }
-
- void ClearMessages() {
- STLDeleteElements(&messages_);
- messages_.clear();
- }
-
- std::vector<IPC::Message*> messages_;
- MessageLoop message_loop_;
-};
-
-class TestTranslationService : public TranslationService {
- public:
- explicit TestTranslationService(IPC::Message::Sender* message_sender)
- : TranslationService(message_sender) {}
-
- virtual int GetSendRequestDelay() const {
- return 0;
- }
-};
-
-class TranslationServiceTest : public testing::Test {
- public:
- TranslationServiceTest() : translation_service_(&message_sender_) {}
-
- virtual void SetUp() {
- URLFetcher::set_factory(&url_fetcher_factory_);
- }
-
- virtual void TearDown() {
- URLFetcher::set_factory(NULL);
- }
-
- protected:
- TestURLFetcherFactory url_fetcher_factory_;
- TestMessageSender message_sender_;
- TestTranslationService translation_service_;
-};
-
-static void SimulateErrorResponse(TestURLFetcher* url_fetcher,
- int response_code) {
- url_fetcher->delegate()->OnURLFetchComplete(url_fetcher,
- url_fetcher->original_url(),
- URLRequestStatus(),
- response_code,
- ResponseCookies(),
- std::string());
-}
-
-// Merges the strings in |text_chunks| into the string that the translation
-// server received.
-static string16 BuildResponseString(const TextChunks& text_chunks) {
- string16 text;
- for (size_t i = 0; i < text_chunks.size(); ++i) {
- text.append(ASCIIToUTF16("<a _CR_TR_ id='"));
- text.append(IntToString16(i));
- text.append(ASCIIToUTF16("'>"));
- text.append(text_chunks.at(i));
- text.append(ASCIIToUTF16("</a>"));
- }
- return text;
-}
-
-static void SimulateSuccessfulResponse(TestURLFetcher* url_fetcher,
- const TextChunksList& text_chunks_list) {
- scoped_ptr<ListValue> list(new ListValue());
- for (TextChunksList::const_iterator iter = text_chunks_list.begin();
- iter != text_chunks_list.end(); ++iter) {
- list->Append(Value::CreateStringValueFromUTF16(BuildResponseString(*iter)));
- }
-
- std::string response;
- base::JSONWriter::Write(list.get(), false, &response);
- url_fetcher->delegate()->OnURLFetchComplete(url_fetcher,
- url_fetcher->original_url(),
- URLRequestStatus(),
- 200,
- ResponseCookies(),
- response);
-}
-
-static void SimulateSimpleSuccessfulResponse(TestURLFetcher* url_fetcher,
- const char* const* c_text_chunks,
- size_t text_chunks_length) {
- TextChunks text_chunks;
- for (size_t i = 0; i < text_chunks_length; i++)
- text_chunks.push_back(ASCIIToUTF16(c_text_chunks[i]));
-
- string16 text = BuildResponseString(text_chunks);
-
- std::string response;
- scoped_ptr<Value> json_text(Value::CreateStringValueFromUTF16(text));
- base::JSONWriter::Write(json_text.get(), false, &response);
- url_fetcher->delegate()->OnURLFetchComplete(url_fetcher,
- url_fetcher->original_url(),
- URLRequestStatus(),
- 200,
- ResponseCookies(),
- response);
-}
-
-// Parses the upload data from |url_fetcher| and puts the value for the q
-// parameters in |text_chunks|.
-static void ExtractQueryStringsFromUploadData(TestURLFetcher* url_fetcher,
- TextChunks* text_chunks) {
- std::string upload_data = url_fetcher->upload_data();
-
- CStringTokenizer str_tok(upload_data.c_str(), upload_data.c_str() +
- upload_data.length(), "&");
- while (str_tok.GetNext()) {
- std::string tok = str_tok.token();
- if (tok.size() > 1U && tok.at(0) == 'q' && tok.at(1) == '=')
- text_chunks->push_back(UnescapeForHTML(ASCIIToUTF16(tok.substr(2))));
- }
-}
-
-TEST_F(TranslationServiceTest, MergeTestChunks) {
- TranslationService translation_service(NULL);
- std::vector<string16> input;
- input.push_back(ASCIIToUTF16("Hello"));
- string16 result = translation_service.MergeTextChunks(input);
- EXPECT_EQ(ASCIIToUTF16("Hello"), result);
- input.push_back(ASCIIToUTF16(" my name"));
- input.push_back(ASCIIToUTF16(" is"));
- input.push_back(ASCIIToUTF16(" Jay."));
- result = translation_service.MergeTextChunks(input);
- EXPECT_EQ(ASCIIToUTF16("<a _CR_TR_ id='0'>Hello</a>"
- "<a _CR_TR_ id='1'> my name</a>"
- "<a _CR_TR_ id='2'> is</a>"
- "<a _CR_TR_ id='3'> Jay.</a>"),
- result);
-}
-
-TEST_F(TranslationServiceTest, RemoveTag) {
- const char* kInputs[] = {
- "", "Hello", "<a ></a>", " <a href='http://www.google.com'> Link </a>",
- "<a >Link", "<a link</a>", "<a id=1><a id=1>broken</a></a>",
- "<a id=1>broken</a></a> bad bad</a>",
- };
- const char* kExpected[] = {
- "", "Hello", "", " Link ", "Link", "<a link", "broken", "broken bad bad"
- };
-
- TranslationService translation_service(NULL);
- ASSERT_EQ(arraysize(kInputs), arraysize(kExpected));
- for (size_t i = 0; i < arraysize(kInputs); ++i) {
- SCOPED_TRACE(::testing::Message::Message() << "Iteration " << i);
- string16 input = ASCIIToUTF16(kInputs[i]);
- string16 output = translation_service.RemoveTag(input);
- EXPECT_EQ(ASCIIToUTF16(kExpected[i]), output);
- }
-}
-
-// Tests that we deal correctly with the various results the translation server
-// can return, including the buggy ones.
-TEST_F(TranslationServiceTest, SplitIntoTextChunks) {
- TranslationService translation_service(NULL);
-
- // Simple case.
- std::vector<string16> text_chunks;
- translation_service.SplitIntoTextChunks(ASCIIToUTF16("Hello"), &text_chunks);
- ASSERT_EQ(1U, text_chunks.size());
- EXPECT_EQ(ASCIIToUTF16("Hello"), text_chunks[0]);
-
- text_chunks.clear();
-
- // Multiple chunks case, correct syntax.
- translation_service.SplitIntoTextChunks(
- ASCIIToUTF16("<a _CR_TR_ id='0'>Bonjour</a>"
- "<a _CR_TR_ id='1'> mon nom</a>"
- "<a _CR_TR_ id='2'> est</a>"
- "<a _CR_TR_ id='3'> Jay.</a>"), &text_chunks);
- ASSERT_EQ(4U, text_chunks.size());
- EXPECT_EQ(ASCIIToUTF16("Bonjour"), text_chunks[0]);
- EXPECT_EQ(ASCIIToUTF16(" mon nom"), text_chunks[1]);
- EXPECT_EQ(ASCIIToUTF16(" est"), text_chunks[2]);
- EXPECT_EQ(ASCIIToUTF16(" Jay."), text_chunks[3]);
- text_chunks.clear();
-
- // Multiple chunks case, duplicate and unbalanced tags.
- // For info, original input:
- // <a _CR_TRANSLATE_ id='0'> Experience </a><a _CR_TRANSLATE_ id='1'>Nexus One
- // </a><a _CR_TRANSLATE_ id='2'>, the new Android phone from Google</a>
- translation_service.SplitIntoTextChunks(
- ASCIIToUTF16("<a _CR_TR_ id='0'>Experience</a> <a _CR_TR_ id='1'>Nexus"
- "<a _CR_TR_ id='2'> One,</a></a> <a _CR_TR_ id='2'>the new "
- "Android Phone</a>"), &text_chunks);
- ASSERT_EQ(3U, text_chunks.size());
- EXPECT_EQ(ASCIIToUTF16("Experience "), text_chunks[0]);
- EXPECT_EQ(ASCIIToUTF16("Nexus"), text_chunks[1]);
- EXPECT_EQ(ASCIIToUTF16(" One, the new Android Phone"), text_chunks[2]);
- text_chunks.clear();
-
- // Other incorrect case:
- // Original input:
- // <a _CR_TR_ id='0'>Benzinpreis-</a><a _CR_TR_ id='1'>vergleich</a>
- translation_service.SplitIntoTextChunks(
- ASCIIToUTF16("<a _CR_TR_ id='0'>Gasoline <a _CR_TR_ id='1'>"
- "price-comparison</a></a>"), &text_chunks);
- ASSERT_EQ(2U, text_chunks.size());
- EXPECT_EQ(ASCIIToUTF16("Gasoline "), text_chunks[0]);
- EXPECT_EQ(ASCIIToUTF16("price-comparison"), text_chunks[1]);
- text_chunks.clear();
-
- // Other incorrect case:
- // Original input:
- // <a _CR_TR_ id='0'>Bußgeld-</a><a _CR_TR_ id='1'>rechner</a>
- translation_service.SplitIntoTextChunks(
- ASCIIToUTF16("<a _CR_TR_ id='1'><a _CR_TR_ id='0'>Fine-computer</a>"
- "</a>"), &text_chunks);
- ASSERT_EQ(2U, text_chunks.size());
- EXPECT_EQ(ASCIIToUTF16(""), text_chunks[0]);
- EXPECT_EQ(ASCIIToUTF16("Fine-computer"), text_chunks[1]);
- text_chunks.clear();
-
- translation_service.SplitIntoTextChunks(
- ASCIIToUTF16("<a _CR_TR_ id='0'>The mountain live .</a> "
- "<a _CR_TR_ id='1'>By Philipp Wittrock</a> <a _CR_TR_ id='0'>are</a> "
- "<a _CR_TR_ id='2'>more ...</a> <a _CR_TR_ id='3'>Video</a> "
- "<a _CR_TR_ id='4'>Forum</a>"), &text_chunks);
- ASSERT_EQ(5U, text_chunks.size());
- EXPECT_EQ(ASCIIToUTF16("The mountain live . "), text_chunks[0]);
- EXPECT_EQ(ASCIIToUTF16("By Philipp Wittrock are "), text_chunks[1]);
- EXPECT_EQ(ASCIIToUTF16("more ... "), text_chunks[2]);
- EXPECT_EQ(ASCIIToUTF16("Video "), text_chunks[3]);
- EXPECT_EQ(ASCIIToUTF16("Forum"), text_chunks[4]);
- text_chunks.clear();
-
- // Make sure we support ending with a start tag.
- translation_service.SplitIntoTextChunks(
- ASCIIToUTF16("<a _CR_TR_ id='0'>Hello</a><a _CR_TR_ id='1'>"),
- &text_chunks);
- ASSERT_EQ(2U, text_chunks.size());
- EXPECT_EQ(ASCIIToUTF16("Hello"), text_chunks[0]);
- EXPECT_EQ(EmptyString16(), text_chunks[1]);
-}
-
-// Tests that a successful translate works as expected.
-TEST_F(TranslationServiceTest, SimpleSuccessfulTranslation) {
- const char* const kEnglishTextChunks[] = {
- "An atom is talking to another atom:",
- "- I think I lost an electron.",
- "- Are you sure?",
- "- I am positive."
- };
-
- const char* const kFrenchTextChunks[] = {
- "Un atome parle a un autre atome:",
- "- Je crois que j'ai perdu un electron.",
- "- T'es sur?",
- "- Je suis positif." // Note that the joke translates poorly in French.
- };
-
- // Translate some text unsecurely.
- std::vector<string16> text_chunks;
- for (size_t i = 0; i < arraysize(kEnglishTextChunks); ++i)
- text_chunks.push_back(ASCIIToUTF16(kEnglishTextChunks[i]));
- translation_service_.Translate(0, 0, 0, text_chunks, "en", "fr", false);
-
- TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(0);
- // The message was too short to triger the sending of a request to the
- // translation request. A task has been pushed for that.
- EXPECT_TRUE(url_fetcher == NULL);
- MessageLoop::current()->RunAllPending();
-
- // Now the task has been run, the message should have been sent.
- url_fetcher = url_fetcher_factory_.GetFetcherByID(0);
- ASSERT_TRUE(url_fetcher != NULL);
- // Check the URL is HTTP.
- EXPECT_FALSE(url_fetcher->original_url().SchemeIsSecure());
-
- // Let's simulate the JSON response from the server.
- SimulateSimpleSuccessfulResponse(url_fetcher, &(kFrenchTextChunks[0]),
- arraysize(kFrenchTextChunks));
-
- // This should have triggered a ViewMsg_TranslateTextReponse message.
- ASSERT_EQ(1U, message_sender_.message_count());
-
- // Test the message has the right translation.
- int routing_id = 0;
- int work_id = 0;
- int error_id = 0;
- std::vector<string16> translated_text_chunks;
- message_sender_.GetMessageParameters(0, &routing_id, &work_id, &error_id,
- &translated_text_chunks);
- EXPECT_EQ(0, routing_id);
- EXPECT_EQ(0, error_id);
- ASSERT_EQ(arraysize(kFrenchTextChunks), translated_text_chunks.size());
- for (size_t i = 0; i < arraysize(kFrenchTextChunks); ++i)
- EXPECT_EQ(ASCIIToUTF16(kFrenchTextChunks[i]), translated_text_chunks[i]);
-}
-
-// Tests that on failure we send the expected error message.
-TEST_F(TranslationServiceTest, FailedTranslation) {
- std::vector<string16> text_chunks;
- text_chunks.push_back(ASCIIToUTF16("Hello"));
- translation_service_.Translate(0, 0, 0, text_chunks, "en", "fr", false);
-
- // Run the task that creates the URLFetcher and sends the request.
- MessageLoop::current()->RunAllPending();
-
- TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(0);
- ASSERT_TRUE(url_fetcher != NULL);
- SimulateErrorResponse(url_fetcher, 500);
-
- // This should have triggered a ViewMsg_TranslateTextReponse message.
- ASSERT_EQ(1U, message_sender_.message_count());
-
- // Test the message has some error.
- int routing_id = 0;
- int work_id = 0;
- int error_id = 0;
- std::vector<string16> translated_text_chunks;
- message_sender_.GetMessageParameters(0, &routing_id, &work_id, &error_id,
- &translated_text_chunks);
-
- EXPECT_NE(0, error_id); // Anything but 0 means there was an error.
- EXPECT_TRUE(translated_text_chunks.empty());
-}
-
-// Tests that a secure translation is done over a secure connection.
-TEST_F(TranslationServiceTest, SecureTranslation) {
- std::vector<string16> text_chunks;
- text_chunks.push_back(ASCIIToUTF16("Hello"));
- translation_service_.Translate(0, 0, 0, text_chunks, "en", "fr",
- true /* secure */);
-
- // Run the task that creates the URLFetcher and sends the request.
- MessageLoop::current()->RunAllPending();
-
- TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(0);
- ASSERT_TRUE(url_fetcher != NULL);
- EXPECT_TRUE(url_fetcher->original_url().SchemeIsSecure());
-}
-
-// Test mixing requests from different renderers.
-TEST_F(TranslationServiceTest, MultipleRVRequests) {
- const char* const kExpectedRV1_1 = "Bonjour RV1";
- const char* const kExpectedRV1_2 = "Encore bonjour RV1";
- const char* const kExpectedRV1_3 = "Encore bonjour a nouveau RV1";
- const char* const kExpectedRV2 = "Bonjour RV2";
- const char* const kExpectedRV3 = "Bonjour RV3";
-
- TextChunks text_chunks;
- text_chunks.push_back(ASCIIToUTF16("Hello RV1"));
- text_chunks.push_back(ASCIIToUTF16("Hello again RV1"));
- translation_service_.Translate(1, 0, 0, text_chunks, "en", "fr", false);
- text_chunks.clear();
- text_chunks.push_back(ASCIIToUTF16("Hello RV2"));
- translation_service_.Translate(2, 0, 0, text_chunks, "en", "fr", false);
- text_chunks.clear();
- text_chunks.push_back(ASCIIToUTF16("Hello again one more time RV1"));
- translation_service_.Translate(1, 0, 1, text_chunks, "en", "fr", false);
- text_chunks.clear();
- text_chunks.push_back(ASCIIToUTF16("Hello RV3"));
- translation_service_.Translate(3, 0, 0, text_chunks, "en", "fr", false);
-
- // Run the tasks that create the URLFetcher and send the requests.
- MessageLoop::current()->RunAllPending();
-
- // We should have 3 pending URL fetchers. (The 2 translate requests for RV1
- // should have been grouped in 1.) Simluate the translation server responses.
- TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(1);
- ASSERT_TRUE(url_fetcher != NULL);
-
- TextChunksList text_chunks_list;
- text_chunks.clear();
- text_chunks.push_back(ASCIIToUTF16(kExpectedRV1_1));
- text_chunks.push_back(ASCIIToUTF16(kExpectedRV1_2));
- text_chunks_list.push_back(text_chunks);
- text_chunks.clear();
- text_chunks.push_back(ASCIIToUTF16(kExpectedRV1_3));
- text_chunks_list.push_back(text_chunks);
- SimulateSuccessfulResponse(url_fetcher, text_chunks_list);
-
- url_fetcher = url_fetcher_factory_.GetFetcherByID(2);
- ASSERT_TRUE(url_fetcher != NULL);
- SimulateSimpleSuccessfulResponse(url_fetcher, &kExpectedRV2, 1);
-
- url_fetcher = url_fetcher_factory_.GetFetcherByID(3);
- ASSERT_TRUE(url_fetcher != NULL);
- SimulateSimpleSuccessfulResponse(url_fetcher, &kExpectedRV3, 1);
-
- // This should have triggered 4 ViewMsg_TranslateTextReponse messages.
- ASSERT_EQ(4U, message_sender_.message_count());
-
- const int kExpectedRoutingID[] = { 1, 1, 2, 3 };
- const int kExpectedWorkID[] = { 0, 1, 0, 0 };
- const size_t kExpectedStringCount[] = { 2U, 1U, 1U, 1U };
-
- // Test the messages have the expected content.
- for (size_t i = 0; i < 4; i++) {
- SCOPED_TRACE(::testing::Message::Message() << "Iteration " << i);
- int routing_id = 0;
- int work_id = 0;
- int error_id = 0;
- std::vector<string16> translated_text_chunks;
- message_sender_.GetMessageParameters(i, &routing_id, &work_id, &error_id,
- &translated_text_chunks);
- EXPECT_EQ(kExpectedRoutingID[i], routing_id);
- EXPECT_EQ(kExpectedWorkID[i], work_id);
- EXPECT_EQ(0, error_id);
- EXPECT_EQ(kExpectedStringCount[i], translated_text_chunks.size());
- // TODO(jcampan): we should compare the strings.
- }
-}
-
-// Tests sending more than the max size.
-TEST_F(TranslationServiceTest, MoreThanMaxSizeRequests) {
- std::string one_kb_string(1024U, 'A');
- TextChunks text_chunks;
- text_chunks.push_back(ASCIIToUTF16(one_kb_string));
- // Send 2 small requests, then a big one.
- translation_service_.Translate(1, 0, 0, text_chunks, "en", "fr", false);
- translation_service_.Translate(1, 0, 1, text_chunks, "en", "fr", false);
- // We need a string big enough to be more than 30KB on top of the other 2
- // requests, but to be less than 30KB when sent (that sizes includes the
- // other parameters required by the translation server).
- std::string twenty_nine_kb_string(29 * 1024, 'G');
- text_chunks.clear();
- text_chunks.push_back(ASCIIToUTF16(twenty_nine_kb_string));
- translation_service_.Translate(1, 0, 2, text_chunks, "en", "fr", false);
-
- // Without any task been run, the 2 first requests should have been sent.
- TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(1);
- TextChunks query_strings;
- ExtractQueryStringsFromUploadData(url_fetcher, &query_strings);
- ASSERT_EQ(2U, query_strings.size());
- EXPECT_EQ(one_kb_string, UTF16ToASCII(query_strings[0]));
- EXPECT_EQ(one_kb_string, UTF16ToASCII(query_strings[1]));
-
- // Then when the task runs, the big request is sent.
- MessageLoop::current()->RunAllPending();
-
- url_fetcher = url_fetcher_factory_.GetFetcherByID(1);
- query_strings.clear();
- ExtractQueryStringsFromUploadData(url_fetcher, &query_strings);
- ASSERT_EQ(1U, query_strings.size());
- EXPECT_EQ(twenty_nine_kb_string, UTF16ToASCII(query_strings[0]));
-}
-
-// Test mixing secure/insecure requests and that secure requests are always sent
-// over HTTPS.
-TEST_F(TranslationServiceTest, MixedHTTPAndHTTPS) {
- const char* kUnsecureMessage = "Hello";
- const char* kSecureMessage = "Hello_Secure";
-
- std::vector<string16> text_chunks;
- text_chunks.push_back(ASCIIToUTF16(kUnsecureMessage));
- translation_service_.Translate(0, 0, 0, text_chunks, "en", "fr", false);
- text_chunks.clear();
- text_chunks.push_back(ASCIIToUTF16(kSecureMessage));
- translation_service_.Translate(0, 0, 0, text_chunks, "en", "fr", true);
-
- // Run the task that creates the URLFetcher and send the request.
- MessageLoop::current()->RunAllPending();
-
- // We only get the last URLFetcher since we id them based on their routing id
- // which we want to be the same in that test. We'll just check that as
- // expected the last one is the HTTPS and contains only the secure string.
- TestURLFetcher* url_fetcher = url_fetcher_factory_.GetFetcherByID(0);
- TextChunks query_strings;
- ExtractQueryStringsFromUploadData(url_fetcher, &query_strings);
- ASSERT_EQ(1U, query_strings.size());
- EXPECT_EQ(kSecureMessage, UTF16ToASCII(query_strings[0]));
-}
diff --git a/chrome/browser/resources/translate.js b/chrome/browser/resources/translate.js
new file mode 100644
index 0000000..d761c11
--- /dev/null
+++ b/chrome/browser/resources/translate.js
@@ -0,0 +1,111 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This code is used in conjunction with the Google Translate Element script.
+// It is injected in a page to translate it from one language to another.
+// It should be included in the page before the Translate Element script.
+
+var cr = {};
+
+cr.googleTranslate = (function() {
+ // Internal states.
+ var lib;
+ var libReady = false;
+ var error = false;
+ var finished = false;
+ var checkReadyCount = 0;
+
+ function checkLibReady() {
+ if (lib.isAvailable()) {
+ libReady = true;
+ return;
+ }
+ if (checkReadyCount++ > 5) {
+ error = true;
+ return;
+ }
+ setTimeout(checkLibReady, 100);
+ }
+
+ function onTranslateProgress(progress, opt_finished, opt_error) {
+ finished = opt_finished;
+ // opt_error can be 'undefined'.
+ if (typeof opt_error == 'boolean' && opt_error) {
+ error = true;
+ // We failed to translate, restore so the page is in a consistent state.
+ lib.restore();
+ }
+ }
+
+ // Public API.
+ return {
+ /**
+ * Whether the library is ready.
+ * The translate function should only be called when |libReady| is true.
+ * @type {boolean}
+ */
+ get libReady() {
+ return libReady;
+ },
+
+ /**
+ * Whether the current translate has finished successfully.
+ * @type {boolean}
+ */
+ get finished() {
+ return finished;
+ },
+
+ /**
+ * Whether an error occured initializing the library of translating the
+ * page.
+ * @type {boolean}
+ */
+ get error() {
+ return error;
+ },
+
+ /**
+ * Translate the page contents. Note that the translation is asynchronous.
+ * You need to regularly check the state of |finished| and |error| to know
+ * if the translation finished or if there was an error.
+ * @param {string} originalLang The language the page is in.
+ * @param {string} targetLang The language the page should be translated to.
+ * @return {boolean} False if the translate library was not ready, in which
+ * case the translation is not started. True otherwise.
+ */
+ translate: function(originalLang, targetLang) {
+ finished = false;
+ error = false;
+ if (!libReady)
+ return false;
+ lib.translatePage(originalLang, targetLang, onTranslateProgress);
+ return true;
+ },
+
+ /**
+ * Reverts the page contents to its original value, effectively reverting
+ * any performed translation. Does nothing if the page was not translated.
+ */
+ revert: function() {
+ lib.restore();
+ },
+
+ /**
+ * Entry point called by the Translate Element once it has been injected in
+ * the page.
+ */
+ onTranslateElementLoad : function() {
+ try {
+ lib = google.translate.TranslateService({});
+ } catch(err) {
+ error = true;
+ return;
+ }
+ // The TranslateService is not available immediately as it needs to start
+ // Flash. Let's wait until it is ready.
+ checkLibReady();
+ }
+ };
+})();
diff --git a/chrome/browser/tab_contents/render_view_context_menu.cc b/chrome/browser/tab_contents/render_view_context_menu.cc
index da20bec..9a4f951 100644
--- a/chrome/browser/tab_contents/render_view_context_menu.cc
+++ b/chrome/browser/tab_contents/render_view_context_menu.cc
@@ -26,7 +26,6 @@
#include "chrome/browser/pref_service.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/renderer_host/render_view_host.h"
-#include "chrome/browser/renderer_host/translation_service.h"
#include "chrome/browser/search_engines/template_url_model.h"
#include "chrome/browser/spellcheck_host.h"
#include "chrome/browser/spellchecker_platform_engine.h"
@@ -407,15 +406,11 @@ void RenderViewContextMenu::AppendPageItems() {
AppendSeparator();
AppendMenuItem(IDS_CONTENT_CONTEXT_SAVEPAGEAS);
AppendMenuItem(IDS_CONTENT_CONTEXT_PRINT);
- if (TranslationService::IsTranslationEnabled() ||
- TranslateManager::test_enabled()) {
- std::string locale = g_browser_process->GetApplicationLocale();
- locale = TranslationService::GetLanguageCode(locale);
- string16 language =
- l10n_util::GetDisplayNameForLocale(locale, locale, true);
- AppendMenuItem(IDS_CONTENT_CONTEXT_TRANSLATE,
- l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_TRANSLATE, language));
- }
+ std::string locale = g_browser_process->GetApplicationLocale();
+ locale = TranslateManager::GetLanguageCode(locale);
+ string16 language = l10n_util::GetDisplayNameForLocale(locale, locale, true);
+ AppendMenuItem(IDS_CONTENT_CONTEXT_TRANSLATE,
+ l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_TRANSLATE, language));
AppendMenuItem(IDS_CONTENT_CONTEXT_VIEWPAGESOURCE);
AppendMenuItem(IDS_CONTENT_CONTEXT_VIEWPAGEINFO);
}
@@ -1052,13 +1047,14 @@ void RenderViewContextMenu::ExecuteItemCommand(int id) {
std::string original_lang =
source_tab_contents_->language_state().original_language();
std::string target_lang = g_browser_process->GetApplicationLocale();
- target_lang = TranslationService::GetLanguageCode(target_lang);
+ target_lang = TranslateManager::GetLanguageCode(target_lang);
// Since the user decided to translate for that language and site, clears
// any preferences for not translating them.
TranslatePrefs prefs(profile_->GetPrefs());
prefs.RemoveLanguageFromBlacklist(original_lang);
prefs.RemoveSiteFromBlacklist(params_.page_url.HostNoBrackets());
- source_tab_contents_->TranslatePage(original_lang, target_lang);
+ Singleton<TranslateManager>::get()->TranslatePage(
+ source_tab_contents_, original_lang, target_lang);
break;
}
diff --git a/chrome/browser/tab_contents/tab_contents.cc b/chrome/browser/tab_contents/tab_contents.cc
index 8f49bb1..683d63a 100644
--- a/chrome/browser/tab_contents/tab_contents.cc
+++ b/chrome/browser/tab_contents/tab_contents.cc
@@ -50,7 +50,6 @@
#include "chrome/browser/renderer_host/render_widget_host_view.h"
#include "chrome/browser/renderer_host/resource_request_details.h"
#include "chrome/browser/renderer_host/site_instance.h"
-#include "chrome/browser/renderer_host/translation_service.h"
#include "chrome/browser/renderer_host/web_cache_manager.h"
#include "chrome/browser/renderer_preferences_util.h"
#include "chrome/browser/search_engines/template_url_fetcher.h"
@@ -833,22 +832,6 @@ void TabContents::ShowPageInfo(const GURL& url,
delegate_->ShowPageInfo(profile(), url, ssl, show_history);
}
-void TabContents::TranslatePage(const std::string& source_lang,
- const std::string& target_lang) {
- NavigationEntry* entry = controller_.GetActiveEntry();
- if (!entry) {
- NOTREACHED();
- return;
- }
- language_state_.set_translation_pending(true);
- render_view_host()->TranslatePage(entry->page_id(), source_lang, target_lang);
-}
-
-void TabContents::RevertTranslatedPage() {
- // TODO(jcampan): revert translated page to original and remove translate
- // infobar.
-}
-
ConstrainedWindow* TabContents::CreateConstrainedDialog(
ConstrainedWindowDelegate* delegate) {
ConstrainedWindow* window =
diff --git a/chrome/browser/tab_contents/tab_contents.h b/chrome/browser/tab_contents/tab_contents.h
index b9e0f15..123157d 100644
--- a/chrome/browser/tab_contents/tab_contents.h
+++ b/chrome/browser/tab_contents/tab_contents.h
@@ -376,13 +376,6 @@ class TabContents : public PageNavigator,
const NavigationEntry::SSLStatus& ssl,
bool show_history);
- // Translates the page contents from |source_lang| to |target_lang|.
- void TranslatePage(const std::string& source_lang,
- const std::string& target_lang);
-
- // Reverts a translated page to original page.
- void RevertTranslatedPage();
-
// Window management ---------------------------------------------------------
// Create a new window constrained to this TabContents' clip and visibility.
diff --git a/chrome/browser/translate/translate_infobars_delegates.cc b/chrome/browser/translate/translate_infobars_delegates.cc
index 5caff33..0f66357 100644
--- a/chrome/browser/translate/translate_infobars_delegates.cc
+++ b/chrome/browser/translate/translate_infobars_delegates.cc
@@ -7,8 +7,8 @@
#include "app/l10n_util.h"
#include "app/resource_bundle.h"
#include "chrome/browser/browser_process.h"
-#include "chrome/browser/renderer_host/translation_service.h"
#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/translate/translate_manager.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
@@ -68,12 +68,12 @@ void TranslateInfoBarDelegate::ModifyTargetLanguage(int lang_index) {
void TranslateInfoBarDelegate::GetAvailableOriginalLanguages(
std::vector<std::string>* languages) {
- TranslationService::GetSupportedLanguages(languages);
+ TranslateManager::GetSupportedLanguages(languages);
}
void TranslateInfoBarDelegate::GetAvailableTargetLanguages(
std::vector<std::string>* languages) {
- TranslationService::GetSupportedLanguages(languages);
+ TranslateManager::GetSupportedLanguages(languages);
}
void TranslateInfoBarDelegate::Translate() {
@@ -81,11 +81,14 @@ void TranslateInfoBarDelegate::Translate() {
// are different, so only in this case is translation really pending.
if (original_lang_index_ != target_lang_index_)
translation_pending_ = true;
- tab_contents_->TranslatePage(original_lang_code(), target_lang_code());
+ Singleton<TranslateManager>::get()->TranslatePage(tab_contents_,
+ original_lang_code(),
+ target_lang_code());
}
void TranslateInfoBarDelegate::RevertTranslation() {
- tab_contents_->RevertTranslatedPage();
+ Singleton<TranslateManager>::get()->RevertTranslation(tab_contents_);
+ tab_contents_->RemoveInfoBar(this);
}
void TranslateInfoBarDelegate::TranslationDeclined() {
@@ -177,7 +180,8 @@ string16 TranslateInfoBarDelegate::GetErrorMessage(
case TranslateErrors::NETWORK:
message_id = IDS_TRANSLATE_INFOBAR_ERROR_CANT_CONNECT;
break;
- case TranslateErrors::SERVER:
+ case TranslateErrors::INITIALIZATION_ERROR:
+ case TranslateErrors::TRANSLATION_ERROR:
message_id = IDS_TRANSLATE_INFOBAR_ERROR_CANT_TRANSLATE;
break;
default:
@@ -196,7 +200,7 @@ TranslateInfoBarDelegate* TranslateInfoBarDelegate::Create(
const std::string& target_lang_code,
TranslateErrors::Type error_type) {
std::vector<std::string> supported_languages;
- TranslationService::GetSupportedLanguages(&supported_languages);
+ TranslateManager::GetSupportedLanguages(&supported_languages);
int original_lang_index = -1;
for (size_t i = 0; i < supported_languages.size(); ++i) {
@@ -241,7 +245,7 @@ TranslateInfoBarDelegate::TranslateInfoBarDelegate(TabContents* tab_contents,
never_translate_site_(false),
always_translate_(false),
error_type_(error_type) {
- TranslationService::GetSupportedLanguages(&supported_languages_);
+ TranslateManager::GetSupportedLanguages(&supported_languages_);
DCHECK(original_lang_index_ > -1);
DCHECK(target_lang_index_ > -1);
}
diff --git a/chrome/browser/translate/translate_manager.cc b/chrome/browser/translate/translate_manager.cc
index 214d731..3968de9 100644
--- a/chrome/browser/translate/translate_manager.cc
+++ b/chrome/browser/translate/translate_manager.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/translate/translate_manager.h"
+#include "app/resource_bundle.h"
#include "base/compiler_specific.h"
#include "base/string_util.h"
#include "chrome/browser/browser_process.h"
@@ -11,7 +12,6 @@
#include "chrome/browser/profile.h"
#include "chrome/browser/renderer_host/render_process_host.h"
#include "chrome/browser/renderer_host/render_view_host.h"
-#include "chrome/browser/renderer_host/translation_service.h"
#include "chrome/browser/tab_contents/language_state.h"
#include "chrome/browser/tab_contents/navigation_controller.h"
#include "chrome/browser/tab_contents/navigation_entry.h"
@@ -24,9 +24,102 @@
#include "chrome/common/notification_source.h"
#include "chrome/common/notification_type.h"
#include "chrome/common/pref_names.h"
+#include "grit/browser_resources.h"
+#include "net/url_request/url_request_status.h"
+
+namespace {
+
+// Mapping from a locale name to a language code name.
+// Locale names not included are translated as is.
+struct LocaleToCLDLanguage {
+ const char* locale_language; // Language Chrome locale is in.
+ const char* cld_language; // Language the CLD reports.
+};
+LocaleToCLDLanguage kLocaleToCLDLanguages[] = {
+ { "en-GB", "en" },
+ { "en-US", "en" },
+ { "es-419", "es" },
+ { "pt-BR", "pt" },
+ { "pt-PT", "pt" },
+};
+
+// The list of languages the Google translation server supports.
+// For information, here is the list of languages that Chrome can be run in
+// but that the translation server does not support:
+// am Amharic
+// bn Bengali
+// gu Gujarati
+// kn Kannada
+// ml Malayalam
+// mr Marathi
+// ta Tamil
+// te Telugu
+const char* kSupportedLanguages[] = {
+ "af", // Afrikaans
+ "sq", // Albanian
+ "ar", // Arabic
+ "be", // Belarusian
+ "bg", // Bulgarian
+ "ca", // Catalan
+ "zh-CN", // Chinese (Simplified)
+ "zh-TW", // Chinese (Traditional)
+ "hr", // Croatian
+ "cs", // Czech
+ "da", // Danish
+ "nl", // Dutch
+ "en", // English
+ "et", // Estonian
+ "fi", // Finnish
+ "fil", // Filipino
+ "fr", // French
+ "gl", // Galician
+ "de", // German
+ "el", // Greek
+ "he", // Hebrew
+ "hi", // Hindi
+ "hu", // Hungarian
+ "is", // Icelandic
+ "id", // Indonesian
+ "it", // Italian
+ "ga", // Irish
+ "ja", // Japanese
+ "ko", // Korean
+ "lv", // Latvian
+ "lt", // Lithuanian
+ "mk", // Macedonian
+ "ms", // Malay
+ "mt", // Maltese
+ "nb", // Norwegian
+ "fa", // Persian
+ "pl", // Polish
+ "pt", // Portuguese
+ "ro", // Romanian
+ "ru", // Russian
+ "sr", // Serbian
+ "sk", // Slovak
+ "sl", // Slovenian
+ "es", // Spanish
+ "sw", // Swahili
+ "sv", // Swedish
+ "th", // Thai
+ "tr", // Turkish
+ "uk", // Ukrainian
+ "vi", // Vietnamese
+ "cy", // Welsh
+ "yi", // Yiddish
+};
+
+const char* const kTranslateScriptURL =
+ "http://translate.google.com/translate_a/element.js?"
+ "cb=cr.googleTranslate.onTranslateElementLoad";
+const char* const kTranslateScriptHeader =
+ "Google-Translate-Element-Mode: library";
+
+} // namespace
// static
-bool TranslateManager::test_enabled_ = false;
+base::LazyInstance<std::set<std::string> >
+ TranslateManager::supported_languages_(base::LINKER_INITIALIZED);
TranslateManager::~TranslateManager() {
}
@@ -36,6 +129,34 @@ bool TranslateManager::IsTranslatableURL(const GURL& url) {
return !url.SchemeIs("chrome");
}
+// static
+void TranslateManager::GetSupportedLanguages(
+ std::vector<std::string>* languages) {
+ DCHECK(languages && languages->empty());
+ for (size_t i = 0; i < arraysize(kSupportedLanguages); ++i)
+ languages->push_back(kSupportedLanguages[i]);
+}
+
+// static
+std::string TranslateManager::GetLanguageCode(
+ const std::string& chrome_locale) {
+ for (size_t i = 0; i < arraysize(kLocaleToCLDLanguages); ++i) {
+ if (chrome_locale == kLocaleToCLDLanguages[i].locale_language)
+ return kLocaleToCLDLanguages[i].cld_language;
+ }
+ return chrome_locale;
+}
+
+// static
+bool TranslateManager::IsSupportedLanguage(const std::string& page_language) {
+ if (supported_languages_.Pointer()->empty()) {
+ for (size_t i = 0; i < arraysize(kSupportedLanguages); ++i)
+ supported_languages_.Pointer()->insert(kSupportedLanguages[i]);
+ }
+ return supported_languages_.Pointer()->find(page_language) !=
+ supported_languages_.Pointer()->end();
+}
+
void TranslateManager::Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) {
@@ -135,6 +256,48 @@ void TranslateManager::Observe(NotificationType type,
}
}
+void TranslateManager::OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data) {
+ scoped_ptr<const URLFetcher> delete_ptr(source);
+ DCHECK(translate_script_request_pending_);
+ translate_script_request_pending_ = false;
+ if (status.status() != URLRequestStatus::SUCCESS || response_code != 200)
+ return; // We could not retrieve the translate script.
+
+ base::StringPiece str = ResourceBundle::GetSharedInstance().
+ GetRawDataResource(IDR_TRANSLATE_JS);
+ DCHECK(translate_script_.empty());
+ str.CopyToString(&translate_script_);
+ translate_script_ += "\n" + data;
+
+ // Execute any pending requests.
+ std::vector<PendingRequest>::const_iterator iter;
+ for (iter = pending_requests_.begin(); iter != pending_requests_.end();
+ ++iter) {
+ const PendingRequest& request = *iter;
+ TabContents* tab = tab_util::GetTabContentsByID(request.render_process_id,
+ request.render_view_id);
+ if (!tab) {
+ // The tab went away while we were retrieving the script.
+ continue;
+ }
+ NavigationEntry* entry = tab->controller().GetActiveEntry();
+ if (!entry || entry->page_id() != request.page_id) {
+ // We navigated away from the page the translation was triggered on.
+ continue;
+ }
+
+ // Translate the page.
+ DoTranslatePage(tab, translate_script_,
+ request.source_lang, request.target_lang);
+ }
+ pending_requests_.clear();
+}
+
// static
bool TranslateManager::IsShowingTranslateInfobar(TabContents* tab) {
for (int i = 0; i < tab->infobar_delegate_count(); ++i) {
@@ -145,10 +308,8 @@ bool TranslateManager::IsShowingTranslateInfobar(TabContents* tab) {
}
TranslateManager::TranslateManager()
- : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
- if (!test_enabled_ && !TranslationService::IsTranslationEnabled())
- return;
-
+ : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
+ translate_script_request_pending_(false) {
notification_registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
NotificationService::AllSources());
notification_registrar_.Add(this, NotificationType::TAB_LANGUAGE_DETERMINED,
@@ -172,8 +333,7 @@ void TranslateManager::InitiateTranslation(TabContents* tab,
std::string target_lang = GetTargetLanguage();
// Nothing to do if either the language Chrome is in or the language of the
// page is not supported by the translation server.
- if (target_lang.empty() ||
- !TranslationService::IsSupportedLanguage(page_lang)) {
+ if (target_lang.empty() || !IsSupportedLanguage(page_lang)) {
return;
}
@@ -194,14 +354,14 @@ void TranslateManager::InitiateTranslation(TabContents* tab,
// page's text is sent to the translate server.
if (TranslatePrefs::ShouldAutoTranslate(prefs, page_lang, target_lang) &&
!tab->profile()->IsOffTheRecord()) {
- tab->TranslatePage(page_lang, target_lang);
+ TranslatePage(tab, page_lang, target_lang);
return;
}
std::string auto_translate_to = tab->language_state().AutoTranslateTo();
if (!auto_translate_to.empty()) {
// This page was navigated through a click from a translated page.
- tab->TranslatePage(page_lang, auto_translate_to);
+ TranslatePage(tab, page_lang, auto_translate_to);
return;
}
@@ -222,6 +382,57 @@ void TranslateManager::InitiateTranslationPosted(int process_id,
InitiateTranslation(tab, page_lang);
}
+void TranslateManager::TranslatePage(TabContents* tab_contents,
+ const std::string& source_lang,
+ const std::string& target_lang) {
+ NavigationEntry* entry = tab_contents->controller().GetActiveEntry();
+ if (!entry) {
+ NOTREACHED();
+ return;
+ }
+ if (!translate_script_.empty()) {
+ DoTranslatePage(tab_contents, translate_script_, source_lang, target_lang);
+ return;
+ }
+
+ // The script is not available yet. Queue that request and query for the
+ // script. Once it is downloaded we'll do the translate.
+ RenderViewHost* rvh = tab_contents->render_view_host();
+ PendingRequest request;
+ request.render_process_id = rvh->process()->id();
+ request.render_view_id = rvh->routing_id();
+ request.page_id = entry->page_id();
+ request.source_lang = source_lang;
+ request.target_lang = target_lang;
+ pending_requests_.push_back(request);
+ RequestTranslateScript();
+}
+
+void TranslateManager::RevertTranslation(TabContents* tab_contents) {
+ NavigationEntry* entry = tab_contents->controller().GetActiveEntry();
+ if (!entry) {
+ NOTREACHED();
+ return;
+ }
+ tab_contents->render_view_host()->RevertTranslation(entry->page_id());
+}
+
+void TranslateManager::DoTranslatePage(TabContents* tab_contents,
+ const std::string& translate_script,
+ const std::string& source_lang,
+ const std::string& target_lang) {
+ NavigationEntry* entry = tab_contents->controller().GetActiveEntry();
+ if (!entry) {
+ NOTREACHED();
+ return;
+ }
+
+ tab_contents->language_state().set_translation_pending(true);
+ tab_contents->render_view_host()->TranslatePage(entry->page_id(),
+ translate_script,
+ source_lang, target_lang);
+}
+
bool TranslateManager::IsAcceptLanguage(TabContents* tab,
const std::string& language) {
PrefService* pref_service = tab->profile()->GetPrefs();
@@ -249,8 +460,8 @@ void TranslateManager::InitAcceptLanguages(PrefService* prefs) {
LanguageSet accept_langs_set;
SplitString(WideToASCII(accept_langs_str), ',', &accept_langs_list);
std::vector<std::string>::const_iterator iter;
- std::string ui_lang = TranslationService::GetLanguageCode(
- g_browser_process->GetApplicationLocale());
+ std::string ui_lang =
+ GetLanguageCode(g_browser_process->GetApplicationLocale());
bool is_ui_english = StartsWithASCII(ui_lang, "en-", false);
for (iter = accept_langs_list.begin();
iter != accept_langs_list.end(); ++iter) {
@@ -273,6 +484,18 @@ void TranslateManager::InitAcceptLanguages(PrefService* prefs) {
accept_languages_[prefs] = accept_langs_set;
}
+void TranslateManager::RequestTranslateScript() {
+ if (translate_script_request_pending_)
+ return;
+
+ translate_script_request_pending_ = true;
+ URLFetcher* fetcher = URLFetcher::Create(0, GURL(kTranslateScriptURL),
+ URLFetcher::GET, this);
+ fetcher->set_request_context(Profile::GetDefaultRequestContext());
+ fetcher->set_extra_request_headers(kTranslateScriptHeader);
+ fetcher->Start();
+}
+
// static
void TranslateManager::AddTranslateInfoBar(
TabContents* tab, TranslateInfoBarDelegate::TranslateState state,
@@ -294,9 +517,9 @@ void TranslateManager::AddTranslateInfoBar(
// static
std::string TranslateManager::GetTargetLanguage() {
- std::string target_lang = TranslationService::GetLanguageCode(
- g_browser_process->GetApplicationLocale());
- if (TranslationService::IsSupportedLanguage(target_lang))
+ std::string target_lang =
+ GetLanguageCode(g_browser_process->GetApplicationLocale());
+ if (IsSupportedLanguage(target_lang))
return target_lang;
return std::string();
}
diff --git a/chrome/browser/translate/translate_manager.h b/chrome/browser/translate/translate_manager.h
index cf0e452..5c7bbb2 100644
--- a/chrome/browser/translate/translate_manager.h
+++ b/chrome/browser/translate/translate_manager.h
@@ -8,9 +8,12 @@
#include <map>
#include <set>
#include <string>
+#include <vector>
+#include "base/lazy_instance.h"
#include "base/singleton.h"
#include "base/task.h"
+#include "chrome/browser/net/url_fetcher.h"
#include "chrome/browser/translate/translate_infobars_delegates.h"
#include "chrome/common/notification_observer.h"
#include "chrome/common/notification_registrar.h"
@@ -25,7 +28,8 @@ class TabContents;
// page translation the user requests.
// It is a singleton.
-class TranslateManager : public NotificationObserver {
+class TranslateManager : public NotificationObserver,
+ public URLFetcher::Delegate {
public:
virtual ~TranslateManager();
@@ -34,6 +38,29 @@ class TranslateManager : public NotificationObserver {
const NotificationSource& source,
const NotificationDetails& details);
+ // URLFetcher::Delegate implementation:
+ virtual void OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const URLRequestStatus& status,
+ int response_code,
+ const ResponseCookies& cookies,
+ const std::string& data);
+
+ // Translates the page contents from |source_lang| to |target_lang|.
+ // The actual translation might be performed asynchronously if the translate
+ // script is not yet available.
+ void TranslatePage(TabContents* tab_contents,
+ const std::string& source_lang,
+ const std::string& target_lang);
+
+ // Reverts the contents of the page in |tab_contents| to its original
+ // language.
+ void RevertTranslation(TabContents* tab_contents);
+
+ // Clears the translate script, so it will be fetched next time we translate.
+ // Currently used by unit-tests.
+ void ClearTranslateScript() { translate_script_.clear(); }
+
// Convenience method to know if a tab is showing a translate infobar.
static bool IsShowingTranslateInfobar(TabContents* tab);
@@ -41,9 +68,16 @@ class TranslateManager : public NotificationObserver {
// (chrome:// and others).
static bool IsTranslatableURL(const GURL& url);
- // Used by unit-test to enable the TranslateManager for testing purpose.
- static void set_test_enabled(bool enabled) { test_enabled_ = enabled; }
- static bool test_enabled() { return test_enabled_; }
+ // Fills |languages| with the list of languages that the translate server can
+ // translate to and from.
+ static void GetSupportedLanguages(std::vector<std::string>* languages);
+
+ // Returns the language code that can be used with the Translate method for a
+ // specified |chrome_locale|.
+ static std::string GetLanguageCode(const std::string& chrome_locale);
+
+ // Returns true if |page_language| is supported by the translation server.
+ static bool IsSupportedLanguage(const std::string& page_language);
protected:
TranslateManager();
@@ -51,6 +85,17 @@ class TranslateManager : public NotificationObserver {
private:
friend struct DefaultSingletonTraits<TranslateManager>;
+ // Structure that describes a translate request.
+ // Translation may be deferred while the translate script is being retrieved
+ // from the translate server.
+ struct PendingRequest {
+ int render_process_id;
+ int render_view_id;
+ int page_id;
+ std::string source_lang;
+ std::string target_lang;
+ };
+
// Starts the translation process on |tab| containing the page in the
// |page_lang| language.
void InitiateTranslation(TabContents* tab, const std::string& page_lang);
@@ -61,6 +106,12 @@ class TranslateManager : public NotificationObserver {
int render_id,
const std::string& page_lang);
+ // Sends a translation request to the RenderView of |tab_contents|.
+ void DoTranslatePage(TabContents* tab_contents,
+ const std::string& translate_script,
+ const std::string& source_lang,
+ const std::string& target_lang);
+
// Returns true if the passed language has been configured by the user as an
// accept language.
bool IsAcceptLanguage(TabContents* tab, const std::string& language);
@@ -69,6 +120,10 @@ class TranslateManager : public NotificationObserver {
// preference in |prefs|.
void InitAcceptLanguages(PrefService* prefs);
+ // Fetches the JS translate script (the script that is injected in the page
+ // to translate it).
+ void RequestTranslateScript();
+
// Convenience method that adds a translate infobar to |tab|.
static void AddTranslateInfoBar(
TabContents* tab,
@@ -92,7 +147,19 @@ class TranslateManager : public NotificationObserver {
ScopedRunnableMethodFactory<TranslateManager> method_factory_;
- static bool test_enabled_;
+ // The JS injected in the page to do the translation.
+ std::string translate_script_;
+
+ // Whether the translate JS is currently being retrieved.
+ bool translate_script_request_pending_;
+
+ // The list of pending translate requests. Translate requests are queued when
+ // the translate script is not ready and has to be fetched from the translate
+ // server.
+ std::vector<PendingRequest> pending_requests_;
+
+ // The languages supported by the translation server.
+ static base::LazyInstance<std::set<std::string> > supported_languages_;
DISALLOW_COPY_AND_ASSIGN(TranslateManager);
};
diff --git a/chrome/browser/translate/translate_manager_unittest.cc b/chrome/browser/translate/translate_manager_unittest.cc
index bc0924c..e543af0 100644
--- a/chrome/browser/translate/translate_manager_unittest.cc
+++ b/chrome/browser/translate/translate_manager_unittest.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/renderer_host/test/test_render_view_host.h"
+#include "chrome/browser/net/test_url_fetcher_factory.h"
#include "chrome/browser/renderer_host/mock_render_process_host.h"
#include "chrome/browser/tab_contents/render_view_context_menu.h"
#include "chrome/browser/translate/translate_infobars_delegates.h"
@@ -50,11 +51,15 @@ class TranslateManagerTest : public RenderViewHostTestHarness,
process()->sink().GetFirstMessageMatching(ViewMsg_TranslatePage::ID);
if (!message)
return false;
- Tuple3<int, std::string, std::string> translate_param;
+ Tuple4<int, std::string, std::string, std::string> translate_param;
ViewMsg_TranslatePage::Read(message, &translate_param);
- *page_id = translate_param.a;
- *original_lang = translate_param.b;
- *target_lang = translate_param.c;
+ if (page_id)
+ *page_id = translate_param.a;
+ // Ignore translate_param.b which is the script injected in the page.
+ if (original_lang)
+ *original_lang = translate_param.c;
+ if (target_lang)
+ *target_lang = translate_param.d;
return true;
}
@@ -111,15 +116,15 @@ class TranslateManagerTest : public RenderViewHostTestHarness,
protected:
virtual void SetUp() {
- TranslateManager::set_test_enabled(true);
- // This must be created after set_test_enabled() has been called to register
- // notifications properly. Note that we do this before calling
+ URLFetcher::set_factory(&url_fetcher_factory_);
+
+ // Access the TranslateManager singleton so it is created before we call
// RenderViewHostTestHarness::SetUp() to match what's done in Chrome, where
- // the TranslateManager is created before the TabContents. This matters for
- // as they both register for similar events and we want the notifications
- // to happen in the same sequence (TranslateManager first, TabContents
- // second).
- translate_manager_.reset(new TestTranslateManager());
+ // the TranslateManager is created before the TabContents. This matters as
+ // they both register for similar events and we want the notifications to
+ // happen in the same sequence (TranslateManager first, TabContents second).
+ // Also clears the translate script so it is fetched everytime.
+ Singleton<TranslateManager>::get()->ClearTranslateScript();
RenderViewHostTestHarness::SetUp();
@@ -137,13 +142,25 @@ class TranslateManagerTest : public RenderViewHostTestHarness,
RenderViewHostTestHarness::TearDown();
- TranslateManager::set_test_enabled(false);
+ URLFetcher::set_factory(NULL);
+ }
+
+ void SimulateURLFetch(bool success) {
+ TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0);
+ ASSERT_TRUE(fetcher);
+ URLRequestStatus status;
+ status.set_status(success ? URLRequestStatus::SUCCESS :
+ URLRequestStatus::FAILED);
+ fetcher->delegate()->OnURLFetchComplete(fetcher, fetcher->original_url(),
+ status, success ? 200 : 500,
+ ResponseCookies(),
+ std::string());
}
private:
NotificationRegistrar notification_registrar_;
-
scoped_ptr<TestTranslateManager> translate_manager_;
+ TestURLFetcherFactory url_fetcher_factory_;
// The list of infobars that have been removed.
// WARNING: the pointers points to deleted objects, use only for comparison.
@@ -251,6 +268,9 @@ TEST_F(TranslateManagerTest, NormalTranslate) {
// Simulate clicking translate.
process()->sink().ClearMessages();
infobar->Translate();
+ // Simulate the translate script being retrieved (it only needs to be done
+ // once in the test as it is cached).
+ SimulateURLFetch(true);
EXPECT_FALSE(InfoBarRemoved());
// Test that we sent the right message to the renderer.
@@ -297,6 +317,26 @@ TEST_F(TranslateManagerTest, NormalTranslate) {
EXPECT_EQ(new_target_lang, target_lang);
}
+TEST_F(TranslateManagerTest, TranslateScriptNotAvailable) {
+ // Simulate navigating to a page.
+ SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr");
+
+ // We should have an info-bar.
+ TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
+ ASSERT_TRUE(infobar != NULL);
+ EXPECT_EQ(TranslateInfoBarDelegate::kBeforeTranslate, infobar->state());
+
+ // Simulate clicking translate.
+ process()->sink().ClearMessages();
+ infobar->Translate();
+ // Simulate a failure retrieving the translate script.
+ SimulateURLFetch(false);
+ EXPECT_FALSE(InfoBarRemoved());
+
+ // We should not have sent any message to translate to the renderer.
+ EXPECT_FALSE(GetTranslateMessage(NULL, NULL, NULL));
+}
+
// Tests that we show/don't show an info-bar for all languages the CLD can
// report.
TEST_F(TranslateManagerTest, TestAllLanguages) {
@@ -376,6 +416,8 @@ TEST_F(TranslateManagerTest, AutoTranslateOnNavigate) {
TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
infobar->Translate();
+ SimulateURLFetch(true); // Simulate the translate script being retrieved.
+
rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "fr", "en",
TranslateErrors::NONE));
@@ -519,6 +561,7 @@ TEST_F(TranslateManagerTest, TranslateCloseInfoBarInPageNavigation) {
TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
infobar->Translate();
+ SimulateURLFetch(true); // Simulate the translate script being retrieved.
rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "fr", "en",
TranslateErrors::NONE));
@@ -547,6 +590,7 @@ TEST_F(TranslateManagerTest, TranslateInPageNavigation) {
TranslateInfoBarDelegate* infobar = GetTranslateInfoBar();
ASSERT_TRUE(infobar != NULL);
infobar->Translate();
+ SimulateURLFetch(true); // Simulate the translate script being retrieved.
rvh()->TestOnMessageReceived(ViewHostMsg_PageTranslated(0, 0, "fr", "en",
TranslateErrors::NONE));
@@ -711,6 +755,7 @@ TEST_F(TranslateManagerTest, AlwaysTranslateLanguagePref) {
SimulateNavigation(GURL("http://www.google.fr"), 0, L"Le Google", "fr");
// It should have triggered an automatic translation to English.
+ SimulateURLFetch(true); // Simulate the translate script being retrieved.
int page_id = 0;
std::string original_lang, target_lang;
EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang));
@@ -769,6 +814,7 @@ TEST_F(TranslateManagerTest, ContextMenu) {
menu->TestExecuteItemCommand(IDS_CONTENT_CONTEXT_TRANSLATE);
// That should have triggered a translation.
+ SimulateURLFetch(true); // Simulate the translate script being retrieved.
int page_id = 0;
std::string original_lang, target_lang;
EXPECT_TRUE(GetTranslateMessage(&page_id, &original_lang, &target_lang));
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 6b23d1f..1ba6cfd 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -76,7 +76,7 @@
'browser/appcache/chrome_appcache_service.cc',
'browser/appcache/chrome_appcache_service.h',
'browser/appcache/view_appcache_internals_job_factory.cc',
- 'browser/appcache/view_appcache_internals_job_factory.h',
+ 'browser/appcache/view_appcache_internals_job_factory.h',
'browser/autocomplete/autocomplete.cc',
'browser/autocomplete/autocomplete.h',
'browser/autocomplete/autocomplete_accessibility.cc',
@@ -1830,8 +1830,6 @@
'browser/renderer_host/socket_stream_host.h',
'browser/renderer_host/sync_resource_handler.cc',
'browser/renderer_host/sync_resource_handler.h',
- 'browser/renderer_host/translation_service.cc',
- 'browser/renderer_host/translation_service.h',
'browser/renderer_host/video_layer.cc',
'browser/renderer_host/video_layer.h',
'browser/renderer_host/video_layer_proxy.cc',
diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi
index 9e80e36..7598f35 100755
--- a/chrome/chrome_renderer.gypi
+++ b/chrome/chrome_renderer.gypi
@@ -145,11 +145,8 @@
'renderer/spellchecker/spellcheck.h',
'renderer/spellchecker/spellcheck_worditerator.cc',
'renderer/spellchecker/spellcheck_worditerator.h',
- 'renderer/translate/text_translator.h',
- 'renderer/translate/text_translator_impl.cc',
- 'renderer/translate/text_translator_impl.h',
- 'renderer/translate/page_translator.cc',
- 'renderer/translate/page_translator.h',
+ 'renderer/translate_helper.cc',
+ 'renderer/translate_helper.h',
'renderer/user_script_idle_scheduler.cc',
'renderer/user_script_idle_scheduler.h',
'renderer/user_script_slave.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 50129c7..7aca9a3 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -844,7 +844,6 @@
'browser/renderer_host/resource_queue_unittest.cc',
'browser/renderer_host/test/render_view_host_unittest.cc',
'browser/renderer_host/test/site_instance_unittest.cc',
- 'browser/renderer_host/translation_service_unittest.cc',
'browser/renderer_host/web_cache_manager_unittest.cc',
'browser/rlz/rlz_unittest.cc',
'browser/safe_browsing/bloom_filter_unittest.cc',
@@ -969,7 +968,7 @@
'renderer/renderer_main_unittest.cc',
'renderer/spellchecker/spellcheck_unittest.cc',
'renderer/spellchecker/spellcheck_worditerator_unittest.cc',
- 'renderer/translate/page_translator_unittest.cc',
+ 'renderer/translate_helper_unittest.cc',
'test/browser_with_test_window_test.cc',
'test/browser_with_test_window_test.h',
'test/file_test_utils.cc',
@@ -1211,7 +1210,7 @@
'test/test_launcher/test_runner.h',
'test/unit/chrome_test_suite.h',
'browser/chromeos/login/wizard_in_process_browser_test.cc',
- 'browser/chromeos/login/wizard_in_process_browser_test.h',
+ 'browser/chromeos/login/wizard_in_process_browser_test.h',
# Actual test sources
'browser/autocomplete/autocomplete_browsertest.cc',
'browser/browser_browsertest.cc',
@@ -1344,7 +1343,7 @@
'browser/chromeos/compact_location_bar_host_browsertest.cc',
'browser/chromeos/compact_navigation_bar_browsertest.cc',
'browser/chromeos/cros/cros_in_process_browser_test.cc',
- 'browser/chromeos/cros/cros_in_process_browser_test.h',
+ 'browser/chromeos/cros/cros_in_process_browser_test.h',
'browser/chromeos/cros/mock_mount_library.cc',
'browser/chromeos/cros/mock_mount_library.h',
'browser/chromeos/login/account_screen_browsertest.cc',
diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h
index 211297a..c76b430 100644
--- a/chrome/common/render_messages.h
+++ b/chrome/common/render_messages.h
@@ -595,34 +595,6 @@ struct ViewMsg_New_Params {
int64 session_storage_namespace_id;
};
-// Message to ask the browser to translate some text from one language to
-// another.
-struct ViewHostMsg_TranslateTextParam {
- // The routing id. Even though ViewHostMsg_TranslateText is a control message
- // (sent to the browser, not to a specific RenderViewHost), the browser needs
- // the routing id in order to send the response back to the right RenderView.
- int routing_id;
-
- // An id used to identify that specific translation.
- int work_id;
-
- // The id of the page this translation originated from.
- int page_id;
-
- // The text chunks that need to be translated.
- std::vector<string16> text_chunks;
-
- // The ISO code of the language the text to translate is in.
- std::string from_language;
-
- // The ISO code of the language the text should be translated to.
- std::string to_language;
-
- // Whether a secure connection should be used when transmitting the text for
- // translation to an external server.
- bool secure;
-};
-
struct ViewHostMsg_RunFileChooser_Params {
enum Mode {
// Requires that the file exists before allowing the user to pick it.
@@ -2569,48 +2541,6 @@ struct ParamTraits<ViewMsg_New_Params> {
}
};
-template<>
-struct ParamTraits<ViewHostMsg_TranslateTextParam> {
- typedef ViewHostMsg_TranslateTextParam param_type;
- static void Write(Message* m, const param_type& p) {
- WriteParam(m, p.routing_id);
- WriteParam(m, p.work_id);
- WriteParam(m, p.page_id);
- WriteParam(m, p.text_chunks);
- WriteParam(m, p.from_language);
- WriteParam(m, p.to_language);
- WriteParam(m, p.secure);
- }
-
- static bool Read(const Message* m, void** iter, param_type* p) {
- return
- ReadParam(m, iter, &p->routing_id) &&
- ReadParam(m, iter, &p->work_id) &&
- ReadParam(m, iter, &p->page_id) &&
- ReadParam(m, iter, &p->text_chunks) &&
- ReadParam(m, iter, &p->from_language) &&
- ReadParam(m, iter, &p->to_language) &&
- ReadParam(m, iter, &p->secure);
- }
- static void Log(const param_type& p, std::wstring* l) {
- l->append(L"(");
- LogParam(p.routing_id, l);
- l->append(L", ");
- LogParam(p.work_id, l);
- l->append(L", ");
- LogParam(p.page_id, l);
- l->append(L", ");
- LogParam(p.text_chunks, l);
- l->append(L", ");
- LogParam(p.from_language, l);
- l->append(L", ");
- LogParam(p.to_language, l);
- l->append(L", ");
- LogParam(p.secure, l);
- l->append(L")");
- }
-};
-
template <>
struct SimilarTypeTraits<TranslateErrors::Type> {
typedef int Type;
diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h
index b1222f1..68d69d0 100644
--- a/chrome/common/render_messages_internal.h
+++ b/chrome/common/render_messages_internal.h
@@ -896,19 +896,18 @@ IPC_BEGIN_MESSAGES(View)
// Tells the renderer to translate the page contents from one language to
// another.
- IPC_MESSAGE_ROUTED3(ViewMsg_TranslatePage,
+ IPC_MESSAGE_ROUTED4(ViewMsg_TranslatePage,
int /* page id */,
+ std::string, /* the script injected in the page */
std::string, /* BCP 47/RFC 5646 language code the page
is in */
std::string /* BCP 47/RFC 5646 language code to translate
to */)
- // Reply to the ViewHostMsg_TranslateText message with the actual translated
- // text chunks.
- IPC_MESSAGE_ROUTED3(ViewMsg_TranslateTextReponse,
- int /* id of translation work */,
- int /* error id of translation work */,
- std::vector<string16> /* the translated text chunks */)
+ // Tells the renderer to revert the text of translated page to its original
+ // contents.
+ IPC_MESSAGE_ROUTED1(ViewMsg_RevertTranslation,
+ int /* page id */)
// Reply in response to ViewHostMsg_Geolocation_RequestPermission.
IPC_MESSAGE_ROUTED2(ViewMsg_Geolocation_PermissionSet,
@@ -2227,11 +2226,6 @@ IPC_BEGIN_MESSAGES(ViewHost)
string16 /* word */,
std::vector<string16> /* suggestions */)
- // Request for text translation.
- // Used when translating a page from one language to another.
- IPC_MESSAGE_CONTROL1(ViewHostMsg_TranslateText,
- ViewHostMsg_TranslateTextParam)
-
//---------------------------------------------------------------------------
// Geolocation services messages
diff --git a/chrome/common/translate_errors.h b/chrome/common/translate_errors.h
index de2e3bd..c8f4225 100644
--- a/chrome/common/translate_errors.h
+++ b/chrome/common/translate_errors.h
@@ -11,8 +11,10 @@ class TranslateErrors {
public:
enum Type {
NONE = 0,
- NETWORK = 1,
- SERVER,
+ NETWORK, // No connectivity.
+ INITIALIZATION_ERROR, // The translation script failed to initialize.
+ TRANSLATION_ERROR, // An error was reported by the translation script
+ // during translation.
};
private:
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc
index cd649e7..6cfb152 100644
--- a/chrome/renderer/render_view.cc
+++ b/chrome/renderer/render_view.cc
@@ -343,12 +343,11 @@ RenderView::RenderView(RenderThreadBase* render_thread,
document_tag_(0),
webkit_preferences_(webkit_preferences),
session_storage_namespace_id_(session_storage_namespace_id),
- ALLOW_THIS_IN_INITIALIZER_LIST(text_translator_(this)),
ALLOW_THIS_IN_INITIALIZER_LIST(cookie_jar_(this)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(translate_helper_(this)),
cross_origin_access_count_(0),
same_origin_access_count_(0) {
ClearBlockedContentSettings();
- page_translator_.reset(new PageTranslator(&text_translator_, this));
}
RenderView::~RenderView() {
@@ -633,7 +632,7 @@ void RenderView::OnMessageReceived(const IPC::Message& message) {
IPC_MESSAGE_HANDLER(ViewMsg_CustomContextMenuAction,
OnCustomContextMenuAction)
IPC_MESSAGE_HANDLER(ViewMsg_TranslatePage, OnTranslatePage)
- IPC_MESSAGE_HANDLER(ViewMsg_TranslateTextReponse, OnTranslateTextResponse)
+ IPC_MESSAGE_HANDLER(ViewMsg_RevertTranslation, OnRevertTranslation)
// Have the super handle all other messages.
IPC_MESSAGE_UNHANDLED(RenderWidget::OnMessageReceived(message))
@@ -2522,12 +2521,6 @@ void RenderView::didCommitProvisionalLoad(WebFrame* frame,
NavigationState* navigation_state =
NavigationState::FromDataSource(frame->dataSource());
- if (!frame->parent()) { // Main frame case.
- // Let the page translator know that the page has changed so it can clear
- // its states.
- page_translator_->MainFrameNavigated();
- }
-
navigation_state->set_commit_load_time(Time::Now());
if (is_new_navigation) {
// When we perform a new navigation, we need to update the previous session
@@ -2653,9 +2646,6 @@ void RenderView::didFinishDocumentLoad(WebFrame* frame) {
}
navigation_state->user_script_idle_scheduler()->DidFinishDocumentLoad();
-
- if (page_translator_->IsPageTranslated())
- page_translator_->TranslateFrame(frame);
}
void RenderView::OnUserScriptIdleTriggered(WebFrame* frame) {
@@ -3182,15 +3172,6 @@ WebCookieJar* RenderView::GetCookieJar() {
return &cookie_jar_;
}
-void RenderView::PageTranslated(int page_id,
- const std::string& original_lang,
- const std::string& target_lang,
- TranslateErrors::Type error_type) {
- Send(new ViewHostMsg_PageTranslated(routing_id_, page_id_,
- original_lang, target_lang,
- error_type));
-}
-
void RenderView::SyncNavigationState() {
if (!webview())
return;
@@ -3808,22 +3789,15 @@ void RenderView::OnCustomContextMenuAction(unsigned action) {
}
void RenderView::OnTranslatePage(int page_id,
+ const std::string& translate_script,
const std::string& source_lang,
const std::string& target_lang) {
- if (page_id != page_id_)
- return; // Not the page we expected, nothing to do.
-
- WebFrame* main_frame = webview()->mainFrame();
- if (!main_frame)
- return;
-
- page_translator_->TranslatePage(page_id, main_frame,
- source_lang, target_lang);
+ translate_helper_.TranslatePage(page_id, source_lang, target_lang,
+ translate_script);
}
-void RenderView::OnTranslateTextResponse(
- int work_id, int error_id, const std::vector<string16>& text_chunks) {
- text_translator_.OnTranslationResponse(work_id, error_id, text_chunks);
+void RenderView::OnRevertTranslation(int page_id) {
+ translate_helper_.RevertTranslation(page_id);
}
void RenderView::OnInstallMissingPlugin() {
diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h
index ffd2ebb..cd42daf 100644
--- a/chrome/renderer/render_view.h
+++ b/chrome/renderer/render_view.h
@@ -40,8 +40,7 @@
#include "chrome/renderer/render_widget.h"
#include "chrome/renderer/render_view_visitor.h"
#include "chrome/renderer/renderer_webcookiejar_impl.h"
-#include "chrome/renderer/translate/page_translator.h"
-#include "chrome/renderer/translate/text_translator_impl.h"
+#include "chrome/renderer/translate_helper.h"
#include "gfx/point.h"
#include "gfx/rect.h"
#include "third_party/skia/include/core/SkBitmap.h"
@@ -135,8 +134,7 @@ class RenderView : public RenderWidget,
public WebKit::WebFrameClient,
public WebKit::WebPageSerializerClient,
public webkit_glue::WebPluginPageDelegate,
- public base::SupportsWeakPtr<RenderView>,
- public PageTranslator::PageTranslatorDelegate {
+ public base::SupportsWeakPtr<RenderView> {
public:
// Visit all RenderViews with a live WebView (i.e., RenderViews that have
// been closed but not yet destroyed are excluded).
@@ -418,12 +416,6 @@ class RenderView : public RenderWidget,
std::string* json_retval);
virtual WebKit::WebCookieJar* GetCookieJar();
- // PageTranslator::PageTranslatorDelegate implementation:
- virtual void PageTranslated(int page_id,
- const std::string& original_lang,
- const std::string& target_lang,
- TranslateErrors::Type error_type);
-
// Do not delete directly. This class is reference counted.
virtual ~RenderView();
@@ -494,8 +486,6 @@ class RenderView : public RenderWidget,
// UserScript::DOCUMENT_IDLE.
void OnUserScriptIdleTriggered(WebKit::WebFrame* frame);
- PageTranslator* page_translator() const { return page_translator_.get(); }
-
#if defined(OS_MACOSX)
// Helper routines for GPU plugin support. Used by the
// WebPluginDelegateProxy, which has a pointer to the RenderView.
@@ -800,15 +790,15 @@ class RenderView : public RenderWidget,
// Execute custom context menu action.
void OnCustomContextMenuAction(unsigned action);
- // Tells the renderer to translate the page contents.
+ // Translates the page contents from |source_lang| to |target_lang| by
+ // injecting |translate_script| in the page.
void OnTranslatePage(int page_id,
+ const std::string& translate_script,
const std::string& source_lang,
const std::string& target_lang);
- // Message that provides the translated text for a request.
- void OnTranslateTextResponse(int work_id,
- int error_id,
- const std::vector<string16>& text_chunks);
+ // Reverts the page's text to its original contents.
+ void OnRevertTranslation(int page_id);
// Exposes the DOMAutomationController object that allows JS to send
// information to the browser process.
@@ -1183,10 +1173,6 @@ class RenderView : public RenderWidget,
// uses it whenever asking the browser process to allocate new storage areas.
int64 session_storage_namespace_id_;
- // Page translation related objects.
- TextTranslatorImpl text_translator_;
- scoped_ptr<PageTranslator> page_translator_;
-
// A list of all pepper plugins that we've created that haven't been
// destroyed yet.
std::set<WebPluginDelegatePepper*> current_pepper_plugins_;
@@ -1206,6 +1192,10 @@ class RenderView : public RenderWidget,
RendererWebCookieJarImpl cookie_jar_;
+ // The object responsible for translating the page contents to other
+ // languages.
+ TranslateHelper translate_helper_;
+
// Site isolation metrics flags. These are per-page-load counts, reset to 0
// in OnClosePage.
int cross_origin_access_count_;
diff --git a/chrome/renderer/translate/page_translator.cc b/chrome/renderer/translate/page_translator.cc
deleted file mode 100644
index cba0001..0000000
--- a/chrome/renderer/translate/page_translator.cc
+++ /dev/null
@@ -1,324 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/renderer/translate/page_translator.h"
-
-#include "base/compiler_specific.h"
-#include "base/message_loop.h"
-#include "base/stl_util-inl.h"
-#include "base/string_util.h"
-#include "base/task.h"
-#include "chrome/renderer/navigation_state.h"
-#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h"
-#include "third_party/WebKit/WebKit/chromium/public/WebDataSource.h"
-#include "third_party/WebKit/WebKit/chromium/public/WebElement.h"
-#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
-#include "third_party/WebKit/WebKit/chromium/public/WebNode.h"
-#include "third_party/WebKit/WebKit/chromium/public/WebNodeList.h"
-#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
-
-namespace {
-
-// The following elements are not supposed to be translated.
-const char* const kSkippedTags[] = { "APPLET", "AREA", "BASE", "FRAME",
- "FRAMESET", "HR", "IFRAME", "IMG", "INPUT", "LINK", "META", "MAP",
- "OBJECT", "PARAM", "SCRIPT", "STYLE", "TEXTAREA" };
-
-// The following tags are not considered as breaking a block of text.
-// Notes: does SPAN belong to this list?
-const char* const kInlineTags[] = { "A", "ABBR", "ACRONYM", "B", "BIG", "DEL",
- "EM", "I", "INS", "S", "SPAN", "STRIKE", "STRONG", "SUB", "SUP", "U" };
-}
-
-// A text node containing only characters in kIgnoredCharacters is not
-// translated.
-const char* const kIgnoredCharacters = ":,.[|]0123456789";
-
-// Returns true when s1 < s2.
-bool PageTranslator::WebStringCompare::operator()(
- const WebKit::WebString& s1, const WebKit::WebString& s2) const {
- int len1 = s1.length();
- int len2 = s2.length();
- int r = base::strncmp16(s1.data(), s2.data(), std::min(len1, len2));
-
- if (r < 0)
- return true;
- else if (r > 0)
- return false;
-
- return len1 < len2;
-}
-
-PageTranslator::PageTranslator(TextTranslator* text_translator,
- PageTranslatorDelegate* delegate)
- : delegate_(delegate),
- text_translator_(text_translator),
- page_id_(-1),
- secure_page_(false) {
- for (size_t i = 0; i < arraysize(kSkippedTags); ++i)
- ignored_tags_.insert(WebKit::WebString(ASCIIToUTF16(kSkippedTags[i])));
- for (size_t i = 0; i < arraysize(kInlineTags); ++i)
- inline_tags_.insert(WebKit::WebString(ASCIIToUTF16(kInlineTags[i])));
- ignore_characters_ = ASCIIToUTF16(kIgnoredCharacters);
- ignore_characters_.append(kWhitespaceUTF16);
-}
-
-PageTranslator::~PageTranslator() {
- ResetPageStates();
-}
-
-void PageTranslator::TranslatePage(int page_id,
- WebKit::WebFrame* main_frame,
- std::string source_lang,
- std::string target_lang) {
- if (page_id != page_id_) {
- // This is a new page, our states are invalid.
- ResetPageStates();
- page_id_ = page_id;
- secure_page_ = static_cast<GURL>(main_frame->top()->url()).SchemeIsSecure();
-
- original_language_ = source_lang;
- current_language_ = target_lang;
-
- // Translate all frames contained within the main-frame.
- for (WebKit::WebFrame* frame = main_frame;
- frame; frame = frame->traverseNext(false)) {
- TranslateFrame(frame);
- }
- return;
- }
-
- // The page has already been translated, the text nodes are already available.
- DCHECK(!text_nodes_.empty());
-
- if (target_lang == original_language_) {
- // Special case where we want to revert to the original language.
- RevertTranslation();
- return;
- }
-
- // Any pending translation is now useless.
- ClearPendingTranslations();
- // No need to parse again the DOM, we have all the text nodes and their
- // original text in |text_nodes_| and |text_chunks_|.
- std::vector<NodeList*>::iterator text_nodes_iter = text_nodes_.begin();
- std::vector<TextChunks*>::iterator text_chunks_iter = text_chunks_.begin();
- for (;text_nodes_iter != text_nodes_.end();
- ++text_nodes_iter, ++text_chunks_iter) {
- DCHECK(text_chunks_iter != text_chunks_.end());
- int work_id = text_translator_->Translate(**text_chunks_iter,
- source_lang, target_lang,
- secure_page_, this);
- pending_translations_[work_id] = *text_nodes_iter;
- }
- current_language_ = target_lang;
-}
-
-void PageTranslator::TranslateFrame(WebKit::WebFrame* web_frame) {
- if (page_id_ == -1)
- return; // The page has not been translated, ignore.
-
- DCHECK(!original_language_.empty() && !current_language_.empty());
-
- WebKit::WebDataSource* ds = web_frame->dataSource();
- NavigationState* navigation_state = NavigationState::FromDataSource(ds);
- DCHECK(navigation_state);
- if (navigation_state->was_translated())
- return; // This frame has already been translated, nothing to do.
-
- // If the frame has no document or an empty document, it may not have been
- // loaded yet.
- if (web_frame->document().isNull() ||
- web_frame->document().childNodes().length() == 0) {
- return;
- }
-
- std::stack<NodeList*> node_list_stack;
- std::vector<NodeList*> text_node_lists;
- TraverseNode(web_frame->document(), &node_list_stack, &text_node_lists);
-
- std::vector<NodeList*>::iterator iter;
- for (iter = text_node_lists.begin(); iter != text_node_lists.end(); ++iter) {
- if ((*iter)->empty()) {
- // Nothing to translate.
- continue;
- }
- TextChunks* text_chunks = new TextChunks; // The text chunks to translate.
- NodeList::iterator text_nodes_iter;
- for (text_nodes_iter = (*iter)->begin();
- text_nodes_iter != (*iter)->end(); ++text_nodes_iter) {
- DCHECK(text_nodes_iter->isTextNode());
- string16 text = static_cast<string16>(text_nodes_iter->nodeValue());
- DCHECK(!ContainsOnlyWhitespace(text));
- text_chunks->push_back(text);
- }
-
- // Send the text for translation.
- int work_id =
- text_translator_->Translate(*text_chunks,
- original_language_, current_language_,
- secure_page_, this);
- pending_translations_[work_id] = *iter;
- // Also store the text nodes and their original text so we can translate to
- // another language if necessary.
- text_nodes_.push_back(*iter);
- text_chunks_.push_back(text_chunks);
- }
-
- navigation_state->set_was_translated(true);
-}
-
-void PageTranslator::MainFrameNavigated() {
- // We can drop all our states, they were related to the previous page.
- ResetPageStates();
-}
-
-bool PageTranslator::IsPageTranslated() {
- return original_language_ != current_language_;
-}
-
-bool PageTranslator::ShouldElementBeTraversed(WebKit::WebElement element) {
- return ignored_tags_.find(element.tagName()) == ignored_tags_.end();
-}
-
-bool PageTranslator::IsInlineElement(WebKit::WebElement element) {
- return inline_tags_.find(element.tagName()) != inline_tags_.end();
-}
-
-void PageTranslator::ClearNodeZone(int work_id) {
- std::map<int, NodeList*>::iterator iter = pending_translations_.find(work_id);
- if (iter == pending_translations_.end()) {
- NOTREACHED() << "Clearing unknown node zone in pending_translations_, "
- "work id=" << work_id;
- return;
- }
- pending_translations_.erase(iter);
-}
-
-void PageTranslator::TranslationError(int work_id, int error_id) {
- // TODO(jcampan): may be we should show somehow that something went wrong to
- // the user?
- ClearNodeZone(work_id);
-}
-
-void PageTranslator::TextTranslated(
- int work_id, const std::vector<string16>& translated_text_chunks) {
- std::map<int, NodeList*>::iterator iter = pending_translations_.find(work_id);
- if (iter == pending_translations_.end()) {
- // We received some translated text we were not expecting. It could be we
- // navigated away from the page or that the translation was undone.
- return;
- }
-
- NodeList* nodes = iter->second;
- // Check the integrity of the response.
- if (translated_text_chunks.size() != nodes->size()) {
- // The server might merge or split chunks in some cases.
- // TODO(jcampan): once the issue is resolved on the server, reenable that
- // NOTREACHED().
- // NOTREACHED() << "Translation results received are inconsistent with the "
- // "request";
- LOG(ERROR) << "translation response for work id " << work_id <<
- " length is " << translated_text_chunks.size() << " expected " <<
- nodes->size();
- ClearNodeZone(work_id);
- return;
- }
-
- for (size_t i = 0; i < translated_text_chunks.size(); ++i)
- (*nodes)[i].setNodeValue(WebKit::WebString(translated_text_chunks[i]));
-
- ClearNodeZone(work_id);
-
- if (delegate_ && pending_translations_.empty()) {
- // TODO(jcivelli): if there's error, pass in the actual error type.
- TranslateErrors::Type error_type = TranslateErrors::NONE;
- delegate_->PageTranslated(page_id_, original_language_, current_language_,
- error_type);
- }
-}
-
-void PageTranslator::TraverseNode(WebKit::WebNode node,
- std::stack<NodeList*>* element_stack,
- std::vector<NodeList*>* text_nodes_list) {
- if (node.isTextNode()) {
- string16 text = static_cast<string16>(node.nodeValue());
- if (ContainsOnlyChars(text, ignore_characters_))
- return; // Ignore text nodes which contains only white-spaces or
- // separators.
-
- DCHECK(!element_stack->empty());
- NodeList* text_nodes = element_stack->top();
- if (text_nodes->empty()) {
- // This node zone is empty, meaning it has not yet been added to
- // |text_nodes|.
- text_nodes_list->push_back(text_nodes);
- }
- text_nodes->push_back(node);
- return;
- }
-
- if (!node.hasChildNodes())
- return;
-
- bool new_text_block = false;
- if (node.isElementNode()) {
- WebKit::WebElement element = node.toElement<WebKit::WebElement>();
- if (!ShouldElementBeTraversed(element))
- return;
-
- if (!IsInlineElement(element)) {
- new_text_block = true;
- NodeList* text_nodes = new NodeList();
- element_stack->push(text_nodes);
- }
- }
-
- WebKit::WebNodeList children = node.childNodes();
- for (size_t i = 0; i < children.length(); i++)
- TraverseNode(children.item(i), element_stack, text_nodes_list);
-
- if (new_text_block) {
- NodeList* text_nodes = element_stack->top();
- element_stack->pop();
- // If no nodes were added to text_nodes, then it has not been added to
- // text_nodes_list and must be deleted.
- if (text_nodes->empty())
- delete text_nodes;
- }
-}
-
-void PageTranslator::ResetPageStates() {
- page_id_ = -1;
- secure_page_ = false;
- STLDeleteElements(&text_nodes_);
- STLDeleteElements(&text_chunks_);
- original_language_.clear();
- current_language_.clear();
- ClearPendingTranslations();
-}
-
-void PageTranslator::ClearPendingTranslations() {
- pending_translations_.clear();
-}
-
-void PageTranslator::RevertTranslation() {
- ClearPendingTranslations();
-
- DCHECK(!text_nodes_.empty());
-
- std::vector<NodeList*>::iterator text_nodes_iter = text_nodes_.begin();
- std::vector<TextChunks*>::iterator text_chunks_iter = text_chunks_.begin();
- for (;text_nodes_iter != text_nodes_.end();
- ++text_nodes_iter, ++text_chunks_iter) {
- DCHECK(text_chunks_iter != text_chunks_.end());
- DCHECK((*text_nodes_iter)->size() == (*text_chunks_iter)->size());
- NodeList::iterator node_iter = (*text_nodes_iter)->begin();
- TextChunks::const_iterator text_iter = (*text_chunks_iter)->begin();
- for (; node_iter != (*text_nodes_iter)->end(); ++node_iter, ++text_iter) {
- node_iter->setNodeValue(*text_iter);
- }
- }
- current_language_ = original_language_;
-}
diff --git a/chrome/renderer/translate/page_translator.h b/chrome/renderer/translate/page_translator.h
deleted file mode 100644
index 2b1a9c3..0000000
--- a/chrome/renderer/translate/page_translator.h
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_RENDERER_TRANSLATE_PAGE_TRANSLATOR_H_
-#define CHROME_RENDERER_TRANSLATE_PAGE_TRANSLATOR_H_
-
-#include <map>
-#include <set>
-#include <stack>
-#include <string>
-#include <vector>
-
-#include "base/logging.h"
-#include "base/scoped_ptr.h"
-#include "base/string16.h"
-#include "chrome/common/translate_errors.h"
-#include "chrome/renderer/translate/text_translator.h"
-#include "third_party/WebKit/WebKit/chromium/public/WebElement.h"
-
-class RenderView;
-
-namespace WebKit {
-class WebFrame;
-class WebNode;
-class WebString;
-}
-
-// The PageTranslator is a service that translates the text content of a web
-// page from one language to another (ex: English to French).
-// It performs the traversal of the DOM of the page to retrieve the text nodes
-// and delegates the actual text translation to a TextTranslator.
-class PageTranslator : public TextTranslator::Delegate {
- public:
- // Note that we don't use the simpler name Delegate as it seems to freak-out
- // the VisualStudio 2005 compiler in render_view.cc. (RenderView would be
- // implementing PageTranslator::Delegate that somehow confuses the compiler
- // into thinking we are talkin about TextTranslator::Delegate.)
- class PageTranslatorDelegate {
- public:
- virtual ~PageTranslatorDelegate() {}
- virtual void PageTranslated(int page_id,
- const std::string& original_lang,
- const std::string& target_lang,
- TranslateErrors::Type error_type) = 0;
- };
-
- // The caller remains the owner of |text_translator|.
- PageTranslator(TextTranslator* text_translator,
- PageTranslatorDelegate* delegate);
- virtual ~PageTranslator();
-
- // Translate the text in the page contained in |main_frame|.
- // It is translated from |source_lang| to |target_lang| where the languages
- // are the ISO codes (ex: en, fr...).
- // All sub-frames contained in |main_frame| are translated.
- void TranslatePage(int page_id,
- WebKit::WebFrame* main_frame,
- std::string source_lang,
- std::string target_lang);
-
- // Translates the contents of |frame| if it has not already been translated
- // and if the main frame was previously translated.
- // (Note this should not be called for the main frame, use TranslatePage for
- // it).
- void TranslateFrame(WebKit::WebFrame* frame);
-
- // Notification that the main frame of the current page has navigated.
- // This invalidates all our page states.
- void MainFrameNavigated();
-
- // Returns true if the current page has been translated.
- bool IsPageTranslated();
-
- // TextTranslator::Delegate implentation:
- virtual void TranslationError(int work_id, int error_id);
- virtual void TextTranslated(
- int work_id, const std::vector<string16>& translated_text);
-
- private:
- // Comparator used in set of WebKit WebStrings.
- struct WebStringCompare {
- bool operator()(const WebKit::WebString& s1,
- const WebKit::WebString& s2) const;
- };
-
- typedef std::vector<WebKit::WebNode> NodeList;
- typedef std::vector<string16> TextChunks;
-
- // Traverses the tree starting at |node| and fills |nodes| with the
- // elements necessary for translation.
- // |element_stack| is used to retrieve the current node list during the tree
- // traversal.
- void TraverseNode(WebKit::WebNode node,
- std::stack<NodeList*>* element_stack,
- std::vector<NodeList*>* nodes);
-
- // Whether this |element| should be parsed or ignored for translation purpose.
- bool ShouldElementBeTraversed(WebKit::WebElement element);
-
- // Whether this element should be considered as part of the other text nodes
- // at the same hiearchical level.
- bool IsInlineElement(WebKit::WebElement element);
-
- // Removes and deletes the NodeZone for |work_id| in pending_translations_.
- void ClearNodeZone(int work_id);
-
- // Clears all the states related to the page's contents.
- void ResetPageStates();
-
- // Clears any pending translation requests. Any response for a pending
- // request received after this call will be ignored.
- void ClearPendingTranslations();
-
- // Reverts the text nodes in the page to their original text.
- void RevertTranslation();
-
- // Our delegate (notified when a page is translated).
- PageTranslatorDelegate* delegate_;
-
- // The TextTranslator is responsible for translating the actual text chunks
- // from one language to another.
- TextTranslator* text_translator_;
-
- // The list of tags we are not interested in parsing when translating.
- std::set<WebKit::WebString, WebStringCompare> ignored_tags_;
-
- // The list of tags that do not break a block of text.
- std::set<WebKit::WebString, WebStringCompare> inline_tags_;
-
- // Mapping from a translation engine work id to the associated nodes.
- std::map<int, NodeList*> pending_translations_;
-
- // The language the page was in originally.
- std::string original_language_;
-
- // The language the page was translated to.
- std::string current_language_;
-
- // The page id of the page last time we translated.
- int page_id_;
-
- // True if the page is served over HTTPS.
- bool secure_page_;
-
- // The list of text zones in the current page, grouped in text zones (text
- // nodes grouped in a same context).
- std::vector<NodeList*> text_nodes_;
- // The original text of the text nodes in |text_nodes_|.
- std::vector<TextChunks*> text_chunks_;
-
- // A text node containing only the characters in this list is not translated.
- string16 ignore_characters_;
-
- DISALLOW_COPY_AND_ASSIGN(PageTranslator);
-};
-
-#endif // CHROME_RENDERER_TRANSLATE_PAGE_TRANSLATOR_H_
diff --git a/chrome/renderer/translate/page_translator_unittest.cc b/chrome/renderer/translate/page_translator_unittest.cc
deleted file mode 100644
index 02f1b32..0000000
--- a/chrome/renderer/translate/page_translator_unittest.cc
+++ /dev/null
@@ -1,215 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/file_path.h"
-#include "base/file_util.h"
-#include "base/path_service.h"
-#include "chrome/renderer/translate/page_translator.h"
-#include "chrome/test/render_view_test.h"
-#include "net/base/net_errors.h"
-
-class TranslatorTest : public RenderViewTest {
- public:
- TranslatorTest() {}
-};
-
-// A TextTranslator used that simply reverse the strings that are provided to
-// it.
-class ReverseTextTranslator : public TextTranslator {
- public:
- ReverseTextTranslator()
- : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
- work_id_counter_(0) {
- }
-
- virtual int Translate(const std::vector<string16>& text_chunks,
- std::string from_lang,
- std::string to_lang,
- bool secure,
- TextTranslator::Delegate* delegate) {
- int work_id = work_id_counter_++;
-
- std::vector<string16> translated_text_chunks;
- for (std::vector<string16>::const_iterator iter = text_chunks.begin();
- iter != text_chunks.end(); ++iter) {
- translated_text_chunks.push_back(ReverseString(*iter));
- }
- MessageLoop::current()->PostTask(FROM_HERE,
- method_factory_.NewRunnableMethod(
- &ReverseTextTranslator::NotifyDelegate,
- work_id,
- translated_text_chunks,
- delegate));
- return work_id;
- }
-
- private:
- void NotifyDelegate(int work_id,
- const std::vector<string16>& text_chunks,
- TextTranslator::Delegate* delegate) {
- delegate->TextTranslated(work_id, text_chunks);
- }
-
- string16 ReverseString(const string16& str) {
- string16 result;
- for (string16::const_reverse_iterator iter = str.rbegin();
- iter != str.rend(); ++iter) {
- result.push_back(*iter);
- }
- return result;
- }
-
- ScopedRunnableMethodFactory<ReverseTextTranslator> method_factory_;
-
- int work_id_counter_;
-
- DISALLOW_COPY_AND_ASSIGN(ReverseTextTranslator);
-};
-
-// A simple ResourceLoaderBridge that always fails to load.
-class DummyResourceLoaderBridge : public webkit_glue::ResourceLoaderBridge {
- public:
- DummyResourceLoaderBridge() { }
-
- virtual void AppendDataToUpload(const char* data, int data_len) {}
- virtual void AppendFileRangeToUpload(
- const FilePath& file_path,
- uint64 offset,
- uint64 length,
- const base::Time& expected_modification_time) {}
- virtual void SetUploadIdentifier(int64 identifier) {}
- virtual bool Start(Peer* peer) { return false; }
- virtual void Cancel() {}
- virtual void SetDefersLoading(bool value) {}
- virtual void SyncLoad(SyncLoadResponse* response) {
- response->status.set_status(URLRequestStatus::FAILED);
- response->status.set_os_error(net::ERR_FAILED);
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(DummyResourceLoaderBridge);
-};
-
-// A ChildThread class that creates a dummy resource loader bridge so that
-// page with resources can be loaded (as data:...) without asserting.
-class TestChildThread : public ChildThread {
- public:
- TestChildThread() {}
-
- virtual webkit_glue::ResourceLoaderBridge* CreateBridge(
- const webkit_glue::ResourceLoaderBridge::RequestInfo& request_info,
- int host_renderer_id,
- int host_render_view_id) {
- return new DummyResourceLoaderBridge;
- }
-
- // Overriden so it does not terminate the message loop. That would assert as
- // the message loop is running in the test.
- virtual void OnProcessFinalRelease() { }
-};
-
-// Tests that we parse and change text in pages correctly.
-// It loads all the <name>_ORIGINAL.html files under the
-// chrome/test/data/translate directory. There must be a matching
-// <name>_TRANLSATED.html in the directory.
-// The _ORIGINAL page is loaded and translated.
-// The result is compared to the _TRANSLATED page.
-// Note: _TRANSLATED.html files can be generated using the reverse_text.py
-// script located in the chrome/test/data/translate directory.
-// TODO(jcampan): http://crbug.com/32217 This test is disabled as it sometimes
-// fails on Windows and always fails on Unix. We need to improve
-// RenderViewTest so it supports loading tags that contain links
-// and sub-resources.
-TEST_F(TranslatorTest, DISABLED_TranslatePages) {
- // Create the RenderThread singleton. Rendering pages requires a
- // VisitedLinkSlave that this object creates.
- RenderThread render_thread;
-
- // Create the ChildThread singleton. It is used to create the resource
- // bridges. (It is owned by the ChildProcess.)
- ChildProcess::current()->set_main_thread(new TestChildThread());
-
- FilePath data_dir;
- ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir));
- data_dir = data_dir.Append(FILE_PATH_LITERAL("chrome"));
- data_dir = data_dir.Append(FILE_PATH_LITERAL("test"));
- data_dir = data_dir.Append(FILE_PATH_LITERAL("data"));
- data_dir = data_dir.Append(FILE_PATH_LITERAL("translate"));
-
- file_util::FileEnumerator file_enumerator(
- data_dir, false, file_util::FileEnumerator::FILES,
- FILE_PATH_LITERAL("*_ORIGINAL.html"));
-
- FilePath original_file_path = file_enumerator.Next();
- while (!original_file_path.empty()) {
- // Locate the _TRANSLATED.html file.
- FilePath::StringType original_base = original_file_path.BaseName().value();
- LOG(INFO) << "Processing file " << original_base;
- size_t orig_index =
- original_base.rfind(FILE_PATH_LITERAL("_ORIGINAL.html"));
- ASSERT_NE(FilePath::StringType::npos, orig_index);
- FilePath::StringType translated_base = original_base.substr(0, orig_index) +
- FILE_PATH_LITERAL("_TRANSLATED.html");
-
- FilePath translated_file_path(original_file_path.DirName());
- translated_file_path = translated_file_path.Append(translated_base);
-
- ASSERT_TRUE(file_util::PathExists(translated_file_path));
-
- // Load the original file.
- int64 size;
- ASSERT_TRUE(file_util::GetFileSize(original_file_path, &size));
- scoped_array<char> buffer(new char[static_cast<size_t>(size) + 1]);
- ASSERT_EQ(size, file_util::ReadFile(original_file_path, buffer.get(),
- static_cast<int>(size)));
- buffer[static_cast<size_t>(size)] = '\0';
- LoadHTML(buffer.get());
-
- WebKit::WebFrame* web_frame = GetMainFrame();
- ASSERT_TRUE(web_frame);
-
- // Translate it.
- ReverseTextTranslator text_translator;
- PageTranslator translator(&text_translator, NULL);
- translator.TranslatePage(0, web_frame, "en", "fr");
-
- // Translation is asynchronous, so we need to process the pending messages
- // to make it happen.
- MessageLoop::current()->RunAllPending();
-
- WebKit::WebString actual_translated_contents = web_frame->contentAsMarkup();
-
- // Load the translated page.
- ASSERT_TRUE(file_util::GetFileSize(translated_file_path, &size));
- buffer.reset(new char[static_cast<size_t>(size) + 1]);
- ASSERT_EQ(size, file_util::ReadFile(translated_file_path, buffer.get(),
- static_cast<int>(size)));
- buffer[static_cast<size_t>(size)] = '\0';
- LoadHTML(buffer.get());
-
- web_frame = GetMainFrame();
- ASSERT_TRUE(web_frame);
- WebKit::WebString expected_translated_contents =
- web_frame->contentAsMarkup();
-
- EXPECT_EQ(expected_translated_contents.length(),
- actual_translated_contents.length());
-
- // We compare the actual and expected results by chunks of 80 chars to make
- // debugging easier.
- int max = std::min(expected_translated_contents.length(),
- actual_translated_contents.length());
- int index = 0;
- while (index < max) {
- int len = std::min(80, max - index);
- string16 expected(expected_translated_contents.data() + index, len);
- string16 actual(actual_translated_contents.data() + index, len);
- ASSERT_EQ(expected, actual);
- index += 80;
- }
-
- // Iterate to the next file to test.
- original_file_path = file_enumerator.Next();
- }
-}
diff --git a/chrome/renderer/translate/text_translator.h b/chrome/renderer/translate/text_translator.h
deleted file mode 100644
index b4e7581..0000000
--- a/chrome/renderer/translate/text_translator.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_RENDERER_TRANSLATE_TEXT_TRANSLATOR_H_
-#define CHROME_RENDERER_TRANSLATE_TEXT_TRANSLATOR_H_
-
-#include <string>
-#include <vector>
-
-#include "base/string16.h"
-
-// TextTranslator is an interface that is implemented by providers that know
-// how to translate text from one language to another.
-// It is asynchronous. Clients call Translate() with the text to translate and
-// receive a work id. The implementation should call the TextTranslated
-// method on the delegate once the text has been translated.
-
-class TextTranslator {
- public:
- class Delegate {
- public:
- virtual ~Delegate() {}
-
- // Notifies that the translation failed for |work_id|.
- virtual void TranslationError(int work_id, int error_id) = 0;
-
- // Notifies that the translation for |work_id| succeeded.
- virtual void TextTranslated(
- int work_id, const std::vector<string16>& translated_text) = 0;
- };
-
- TextTranslator() {}
- virtual ~TextTranslator() {}
-
- // Initiates the translation of the |text| provided, from the language
- // |from_lang| to |to_lang| (these are the ISO language code, for example en,
- // fr, ja...). If |secure| is true then a secure communication method (HTTPS)
- // should be used if using a remote resource to perform the translation.
- // Returns a work id that is passed as a parameter when delegate methods are
- // called on |delegate| to notify the translation succeeded/failed.
- virtual int Translate(const std::vector<string16>& text,
- std::string from_lang,
- std::string to_lang,
- bool secure,
- TextTranslator::Delegate* delegate) = 0;
-};
-
-#endif // CHROME_RENDERER_TRANSLATE_TEXT_TRANSLATOR_H_
diff --git a/chrome/renderer/translate/text_translator_impl.cc b/chrome/renderer/translate/text_translator_impl.cc
deleted file mode 100644
index d2b668c..0000000
--- a/chrome/renderer/translate/text_translator_impl.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/renderer/translate/text_translator_impl.h"
-
-#include "chrome/common/render_messages.h"
-#include "chrome/renderer/render_view.h"
-
-TextTranslatorImpl::TextTranslatorImpl(RenderView* render_view)
- : render_view_(render_view),
- work_id_counter_(0) {
-}
-
-void TextTranslatorImpl::OnTranslationResponse(
- int work_id, int error_id, const std::vector<string16>& text_chunks) {
- if (error_id) {
- render_view_->page_translator()->TranslationError(work_id, error_id);
- return;
- }
- render_view_->page_translator()->TextTranslated(work_id, text_chunks);
-}
-
-int TextTranslatorImpl::Translate(const std::vector<string16>& text,
- std::string from_lang,
- std::string to_lang,
- bool secure,
- TextTranslator::Delegate* delegate) {
- ViewHostMsg_TranslateTextParam param;
- param.routing_id = render_view_->routing_id();
- param.page_id = render_view_->page_id();
- param.work_id = work_id_counter_++;
- param.from_language = from_lang;
- param.to_language = to_lang;
- param.text_chunks = text;
- param.secure = secure;
-
- render_view_->Send(new ViewHostMsg_TranslateText(param));
-
- return param.work_id;
-}
diff --git a/chrome/renderer/translate/text_translator_impl.h b/chrome/renderer/translate/text_translator_impl.h
deleted file mode 100644
index 566a084..0000000
--- a/chrome/renderer/translate/text_translator_impl.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_RENDERER_TRANSLATE_TEXT_TRANSLATOR_IMPL_H_
-#define CHROME_RENDERER_TRANSLATE_TEXT_TRANSLATOR_IMPL_H_
-
-#include <string>
-#include <vector>
-
-#include "base/logging.h"
-#include "chrome/renderer/translate/text_translator.h"
-
-class RenderView;
-
-// An implementation of the TextTranslator that sends translation requests
-// to the browser.
-// There is one instance of TextTranslatorImpl per RenderViewHost and the
-// RenderViewHost owns that instance.
-//
-// TODO(jcampan): limit the number of translation requests in flight so not to
-// swamp the browser's resource dispatcher host.
-
-class TextTranslatorImpl : public TextTranslator {
- public:
- explicit TextTranslatorImpl(RenderView* render_view);
-
- // Called by the renderer to notify a translation response has been received.
- // |error_id| is different than 0 if an error occurred.
- void OnTranslationResponse(int work_id,
- int error_id,
- const std::vector<string16>& text_chunks);
-
- // TextTranslator implementation.
- virtual int Translate(const std::vector<string16>& text,
- std::string from_lang,
- std::string to_lang,
- bool secure,
- TextTranslator::Delegate* delegate);
- private:
- // The render view through which translation requests/responses are
- // sent/received.
- RenderView* render_view_;
-
- // The counter used to create work ids.
- int work_id_counter_;
-
- DISALLOW_COPY_AND_ASSIGN(TextTranslatorImpl);
-};
-
-#endif // CHROME_RENDERER_TRANSLATE_TEXT_TRANSLATOR_IMPL_H_
diff --git a/chrome/renderer/translate_helper.cc b/chrome/renderer/translate_helper.cc
new file mode 100644
index 0000000..f2a3f66
--- /dev/null
+++ b/chrome/renderer/translate_helper.cc
@@ -0,0 +1,230 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/renderer/translate_helper.h"
+
+#include "base/compiler_specific.h"
+#include "chrome/renderer/render_view.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebScriptSource.h"
+
+using WebKit::WebFrame;
+using WebKit::WebScriptSource;
+
+// The delay in millliseconds that we'll wait before checking to see if the
+// translate library injected in the page is ready.
+static const int kTranslateInitCheckDelayMs = 150;
+
+// The maximum number of times we'll check to see if the translate library
+// injected in the page is ready.
+static const int kMaxTranslateInitCheckAttempts = 5;
+
+// The delay we wait in milliseconds before checking whether the translation has
+// finished.
+static const int kTranslateStatusCheckDelayMs = 400;
+
+////////////////////////////////////////////////////////////////////////////////
+// TranslateHelper, public:
+//
+TranslateHelper::TranslateHelper(RenderView* render_view)
+ : render_view_(render_view),
+ ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
+}
+
+void TranslateHelper::TranslatePage(int page_id,
+ const std::string& source_lang,
+ const std::string& target_lang,
+ const std::string& translate_script) {
+ if (render_view_->page_id() != page_id)
+ return; // We navigated away, nothing to do.
+
+ if (!IsTranslateLibAvailable()) {
+ // Evaluate the script to add the translation related method to the global
+ // context of the page.
+ ExecuteScript(translate_script);
+ 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);
+}
+
+void TranslateHelper::RevertTranslation(int page_id) {
+ if (render_view_->page_id() != page_id)
+ return; // We navigated away, nothing to do.
+
+ if (!IsTranslateLibAvailable()) {
+ NOTREACHED();
+ return;
+ }
+
+ WebFrame* main_frame = render_view_->webview()->mainFrame();
+ if (!main_frame)
+ return;
+
+ main_frame->executeScript(
+ WebScriptSource(ASCIIToUTF16("cr.googleTranslate.revert()")));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TranslateHelper, protected:
+//
+bool TranslateHelper::IsTranslateLibAvailable() {
+ bool lib_available = false;
+ if (!ExecuteScriptAndGetBoolResult(
+ "typeof cr != 'undefined' && typeof cr.googleTranslate != 'undefined' && "
+ "typeof cr.googleTranslate.translate == 'function'", &lib_available)) {
+ NOTREACHED();
+ return false;
+ }
+ return lib_available;
+}
+
+bool TranslateHelper::IsTranslateLibReady() {
+ bool lib_ready = false;
+ if (!ExecuteScriptAndGetBoolResult("cr.googleTranslate.libReady",
+ &lib_ready)) {
+ NOTREACHED();
+ return false;
+ }
+ return lib_ready;
+}
+
+bool TranslateHelper::HasTranslationFinished() {
+ bool translation_finished = false;
+ if (!ExecuteScriptAndGetBoolResult("cr.googleTranslate.finished",
+ &translation_finished)) {
+ NOTREACHED() << "crGoogleTranslateGetFinished returned unexpected value.";
+ return true;
+ }
+
+ return translation_finished;
+}
+
+bool TranslateHelper::HasTranslationFailed() {
+ bool translation_failed = false;
+ if (!ExecuteScriptAndGetBoolResult("cr.googleTranslate.error",
+ &translation_failed)) {
+ NOTREACHED() << "crGoogleTranslateGetError returned unexpected value.";
+ return true;
+ }
+
+ return translation_failed;
+}
+
+bool TranslateHelper::StartTranslation(const std::string& source_lang,
+ const std::string& target_lang) {
+ bool translate_success = false;
+ if (!ExecuteScriptAndGetBoolResult("cr.googleTranslate.translate('" +
+ source_lang + "','" + target_lang + "')",
+ &translate_success)) {
+ NOTREACHED();
+ return false;
+ }
+ return translate_success;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// 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())
+ 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);
+ return; // There was an error.
+ }
+
+ if (HasTranslationFinished()) {
+ // Translation was successfull, notify the browser.
+ render_view_->Send(new ViewHostMsg_PageTranslated(
+ render_view_->routing_id(), render_view_->page_id(),
+ 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),
+ DontDelayTasks() ? 0 : kTranslateStatusCheckDelayMs);
+}
+
+bool TranslateHelper::ExecuteScript(const std::string& script) {
+ WebFrame* main_frame = render_view_->webview()->mainFrame();
+ if (!main_frame)
+ return false;
+ main_frame->executeScript(WebScriptSource(ASCIIToUTF16(script)));
+ return true;
+}
+
+bool TranslateHelper::ExecuteScriptAndGetBoolResult(const std::string& script,
+ bool* 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->IsBoolean())
+ return false;
+
+ *value = v->BooleanValue();
+ return true;
+}
+
+void TranslateHelper::TranslatePageImpl(int page_id,
+ const std::string& source_lang,
+ const std::string& target_lang,
+ int count) {
+ DCHECK_LT(count, kMaxTranslateInitCheckAttempts);
+ 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);
+ 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);
+ return;
+ }
+ // Check the status of the translation.
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ method_factory_.NewRunnableMethod(&TranslateHelper::CheckTranslateStatus,
+ page_id, source_lang, target_lang),
+ DontDelayTasks() ? 0 : kTranslateStatusCheckDelayMs);
+}
+
+void TranslateHelper::NotifyBrowserTranslationFailed(
+ const std::string& source_lang,
+ const std::string& target_lang,
+ TranslateErrors::Type error) {
+ // 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));
+}
diff --git a/chrome/renderer/translate_helper.h b/chrome/renderer/translate_helper.h
new file mode 100644
index 0000000..efd5a72
--- /dev/null
+++ b/chrome/renderer/translate_helper.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_RENDERER_TRANSLATE_HELPER_H_
+#define CHROME_RENDERER_TRANSLATE_HELPER_H_
+
+#include <string>
+
+#include "base/task.h"
+#include "chrome/common/translate_errors.h"
+
+class RenderView;
+
+// This class deals with page translation.
+// There is one TranslateHelper per RenderView.
+
+class TranslateHelper {
+ public:
+ explicit TranslateHelper(RenderView* render_view);
+ virtual ~TranslateHelper() {}
+
+ // Translates the page contents from |source_lang| to |target_lang|.
+ // Does nothing if |page_id| is not the current page id.
+ // If the library is not ready, it will post a task to try again after 50ms.
+ void TranslatePage(int page_id,
+ const std::string& source_lang,
+ const std::string& target_lang,
+ const std::string& translate_script);
+
+ // Reverts the page's text to its original contents.
+ void RevertTranslation(int page_id);
+
+ protected:
+ // The following methods are protected so they can be overridden in
+ // unit-tests.
+
+ // Returns true if the translate library is available, meaning the JavaScript
+ // has already been injected in that page.
+ virtual bool IsTranslateLibAvailable();
+
+ // Returns true if the translate library has been initialized successfully.
+ virtual bool IsTranslateLibReady();
+
+ // Returns true if the translation script has finished translating the page.
+ virtual bool HasTranslationFinished();
+
+ // Returns true if the translation script has reported an error performing the
+ // translation.
+ virtual bool HasTranslationFailed();
+
+ // 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);
+
+ // Used in unit-tests. Makes the various tasks be posted immediately so that
+ // the tests don't have to wait before checking states.
+ virtual bool DontDelayTasks() { return false; }
+
+ private:
+ // 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);
+
+ // Executes the JavaScript code in |script| in the main frame of
+ // |render_view_host_|.
+ // Returns true if the code was executed successfully.
+ bool ExecuteScript(const std::string& script);
+
+ // Executes the JavaScript code in |script| in the main frame of
+ // |render_view_host_|, and sets |value| to the boolean returned by the script
+ // evaluation. Returns true if the script was run successfully and returned
+ // a boolean, false otherwise
+ bool ExecuteScriptAndGetBoolResult(const std::string& script, bool* 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);
+
+ // 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);
+
+ // The RenderView we are performing translations for.
+ RenderView* render_view_;
+
+ // Method factory used to make calls to TranslatePageImpl.
+ ScopedRunnableMethodFactory<TranslateHelper> method_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(TranslateHelper);
+};
+
+#endif // CHROME_RENDERER_TRANSLATE_HELPER_H_
diff --git a/chrome/renderer/translate_helper_unittest.cc b/chrome/renderer/translate_helper_unittest.cc
new file mode 100644
index 0000000..c0f0e5e
--- /dev/null
+++ b/chrome/renderer/translate_helper_unittest.cc
@@ -0,0 +1,169 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/renderer/translate_helper.h"
+#include "chrome/test/render_view_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::AtLeast;
+using testing::Return;
+
+class TestTranslateHelper : public TranslateHelper {
+ public:
+ explicit TestTranslateHelper(RenderView* render_view)
+ : TranslateHelper(render_view) {
+ }
+
+ virtual bool DontDelayTasks() { return true; }
+
+ MOCK_METHOD0(IsTranslateLibAvailable, bool());
+ 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));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestTranslateHelper);
+};
+
+class TranslateHelperTest : public RenderViewTest {
+ public:
+ TranslateHelperTest() {}
+
+ protected:
+ virtual void SetUp() {
+ RenderViewTest::SetUp();
+ translate_helper_.reset(new TestTranslateHelper(view_));
+ }
+
+ bool GetPageTranslatedMessage(int* page_id,
+ std::string* original_lang,
+ std::string* target_lang,
+ TranslateErrors::Type* error) {
+ const IPC::Message* message = render_thread_.sink().
+ GetUniqueMessageMatching(ViewHostMsg_PageTranslated::ID);
+ if (!message)
+ return false;
+ Tuple4<int, std::string, std::string, TranslateErrors::Type>
+ translate_param;
+ ViewHostMsg_PageTranslated::Read(message, &translate_param);
+ if (page_id)
+ *page_id = translate_param.a;
+ if (original_lang)
+ *original_lang = translate_param.b;
+ if (target_lang)
+ *target_lang = translate_param.c;
+ if (error)
+ *error = translate_param.d;
+ return true;
+ }
+
+ scoped_ptr<TestTranslateHelper> translate_helper_;
+};
+
+// Tests that the browser gets notified of the translation failure if the
+// translate library fails/times-out during initialization.
+TEST_F(TranslateHelperTest, TranslateLibNeverReady) {
+ // 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())
+ .Times(AtLeast(5)) // See kMaxTranslateInitCheckAttempts in
+ // translate_helper.cc
+ .WillRepeatedly(Return(false));
+
+ translate_helper_->TranslatePage(view_->page_id(), "en", "fr", std::string());
+ MessageLoop::current()->RunAllPending();
+
+ int page_id;
+ TranslateErrors::Type error;
+ ASSERT_TRUE(GetPageTranslatedMessage(&page_id, NULL, NULL, &error));
+ EXPECT_EQ(view_->page_id(), page_id);
+ EXPECT_EQ(TranslateErrors::INITIALIZATION_ERROR, error);
+}
+
+// Tests that the browser gets notified of the translation success when the
+// translation succeeds.
+TEST_F(TranslateHelperTest, TranslateSuccess) {
+ // 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(false))
+ .WillOnce(Return(true));
+
+ EXPECT_CALL(*translate_helper_, StartTranslation("en", "fr"))
+ .WillOnce(Return(true));
+
+ // Succeed after few checks.
+ EXPECT_CALL(*translate_helper_, HasTranslationFailed())
+ .WillRepeatedly(Return(false));
+ EXPECT_CALL(*translate_helper_, HasTranslationFinished())
+ .WillOnce(Return(false))
+ .WillOnce(Return(false))
+ .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());
+ 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 the browser gets notified of the translation failure when the
+// translation fails.
+TEST_F(TranslateHelperTest, TranslateFailure) {
+ // 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_, StartTranslation("en", "fr"))
+ .WillOnce(Return(true));
+
+ // Fail after few checks.
+ EXPECT_CALL(*translate_helper_, HasTranslationFailed())
+ .WillOnce(Return(false))
+ .WillOnce(Return(false))
+ .WillOnce(Return(false))
+ .WillOnce(Return(true));
+
+ EXPECT_CALL(*translate_helper_, HasTranslationFinished())
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return(false));
+
+ translate_helper_->TranslatePage(view_->page_id(), "en", "fr", std::string());
+ MessageLoop::current()->RunAllPending();
+
+ int page_id;
+ TranslateErrors::Type error;
+ ASSERT_TRUE(GetPageTranslatedMessage(&page_id, NULL, NULL, &error));
+ EXPECT_EQ(view_->page_id(), page_id);
+ EXPECT_EQ(TranslateErrors::TRANSLATION_ERROR, error);
+}
diff --git a/chrome/test/data/translate/basic_ORIGINAL.html b/chrome/test/data/translate/basic_ORIGINAL.html
deleted file mode 100644
index 420ca73..0000000
--- a/chrome/test/data/translate/basic_ORIGINAL.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<html>
-
-<script>
-function buttonClicked() {
- div = document.getElementById("lastDiv");
- bold = document.getElementById("bold");
-
- text_node = document.createTextNode("Hello!");
- // div.childNodes[0].appendChild(text_node);
- p_node = document.createElement("p");
- p_node.appendChild(text_node);
- bold.appendChild(p_node);
-}
-</script>
-
-<body>
-
- <p>A simple paragraph. Nothing to see here, move along!</p>
-
- <p>This is a paragraph with a <a href="">link</a> and some <b id="bold">bold text</b> in it!</p>
- <p>This on uses a <SPAN>span</SPAN> &nbsp;</p>
-
-
- <dIV>
- <div>This is a first div <div>with an inner div</div> and that's it</div>
- <div id="lastDiv">OK, last div 0.69%</div>
- </div>
-
- <button onclick="buttonClicked()">Click me&nbsp;</button>
-</body>
-
-</html>
diff --git a/chrome/test/data/translate/basic_TRANSLATED.html b/chrome/test/data/translate/basic_TRANSLATED.html
deleted file mode 100644
index b95f28b..0000000
--- a/chrome/test/data/translate/basic_TRANSLATED.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<html>
-
-<script>
-function buttonClicked() {
- div = document.getElementById("lastDiv");
- bold = document.getElementById("bold");
-
- text_node = document.createTextNode("Hello!");
- // div.childNodes[0].appendChild(text_node);
- p_node = document.createElement("p");
- p_node.appendChild(text_node);
- bold.appendChild(p_node);
-}
-</script>
-
-<body>
-
- <p>!gnola evom ,ereh ees ot gnihtoN .hpargarap elpmis A</p>
-
- <p> a htiw hpargarap a si sihT<a href="">knil</a> emos dna <b id="bold">txet dlob</b>!ti ni </p>
- <p> a sesu no sihT<SPAN>naps</SPAN> &nbsp;</p>
-
-
- <dIV>
- <div> vid tsrif a si sihT<div>vid renni na htiw</div>ti s'taht dna </div>
- <div id="lastDiv">&#37;96.0 vid tsal ,KO</div>
- </div>
-
- <button onclick="buttonClicked()">&nbsp;em kcilC</button>
-</body>
-
-</html>
-
diff --git a/chrome/test/data/translate/reverse_text.py b/chrome/test/data/translate/reverse_text.py
deleted file mode 100644
index baed9d1..0000000
--- a/chrome/test/data/translate/reverse_text.py
+++ /dev/null
@@ -1,223 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2009 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-""" Reverses the text of an HTML file.
-
-This classes poorly parses an HTML file and reverse the text strings (and only
-the text, not the tags).
-It is used to generates the _TRANSLATED.html files that the translator unittest
-uses.
-Note it is very hacky and buggy.
-"""
-
-import codecs
-import re
-import sys
-
-def Error(msg):
- print msg;
- sys.exit(1);
-
-class HTMLParser:
- # IGNORED_[PAIRED|SINGLE]_TAGS should be kept in sync with kSkippedTags (see
- # chrome/renderer/translator.cc).
- # Paired tags are tags that are expected to have an opening and closing tag,
- # the entire zone they contain is ignored.
- # Single tags are not closed and are ignored.
- IGNORED_PAIRED_TAGS = [ "APPLET", "AREA", "BASE", "FRAME", "FRAMESET", "HR",
- "IFRAME", "MAP", "OBJECT", "PARAM", "SCRIPT", "STYLE",
- "TEXTAREA" ];
- IGNORED_SINGLE_TAGS = [ "META", "LINK", "IMG", "INPUT" ];
-
- def __init__(self, input_path, output_path):
- try:
- input_file = codecs.open(input_path, 'r', 'utf-8');
- except IOError:
- Error("Failed to open '" + input_path + "' for reading.");
-
- self.html_contents = input_file.read();
- # Python does not have a find method case-insensitive, so we keep a lower
- # case copy of the contents.
- self.html_contents_lower = self.html_contents.lower();
-
- input_file.close();
-
- self.read_index = 0
- self.write_index = 0
- try:
- self.output_file = codecs.open(output_path, 'w', 'utf-8');
- except IOError:
- Error("Failed to open '" + output_path + "' for writting.");
-
- def printDebug(self, msg):
- print u"** %s" % msg.encode('ascii', 'replace')
-
- def removeBlanks(self, str):
- p = re.compile('\s');
- return p.sub('', str);
-
- def extractTagName(self, str):
- closing_tag = False;
- str = str.strip();
- if str[0] != "<":
- Error("Interal error: attempting to extract tag name from invalid tag: " +
- str);
- if str[1] == "/":
- closing_tag = True;
-
- p = re.compile('</?\s*(\w*).*');
- m = p.match(str);
- if m == None:
- Error("Interal error: failed to extract tag name from tag: " + str);
- return (m.group(1).lower(), closing_tag);
-
- def shouldIgnoreTag(self, tag):
- """Returns a tuple (tag should be ignored, pared tags)
- """
- tag = tag.upper();
- for tag_to_ignore in self.IGNORED_PAIRED_TAGS:
- if tag_to_ignore == tag:
- return True, True;
- for tag_to_ignore in self.IGNORED_SINGLE_TAGS:
- if tag_to_ignore == tag:
- return True, False;
- return False, False;
-
- def skipToEndTag(self, tag):
- """ Move the read_index to the position after the closing tag matching
- |tag| and copies all the skipped data to the output file."""
- index = self.html_contents_lower.find("</" + tag, self.read_index);
- if index == -1:
- Error("Failed to find tag end for tag " + tag + " at index " +
- str(self.read_index));
- self.writeToOutputFile(self.html_contents[self.read_index:]);
- else:
- self.writeToOutputFile(self.html_contents[self.read_index:index]);
- self.read_index = index;
-
- def writeToOutputFile(self, text):
- try:
- self.output_file.write(text)
- except IOError:
- Error("Failed to write to output file.");
- # DEBUG
- if len(text) > 100000:
- Error("Writting too much text: " + text);
-# self.printDebug("Writting: " + text);
-# self.write_index += len(text);
-# self.printDebug("Wrote " + str(len(text)) + " bytes, write len=" + str(self.write_index));
-
- def getNextTag(self):
- """Moves the read_index to the end of the next tag and writes the tag to the
- output file.
- Returns a tuple end of file reached, tag name, if closing tag.
- """
-
- start_index = self.html_contents.find("<", self.read_index);
- if start_index == -1:
- self.writeToOutputFile(self.html_contents[self.read_index:]);
- return (True, "", False);
- stop_index = self.html_contents.find(">", start_index);
- if stop_index == -1:
- print "Unclosed tag found.";
- self.writeToOutputFile(self.html_contents[self.read_index:]);
- return (True, "", False);
-
- # Write to the file the current text reverted.
- # No need to do it if the string is only blanks, that would break the
- # indentation.
- text = self.html_contents[self.read_index:start_index]
- text = self.processText(text);
- self.writeToOutputFile(text);
-
- tag = self.html_contents[start_index:stop_index + 1];
- self.writeToOutputFile(tag);
- self.read_index = stop_index + 1;
- tag_name, closing_tag = self.extractTagName(tag);
-# self.printDebug("Raw tag=" + tag);
-# self.printDebug("tag=" + tag_name + " closing=" + str(closing_tag));
-# self.printDebug("read_index=" + str(self.read_index));
-
- return (False, tag_name, closing_tag);
-
- def processText(self, text):
- if text.isspace():
- return text;
-
- # Special case of lonely &nbsp; with spaces. It should not be reversed as
- # the renderer does not "translate" it as it is seen as empty string.
- if text.strip().lower() == '&nbsp;':
- return text;
-
- # We reverse the string manually so to preserve &nbsp; and friends.
- p = re.compile(r'&#\d{1,5};|&\w{2,6};');
- # We create a dictionary where the key is the index at which the ASCII code
- # starts and the value the index at which it ends.
- entityNameIndexes = dict();
- for match in p.finditer(text):
- entityNameIndexes[match.start()] = match.end();
- result = ""
- i = 0;
- while i < len(text):
- if entityNameIndexes.has_key(i):
- end_index = entityNameIndexes[i];
- result = text[i:end_index] + result;
- i = end_index;
- elif text[i] == "%": # Replace percent to avoid percent encoding.
- result = "&#37;" + result;
- i = i + 1;
- else:
- result = text[i] + result;
- i = i + 1;
-
- return result;
-
- def processTagContent(self):
- """Reads the text from the current index to the next tag and writes the text
- in reverse to the output file.
- """
- stop_index = self.html_contents.find("<", self.read_index);
- if stop_index == -1:
- text = self.html_contents[self.read_index:];
- self.read_index += len(text);
- else:
- text = self.html_contents[self.read_index:stop_index];
- self.read_index = stop_index;
- text = self.processText(text);
- self.writeToOutputFile(text);
-
- def start(self):
- while True:
- end_of_file, tag, closing_tag = self.getNextTag();
- # if closing_tag:
- # self.printDebug("Read tag: /" + tag);
- # else:
- # self.printDebug("Read tag: " + tag);
-
- if end_of_file: # We reached the end of the file.
- self.writeToOutputFile(self.html_contents[self.read_index:]);
- print "Done.";
- sys.exit(0);
-
- if closing_tag:
- continue;
-
- ignore_tag, paired_tag = self.shouldIgnoreTag(tag);
- if ignore_tag and paired_tag:
- self.skipToEndTag(tag);
-
- # Read and reverse the text in the tab.
- self.processTagContent();
-
-def main():
- if len(sys.argv) != 3:
- Error("Reverse the text in HTML pages\n"
- "Usage reversetext.py <original_file.html> <dest_file.html>");
-
- html_parser = HTMLParser(sys.argv[1], sys.argv[2]);
- html_parser.start();
-
-if __name__ == "__main__":
- main()