summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser')
-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
16 files changed, 514 insertions, 1469 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));