summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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()