summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhbono@chromium.org <hbono@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-03 06:39:01 +0000
committerhbono@chromium.org <hbono@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-03 06:39:01 +0000
commit19176d47a9da4dd0d25470360564762fcf0061be (patch)
treea53124707356a628931c44bd1407d48c0dfeebd9
parentf46a4015b76c47e69b869c53dec4f2d812a111de (diff)
downloadchromium_src-19176d47a9da4dd0d25470360564762fcf0061be.zip
chromium_src-19176d47a9da4dd0d25470360564762fcf0061be.tar.gz
chromium_src-19176d47a9da4dd0d25470360564762fcf0061be.tar.bz2
In-te-grate hy-phen-ator to con-tent.
This change integrates the Hyphenator class to content so Chrome can use it. This change basically consists of two parts: * Adds a couple of IPC messages so a renderer asks a browser to open hyphenation dictionaries; * Adds a HyphenatorMessageFilter class, which opens hyphenation dictionaries in a browser process; * Changes the Hyphenator class to send IPC messages to open hyphenation dictionaries. BUG=47083 TEST=HyphenatorTest.SetDictionary,HyphenatorMessageFilterTest.OpenDictionary Review URL: https://chromiumcodereview.appspot.com/10854245 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@154663 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--content/browser/hyphenator/hyphenator_message_filter.cc110
-rw-r--r--content/browser/hyphenator/hyphenator_message_filter.h77
-rw-r--r--content/browser/hyphenator/hyphenator_message_filter_unittest.cc154
-rw-r--r--content/browser/renderer_host/render_process_host_impl.cc4
-rw-r--r--content/common/content_message_generator.h1
-rw-r--r--content/common/hyphenator_messages.h24
-rw-r--r--content/content_browser.gypi2
-rw-r--r--content/content_tests.gypi1
-rw-r--r--content/public/test/mock_render_process_host.cc4
-rw-r--r--content/public/test/mock_render_thread.cc13
-rw-r--r--content/public/test/mock_render_thread.h3
-rw-r--r--content/renderer/hyphenator/hyphenator.cc51
-rw-r--r--content/renderer/hyphenator/hyphenator.h20
-rw-r--r--content/renderer/hyphenator/hyphenator_unittest.cc100
-rw-r--r--content/renderer/renderer_webkitplatformsupport_impl.cc33
-rw-r--r--content/renderer/renderer_webkitplatformsupport_impl.h8
-rw-r--r--ipc/ipc_message_utils.h1
17 files changed, 599 insertions, 7 deletions
diff --git a/content/browser/hyphenator/hyphenator_message_filter.cc b/content/browser/hyphenator/hyphenator_message_filter.cc
new file mode 100644
index 0000000..b8e13ab
--- /dev/null
+++ b/content/browser/hyphenator/hyphenator_message_filter.cc
@@ -0,0 +1,110 @@
+// Copyright (c) 2012 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/base_paths.h"
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/string16.h"
+#include "content/browser/hyphenator/hyphenator_message_filter.h"
+#include "content/common/hyphenator_messages.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/common/content_paths.h"
+
+namespace {
+
+// A helper function that closes the specified file in the FILE thread. This
+// function may be called after the HyphenatorMessageFilter object that owns the
+// specified file is deleted, i.e. this function must not depend on the object.
+void CloseDictionary(base::PlatformFile file) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
+ base::ClosePlatformFile(file);
+}
+
+} // namespace
+
+namespace content {
+
+HyphenatorMessageFilter::HyphenatorMessageFilter(
+ content::RenderProcessHost* render_process_host)
+ : render_process_host_(render_process_host),
+ dictionary_file_(base::kInvalidPlatformFileValue),
+ weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
+}
+
+HyphenatorMessageFilter::~HyphenatorMessageFilter() {
+ // Post a FILE task that deletes the dictionary file. This message filter is
+ // usually deleted on the IO thread, which does not allow file operations.
+ if (dictionary_file_ != base::kInvalidPlatformFileValue) {
+ content::BrowserThread::PostTask(
+ BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&CloseDictionary, dictionary_file_));
+ }
+}
+
+void HyphenatorMessageFilter::SetDictionaryBase(const FilePath& base) {
+ dictionary_base_ = base;
+}
+
+void HyphenatorMessageFilter::OverrideThreadForMessage(
+ const IPC::Message& message,
+ BrowserThread::ID* thread) {
+ if (message.type() == HyphenatorHostMsg_OpenDictionary::ID)
+ *thread = BrowserThread::UI;
+}
+
+bool HyphenatorMessageFilter::OnMessageReceived(
+ const IPC::Message& message,
+ bool* message_was_ok) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(HyphenatorMessageFilter,
+ message,
+ *message_was_ok)
+ IPC_MESSAGE_HANDLER(HyphenatorHostMsg_OpenDictionary, OnOpenDictionary)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP_EX()
+ return handled;
+}
+
+void HyphenatorMessageFilter::OnOpenDictionary(const string16& locale) {
+ if (dictionary_file_ != base::kInvalidPlatformFileValue) {
+ SendDictionary();
+ return;
+ }
+ content::BrowserThread::PostTaskAndReply(
+ content::BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&HyphenatorMessageFilter::OpenDictionary, this, locale),
+ base::Bind(&HyphenatorMessageFilter::SendDictionary,
+ weak_factory_.GetWeakPtr()));
+}
+
+void HyphenatorMessageFilter::OpenDictionary(const string16& locale) {
+ DCHECK(dictionary_file_ == base::kInvalidPlatformFileValue);
+
+ if (dictionary_base_.empty())
+ PathService::Get(base::DIR_EXE, &dictionary_base_);
+ std::string rule_file = locale.empty() ? "en-US" : UTF16ToASCII(locale);
+ rule_file.append("-1-0.dic");
+ FilePath rule_path = dictionary_base_.AppendASCII(rule_file);
+ dictionary_file_ = base::CreatePlatformFile(
+ rule_path,
+ base::PLATFORM_FILE_READ | base::PLATFORM_FILE_OPEN,
+ NULL, NULL);
+}
+
+void HyphenatorMessageFilter::SendDictionary() {
+ IPC::PlatformFileForTransit file = IPC::InvalidPlatformFileForTransit();
+ if (dictionary_file_ != base::kInvalidPlatformFileValue) {
+ file = IPC::GetFileHandleForProcess(
+ dictionary_file_,
+ render_process_host_->GetHandle(),
+ false);
+ }
+ Send(new HyphenatorMsg_SetDictionary(file));
+}
+
+} // namespace content
diff --git a/content/browser/hyphenator/hyphenator_message_filter.h b/content/browser/hyphenator/hyphenator_message_filter.h
new file mode 100644
index 0000000..2421610
--- /dev/null
+++ b/content/browser/hyphenator/hyphenator_message_filter.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2012 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 CONTENT_BROWSER_HYPHENATOR_HYPHENATOR_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_HYPHENATOR_HYPHENATOR_MESSAGE_FILTER_H_
+
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "base/platform_file.h"
+#include "content/common/content_export.h"
+#include "content/public/browser/browser_message_filter.h"
+
+namespace content {
+class RenderProcessHost;
+
+// This class is a message filter that handles a HyphenatorHost message. When
+// this class receives a HyphenatorHostMsg_OpenDictionary message, it opens the
+// specified dictionary and sends its file handle.
+class CONTENT_EXPORT HyphenatorMessageFilter
+ : public content::BrowserMessageFilter {
+ public:
+ explicit HyphenatorMessageFilter(
+ content::RenderProcessHost* render_process_host);
+
+ // Changes the directory that includes dictionary files. This function
+ // provides a method that allows applications to change the directory
+ // containing hyphenation dictionaries. When a renderer requests a hyphnation
+ // dictionary, this class appends a file name (which consists of a locale, a
+ // version number, and an extension) and use it as a dictionary file.
+ void SetDictionaryBase(const FilePath& directory);
+
+ // content::BrowserMessageFilter implementation.
+ virtual void OverrideThreadForMessage(
+ const IPC::Message& message,
+ content::BrowserThread::ID* thread) OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok) OVERRIDE;
+
+ private:
+ friend class TestHyphenatorMessageFilter;
+
+ virtual ~HyphenatorMessageFilter();
+
+ virtual void OnOpenDictionary(const string16& locale);
+
+ // Opens a hyphenation dictionary for the specified locale. When this locale
+ // is an empty string, this function uses US English ("en-US").
+ void OpenDictionary(const string16& locale);
+
+ // Sends the hyphenation dictionary file to a renderer in response to its
+ // request. If this class cannot open the specified dictionary file, this
+ // function sends an IPC::InvalidPlatformFileForTransit value to tell the
+ // renderer that a browser cannot open the file.
+ void SendDictionary();
+
+ // The RenderProcessHost object that owns this filter. This class uses this
+ // object to retrieve the process handle used for creating
+ // PlatformFileForTransit objects.
+ content::RenderProcessHost* render_process_host_;
+
+ // The directory that includes dictionary files. The default value is the
+ // directory containing the executable file.
+ FilePath dictionary_base_;
+
+ // A cached dictionary file.
+ base::PlatformFile dictionary_file_;
+
+ base::WeakPtrFactory<HyphenatorMessageFilter> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(HyphenatorMessageFilter);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_HYPHENATOR_HYPHENATOR_MESSAGE_FILTER_H_
diff --git a/content/browser/hyphenator/hyphenator_message_filter_unittest.cc b/content/browser/hyphenator/hyphenator_message_filter_unittest.cc
new file mode 100644
index 0000000..e8b5467
--- /dev/null
+++ b/content/browser/hyphenator/hyphenator_message_filter_unittest.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2012 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 "content/browser/hyphenator/hyphenator_message_filter.h"
+
+#include "base/base_paths.h"
+#include "base/file_path.h"
+#include "base/path_service.h"
+#include "base/string16.h"
+#include "base/utf_string_conversions.h"
+#include "content/common/hyphenator_messages.h"
+#include "content/public/test/mock_render_process_host.h"
+#include "content/public/test/test_browser_context.h"
+#include "ipc/ipc_message_utils.h"
+#include "ipc/ipc_platform_file.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+// A class derived from the HyphenatorMessageFilter class used in unit tests.
+// This class overrides some methods so we can test the HyphenatorMessageFilter
+// class without posting tasks.
+class TestHyphenatorMessageFilter : public content::HyphenatorMessageFilter {
+ public:
+ explicit TestHyphenatorMessageFilter(content::RenderProcessHost* host)
+ : content::HyphenatorMessageFilter(host),
+ type_(0),
+ file_(base::kInvalidPlatformFileValue) {
+ }
+
+ const string16& locale() const { return locale_; }
+ uint32 type() const { return type_; }
+ base::PlatformFile file() const { return file_; }
+
+ // content::BrowserMessageFilter implementation.
+ virtual bool Send(IPC::Message* message) OVERRIDE {
+ if (message->type() != HyphenatorMsg_SetDictionary::ID)
+ return false;
+
+ // Read the PlatformFileForTransit object and check if its value is
+ // kInvalidPlatformFileValue. Close the incoming file if it is not
+ // kInvalidPlatformFileValue to prevent leaving the dictionary file open.
+ type_ = message->type();
+ PickleIterator iter(*message);
+ IPC::PlatformFileForTransit file;
+ IPC::ParamTraits<IPC::PlatformFileForTransit>::Read(message, &iter, &file);
+ file_ = IPC::PlatformFileForTransitToPlatformFile(file);
+ return true;
+ }
+
+ void SetDictionary(base::PlatformFile file) {
+ dictionary_file_ = file;
+ }
+
+ void Reset() {
+ if (dictionary_file_ != base::kInvalidPlatformFileValue) {
+ base::ClosePlatformFile(dictionary_file_);
+ dictionary_file_ = base::kInvalidPlatformFileValue;
+ }
+ locale_.clear();
+ type_ = 0;
+ if (file_ != base::kInvalidPlatformFileValue) {
+ base::ClosePlatformFile(file_);
+ file_ = base::kInvalidPlatformFileValue;
+ }
+ }
+
+ private:
+ virtual ~TestHyphenatorMessageFilter() {
+ }
+
+ // content::HyphenatorMessageFilter implementation. This function emulates the
+ // original implementation without posting a task.
+ virtual void OnOpenDictionary(const string16& locale) OVERRIDE {
+ locale_ = locale;
+ if (dictionary_file_ == base::kInvalidPlatformFileValue)
+ OpenDictionary(locale);
+ SendDictionary();
+ }
+
+ string16 locale_;
+ uint32 type_;
+ base::PlatformFile file_;
+};
+
+} // namespace content
+
+class HyphenatorMessageFilterTest : public testing::Test {
+ public:
+ HyphenatorMessageFilterTest() {
+ context_.reset(new content::TestBrowserContext);
+ host_.reset(new content::MockRenderProcessHost(context_.get()));
+ filter_ = new content::TestHyphenatorMessageFilter(host_.get());
+ }
+
+ virtual ~HyphenatorMessageFilterTest() {}
+
+ scoped_ptr<content::TestBrowserContext> context_;
+ scoped_ptr<content::MockRenderProcessHost> host_;
+ scoped_refptr<content::TestHyphenatorMessageFilter> filter_;
+};
+
+// Verifies IPC messages sent by the HyphenatorMessageFilter class when it
+// receives IPC messages (HyphenatorHostMsg_OpenDictionary).
+TEST_F(HyphenatorMessageFilterTest, OpenDictionary) {
+ // Send a HyphenatorHostMsg_OpenDictionary message with an invalid locale and
+ // verify it sends a HyphenatorMsg_SetDictionary message with an invalid file.
+ string16 invalid_locale(ASCIIToUTF16("xx-xx"));
+ IPC::Message invalid_message(
+ 0, HyphenatorHostMsg_OpenDictionary::ID, IPC::Message::PRIORITY_NORMAL);
+ invalid_message.WriteString16(invalid_locale);
+
+ bool message_was_ok = false;
+ filter_->OnMessageReceived(invalid_message, &message_was_ok);
+ EXPECT_TRUE(message_was_ok);
+ EXPECT_EQ(invalid_locale, filter_->locale());
+ EXPECT_EQ(HyphenatorMsg_SetDictionary::ID, filter_->type());
+ EXPECT_EQ(base::kInvalidPlatformFileValue, filter_->file());
+
+ filter_->Reset();
+
+ // Open a sample dictionary file and attach it to the
+ // HyphenatorMessageFilter class so it can return a valid file.
+ FilePath path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ path = path.Append(FILE_PATH_LITERAL("third_party"));
+ path = path.Append(FILE_PATH_LITERAL("hyphen"));
+ path = path.Append(FILE_PATH_LITERAL("hyph_en_US.dic"));
+ base::PlatformFile file = base::CreatePlatformFile(
+ path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
+ NULL, NULL);
+ EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ filter_->SetDictionary(file);
+
+ // Send a HyphenatorHostMsg_OpenDictionary message with an empty locale and
+ // verify it sends a HyphenatorMsg_SetDictionary message with a valid file.
+ string16 empty_locale;
+ IPC::Message valid_message(
+ 0, HyphenatorHostMsg_OpenDictionary::ID, IPC::Message::PRIORITY_NORMAL);
+ valid_message.WriteString16(empty_locale);
+
+ message_was_ok = false;
+ filter_->OnMessageReceived(valid_message, &message_was_ok);
+ EXPECT_TRUE(message_was_ok);
+ EXPECT_EQ(empty_locale, filter_->locale());
+ EXPECT_EQ(HyphenatorMsg_SetDictionary::ID, filter_->type());
+ EXPECT_NE(base::kInvalidPlatformFileValue, filter_->file());
+
+ // Delete all resources used by this test.
+ filter_->Reset();
+ if (file != base::kInvalidPlatformFileValue)
+ base::ClosePlatformFile(file);
+}
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 8d55699..694091f 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -55,6 +55,7 @@
#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/browser/histogram_message_filter.h"
+#include "content/browser/hyphenator/hyphenator_message_filter.h"
#include "content/browser/in_process_webkit/indexed_db_context_impl.h"
#include "content/browser/in_process_webkit/indexed_db_dispatcher_host.h"
#include "content/browser/mime_registry_message_filter.h"
@@ -608,7 +609,8 @@ void RenderProcessHostImpl::CreateMessageFilters() {
GetContentClient()->browser()->CreateQuotaPermissionContext()));
channel_->AddFilter(new GamepadBrowserMessageFilter(this));
channel_->AddFilter(new ProfilerMessageFilter(PROCESS_TYPE_RENDERER));
- channel_->AddFilter(new content::HistogramMessageFilter());
+ channel_->AddFilter(new HistogramMessageFilter());
+ channel_->AddFilter(new HyphenatorMessageFilter(this));
}
int RenderProcessHostImpl::GetNextRoutingID() {
diff --git a/content/common/content_message_generator.h b/content/common/content_message_generator.h
index 741152d..1bce78a 100644
--- a/content/common/content_message_generator.h
+++ b/content/common/content_message_generator.h
@@ -23,6 +23,7 @@
#include "content/common/gamepad_messages.h"
#include "content/common/geolocation_messages.h"
#include "content/common/gpu/gpu_messages.h"
+#include "content/common/hyphenator_messages.h"
#include "content/common/indexed_db/indexed_db_messages.h"
#include "content/common/intents_messages.h"
#include "content/common/java_bridge_messages.h"
diff --git a/content/common/hyphenator_messages.h b/content/common/hyphenator_messages.h
new file mode 100644
index 0000000..e68985d
--- /dev/null
+++ b/content/common/hyphenator_messages.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 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.
+
+// IPC messages for hyphenation.
+// Message definition file, included multiple times, hence no include guard.
+
+#include "ipc/ipc_message_macros.h"
+#include "ipc/ipc_platform_file.h"
+
+#define IPC_MESSAGE_START HyphenatorMsgStart
+
+// Opens the specified hyphenation dictionary. This message is expected to be
+// sent when WebKit calls the canHyphenate function, i.e. when it starts
+// layouting text. At this time, WebKit does not actually need this dictionary
+// to hyphenate words. Therefore, a renderer does not need to wait for a browser
+// to open the specified dictionary.
+IPC_MESSAGE_CONTROL1(HyphenatorHostMsg_OpenDictionary,
+ string16 /* locale */)
+
+// Sends the hyphenation dictionary to the renderer. This messages is sent in
+// response to a HyphenatorHostMsg_OpenDictionary message.
+IPC_MESSAGE_CONTROL1(HyphenatorMsg_SetDictionary,
+ IPC::PlatformFileForTransit /* dict_file */)
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index a975075..76d4fab 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -448,6 +448,8 @@
'browser/histogram_synchronizer.h',
'browser/host_zoom_map_impl.cc',
'browser/host_zoom_map_impl.h',
+ 'browser/hyphenator/hyphenator_message_filter.cc',
+ 'browser/hyphenator/hyphenator_message_filter.h',
'browser/in_process_webkit/browser_webkitplatformsupport_impl.cc',
'browser/in_process_webkit/browser_webkitplatformsupport_impl.h',
'browser/in_process_webkit/indexed_db_callbacks.cc',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index ecb6977..d4590b5 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -273,6 +273,7 @@
'browser/geolocation/win7_location_provider_unittest_win.cc',
'browser/gpu/gpu_data_manager_impl_unittest.cc',
'browser/host_zoom_map_impl_unittest.cc',
+ 'browser/hyphenator/hyphenator_message_filter_unittest.cc',
'browser/in_process_webkit/indexed_db_quota_client_unittest.cc',
'browser/in_process_webkit/indexed_db_unittest.cc',
'browser/in_process_webkit/webkit_thread_unittest.cc',
diff --git a/content/public/test/mock_render_process_host.cc b/content/public/test/mock_render_process_host.cc
index cc53cdb..39dfaf5 100644
--- a/content/public/test/mock_render_process_host.cc
+++ b/content/public/test/mock_render_process_host.cc
@@ -101,7 +101,9 @@ void MockRenderProcessHost::DumpHandles() {
}
base::ProcessHandle MockRenderProcessHost::GetHandle() {
- return base::kNullProcessHandle;
+ // Return the current-process handle for the IPC::GetFileHandleForProcess
+ // function.
+ return base::Process::Current().handle();
}
bool MockRenderProcessHost::Send(IPC::Message* msg) {
diff --git a/content/public/test/mock_render_thread.cc b/content/public/test/mock_render_thread.cc
index 50d9182..50d3968 100644
--- a/content/public/test/mock_render_thread.cc
+++ b/content/public/test/mock_render_thread.cc
@@ -98,10 +98,21 @@ int MockRenderThread::GenerateRoutingID() {
void MockRenderThread::AddFilter(IPC::ChannelProxy::MessageFilter* filter) {
filter->OnFilterAdded(&sink());
+ // Add this filter to a vector so the MockRenderThread::RemoveFilter function
+ // can check if this filter is added.
+ filters_.push_back(make_scoped_refptr(filter));
}
void MockRenderThread::RemoveFilter(IPC::ChannelProxy::MessageFilter* filter) {
- filter->OnFilterRemoved();
+ // Emulate the IPC::ChannelProxy::OnRemoveFilter function.
+ for (size_t i = 0; i < filters_.size(); ++i) {
+ if (filters_[i].get() == filter) {
+ filter->OnFilterRemoved();
+ filters_.erase(filters_.begin() + i);
+ return;
+ }
+ }
+ NOTREACHED() << "filter to be removed not found";
}
void MockRenderThread::SetOutgoingMessageFilter(
diff --git a/content/public/test/mock_render_thread.h b/content/public/test/mock_render_thread.h
index a2444eb..c0b3b90 100644
--- a/content/public/test/mock_render_thread.h
+++ b/content/public/test/mock_render_thread.h
@@ -148,6 +148,9 @@ class MockRenderThread : public RenderThread {
// The last known good deserializer for sync messages.
scoped_ptr<IPC::MessageReplyDeserializer> reply_deserializer_;
+
+ // A list of message filters added to this thread.
+ std::vector<scoped_refptr<IPC::ChannelProxy::MessageFilter> > filters_;
};
} // namespace content
diff --git a/content/renderer/hyphenator/hyphenator.cc b/content/renderer/hyphenator/hyphenator.cc
index da92f9e..ef029d2 100644
--- a/content/renderer/hyphenator/hyphenator.cc
+++ b/content/renderer/hyphenator/hyphenator.cc
@@ -9,6 +9,8 @@
#include "base/memory/scoped_ptr.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
+#include "content/common/hyphenator_messages.h"
+#include "content/public/renderer/render_thread.h"
#include "third_party/hyphen/hyphen.h"
#include "unicode/uscript.h"
@@ -189,23 +191,42 @@ Hyphenator::Hyphenator(base::PlatformFile file)
Hyphenator::~Hyphenator() {
if (dictionary_)
hnj_hyphen_free(dictionary_);
+ if (rule_file_ != base::kInvalidPlatformFileValue)
+ base::ClosePlatformFile(rule_file_);
}
bool Hyphenator::Initialize() {
if (dictionary_)
return true;
+ // Attach the dictionary file to the MemoryMappedFile object. When it
+ // succeeds, this class does not have to close this file because it is closed
+ // by the MemoryMappedFile class. To prevent this class from closing this
+ // file, we reset its handle.
rule_map_.reset(new file_util::MemoryMappedFile);
if (!rule_map_->Initialize(rule_file_))
return false;
+ rule_file_ = base::kInvalidPlatformFileValue;
dictionary_ = hnj_hyphen_load(rule_map_->data(), rule_map_->length());
return !!dictionary_;
}
+bool Hyphenator::Attach(content::RenderThread* thread, const string16& locale) {
+ if (!thread)
+ return false;
+ locale_.assign(locale);
+ thread->AddObserver(this);
+ return thread->Send(new HyphenatorHostMsg_OpenDictionary(locale));
+}
+
+bool Hyphenator::CanHyphenate(const string16& locale) {
+ return !locale_.compare(locale);
+}
+
size_t Hyphenator::ComputeLastHyphenLocation(const string16& word,
size_t before_index) {
- if (!dictionary_ || word.empty())
+ if (!Initialize() || word.empty())
return 0;
// Call the hyphen library to get all hyphenation points, i.e. positions where
@@ -228,4 +249,32 @@ size_t Hyphenator::ComputeLastHyphenLocation(const string16& word,
return 0;
}
+bool Hyphenator::OnControlMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(Hyphenator, message)
+ IPC_MESSAGE_HANDLER(HyphenatorMsg_SetDictionary, OnSetDictionary)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void Hyphenator::OnSetDictionary(IPC::PlatformFileForTransit file) {
+ base::PlatformFile rule_file =
+ IPC::PlatformFileForTransitToPlatformFile(file);
+ if (rule_file == base::kInvalidPlatformFileValue)
+ return;
+ // Delete the current dictionary and save the given file to this object. We
+ // initialize the hyphen library the first time when WebKit actually
+ // hyphenates a word, i.e. when WebKit calls the ComputeLastHyphenLocation
+ // function. (WebKit does not always hyphenate words even when it calls the
+ // CanHyphenate function, e.g. WebKit does not have to hyphenate words when it
+ // does not have to break text into lines.)
+ if (dictionary_) {
+ hnj_hyphen_free(dictionary_);
+ dictionary_ = NULL;
+ }
+ rule_map_.reset();
+ rule_file_ = rule_file;
+}
+
} // namespace content
diff --git a/content/renderer/hyphenator/hyphenator.h b/content/renderer/hyphenator/hyphenator.h
index 561af80..0777364 100644
--- a/content/renderer/hyphenator/hyphenator.h
+++ b/content/renderer/hyphenator/hyphenator.h
@@ -11,6 +11,8 @@
#include "base/platform_file.h"
#include "base/string16.h"
#include "content/common/content_export.h"
+#include "content/public/renderer/render_process_observer.h"
+#include "ipc/ipc_platform_file.h"
namespace file_util {
class MemoryMappedFile;
@@ -19,33 +21,47 @@ class MemoryMappedFile;
typedef struct _HyphenDict HyphenDict;
namespace content {
+class RenderThread;
// A class that hyphenates a word. This class encapsulates the hyphen library
// and manages resources used by the library. When this class uses a huge
// dictionary, it takes lots of memory (~1.3MB for English). A renderer should
// create this object only when it renders a page that needs hyphenation and
// deletes it when it moves to a page that does not need hyphenation.
-class CONTENT_EXPORT Hyphenator {
+class CONTENT_EXPORT Hyphenator : public RenderProcessObserver {
public:
explicit Hyphenator(base::PlatformFile file);
- ~Hyphenator();
+ virtual ~Hyphenator();
// Initializes the hyphen library and allocates resources needed for
// hyphenation.
bool Initialize();
+ bool Attach(content::RenderThread* thread, const string16& locale);
+
+ // Returns whether this object can hyphenate words. When this object does not
+ // have a dictionary file attached, this function sends an IPC request to open
+ // the file.
+ bool CanHyphenate(const string16& locale);
+
// Returns the last hyphenation point, the position where we can insert a
// hyphen, before the given position. If there are not any hyphenation points,
// this function returns 0.
size_t ComputeLastHyphenLocation(const string16& word, size_t before_index);
+ // RenderProcessObserver implementation.
+ virtual bool OnControlMessageReceived(const IPC::Message& message) OVERRIDE;
+
private:
+ void OnSetDictionary(IPC::PlatformFileForTransit rule_file);
+
// The dictionary used by the hyphen library.
HyphenDict* dictionary_;
// The dictionary file and its memory-mapping object. (Our copy of the hyphen
// library uses a memory-mapped file opened by a browser so renderers can use
// it without opening the file.)
+ string16 locale_;
base::PlatformFile rule_file_;
scoped_ptr<file_util::MemoryMappedFile> rule_map_;
diff --git a/content/renderer/hyphenator/hyphenator_unittest.cc b/content/renderer/hyphenator/hyphenator_unittest.cc
index 84c1ce1..1a830e2 100644
--- a/content/renderer/hyphenator/hyphenator_unittest.cc
+++ b/content/renderer/hyphenator/hyphenator_unittest.cc
@@ -7,15 +7,79 @@
#include "base/path_service.h"
#include "base/platform_file.h"
#include "base/utf_string_conversions.h"
+#include "content/common/hyphenator_messages.h"
+#include "content/public/test/mock_render_thread.h"
+#include "ipc/ipc_listener.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/hyphen/hyphen.h"
+namespace {
+
+// A mock message listener that listens for HyphenatorHost messages. This class
+// intercepts a HyphenatorHostMsg_OpenDictionary message sent to an
+// IPC::TestSink object and emulates the HyphenatorMessageFilter class.
+class MockListener : public IPC::Listener {
+ public:
+ MockListener(content::Hyphenator* hyphenator, const string16& locale)
+ : hyphenator_(hyphenator),
+ locale_(locale) {
+ }
+ virtual ~MockListener() {
+ }
+
+ // IPC::ChannelProxy::MessageFilter implementation.
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
+ if (message.type() != HyphenatorHostMsg_OpenDictionary::ID)
+ return false;
+
+ // Retrieve the locale parameter directly because HyphenatorHost messages
+ // are internal messages and unit tests cannot access its member functions,
+ // i.e. unit tests cannot call the HyphenatorHostMsg_OpenDictionary::Read
+ // function.
+ PickleIterator iter(message);
+ string16 locale;
+ EXPECT_TRUE(message.ReadString16(&iter, &locale));
+ EXPECT_EQ(locale_, locale);
+
+ // Open the default dictionary and call the OnControllMessageReceived
+ // function with a HyphenatorMsg_SetDictionary message.
+ FilePath dictionary_path;
+ if (!PathService::Get(base::DIR_SOURCE_ROOT, &dictionary_path))
+ return false;
+ dictionary_path = dictionary_path.AppendASCII("third_party");
+ dictionary_path = dictionary_path.AppendASCII("hyphen");
+ dictionary_path = dictionary_path.AppendASCII("hyph_en_US.dic");
+ base::PlatformFile file = base::CreatePlatformFile(
+ dictionary_path, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
+ NULL, NULL);
+ EXPECT_NE(base::kInvalidPlatformFileValue, file);
+
+ IPC::Message response(
+ 0, HyphenatorMsg_SetDictionary::ID, IPC::Message::PRIORITY_NORMAL);
+ IPC::PlatformFileForTransit transit = IPC::GetFileHandleForProcess(
+ file, GetHandle(), false);
+ IPC::ParamTraits<IPC::PlatformFileForTransit>::Write(&response, transit);
+ hyphenator_->OnControlMessageReceived(response);
+ base::ClosePlatformFile(file);
+ return true;
+ }
+
+ private:
+ base::ProcessHandle GetHandle() const {
+ return base::Process::Current().handle();
+ }
+
+ content::Hyphenator* hyphenator_;
+ string16 locale_;
+};
+
+} // namespace
+
// A unit test for our hyphenator. This class loads a sample hyphenation
// dictionary and hyphenates words.
class HyphenatorTest : public testing::Test {
public:
HyphenatorTest() {
- Initialize();
}
bool Initialize() {
@@ -49,8 +113,22 @@ class HyphenatorTest : public testing::Test {
return hyphenated_word;
}
+ bool OpenDictionary(const string16& locale) {
+ hyphenator_.reset(new content::Hyphenator(base::kInvalidPlatformFileValue));
+ thread_.reset(new content::MockRenderThread());
+ listener_.reset(new MockListener(hyphenator_.get(), locale));
+ thread_->sink().AddFilter(listener_.get());
+ return hyphenator_->Attach(thread_.get(), locale);
+ }
+
+ size_t GetMessageCount() const {
+ return thread_->sink().message_count();
+ }
+
private:
scoped_ptr<content::Hyphenator> hyphenator_;
+ scoped_ptr<content::MockRenderThread> thread_;
+ scoped_ptr<MockListener> listener_;
};
// Verifies that our hyphenator yields the same hyphenated words as the original
@@ -82,9 +160,29 @@ TEST_F(HyphenatorTest, HyphenateWords) {
{ "undone.", "un-done." },
{ "unnecessary", "un-nec-es-sary" },
};
+ Initialize();
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
string16 input = ASCIIToUTF16(kTestCases[i].input);
string16 expected = ASCIIToUTF16(kTestCases[i].expected);
EXPECT_EQ(expected, Hyphenate(input));
}
}
+
+// Verifies that our hyphenator sends a HyphenatorHostMsg_OpenDictionary
+// message to ask a browser to open a dictionary. Also, this test verifies that
+// our hyphenator can hyphnate words when the Hyphenator::SetDictionary function
+// is called.
+TEST_F(HyphenatorTest, openDictionary) {
+ // Send a HyphenatorHostMsg_OpenDictionary message and verify it is handled by
+ // our MockListner class.
+ EXPECT_TRUE(OpenDictionary(string16()));
+ EXPECT_EQ(0U, GetMessageCount());
+
+ // Verify that we can now hyphenate words. When the MockListener class
+ // receives a HyphenatorHostMsg_OpenDictionary message, it calls the
+ // OnControlMessageReceived function with a HyphenatorMsg_SetDictionary
+ // message. So, the Hyphenate function should be able to hyphenate words now.
+ string16 input = ASCIIToUTF16("hyphenation");
+ string16 expected = ASCIIToUTF16("hy-phen-ation");
+ EXPECT_EQ(expected, Hyphenate(input));
+}
diff --git a/content/renderer/renderer_webkitplatformsupport_impl.cc b/content/renderer/renderer_webkitplatformsupport_impl.cc
index b2096ac..5f05a8f 100644
--- a/content/renderer/renderer_webkitplatformsupport_impl.cc
+++ b/content/renderer/renderer_webkitplatformsupport_impl.cc
@@ -25,6 +25,7 @@
#include "content/public/renderer/content_renderer_client.h"
#include "content/renderer/dom_storage/webstoragenamespace_impl.h"
#include "content/renderer/gamepad_shared_memory_reader.h"
+#include "content/renderer/hyphenator/hyphenator.h"
#include "content/renderer/media/audio_hardware.h"
#include "content/renderer/media/renderer_webaudiodevice_impl.h"
#include "content/renderer/render_thread_impl.h"
@@ -708,3 +709,35 @@ GpuChannelHostFactory*
RendererWebKitPlatformSupportImpl::GetGpuChannelHostFactory() {
return RenderThreadImpl::current();
}
+
+//------------------------------------------------------------------------------
+
+bool RendererWebKitPlatformSupportImpl::canHyphenate(
+ const WebKit::WebString& locale) {
+ // Return false unless WebKit asks for US English dictionaries because WebKit
+ // can currently hyphenate only English words.
+ if (!locale.isEmpty() && !locale.equals("en-US"))
+ return false;
+
+ // Create a hyphenator object and attach it to the render thread so it can
+ // receive a dictionary file opened by a browser.
+ if (!hyphenator_.get()) {
+ hyphenator_.reset(new content::Hyphenator(base::kInvalidPlatformFileValue));
+ if (!hyphenator_.get())
+ return false;
+ return hyphenator_->Attach(RenderThreadImpl::current(), locale);
+ }
+ return hyphenator_->CanHyphenate(locale);
+}
+
+size_t RendererWebKitPlatformSupportImpl::computeLastHyphenLocation(
+ const char16* characters,
+ size_t length,
+ size_t before_index,
+ const WebKit::WebString& locale) {
+ // Crash if WebKit calls this function when canHyphenate returns false.
+ DCHECK(locale.isEmpty() || locale.equals("en-US"));
+ DCHECK(hyphenator_.get());
+ return hyphenator_->ComputeLastHyphenLocation(string16(characters, length),
+ before_index);
+}
diff --git a/content/renderer/renderer_webkitplatformsupport_impl.h b/content/renderer/renderer_webkitplatformsupport_impl.h
index 62bd512..b4f798b 100644
--- a/content/renderer/renderer_webkitplatformsupport_impl.h
+++ b/content/renderer/renderer_webkitplatformsupport_impl.h
@@ -18,6 +18,7 @@ class WebFileSystemImpl;
namespace content {
class GamepadSharedMemoryReader;
+class Hyphenator;
}
namespace webkit_glue {
@@ -84,6 +85,11 @@ class CONTENT_EXPORT RendererWebKitPlatformSupportImpl
WebKit::WebPeerConnection00HandlerClient* client) OVERRIDE;
virtual WebKit::WebMediaStreamCenter* createMediaStreamCenter(
WebKit::WebMediaStreamCenterClient* client) OVERRIDE;
+ virtual bool canHyphenate(const WebKit::WebString& locale) OVERRIDE;
+ virtual size_t computeLastHyphenLocation(const char16* characters,
+ size_t length,
+ size_t before_index,
+ const WebKit::WebString& locale) OVERRIDE;
// Disables the WebSandboxSupport implementation for testing.
// Tests that do not set up a full sandbox environment should call
@@ -132,6 +138,8 @@ class CONTENT_EXPORT RendererWebKitPlatformSupportImpl
scoped_ptr<WebKit::WebBlobRegistry> blob_registry_;
scoped_ptr<content::GamepadSharedMemoryReader> gamepad_shared_memory_reader_;
+
+ scoped_ptr<content::Hyphenator> hyphenator_;
};
#endif // CONTENT_RENDERER_RENDERER_WEBKITPLATFORMSUPPORT_IMPL_H_
diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h
index 0d92f67..37f3afe 100644
--- a/ipc/ipc_message_utils.h
+++ b/ipc/ipc_message_utils.h
@@ -104,6 +104,7 @@ enum IPCMessageStart {
ChromotingMsgStart,
OldBrowserPluginMsgStart,
BrowserPluginMsgStart,
+ HyphenatorMsgStart,
AndroidWebViewMsgStart,
LastIPCMsgStart // Must come last.
};