summaryrefslogtreecommitdiffstats
path: root/chrome/renderer
diff options
context:
space:
mode:
authorjcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-14 19:36:32 +0000
committerjcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-14 19:36:32 +0000
commit0bedb8a46053e44f667da0e800568c38e2150e4a (patch)
treecd14724a4078a65970c56e97cbd7c5a904c2f14c /chrome/renderer
parent6fa508a1cef4a920f570174c77eafc5f21d808eb (diff)
downloadchromium_src-0bedb8a46053e44f667da0e800568c38e2150e4a.zip
chromium_src-0bedb8a46053e44f667da0e800568c38e2150e4a.tar.gz
chromium_src-0bedb8a46053e44f667da0e800568c38e2150e4a.tar.bz2
This CL contains the back-end implementation of the translate feature. It adds a Translate method to the renderer.
On invocation this method triggers a traversal of the DOM page to retrieve the text nodes. The text node contents are then sent to the browser for actual translation (at this point, we just up-case the text for testing purpose). The browser sends back the translated text to the renderer that replace the DOM text node values with the translated text. BUG=None TEST=Run the unit-tests. Review URL: http://codereview.chromium.org/547013 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@36258 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer')
-rw-r--r--chrome/renderer/render_view.cc17
-rw-r--r--chrome/renderer/render_view.h13
-rw-r--r--chrome/renderer/translate/page_translator.cc184
-rw-r--r--chrome/renderer/translate/page_translator.h95
-rw-r--r--chrome/renderer/translate/page_translator_unittest.cc212
-rw-r--r--chrome/renderer/translate/text_translator.h49
-rw-r--r--chrome/renderer/translate/text_translator_impl.cc40
-rw-r--r--chrome/renderer/translate/text_translator_impl.h51
8 files changed, 660 insertions, 1 deletions
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc
index ebc1cd6..885d6eb 100644
--- a/chrome/renderer/render_view.cc
+++ b/chrome/renderer/render_view.cc
@@ -74,11 +74,13 @@
#include "third_party/WebKit/WebKit/chromium/public/WebCString.h"
#include "third_party/WebKit/WebKit/chromium/public/WebDataSource.h"
#include "third_party/WebKit/WebKit/chromium/public/WebDevToolsAgent.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/WebKit/chromium/public/WebDragData.h"
#include "third_party/WebKit/WebKit/chromium/public/WebFormElement.h"
#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/WebKit/chromium/public/WebHistoryItem.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/WebPageSerializer.h"
#include "third_party/WebKit/WebKit/chromium/public/WebPoint.h"
#include "third_party/WebKit/WebKit/chromium/public/WebRange.h"
@@ -278,7 +280,9 @@ RenderView::RenderView(RenderThreadBase* render_thread,
has_document_tag_(false),
#endif
document_tag_(0),
- webkit_preferences_(webkit_preferences) {
+ webkit_preferences_(webkit_preferences),
+ ALLOW_THIS_IN_INITIALIZER_LIST(text_translator_(this)) {
+ page_translator_.reset(new PageTranslator(&text_translator_));
}
RenderView::~RenderView() {
@@ -531,6 +535,7 @@ void RenderView::OnMessageReceived(const IPC::Message& message) {
OnExecuteCode)
IPC_MESSAGE_HANDLER(ViewMsg_CustomContextMenuAction,
OnCustomContextMenuAction)
+ IPC_MESSAGE_HANDLER(ViewMsg_TranslateTextReponse, OnTranslateTextResponse)
// Have the super handle all other messages.
IPC_MESSAGE_UNHANDLED(RenderWidget::OnMessageReceived(message))
@@ -3241,6 +3246,15 @@ void RenderView::OnCustomContextMenuAction(unsigned action) {
webview()->performCustomContextMenuAction(action);
}
+void RenderView::OnTranslateTextResponse(
+ int work_id, int error_id, const std::vector<string16>& text_chunks) {
+ if (error_id) {
+ page_translator_->TranslationError(work_id, error_id);
+ return;
+ }
+ page_translator_->TextTranslated(work_id, text_chunks);
+}
+
void RenderView::OnInstallMissingPlugin() {
// This could happen when the first default plugin is deleted.
if (first_default_plugin_)
@@ -3978,3 +3992,4 @@ bool RenderView::SendAndRunNestedMessageLoop(IPC::SyncMessage* message) {
return rv;
}
+
diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h
index f390af6..7a4ebc4 100644
--- a/chrome/renderer/render_view.h
+++ b/chrome/renderer/render_view.h
@@ -36,6 +36,8 @@
#include "chrome/renderer/notification_provider.h"
#include "chrome/renderer/render_widget.h"
#include "chrome/renderer/render_view_visitor.h"
+#include "chrome/renderer/translate/page_translator.h"
+#include "chrome/renderer/translate/text_translator_impl.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "testing/gtest/include/gtest/gtest_prod.h"
#include "third_party/WebKit/WebKit/chromium/public/WebConsoleMessage.h"
@@ -432,6 +434,8 @@ class RenderView : public RenderWidget,
// UserScript::DOCUMENT_IDLE.
void OnUserScriptIdleTriggered(WebKit::WebFrame* frame);
+ PageTranslator* page_translator() const { return page_translator_.get(); }
+
// Returns the ISO 639_1 language code of the current page
// (ex: en, fr, zh...). Returns 'unknown' if the language could not be
// determined.
@@ -679,6 +683,11 @@ class RenderView : public RenderWidget,
// Execute custom context menu action.
void OnCustomContextMenuAction(unsigned action);
+ // Message that provides the translated text for a request.
+ void OnTranslateTextResponse(int work_id,
+ int error_id,
+ const std::vector<string16>& text_chunks);
+
// Exposes the DOMAutomationController object that allows JS to send
// information to the browser process.
void BindDOMAutomationController(WebKit::WebFrame* webframe);
@@ -998,6 +1007,10 @@ class RenderView : public RenderWidget,
HostZoomLevels host_zoom_levels_;
+ // Page translation related objects.
+ TextTranslatorImpl text_translator_;
+ scoped_ptr<PageTranslator> page_translator_;
+
DISALLOW_COPY_AND_ASSIGN(RenderView);
};
diff --git a/chrome/renderer/translate/page_translator.cc b/chrome/renderer/translate/page_translator.cc
new file mode 100644
index 0000000..1bb245a
--- /dev/null
+++ b/chrome/renderer/translate/page_translator.cc
@@ -0,0 +1,184 @@
+// 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 "third_party/WebKit/WebKit/chromium/public/WebDocument.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"
+
+// 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" };
+
+// 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)
+ : text_translator_(text_translator) {
+ 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])));
+}
+
+PageTranslator::~PageTranslator() {
+ STLDeleteContainerPairSecondPointers(pending_translations_.begin(),
+ pending_translations_.end());
+}
+
+void PageTranslator::Translate(WebKit::WebFrame* web_frame,
+ std::string from_lang,
+ std::string to_lang) {
+ 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.
+ delete *iter;
+ continue;
+ }
+ std::vector<string16> text_chunks; // The text chunks to translate.
+ std::vector<WebKit::WebNode>::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.
+ bool secure = static_cast<GURL>(web_frame->top()->url()).SchemeIsSecure();
+ int work_id =
+ text_translator_->Translate(text_chunks, from_lang, to_lang, secure,
+ this);
+ pending_translations_[work_id] = *iter;
+ }
+}
+
+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;
+ }
+ delete iter->second;
+ 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()) {
+ NOTREACHED() << "Translation results received for unknown node zone";
+ return;
+ }
+
+ NodeList* nodes = iter->second;
+ // Check the integrity of the response.
+ if (translated_text_chunks.size() != nodes->size()) {
+ // TODO(jcampan) reenable when we figured out why the server messed up the
+ // anchor tags.
+ // NOTREACHED() << "Translation results received are inconsistent with the "
+ // "request";
+ return;
+ }
+
+ for (size_t i = 0; i < translated_text_chunks.size(); ++i)
+ (*nodes)[i].setNodeValue(WebKit::WebString(translated_text_chunks[i]));
+
+ ClearNodeZone(work_id);
+}
+
+void PageTranslator::TraverseNode(WebKit::WebNode node,
+ std::stack<NodeList*>* element_stack,
+ std::vector<NodeList*>* text_nodes_list) {
+ if (node.isTextNode()) {
+ if (ContainsOnlyWhitespace(static_cast<string16>(node.nodeValue())))
+ return; // Ignore text nodes with only white-spaces.
+
+ 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;
+ }
+}
diff --git a/chrome/renderer/translate/page_translator.h b/chrome/renderer/translate/page_translator.h
new file mode 100644
index 0000000..f7135be
--- /dev/null
+++ b/chrome/renderer/translate/page_translator.h
@@ -0,0 +1,95 @@
+// 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/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:
+ // The caller remains the owner of |text_translator|.
+ explicit PageTranslator(TextTranslator* text_translator);
+ virtual ~PageTranslator();
+
+ // Starts the translation process of |web_frame| from |from_lang| to |to_lang|
+ // where the languages are the ISO codes (ex: en, fr...).
+ void Translate(WebKit::WebFrame* web_frame,
+ std::string from_lang,
+ std::string to_lang);
+
+ // 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;
+
+ // 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);
+
+ // The RenderView we are providing translations for.
+ RenderView* render_view_;
+
+ // 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_;
+
+ 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
new file mode 100644
index 0000000..4bc1e02
--- /dev/null
+++ b/chrome/renderer/translate/page_translator_unittest.cc
@@ -0,0 +1,212 @@
+// 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) {}
+ 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);
+ translator.Translate(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
new file mode 100644
index 0000000..b4e7581
--- /dev/null
+++ b/chrome/renderer/translate/text_translator.h
@@ -0,0 +1,49 @@
+// 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
new file mode 100644
index 0000000..40e2c16
--- /dev/null
+++ b/chrome/renderer/translate/text_translator_impl.cc
@@ -0,0 +1,40 @@
+// 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.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
new file mode 100644
index 0000000..566a084
--- /dev/null
+++ b/chrome/renderer/translate/text_translator_impl.h
@@ -0,0 +1,51 @@
+// 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_