summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhbono@chromium.org <hbono@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-04 10:39:23 +0000
committerhbono@chromium.org <hbono@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-04 10:39:23 +0000
commite6ae0f6c421776ee0a22af793469bd892876f9ee (patch)
tree4f43ac40627e7d8f7feb8b7ef84df057a70cc1e4
parent75a82ff9193029ffc228b2ce0a5cdecc5ce728f3 (diff)
downloadchromium_src-e6ae0f6c421776ee0a22af793469bd892876f9ee.zip
chromium_src-e6ae0f6c421776ee0a22af793469bd892876f9ee.tar.gz
chromium_src-e6ae0f6c421776ee0a22af793469bd892876f9ee.tar.bz2
Send IME events to windowless plug-ins (Chromium side)
This change adds a new class WebPluginIMEWin that converts the platform-independent IME data sent from a renderer process (or WebKit) to the Win32 IME messages and send them to a plug-in. To allow the plug-in to retrieve the IME data with IMM32 function calls, this change also adds a patch to GetProcessAddress(). (Flash seems to retrieve the pointers to IMM32 function with this function.) This change also sends IME status retrieved from the plug-in to a browser process (via a renderer process). BUG=82507 TEST=manual Review URL: http://codereview.chromium.org/7082034 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@103869 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--content/common/plugin_messages.h21
-rw-r--r--content/plugin/webplugin_delegate_stub.cc25
-rw-r--r--content/plugin/webplugin_delegate_stub.h7
-rw-r--r--content/plugin/webplugin_proxy.cc13
-rw-r--r--content/plugin/webplugin_proxy.h8
-rw-r--r--content/renderer/render_view.cc51
-rw-r--r--content/renderer/render_view.h9
-rw-r--r--content/renderer/webplugin_delegate_proxy.cc51
-rw-r--r--content/renderer/webplugin_delegate_proxy.h13
-rw-r--r--webkit/glue/webkit_glue.gypi2
-rw-r--r--webkit/plugins/npapi/webplugin_delegate_impl.h27
-rw-r--r--webkit/plugins/npapi/webplugin_delegate_impl_win.cc52
-rw-r--r--webkit/plugins/npapi/webplugin_ime_win.cc323
-rw-r--r--webkit/plugins/npapi/webplugin_ime_win.h190
14 files changed, 789 insertions, 3 deletions
diff --git a/content/common/plugin_messages.h b/content/common/plugin_messages.h
index 2dc4959..8b57345 100644
--- a/content/common/plugin_messages.h
+++ b/content/common/plugin_messages.h
@@ -203,6 +203,17 @@ IPC_SYNC_MESSAGE_ROUTED1_2(PluginMsg_HandleInputEvent,
IPC_MESSAGE_ROUTED1(PluginMsg_SetContentAreaFocus,
bool /* has_focus */)
+#if defined(OS_WIN)
+IPC_MESSAGE_ROUTED4(PluginMsg_ImeCompositionUpdated,
+ string16 /* text */,
+ std::vector<int> /* clauses */,
+ std::vector<int>, /* target */
+ int /* cursor_position */)
+
+IPC_MESSAGE_ROUTED1(PluginMsg_ImeCompositionCompleted,
+ string16 /* text */)
+#endif
+
#if defined(OS_MACOSX)
IPC_MESSAGE_ROUTED1(PluginMsg_SetWindowFocus,
bool /* has_focus */)
@@ -304,6 +315,16 @@ IPC_SYNC_MESSAGE_ROUTED1_0(PluginHostMsg_SetWindow,
// in HandleEvent calls.
IPC_SYNC_MESSAGE_ROUTED1_0(PluginHostMsg_SetWindowlessPumpEvent,
HANDLE /* modal_loop_pump_messages_event */)
+
+// Send the IME status retrieved from a windowless plug-in. A windowless plug-in
+// uses the IME attached to a browser process as a renderer does. A plug-in
+// sends this message to control the IME status of a browser process. I would
+// note that a plug-in sends this message to a renderer process that hosts this
+// plug-in (not directly to a browser process) so the renderer process can
+// update its IME status.
+IPC_MESSAGE_ROUTED2(PluginHostMsg_NotifyIMEStatus,
+ int /* input_type */,
+ gfx::Rect /* caret_rect */)
#endif
IPC_MESSAGE_ROUTED1(PluginHostMsg_URLRequest,
diff --git a/content/plugin/webplugin_delegate_stub.cc b/content/plugin/webplugin_delegate_stub.cc
index dce1058..2d1037c 100644
--- a/content/plugin/webplugin_delegate_stub.cc
+++ b/content/plugin/webplugin_delegate_stub.cc
@@ -110,6 +110,12 @@ bool WebPluginDelegateStub::OnMessageReceived(const IPC::Message& msg) {
IPC_MESSAGE_HANDLER(PluginMsg_SendJavaScriptStream,
OnSendJavaScriptStream)
IPC_MESSAGE_HANDLER(PluginMsg_SetContentAreaFocus, OnSetContentAreaFocus)
+#if defined(OS_WIN)
+ IPC_MESSAGE_HANDLER(PluginMsg_ImeCompositionUpdated,
+ OnImeCompositionUpdated)
+ IPC_MESSAGE_HANDLER(PluginMsg_ImeCompositionCompleted,
+ OnImeCompositionCompleted)
+#endif
#if defined(OS_MACOSX)
IPC_MESSAGE_HANDLER(PluginMsg_SetWindowFocus, OnSetWindowFocus)
IPC_MESSAGE_HANDLER(PluginMsg_ContainerHidden, OnContainerHidden)
@@ -324,6 +330,25 @@ void WebPluginDelegateStub::OnSetContentAreaFocus(bool has_focus) {
delegate_->SetContentAreaHasFocus(has_focus);
}
+#if defined(OS_WIN)
+void WebPluginDelegateStub::OnImeCompositionUpdated(
+ const string16& text,
+ const std::vector<int>& clauses,
+ const std::vector<int>& target,
+ int cursor_position) {
+ if (delegate_)
+ delegate_->ImeCompositionUpdated(text, clauses, target, cursor_position);
+#if defined(OS_WIN)
+ webplugin_->UpdateIMEStatus();
+#endif
+}
+
+void WebPluginDelegateStub::OnImeCompositionCompleted(const string16& text) {
+ if (delegate_)
+ delegate_->ImeCompositionCompleted(text);
+}
+#endif
+
#if defined(OS_MACOSX)
void WebPluginDelegateStub::OnSetWindowFocus(bool has_focus) {
if (delegate_)
diff --git a/content/plugin/webplugin_delegate_stub.h b/content/plugin/webplugin_delegate_stub.h
index 4a12b9a..a3be8a0 100644
--- a/content/plugin/webplugin_delegate_stub.h
+++ b/content/plugin/webplugin_delegate_stub.h
@@ -81,6 +81,13 @@ class WebPluginDelegateStub : public IPC::Channel::Listener,
void OnGetFormValue(string16* value, bool* success);
void OnSetContentAreaFocus(bool has_focus);
+#if defined(OS_WIN)
+ void OnImeCompositionUpdated(const string16& text,
+ const std::vector<int>& clauses,
+ const std::vector<int>& target,
+ int cursor_position);
+ void OnImeCompositionCompleted(const string16& text);
+#endif
#if defined(OS_MACOSX)
void OnSetWindowFocus(bool has_focus);
void OnContainerHidden();
diff --git a/content/plugin/webplugin_proxy.cc b/content/plugin/webplugin_proxy.cc
index 2f7ef5f..4b167e1 100644
--- a/content/plugin/webplugin_proxy.cc
+++ b/content/plugin/webplugin_proxy.cc
@@ -747,3 +747,16 @@ void WebPluginProxy::ResourceClientDeleted(
void WebPluginProxy::URLRedirectResponse(bool allow, int resource_id) {
Send(new PluginHostMsg_URLRedirectResponse(route_id_, allow, resource_id));
}
+
+#if defined(OS_WIN)
+void WebPluginProxy::UpdateIMEStatus() {
+ // Retrieve the IME status from a plug-in and send it to a renderer process
+ // when the plug-in has updated it.
+ int input_type;
+ gfx::Rect caret_rect;
+ if (!delegate_->GetIMEStatus(&input_type, &caret_rect))
+ return;
+
+ Send(new PluginHostMsg_NotifyIMEStatus(route_id_, input_type, caret_rect));
+}
+#endif
diff --git a/content/plugin/webplugin_proxy.h b/content/plugin/webplugin_proxy.h
index b6e3175..54f8894 100644
--- a/content/plugin/webplugin_proxy.h
+++ b/content/plugin/webplugin_proxy.h
@@ -168,6 +168,14 @@ class WebPluginProxy : public webkit::npapi::WebPlugin {
virtual void URLRedirectResponse(bool allow, int resource_id);
+#if defined(OS_WIN)
+ // Retrieves the IME status from a windowless plug-in and sends it to a
+ // renderer process. A renderer process will convert the coordinates from
+ // local to the window coordinates and send the converted coordinates to a
+ // browser process.
+ void UpdateIMEStatus();
+#endif
+
private:
bool Send(IPC::Message* msg);
diff --git a/content/renderer/render_view.cc b/content/renderer/render_view.cc
index 8311958..5a20971 100644
--- a/content/renderer/render_view.cc
+++ b/content/renderer/render_view.cc
@@ -327,6 +327,9 @@ RenderView::RenderView(RenderThreadBase* render_thread,
cached_has_main_frame_horizontal_scrollbar_(false),
cached_has_main_frame_vertical_scrollbar_(false),
ALLOW_THIS_IN_INITIALIZER_LIST(pepper_delegate_(this)),
+#if defined(OS_WIN)
+ focused_plugin_id_(-1),
+#endif
ALLOW_THIS_IN_INITIALIZER_LIST(cookie_jar_(this)),
geolocation_dispatcher_(NULL),
speech_input_dispatcher_(NULL),
@@ -4045,6 +4048,32 @@ void RenderView::OnImeSetComposition(
// TODO(kinaba) This temporal remedy can be removed after PPAPI is extended
// with an IME handling interface.
if (!pepper_delegate_.IsPluginFocused()) {
+#if defined(OS_WIN)
+ // When a plug-in has focus, we create platform-specific IME data used by
+ // our IME emulator and send it directly to the focused plug-in, i.e. we
+ // bypass WebKit. (WebPluginDelegate dispatches this IME data only when its
+ // instance ID is the same one as the specified ID.)
+ if (focused_plugin_id_ >= 0) {
+ std::vector<int> clauses;
+ std::vector<int> target;
+ for (size_t i = 0; i < underlines.size(); ++i) {
+ clauses.push_back(underlines[i].startOffset);
+ clauses.push_back(underlines[i].endOffset);
+ if (underlines[i].thick) {
+ target.clear();
+ target.push_back(underlines[i].startOffset);
+ target.push_back(underlines[i].endOffset);
+ }
+ }
+ std::set<WebPluginDelegateProxy*>::iterator it;
+ for (it = plugin_delegates_.begin();
+ it != plugin_delegates_.end(); ++it) {
+ (*it)->ImeCompositionUpdated(text, clauses, target, selection_end,
+ focused_plugin_id_);
+ }
+ return;
+ }
+#endif
RenderWidget::OnImeSetComposition(text,
underlines,
selection_start,
@@ -4069,6 +4098,19 @@ void RenderView::OnImeConfirmComposition(const string16& text) {
webwidget_->handleInputEvent(char_event);
}
} else {
+#if defined(OS_WIN)
+ // Same as OnImeSetComposition(), we send the text from IMEs directly to
+ // plug-ins. When we send IME text directly to plug-ins, we should not send
+ // it to WebKit to prevent WebKit from controlling IMEs.
+ if (focused_plugin_id_ >= 0) {
+ std::set<WebPluginDelegateProxy*>::iterator it;
+ for (it = plugin_delegates_.begin();
+ it != plugin_delegates_.end(); ++it) {
+ (*it)->ImeCompositionCompleted(text, focused_plugin_id_);
+ }
+ return;
+ }
+#endif
RenderWidget::OnImeConfirmComposition(text);
}
}
@@ -4099,6 +4141,15 @@ bool RenderView::CanComposeInline() {
return true;
}
+#if defined(OS_WIN)
+void RenderView::PluginFocusChanged(bool focused, int plugin_id) {
+ if (focused)
+ focused_plugin_id_ = plugin_id;
+ else
+ focused_plugin_id_ = -1;
+}
+#endif
+
#if defined(OS_MACOSX)
void RenderView::PluginFocusChanged(bool focused, int plugin_id) {
IPC::Message* msg = new ViewHostMsg_PluginFocusChanged(routing_id(),
diff --git a/content/renderer/render_view.h b/content/renderer/render_view.h
index e22152e..fe293aa 100644
--- a/content/renderer/render_view.h
+++ b/content/renderer/render_view.h
@@ -309,10 +309,12 @@ class RenderView : public RenderWidget,
// Request updated policy regarding firewall NAT traversal being enabled.
void RequestRemoteAccessClientFirewallTraversal();
-#if defined(OS_MACOSX)
+#if defined(OS_MACOSX) || defined(OS_WIN)
// Informs the render view that the given plugin has gained or lost focus.
void PluginFocusChanged(bool focused, int plugin_id);
+#endif
+#if defined(OS_MACOSX)
// Starts plugin IME.
void StartPluginIme();
@@ -1130,6 +1132,11 @@ class RenderView : public RenderWidget,
// or tab focus and visibily. These are non-owning references.
std::set<WebPluginDelegateProxy*> plugin_delegates_;
+#if defined(OS_WIN)
+ // The ID of the focused NPAPI plug-in.
+ int focused_plugin_id_;
+#endif
+
// Helper objects ------------------------------------------------------------
RendererWebCookieJarImpl cookie_jar_;
diff --git a/content/renderer/webplugin_delegate_proxy.cc b/content/renderer/webplugin_delegate_proxy.cc
index 0f84f1b..121f5a3 100644
--- a/content/renderer/webplugin_delegate_proxy.cc
+++ b/content/renderer/webplugin_delegate_proxy.cc
@@ -190,7 +190,7 @@ WebPluginDelegateProxy::SharedBitmap::SharedBitmap() {}
WebPluginDelegateProxy::SharedBitmap::~SharedBitmap() {}
void WebPluginDelegateProxy::PluginDestroyed() {
-#if defined(OS_MACOSX)
+#if defined(OS_MACOSX) || defined(OS_WIN)
// Ensure that the renderer doesn't think the plugin still has focus.
if (render_view_)
render_view_->PluginFocusChanged(false, instance_id_);
@@ -425,6 +425,8 @@ bool WebPluginDelegateProxy::OnMessageReceived(const IPC::Message& msg) {
#if defined(OS_WIN)
IPC_MESSAGE_HANDLER(PluginHostMsg_SetWindowlessPumpEvent,
OnSetWindowlessPumpEvent)
+ IPC_MESSAGE_HANDLER(PluginHostMsg_NotifyIMEStatus,
+ OnNotifyIMEStatus)
#endif
IPC_MESSAGE_HANDLER(PluginHostMsg_CancelResource, OnCancelResource)
IPC_MESSAGE_HANDLER(PluginHostMsg_InvalidateRect, OnInvalidateRect)
@@ -482,7 +484,7 @@ void WebPluginDelegateProxy::OnChannelError() {
if (!channel_host_->expecting_shutdown())
render_view_->PluginCrashed(info_.path);
-#if defined(OS_MACOSX)
+#if defined(OS_MACOSX) || defined(OS_WIN)
// Ensure that the renderer doesn't think the plugin still has focus.
if (render_view_)
render_view_->PluginFocusChanged(false, instance_id_);
@@ -939,6 +941,10 @@ void WebPluginDelegateProxy::DidFinishLoadWithReason(
void WebPluginDelegateProxy::SetFocus(bool focused) {
Send(new PluginMsg_SetFocus(instance_id_, focused));
+#if defined(OS_WIN)
+ if (render_view_)
+ render_view_->PluginFocusChanged(focused, instance_id_);
+#endif
}
bool WebPluginDelegateProxy::HandleInputEvent(
@@ -971,6 +977,35 @@ void WebPluginDelegateProxy::SetContentAreaFocus(bool has_focus) {
Send(msg);
}
+#if defined(OS_WIN)
+void WebPluginDelegateProxy::ImeCompositionUpdated(
+ const string16& text,
+ const std::vector<int>& clauses,
+ const std::vector<int>& target,
+ int cursor_position,
+ int plugin_id) {
+ // Dispatch the raw IME data if this plug-in is the focused one.
+ if (instance_id_ != plugin_id)
+ return;
+
+ IPC::Message* msg = new PluginMsg_ImeCompositionUpdated(instance_id_,
+ text, clauses, target, cursor_position);
+ msg->set_unblock(true);
+ Send(msg);
+}
+
+void WebPluginDelegateProxy::ImeCompositionCompleted(const string16& text,
+ int plugin_id) {
+ // Dispatch the IME text if this plug-in is the focused one.
+ if (instance_id_ != plugin_id)
+ return;
+
+ IPC::Message* msg = new PluginMsg_ImeCompositionCompleted(instance_id_, text);
+ msg->set_unblock(true);
+ Send(msg);
+}
+#endif
+
#if defined(OS_MACOSX)
void WebPluginDelegateProxy::SetWindowFocus(bool window_has_focus) {
IPC::Message* msg = new PluginMsg_SetWindowFocus(instance_id_,
@@ -1056,6 +1091,18 @@ void WebPluginDelegateProxy::OnSetWindowlessPumpEvent(
modal_loop_pump_messages_event_.reset(
new base::WaitableEvent(modal_loop_pump_messages_event));
}
+
+void WebPluginDelegateProxy::OnNotifyIMEStatus(int input_type,
+ const gfx::Rect& caret_rect) {
+ if (!render_view_)
+ return;
+
+ render_view_->Send(new ViewHostMsg_ImeUpdateTextInputState(
+ render_view_->routing_id(),
+ static_cast<ui::TextInputType>(input_type),
+ true,
+ caret_rect));
+}
#endif
void WebPluginDelegateProxy::OnCancelResource(int id) {
diff --git a/content/renderer/webplugin_delegate_proxy.h b/content/renderer/webplugin_delegate_proxy.h
index 5b66a3c..6cfc3a3 100644
--- a/content/renderer/webplugin_delegate_proxy.h
+++ b/content/renderer/webplugin_delegate_proxy.h
@@ -82,6 +82,18 @@ class WebPluginDelegateProxy
// Informs the plugin that its containing content view has gained or lost
// first responder status.
virtual void SetContentAreaFocus(bool has_focus);
+#if defined(OS_WIN)
+ // Informs the plugin that plugin IME has updated its status.
+ virtual void ImeCompositionUpdated(
+ const string16& text,
+ const std::vector<int>& clauses,
+ const std::vector<int>& target,
+ int cursor_position,
+ int plugin_id);
+ // Informs the plugin that plugin IME has completed.
+ // If |text| is empty, composition was cancelled.
+ virtual void ImeCompositionCompleted(const string16& text, int plugin_id);
+#endif
#if defined(OS_MACOSX)
// Informs the plugin that its enclosing window has gained or lost focus.
virtual void SetWindowFocus(bool window_has_focus);
@@ -139,6 +151,7 @@ class WebPluginDelegateProxy
void OnSetWindow(gfx::PluginWindowHandle window);
#if defined(OS_WIN)
void OnSetWindowlessPumpEvent(HANDLE modal_loop_pump_messages_event);
+ void OnNotifyIMEStatus(const int input_mode, const gfx::Rect& caret_rect);
#endif
void OnCompleteURL(const std::string& url_in, std::string* url_out,
bool* result);
diff --git a/webkit/glue/webkit_glue.gypi b/webkit/glue/webkit_glue.gypi
index 9149e35..596c8fa 100644
--- a/webkit/glue/webkit_glue.gypi
+++ b/webkit/glue/webkit_glue.gypi
@@ -180,6 +180,8 @@
'../plugins/npapi/webplugin_delegate_impl_gtk.cc',
'../plugins/npapi/webplugin_delegate_impl_mac.mm',
'../plugins/npapi/webplugin_delegate_impl_win.cc',
+ '../plugins/npapi/webplugin_ime_win.cc',
+ '../plugins/npapi/webplugin_ime_win.h',
'../plugins/npapi/webplugin_impl.cc',
'../plugins/npapi/webplugin_impl.h',
'../plugins/npapi/webview_plugin.cc',
diff --git a/webkit/plugins/npapi/webplugin_delegate_impl.h b/webkit/plugins/npapi/webplugin_delegate_impl.h
index f8e20fc..a475f05 100644
--- a/webkit/plugins/npapi/webplugin_delegate_impl.h
+++ b/webkit/plugins/npapi/webplugin_delegate_impl.h
@@ -55,6 +55,10 @@ class QuickDrawDrawingManager;
#endif // NP_NO_QUICKDRAW
#endif // OS_MACOSX
+#if defined(OS_WIN)
+class WebPluginIMEWin;
+#endif // OS_WIN
+
// An implementation of WebPluginDelegate that runs in the plugin process,
// proxied from the renderer by WebPluginDelegateProxy.
class WebPluginDelegateImpl : public WebPluginDelegate {
@@ -79,6 +83,7 @@ class WebPluginDelegateImpl : public WebPluginDelegate {
PLUGIN_QUIRK_IGNORE_FIRST_SETWINDOW_CALL = 65536, // Windows.
PLUGIN_QUIRK_REPARENT_IN_BROWSER = 131072, // Windows
PLUGIN_QUIRK_PATCH_GETKEYSTATE = 262144, // Windows
+ PLUGIN_QUIRK_EMULATE_IME = 524288, // Windows.
};
static WebPluginDelegateImpl* Create(const FilePath& filename,
@@ -142,6 +147,21 @@ class WebPluginDelegateImpl : public WebPluginDelegate {
// Informs the plugin that the view it is in has gained or lost focus.
void SetContentAreaHasFocus(bool has_focus);
+#if defined(OS_WIN)
+ // Informs the plug-in that an IME has changed its status.
+ void ImeCompositionUpdated(const string16& text,
+ const std::vector<int>& clauses,
+ const std::vector<int>& target,
+ int cursor_position);
+
+ // Informs the plugin that IME composition has completed./ If |text| is empty,
+ // IME was cancelled.
+ void ImeCompositionCompleted(const string16& text);
+
+ // Returns the IME status retrieved from a plug-in.
+ bool GetIMEStatus(int* input_type, gfx::Rect* caret_rect);
+#endif
+
#if defined(OS_MACOSX)
// Informs the plugin that the geometry has changed, as with UpdateGeometry,
// but also includes the new buffer context for that new geometry.
@@ -318,6 +338,10 @@ class WebPluginDelegateImpl : public WebPluginDelegate {
// Used to throttle WM_USER+1 messages in Flash.
uint32 last_message_;
bool is_calling_wndproc;
+
+ // An IME emulator used by a windowless plug-in to retrieve IME data through
+ // IMM32 functions.
+ scoped_ptr<WebPluginIMEWin> plugin_ime_;
#endif // defined(OS_WIN)
#if defined(USE_X11)
@@ -389,6 +413,9 @@ class WebPluginDelegateImpl : public WebPluginDelegate {
HKEY key, DWORD index, LPWSTR name, LPDWORD name_size, LPDWORD reserved,
LPWSTR class_name, LPDWORD class_size, PFILETIME last_write_time);
+ // GetProcAddress intercepter for windowless plugins.
+ static FARPROC WINAPI GetProcAddressPatch(HMODULE module, LPCSTR name);
+
// The mouse hook proc which handles mouse capture in windowed plugins.
static LRESULT CALLBACK MouseHookProc(int code, WPARAM wParam,
LPARAM lParam);
diff --git a/webkit/plugins/npapi/webplugin_delegate_impl_win.cc b/webkit/plugins/npapi/webplugin_delegate_impl_win.cc
index 07102c1..44a5c50 100644
--- a/webkit/plugins/npapi/webplugin_delegate_impl_win.cc
+++ b/webkit/plugins/npapi/webplugin_delegate_impl_win.cc
@@ -32,6 +32,7 @@
#include "webkit/plugins/npapi/plugin_list.h"
#include "webkit/plugins/npapi/plugin_stream_url.h"
#include "webkit/plugins/npapi/webplugin.h"
+#include "webkit/plugins/npapi/webplugin_ime_win.h"
using WebKit::WebCursorInfo;
using WebKit::WebKeyboardEvent;
@@ -86,6 +87,10 @@ base::LazyInstance<base::win::IATPatchFunction> g_iat_patch_set_cursor(
base::LazyInstance<base::win::IATPatchFunction> g_iat_patch_reg_enum_key_ex_w(
base::LINKER_INITIALIZED);
+// Helper object for patching the GetProcAddress API.
+base::LazyInstance<base::win::IATPatchFunction> g_iat_patch_get_proc_address(
+ base::LINKER_INITIALIZED);
+
// Helper object for patching the GetKeyState API.
base::LazyInstance<base::win::IATPatchFunction> g_iat_patch_get_key_state(
base::LINKER_INITIALIZED);
@@ -348,6 +353,7 @@ WebPluginDelegateImpl::WebPluginDelegateImpl(
quirks_ |= PLUGIN_QUIRK_REPARENT_IN_BROWSER |
PLUGIN_QUIRK_PATCH_GETKEYSTATE;
}
+ quirks_ |= PLUGIN_QUIRK_EMULATE_IME;
} else if (filename == kAcrobatReaderPlugin) {
// Check for the version number above or equal 9.
int major_version = GetPluginMajorVersion(plugin_info);
@@ -488,6 +494,17 @@ bool WebPluginDelegateImpl::PlatformInitialize() {
WebPluginDelegateImpl::RegEnumKeyExWPatch);
}
+ // Flash retrieves the pointers to IMM32 functions with GetProcAddress() calls
+ // and use them to retrieve IME data. We add a patch to this function so we
+ // can dispatch these IMM32 calls to the WebPluginIMEWin class, which emulates
+ // IMM32 functions for Flash.
+ if (!g_iat_patch_get_proc_address.Pointer()->is_patched() &&
+ (quirks_ & PLUGIN_QUIRK_EMULATE_IME)) {
+ g_iat_patch_get_proc_address.Pointer()->Patch(
+ GetPluginPath().value().c_str(), "kernel32.dll", "GetProcAddress",
+ GetProcAddressPatch);
+ }
+
// Under UIPI the key state does not get forwarded properly to the child
// plugin window. So, instead we track the key state manually and intercept
// GetKeyState.
@@ -1482,6 +1499,41 @@ LONG WINAPI WebPluginDelegateImpl::RegEnumKeyExWPatch(
return rv;
}
+void WebPluginDelegateImpl::ImeCompositionUpdated(
+ const string16& text,
+ const std::vector<int>& clauses,
+ const std::vector<int>& target,
+ int cursor_position) {
+ if (!plugin_ime_.get())
+ plugin_ime_.reset(new WebPluginIMEWin);
+
+ plugin_ime_->CompositionUpdated(text, clauses, target, cursor_position);
+ plugin_ime_->SendEvents(instance());
+}
+
+void WebPluginDelegateImpl::ImeCompositionCompleted(const string16& text) {
+ if (!plugin_ime_.get())
+ plugin_ime_.reset(new WebPluginIMEWin);
+ plugin_ime_->CompositionCompleted(text);
+ plugin_ime_->SendEvents(instance());
+}
+
+bool WebPluginDelegateImpl::GetIMEStatus(int* input_type,
+ gfx::Rect* caret_rect) {
+ if (!plugin_ime_.get())
+ return false;
+ return plugin_ime_->GetStatus(input_type, caret_rect);
+}
+
+// static
+FARPROC WINAPI WebPluginDelegateImpl::GetProcAddressPatch(HMODULE module,
+ LPCSTR name) {
+ FARPROC imm_function = WebPluginIMEWin::GetProcAddress(name);
+ if (imm_function)
+ return imm_function;
+ return ::GetProcAddress(module, name);
+}
+
void WebPluginDelegateImpl::HandleCaptureForMessage(HWND window,
UINT message) {
if (!WebPluginDelegateImpl::IsPluginDelegateWindow(window))
diff --git a/webkit/plugins/npapi/webplugin_ime_win.cc b/webkit/plugins/npapi/webplugin_ime_win.cc
new file mode 100644
index 0000000..3c9f7a8
--- /dev/null
+++ b/webkit/plugins/npapi/webplugin_ime_win.cc
@@ -0,0 +1,323 @@
+// Copyright (c) 2011 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 "webkit/plugins/npapi/webplugin_ime_win.h"
+
+#include <string>
+#include <vector>
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/synchronization/lock.h"
+#include "base/utf_string_conversions.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
+#include "webkit/plugins/npapi/plugin_constants_win.h"
+#include "webkit/plugins/npapi/plugin_instance.h"
+
+#pragma comment(lib, "imm32.lib")
+
+using WebKit::WebInputEvent;
+using WebKit::WebCompositionEvent;
+
+namespace webkit {
+namespace npapi {
+
+// A critical section that prevents two or more plug-ins from accessing a
+// WebPluginIMEWin instance through our patch function.
+base::LazyInstance<base::Lock> g_webplugin_ime_lock(base::LINKER_INITIALIZED);
+
+WebPluginIMEWin* WebPluginIMEWin::instance_ = NULL;
+
+WebPluginIMEWin::WebPluginIMEWin()
+ : cursor_position_(0),
+ delta_start_(0),
+ composing_text_(false),
+ support_ime_messages_(false),
+ status_updated_(false),
+ input_type_(1) {
+}
+
+WebPluginIMEWin::~WebPluginIMEWin() {
+}
+
+void WebPluginIMEWin::CompositionUpdated(const string16& text,
+ std::vector<int> clauses,
+ std::vector<int> target,
+ int cursor_position) {
+ // Send a WM_IME_STARTCOMPOSITION message when a user starts a composition.
+ NPEvent np_event;
+ if (!composing_text_) {
+ composing_text_ = true;
+ result_text_.clear();
+
+ np_event.event = WM_IME_STARTCOMPOSITION;
+ np_event.wParam = 0;
+ np_event.lParam = 0;
+ events_.push_back(np_event);
+ }
+
+ // We can update the following values from this event: GCS_COMPSTR,
+ // GCS_COMPATTR, GCSCOMPCLAUSE, GCS_CURSORPOS, and GCS_DELTASTART. We send a
+ // WM_IME_COMPOSITION message to notify the list of updated values.
+ np_event.event = WM_IME_COMPOSITION;
+ np_event.wParam = 0;
+ np_event.lParam = GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE |
+ GCS_CURSORPOS | GCS_DELTASTART;
+ events_.push_back(np_event);
+
+ // Converts this event to the IMM32 data so we do not have to convert it every
+ // time when a plug-in call an IMM32 function.
+ composition_text_ = text;
+
+ // Create the composition clauses returned when a plug-in calls
+ // ImmGetCompositionString() with GCS_COMPCLAUSE.
+ composition_clauses_.clear();
+ for (size_t i = 0; i < clauses.size(); ++i)
+ composition_clauses_.push_back(clauses[i]);
+
+ // Create the composition attributes used by GCS_COMPATTR.
+ if (target.size() == 2) {
+ composition_attributes_.assign(text.length(), ATTR_CONVERTED);
+ for (int i = target[0]; i < target[1]; ++i)
+ composition_attributes_[i] = ATTR_TARGET_CONVERTED;
+ } else {
+ composition_attributes_.assign(text.length(), ATTR_INPUT);
+ }
+
+ cursor_position_ = cursor_position;
+ delta_start_ = cursor_position;
+}
+
+void WebPluginIMEWin::CompositionCompleted(const string16& text) {
+ composing_text_ = false;
+
+ // We should update the following values when we finish a composition:
+ // GCS_RESULTSTR, GCS_RESULTCLAUSE, GCS_CURSORPOS, and GCS_DELTASTART. We
+ // send a WM_IME_COMPOSITION message to notify the list of updated values.
+ NPEvent np_event;
+ np_event.event = WM_IME_COMPOSITION;
+ np_event.wParam = 0;
+ np_event.lParam = GCS_CURSORPOS | GCS_DELTASTART | GCS_RESULTSTR |
+ GCS_RESULTCLAUSE;
+ events_.push_back(np_event);
+
+ // We also send a WM_IME_ENDCOMPOSITION message after the final
+ // WM_IME_COMPOSITION message (i.e. after finishing a composition).
+ np_event.event = WM_IME_ENDCOMPOSITION;
+ np_event.wParam = 0;
+ np_event.lParam = 0;
+ events_.push_back(np_event);
+
+ // If the target plug-in does not seem to support IME messages, we send
+ // each character in IME text with a WM_CHAR message so the plug-in can
+ // insert the IME text.
+ if (!support_ime_messages_) {
+ np_event.event = WM_CHAR;
+ np_event.wParam = 0;
+ np_event.lParam = 0;
+ for (size_t i = 0; i < result_text_.length(); ++i) {
+ np_event.wParam = result_text_[i];
+ events_.push_back(np_event);
+ }
+ }
+
+ // Updated the result text and its clause. (Unlike composition clauses, a
+ // result clause consists of only one region.)
+ result_text_ = text;
+
+ result_clauses_[0] = 0;
+ result_clauses_[1] = result_text_.length();
+
+ cursor_position_ = result_clauses_[1];
+ delta_start_ = result_clauses_[1];
+}
+
+bool WebPluginIMEWin::SendEvents(PluginInstance* instance) {
+ // We allow the patch functions to access this WebPluginIMEWin instance only
+ // while we send IME events to the plug-in.
+ ScopedLock lock(this);
+
+ bool ret = true;
+ for (std::vector<NPEvent>::iterator it = events_.begin();
+ it != events_.end(); ++it) {
+ if (!instance->NPP_HandleEvent(&(*it)))
+ ret = false;
+ }
+
+ events_.clear();
+ return ret;
+}
+
+bool WebPluginIMEWin::GetStatus(int* input_type, gfx::Rect* caret_rect) {
+ bool status_updated = status_updated_;
+ if (status_updated) {
+ *input_type = input_type_;
+ *caret_rect = caret_rect_;
+ status_updated_ = false;
+ }
+ return status_updated;
+}
+
+// static
+FARPROC WebPluginIMEWin::GetProcAddress(LPCSTR name) {
+ static const struct {
+ const char* name;
+ FARPROC function;
+ } kImm32Functions[] = {
+ { "ImmAssociateContextEx",
+ reinterpret_cast<FARPROC>(ImmAssociateContextEx) },
+ { "ImmGetCompositionStringW",
+ reinterpret_cast<FARPROC>(ImmGetCompositionStringW) },
+ { "ImmGetContext", reinterpret_cast<FARPROC>(ImmGetContext) },
+ { "ImmReleaseContext", reinterpret_cast<FARPROC>(ImmReleaseContext) },
+ { "ImmSetCandidateWindow",
+ reinterpret_cast<FARPROC>(ImmSetCandidateWindow) },
+ { "ImmSetOpenStatus", reinterpret_cast<FARPROC>(ImmSetOpenStatus) },
+ };
+ for (int i = 0; i < arraysize(kImm32Functions); ++i) {
+ if (!lstrcmpiA(name, kImm32Functions[i].name))
+ return kImm32Functions[i].function;
+ }
+ return NULL;
+}
+
+void WebPluginIMEWin::Lock() {
+ g_webplugin_ime_lock.Pointer()->Acquire();
+ instance_ = this;
+}
+
+void WebPluginIMEWin::Unlock() {
+ instance_ = NULL;
+ g_webplugin_ime_lock.Pointer()->Release();
+}
+
+// static
+WebPluginIMEWin* WebPluginIMEWin::GetInstance(HIMC context) {
+ return instance_ && context == reinterpret_cast<HIMC>(instance_) ?
+ instance_ : NULL;
+}
+
+// static
+BOOL WINAPI WebPluginIMEWin::ImmAssociateContextEx(HWND window,
+ HIMC context,
+ DWORD flags) {
+ return TRUE;
+}
+
+// static
+LONG WINAPI WebPluginIMEWin::ImmGetCompositionStringW(HIMC context,
+ DWORD index,
+ LPVOID dst_data,
+ DWORD dst_size) {
+ WebPluginIMEWin* instance = GetInstance(context);
+ if (!instance)
+ return ::ImmGetCompositionStringW(context, index, dst_data, dst_size);
+
+ const void* src_data = NULL;
+ DWORD src_size = 0;
+ switch (index) {
+ case GCS_COMPSTR:
+ src_data = instance->composition_text_.c_str();
+ src_size = instance->composition_text_.length() * sizeof(wchar_t);
+ break;
+
+ case GCS_COMPATTR:
+ src_data = instance->composition_attributes_.c_str();
+ src_size = instance->composition_attributes_.length();
+ break;
+
+ case GCS_COMPCLAUSE:
+ src_data = &instance->composition_clauses_[0];
+ src_size = instance->composition_clauses_.size() * sizeof(uint32);
+ break;
+
+ case GCS_CURSORPOS:
+ return instance->cursor_position_;
+
+ case GCS_DELTASTART:
+ return instance->delta_start_;
+
+ case GCS_RESULTSTR:
+ src_data = instance->result_text_.c_str();
+ src_size = instance->result_text_.length() * sizeof(wchar_t);
+ break;
+
+ case GCS_RESULTCLAUSE:
+ src_data = &instance->result_clauses_[0];
+ src_size = sizeof(instance->result_clauses_);
+ break;
+
+ default:
+ break;
+ }
+ if (!src_data || !src_size)
+ return IMM_ERROR_NODATA;
+
+ if (dst_size >= src_size)
+ memcpy(dst_data, src_data, src_size);
+
+ return src_size;
+}
+
+// static
+HIMC WINAPI WebPluginIMEWin::ImmGetContext(HWND window) {
+ // Call the original ImmGetContext() function if the given window is the one
+ // created in WebPluginDelegateImpl::WindowedCreatePlugin(). (We attached IME
+ // context only with the windows created in this function.) On the other hand,
+ // some windowless plug-ins (such as Flash) call this function with a dummy
+ // window handle. We return our dummy IME context for these plug-ins so they
+ // can use our IME emulator.
+ if (IsWindow(window)) {
+ wchar_t name[128];
+ GetClassName(window, &name[0], arraysize(name));
+ if (!wcscmp(&name[0], kNativeWindowClassName))
+ return ::ImmGetContext(window);
+ }
+
+ WebPluginIMEWin* instance = instance_;
+ if (instance)
+ instance->support_ime_messages_ = true;
+ return reinterpret_cast<HIMC>(instance);
+}
+
+// static
+BOOL WINAPI WebPluginIMEWin::ImmReleaseContext(HWND window, HIMC context) {
+ if (!GetInstance(context))
+ return ::ImmReleaseContext(window, context);
+ return TRUE;
+}
+
+// static
+BOOL WINAPI WebPluginIMEWin::ImmSetCandidateWindow(HIMC context,
+ CANDIDATEFORM* candidate) {
+ WebPluginIMEWin* instance = GetInstance(context);
+ if (!instance)
+ return ::ImmSetCandidateWindow(context, candidate);
+
+ gfx::Rect caret_rect(candidate->rcArea);
+ if ((candidate->dwStyle & CFS_EXCLUDE) &&
+ instance->caret_rect_ != caret_rect) {
+ instance->caret_rect_ = caret_rect;
+ instance->status_updated_ = true;
+ }
+ return TRUE;
+}
+
+// static
+BOOL WINAPI WebPluginIMEWin::ImmSetOpenStatus(HIMC context, BOOL open) {
+ WebPluginIMEWin* instance = GetInstance(context);
+ if (!instance)
+ return ::ImmSetOpenStatus(context, open);
+
+ int input_type = open ? 1 : 0;
+ if (instance->input_type_ != input_type) {
+ instance->input_type_ = input_type;
+ instance->status_updated_ = true;
+ }
+
+ return TRUE;
+}
+
+} // namespace npapi
+} // namespace webkit
diff --git a/webkit/plugins/npapi/webplugin_ime_win.h b/webkit/plugins/npapi/webplugin_ime_win.h
new file mode 100644
index 0000000..8d876b7
--- /dev/null
+++ b/webkit/plugins/npapi/webplugin_ime_win.h
@@ -0,0 +1,190 @@
+// Copyright (c) 2011 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 WEBKIT_PLUGINS_NPAPI_WEBPLUGIN_IME_WIN_H_
+#define WEBKIT_PLUGINS_NPAPI_WEBPLUGIN_IME_WIN_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/string16.h"
+#include "third_party/npapi/bindings/npapi.h"
+#include "ui/gfx/rect.h"
+
+namespace WebKit {
+class WebInputEvent;
+class WebCompositionEvent;
+}
+
+namespace webkit {
+namespace npapi {
+
+class PluginInstance;
+
+// A class that emulates an IME for windowless plug-ins. A windowless plug-in
+// does not have a window. Therefore, we cannot attach an IME to a windowless
+// plug-in. To allow such windowless plug-ins to use IMEs without any changes to
+// them, this class receives the IME data from a browser and patches IMM32
+// functions to return the IME data when a windowless plug-in calls IMM32
+// functions. I would not Flash retrieves pointers to IMM32 functions with
+// GetProcAddress(), this class also needs a hook to GetProcAddress() to
+// dispatch IMM32 function calls from a plug-in to this class as listed in the
+// following snippet.
+//
+// FARPROC WINAPI GetProcAddressPatch(HMODULE module, LPCSTR name) {
+// FARPROC* proc = WebPluginIMEWin::GetProcAddress(name);
+// if (proc)
+// return proc;
+// return ::GetProcAddress(module, name);
+// }
+// ...
+// app::win::IATPatchFunction get_proc_address;
+// get_proc_address.Patch(
+// GetPluginPath().value().c_str(), "kernel32.dll", "GetProcAddress",
+// GetProcAddressPatch);
+//
+// After we successfuly dispatch IMM32 calls from a plug-in to this class, we
+// need to update its IME data so the class can return it to the plug-in through
+// its IMM32 calls. To update the IME data, we call CompositionUpdated() or
+// CompositionCompleted() BEFORE sending an IMM32 Window message to the plugin
+// with a SendEvents() call as listed in the following snippet. (Plug-ins call
+// IMM32 functions when it receives IMM32 window messages. We need to update the
+// IME data of this class before sending IMM32 messages so the plug-ins can get
+// the latest data.)
+//
+// WebPluginIMEWin ime;
+// ...
+// string16 text = "composing";
+// std::vector<int> clauses;
+// clauses.push_back(0);
+// clauses.push_back(text.length());
+// std::vector<int> target;
+// ime.CompositionUpdated(text, clauses, target, text.length());
+// ime.SendEvents(instance());
+//
+// string16 result = "result";
+// ime.CompositionCompleted(result);
+// ime.SendEvents(instance());
+//
+// This class also provides GetStatus() so we can retrieve the IME status
+// changed by a plug-in with IMM32 functions. This function is mainly used for
+// retrieving the position of a caret.
+//
+class WebPluginIMEWin {
+ public:
+ // A simple class that allows a plug-in to access a WebPluginIMEWin instance
+ // only in a scope.
+ class ScopedLock {
+ public:
+ explicit ScopedLock(WebPluginIMEWin* instance) : instance_(instance) {
+ if (instance_)
+ instance_->Lock();
+ }
+ ~ScopedLock() {
+ if (instance_)
+ instance_->Unlock();
+ }
+
+ private:
+ WebPluginIMEWin* instance_;
+ };
+
+ WebPluginIMEWin();
+ ~WebPluginIMEWin();
+
+ // Sends raw IME events sent from a browser to this IME emulator and updates
+ // the list of Windows events to be sent to a plug-in. A raw IME event is
+ // mapped to two or more Windows events and it is not so trivial to send these
+ // Windows events to a plug-in. This function inserts Windows events in the
+ // order expected by a plug-in.
+ void CompositionUpdated(const string16& text,
+ std::vector<int> clauses,
+ std::vector<int> target,
+ int cursor_position);
+ void CompositionCompleted(const string16& text);
+
+ // Send all the events added in Update() to a plug-in.
+ bool SendEvents(PluginInstance* instance);
+
+ // Retrieves the status of this IME emulator.
+ bool GetStatus(int* input_type, gfx::Rect* caret_rect);
+
+ // Returns the pointers to IMM32-emulation functions implemented by this
+ // class. This function is used for over-writing the ones returned from
+ // GetProcAddress() calls of Win32 API.
+ static FARPROC GetProcAddress(const char* name);
+
+ private:
+ // Allow (or disallow) the patch functions to use this WebPluginIMEWin
+ // instance through our patch functions. Our patch functions need a static
+ // member variable |instance_| to access a WebPluginIMEWIn instance. We lock
+ // this static variable to prevent two or more plug-ins from accessing a
+ // WebPluginIMEWin instance.
+ void Lock();
+ void Unlock();
+
+ // Retrieve the instance of this class.
+ static WebPluginIMEWin* GetInstance(HIMC context);
+
+ // IMM32 patch functions implemented by this class.
+ static BOOL WINAPI ImmAssociateContextEx(HWND window,
+ HIMC context,
+ DWORD flags);
+ static LONG WINAPI ImmGetCompositionStringW(HIMC context,
+ DWORD index,
+ LPVOID dst_data,
+ DWORD dst_size);
+ static HIMC WINAPI ImmGetContext(HWND window);
+ static BOOL WINAPI ImmReleaseContext(HWND window, HIMC context);
+ static BOOL WINAPI ImmSetCandidateWindow(HIMC context,
+ CANDIDATEFORM* candidate);
+ static BOOL WINAPI ImmSetOpenStatus(HIMC context, BOOL open);
+
+ // a list of NPEvents to be sent to a plug-in.
+ std::vector<NPEvent> events_;
+
+ // The return value for GCS_COMPSTR.
+ string16 composition_text_;
+
+ // The return value for GCS_RESULTSTR.
+ string16 result_text_;
+
+ // The return value for GCS_COMPATTR.
+ std::string composition_attributes_;
+
+ // The return value for GCS_COMPCLAUSE.
+ std::vector<uint32> composition_clauses_;
+
+ // The return value for GCS_RESULTCLAUSE.
+ uint32 result_clauses_[2];
+
+ // The return value for GCS_CURSORPOS.
+ int cursor_position_;
+
+ // The return value for GCS_DELTASTART.
+ int delta_start_;
+
+ // Whether we are composing text. This variable is used for sending a
+ // WM_IME_STARTCOMPOSITION message when we start composing IME text.
+ bool composing_text_;
+
+ // Whether a plug-in supports IME messages. When a plug-in cannot handle
+ // IME messages, we need to send the IME text with WM_CHAR messages as Windows
+ // does.
+ bool support_ime_messages_;
+
+ // The IME status received from a plug-in.
+ bool status_updated_;
+ int input_type_;
+ gfx::Rect caret_rect_;
+
+ // The pointer to the WebPluginIMEWin instance used by patch functions.
+ static WebPluginIMEWin* instance_;
+};
+
+} // namespace npapi
+} // namespace webkit
+
+#endif // WEBKIT_PLUGINS_NPAPI_WEBPLUGIN_IME_WIN_H_