1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/child/npapi/webplugin_ime_win.h"
11 #include "base/lazy_instance.h"
12 #include "base/logging.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/synchronization/lock.h"
15 #include "content/child/npapi/plugin_instance.h"
16 #include "content/common/plugin_constants_win.h"
18 #pragma comment(lib, "imm32.lib")
22 // A critical section that prevents two or more plugins from accessing a
23 // WebPluginIMEWin instance through our patch function.
24 base::LazyInstance
<base::Lock
>::Leaky
25 g_webplugin_ime_lock
= LAZY_INSTANCE_INITIALIZER
;
27 WebPluginIMEWin
* WebPluginIMEWin::instance_
= NULL
;
29 WebPluginIMEWin::WebPluginIMEWin()
30 : cursor_position_(0),
32 composing_text_(false),
33 support_ime_messages_(false),
34 status_updated_(false),
36 memset(result_clauses_
, 0, sizeof(result_clauses_
));
39 WebPluginIMEWin::~WebPluginIMEWin() {
42 void WebPluginIMEWin::CompositionUpdated(const base::string16
& text
,
43 std::vector
<int> clauses
,
44 std::vector
<int> target
,
45 int cursor_position
) {
46 // Send a WM_IME_STARTCOMPOSITION message when a user starts a composition.
48 if (!composing_text_
) {
49 composing_text_
= true;
52 np_event
.event
= WM_IME_STARTCOMPOSITION
;
55 events_
.push_back(np_event
);
58 // We can update the following values from this event: GCS_COMPSTR,
59 // GCS_COMPATTR, GCSCOMPCLAUSE, GCS_CURSORPOS, and GCS_DELTASTART. We send a
60 // WM_IME_COMPOSITION message to notify the list of updated values.
61 np_event
.event
= WM_IME_COMPOSITION
;
63 np_event
.lParam
= GCS_COMPSTR
| GCS_COMPATTR
| GCS_COMPCLAUSE
|
64 GCS_CURSORPOS
| GCS_DELTASTART
;
65 events_
.push_back(np_event
);
67 // Converts this event to the IMM32 data so we do not have to convert it every
68 // time when a plugin call an IMM32 function.
69 composition_text_
= text
;
71 // Create the composition clauses returned when a plugin calls
72 // ImmGetCompositionString() with GCS_COMPCLAUSE.
73 composition_clauses_
.clear();
74 for (size_t i
= 0; i
< clauses
.size(); ++i
)
75 composition_clauses_
.push_back(clauses
[i
]);
77 // Create the composition attributes used by GCS_COMPATTR.
78 if (target
.size() == 2) {
79 composition_attributes_
.assign(text
.length(), ATTR_CONVERTED
);
80 for (int i
= target
[0]; i
< target
[1]; ++i
)
81 composition_attributes_
[i
] = ATTR_TARGET_CONVERTED
;
83 composition_attributes_
.assign(text
.length(), ATTR_INPUT
);
86 cursor_position_
= cursor_position
;
87 delta_start_
= cursor_position
;
90 void WebPluginIMEWin::CompositionCompleted(const base::string16
& text
) {
91 composing_text_
= false;
93 // We should update the following values when we finish a composition:
94 // GCS_RESULTSTR, GCS_RESULTCLAUSE, GCS_CURSORPOS, and GCS_DELTASTART. We
95 // send a WM_IME_COMPOSITION message to notify the list of updated values.
97 np_event
.event
= WM_IME_COMPOSITION
;
99 np_event
.lParam
= GCS_CURSORPOS
| GCS_DELTASTART
| GCS_RESULTSTR
|
101 events_
.push_back(np_event
);
103 // We also send a WM_IME_ENDCOMPOSITION message after the final
104 // WM_IME_COMPOSITION message (i.e. after finishing a composition).
105 np_event
.event
= WM_IME_ENDCOMPOSITION
;
108 events_
.push_back(np_event
);
110 // If the target plugin does not seem to support IME messages, we send
111 // each character in IME text with a WM_CHAR message so the plugin can
112 // insert the IME text.
113 if (!support_ime_messages_
) {
114 np_event
.event
= WM_CHAR
;
117 for (size_t i
= 0; i
< result_text_
.length(); ++i
) {
118 np_event
.wParam
= result_text_
[i
];
119 events_
.push_back(np_event
);
123 // Updated the result text and its clause. (Unlike composition clauses, a
124 // result clause consists of only one region.)
127 result_clauses_
[0] = 0;
128 result_clauses_
[1] = result_text_
.length();
130 cursor_position_
= result_clauses_
[1];
131 delta_start_
= result_clauses_
[1];
134 bool WebPluginIMEWin::SendEvents(PluginInstance
* instance
) {
135 // We allow the patch functions to access this WebPluginIMEWin instance only
136 // while we send IME events to the plugin.
137 ScopedLock
lock(this);
140 for (std::vector
<NPEvent
>::iterator it
= events_
.begin();
141 it
!= events_
.end(); ++it
) {
142 if (!instance
->NPP_HandleEvent(&(*it
)))
150 bool WebPluginIMEWin::GetStatus(int* input_type
, gfx::Rect
* caret_rect
) {
151 *input_type
= input_type_
;
152 *caret_rect
= caret_rect_
;
157 FARPROC
WebPluginIMEWin::GetProcAddress(LPCSTR name
) {
158 static const struct {
161 } kImm32Functions
[] = {
162 { "ImmAssociateContextEx",
163 reinterpret_cast<FARPROC
>(ImmAssociateContextEx
) },
164 { "ImmGetCompositionStringW",
165 reinterpret_cast<FARPROC
>(ImmGetCompositionStringW
) },
166 { "ImmGetContext", reinterpret_cast<FARPROC
>(ImmGetContext
) },
167 { "ImmReleaseContext", reinterpret_cast<FARPROC
>(ImmReleaseContext
) },
168 { "ImmSetCandidateWindow",
169 reinterpret_cast<FARPROC
>(ImmSetCandidateWindow
) },
170 { "ImmSetOpenStatus", reinterpret_cast<FARPROC
>(ImmSetOpenStatus
) },
172 for (int i
= 0; i
< arraysize(kImm32Functions
); ++i
) {
173 if (!lstrcmpiA(name
, kImm32Functions
[i
].name
))
174 return kImm32Functions
[i
].function
;
179 void WebPluginIMEWin::Lock() {
180 g_webplugin_ime_lock
.Pointer()->Acquire();
184 void WebPluginIMEWin::Unlock() {
186 g_webplugin_ime_lock
.Pointer()->Release();
190 WebPluginIMEWin
* WebPluginIMEWin::GetInstance(HIMC context
) {
191 return instance_
&& context
== reinterpret_cast<HIMC
>(instance_
) ?
196 BOOL WINAPI
WebPluginIMEWin::ImmAssociateContextEx(HWND window
,
199 WebPluginIMEWin
* instance
= GetInstance(context
);
201 return ::ImmAssociateContextEx(window
, context
, flags
);
203 int input_type
= !context
&& !flags
;
204 instance
->input_type_
= input_type
;
205 instance
->status_updated_
= true;
210 LONG WINAPI
WebPluginIMEWin::ImmGetCompositionStringW(HIMC context
,
214 WebPluginIMEWin
* instance
= GetInstance(context
);
216 return ::ImmGetCompositionStringW(context
, index
, dst_data
, dst_size
);
218 const void* src_data
= NULL
;
222 src_data
= instance
->composition_text_
.c_str();
223 src_size
= instance
->composition_text_
.length() * sizeof(wchar_t);
227 src_data
= instance
->composition_attributes_
.c_str();
228 src_size
= instance
->composition_attributes_
.length();
232 src_data
= &instance
->composition_clauses_
[0];
233 src_size
= instance
->composition_clauses_
.size() * sizeof(uint32
);
237 return instance
->cursor_position_
;
240 return instance
->delta_start_
;
243 src_data
= instance
->result_text_
.c_str();
244 src_size
= instance
->result_text_
.length() * sizeof(wchar_t);
247 case GCS_RESULTCLAUSE
:
248 src_data
= &instance
->result_clauses_
[0];
249 src_size
= sizeof(instance
->result_clauses_
);
255 if (!src_data
|| !src_size
)
256 return IMM_ERROR_NODATA
;
258 if (dst_size
>= src_size
)
259 memcpy(dst_data
, src_data
, src_size
);
265 HIMC WINAPI
WebPluginIMEWin::ImmGetContext(HWND window
) {
266 // Call the original ImmGetContext() function if the given window is the one
267 // created in WebPluginDelegateImpl::WindowedCreatePlugin(). (We attached IME
268 // context only with the windows created in this function.) On the other hand,
269 // some windowless plugins (such as Flash) call this function with a dummy
270 // window handle. We return our dummy IME context for these plugins so they
271 // can use our IME emulator.
272 if (IsWindow(window
)) {
274 GetClassName(window
, &name
[0], arraysize(name
));
275 if (!wcscmp(&name
[0], kNativeWindowClassName
))
276 return ::ImmGetContext(window
);
279 WebPluginIMEWin
* instance
= instance_
;
281 instance
->support_ime_messages_
= true;
282 return reinterpret_cast<HIMC
>(instance
);
286 BOOL WINAPI
WebPluginIMEWin::ImmReleaseContext(HWND window
, HIMC context
) {
287 if (!GetInstance(context
))
288 return ::ImmReleaseContext(window
, context
);
293 BOOL WINAPI
WebPluginIMEWin::ImmSetCandidateWindow(HIMC context
,
294 CANDIDATEFORM
* candidate
) {
295 WebPluginIMEWin
* instance
= GetInstance(context
);
297 return ::ImmSetCandidateWindow(context
, candidate
);
299 gfx::Rect
caret_rect(candidate
->rcArea
);
300 if ((candidate
->dwStyle
& CFS_EXCLUDE
) &&
301 instance
->caret_rect_
!= caret_rect
) {
302 instance
->caret_rect_
= caret_rect
;
303 instance
->status_updated_
= true;
309 BOOL WINAPI
WebPluginIMEWin::ImmSetOpenStatus(HIMC context
, BOOL open
) {
310 WebPluginIMEWin
* instance
= GetInstance(context
);
312 return ::ImmSetOpenStatus(context
, open
);
314 int input_type
= open
? 1 : 0;
315 if (instance
->input_type_
!= input_type
) {
316 instance
->input_type_
= input_type
;
317 instance
->status_updated_
= true;
323 } // namespace content