diff options
author | jcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-14 19:36:32 +0000 |
---|---|---|
committer | jcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-14 19:36:32 +0000 |
commit | 0bedb8a46053e44f667da0e800568c38e2150e4a (patch) | |
tree | cd14724a4078a65970c56e97cbd7c5a904c2f14c /chrome/renderer | |
parent | 6fa508a1cef4a920f570174c77eafc5f21d808eb (diff) | |
download | chromium_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.cc | 17 | ||||
-rw-r--r-- | chrome/renderer/render_view.h | 13 | ||||
-rw-r--r-- | chrome/renderer/translate/page_translator.cc | 184 | ||||
-rw-r--r-- | chrome/renderer/translate/page_translator.h | 95 | ||||
-rw-r--r-- | chrome/renderer/translate/page_translator_unittest.cc | 212 | ||||
-rw-r--r-- | chrome/renderer/translate/text_translator.h | 49 | ||||
-rw-r--r-- | chrome/renderer/translate/text_translator_impl.cc | 40 | ||||
-rw-r--r-- | chrome/renderer/translate/text_translator_impl.h | 51 |
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_ |