diff options
author | hbono@chromium.org <hbono@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-04 10:39:23 +0000 |
---|---|---|
committer | hbono@chromium.org <hbono@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-04 10:39:23 +0000 |
commit | e6ae0f6c421776ee0a22af793469bd892876f9ee (patch) | |
tree | 4f43ac40627e7d8f7feb8b7ef84df057a70cc1e4 | |
parent | 75a82ff9193029ffc228b2ce0a5cdecc5ce728f3 (diff) | |
download | chromium_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.h | 21 | ||||
-rw-r--r-- | content/plugin/webplugin_delegate_stub.cc | 25 | ||||
-rw-r--r-- | content/plugin/webplugin_delegate_stub.h | 7 | ||||
-rw-r--r-- | content/plugin/webplugin_proxy.cc | 13 | ||||
-rw-r--r-- | content/plugin/webplugin_proxy.h | 8 | ||||
-rw-r--r-- | content/renderer/render_view.cc | 51 | ||||
-rw-r--r-- | content/renderer/render_view.h | 9 | ||||
-rw-r--r-- | content/renderer/webplugin_delegate_proxy.cc | 51 | ||||
-rw-r--r-- | content/renderer/webplugin_delegate_proxy.h | 13 | ||||
-rw-r--r-- | webkit/glue/webkit_glue.gypi | 2 | ||||
-rw-r--r-- | webkit/plugins/npapi/webplugin_delegate_impl.h | 27 | ||||
-rw-r--r-- | webkit/plugins/npapi/webplugin_delegate_impl_win.cc | 52 | ||||
-rw-r--r-- | webkit/plugins/npapi/webplugin_ime_win.cc | 323 | ||||
-rw-r--r-- | webkit/plugins/npapi/webplugin_ime_win.h | 190 |
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_ |