OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "content/child/npapi/webplugin_ime_win.h" | |
6 | |
7 #include <stddef.h> | |
8 #include <string.h> | |
9 | |
10 #include <cstring> | |
11 #include <string> | |
12 #include <vector> | |
13 | |
14 #include "base/lazy_instance.h" | |
15 #include "base/logging.h" | |
16 #include "base/macros.h" | |
17 #include "base/strings/utf_string_conversions.h" | |
18 #include "base/synchronization/lock.h" | |
19 #include "content/child/npapi/plugin_instance.h" | |
20 #include "content/common/plugin_constants_win.h" | |
21 | |
22 #pragma comment(lib, "imm32.lib") | |
23 | |
24 namespace content { | |
25 | |
26 // A critical section that prevents two or more plugins from accessing a | |
27 // WebPluginIMEWin instance through our patch function. | |
28 base::LazyInstance<base::Lock>::Leaky | |
29 g_webplugin_ime_lock = LAZY_INSTANCE_INITIALIZER; | |
30 | |
31 WebPluginIMEWin* WebPluginIMEWin::instance_ = NULL; | |
32 | |
33 WebPluginIMEWin::WebPluginIMEWin() | |
34 : cursor_position_(0), | |
35 delta_start_(0), | |
36 composing_text_(false), | |
37 support_ime_messages_(false), | |
38 status_updated_(false), | |
39 input_type_(1) { | |
40 memset(result_clauses_, 0, sizeof(result_clauses_)); | |
41 } | |
42 | |
43 WebPluginIMEWin::~WebPluginIMEWin() { | |
44 } | |
45 | |
46 void WebPluginIMEWin::CompositionUpdated(const base::string16& text, | |
47 std::vector<int> clauses, | |
48 std::vector<int> target, | |
49 int cursor_position) { | |
50 // Send a WM_IME_STARTCOMPOSITION message when a user starts a composition. | |
51 NPEvent np_event; | |
52 if (!composing_text_) { | |
53 composing_text_ = true; | |
54 result_text_.clear(); | |
55 | |
56 np_event.event = WM_IME_STARTCOMPOSITION; | |
57 np_event.wParam = 0; | |
58 np_event.lParam = 0; | |
59 events_.push_back(np_event); | |
60 } | |
61 | |
62 // We can update the following values from this event: GCS_COMPSTR, | |
63 // GCS_COMPATTR, GCSCOMPCLAUSE, GCS_CURSORPOS, and GCS_DELTASTART. We send a | |
64 // WM_IME_COMPOSITION message to notify the list of updated values. | |
65 np_event.event = WM_IME_COMPOSITION; | |
66 np_event.wParam = 0; | |
67 np_event.lParam = GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE | | |
68 GCS_CURSORPOS | GCS_DELTASTART; | |
69 events_.push_back(np_event); | |
70 | |
71 // Converts this event to the IMM32 data so we do not have to convert it every | |
72 // time when a plugin call an IMM32 function. | |
73 composition_text_ = text; | |
74 | |
75 // Create the composition clauses returned when a plugin calls | |
76 // ImmGetCompositionString() with GCS_COMPCLAUSE. | |
77 composition_clauses_.clear(); | |
78 for (size_t i = 0; i < clauses.size(); ++i) | |
79 composition_clauses_.push_back(clauses[i]); | |
80 | |
81 // Create the composition attributes used by GCS_COMPATTR. | |
82 if (target.size() == 2) { | |
83 composition_attributes_.assign(text.length(), ATTR_CONVERTED); | |
84 for (int i = target[0]; i < target[1]; ++i) | |
85 composition_attributes_[i] = ATTR_TARGET_CONVERTED; | |
86 } else { | |
87 composition_attributes_.assign(text.length(), ATTR_INPUT); | |
88 } | |
89 | |
90 cursor_position_ = cursor_position; | |
91 delta_start_ = cursor_position; | |
92 } | |
93 | |
94 void WebPluginIMEWin::CompositionCompleted(const base::string16& text) { | |
95 composing_text_ = false; | |
96 | |
97 // We should update the following values when we finish a composition: | |
98 // GCS_RESULTSTR, GCS_RESULTCLAUSE, GCS_CURSORPOS, and GCS_DELTASTART. We | |
99 // send a WM_IME_COMPOSITION message to notify the list of updated values. | |
100 NPEvent np_event; | |
101 np_event.event = WM_IME_COMPOSITION; | |
102 np_event.wParam = 0; | |
103 np_event.lParam = GCS_CURSORPOS | GCS_DELTASTART | GCS_RESULTSTR | | |
104 GCS_RESULTCLAUSE; | |
105 events_.push_back(np_event); | |
106 | |
107 // We also send a WM_IME_ENDCOMPOSITION message after the final | |
108 // WM_IME_COMPOSITION message (i.e. after finishing a composition). | |
109 np_event.event = WM_IME_ENDCOMPOSITION; | |
110 np_event.wParam = 0; | |
111 np_event.lParam = 0; | |
112 events_.push_back(np_event); | |
113 | |
114 // If the target plugin does not seem to support IME messages, we send | |
115 // each character in IME text with a WM_CHAR message so the plugin can | |
116 // insert the IME text. | |
117 if (!support_ime_messages_) { | |
118 np_event.event = WM_CHAR; | |
119 np_event.wParam = 0; | |
120 np_event.lParam = 0; | |
121 for (size_t i = 0; i < result_text_.length(); ++i) { | |
122 np_event.wParam = result_text_[i]; | |
123 events_.push_back(np_event); | |
124 } | |
125 } | |
126 | |
127 // Updated the result text and its clause. (Unlike composition clauses, a | |
128 // result clause consists of only one region.) | |
129 result_text_ = text; | |
130 | |
131 result_clauses_[0] = 0; | |
132 result_clauses_[1] = result_text_.length(); | |
133 | |
134 cursor_position_ = result_clauses_[1]; | |
135 delta_start_ = result_clauses_[1]; | |
136 } | |
137 | |
138 bool WebPluginIMEWin::SendEvents(PluginInstance* instance) { | |
139 // We allow the patch functions to access this WebPluginIMEWin instance only | |
140 // while we send IME events to the plugin. | |
141 ScopedLock lock(this); | |
142 | |
143 bool ret = true; | |
144 for (std::vector<NPEvent>::iterator it = events_.begin(); | |
145 it != events_.end(); ++it) { | |
146 if (!instance->NPP_HandleEvent(&(*it))) | |
147 ret = false; | |
148 } | |
149 | |
150 events_.clear(); | |
151 return ret; | |
152 } | |
153 | |
154 bool WebPluginIMEWin::GetStatus(int* input_type, gfx::Rect* caret_rect) { | |
155 *input_type = input_type_; | |
156 *caret_rect = caret_rect_; | |
157 return true; | |
158 } | |
159 | |
160 // static | |
161 FARPROC WebPluginIMEWin::GetProcAddress(LPCSTR name) { | |
162 static const struct { | |
163 const char* name; | |
164 FARPROC function; | |
165 } kImm32Functions[] = { | |
166 { "ImmAssociateContextEx", | |
167 reinterpret_cast<FARPROC>(ImmAssociateContextEx) }, | |
168 { "ImmGetCompositionStringW", | |
169 reinterpret_cast<FARPROC>(ImmGetCompositionStringW) }, | |
170 { "ImmGetContext", reinterpret_cast<FARPROC>(ImmGetContext) }, | |
171 { "ImmReleaseContext", reinterpret_cast<FARPROC>(ImmReleaseContext) }, | |
172 { "ImmSetCandidateWindow", | |
173 reinterpret_cast<FARPROC>(ImmSetCandidateWindow) }, | |
174 { "ImmSetOpenStatus", reinterpret_cast<FARPROC>(ImmSetOpenStatus) }, | |
175 }; | |
176 for (size_t i = 0; i < arraysize(kImm32Functions); ++i) { | |
177 if (!lstrcmpiA(name, kImm32Functions[i].name)) | |
178 return kImm32Functions[i].function; | |
179 } | |
180 return NULL; | |
181 } | |
182 | |
183 void WebPluginIMEWin::Lock() { | |
184 g_webplugin_ime_lock.Pointer()->Acquire(); | |
185 instance_ = this; | |
186 } | |
187 | |
188 void WebPluginIMEWin::Unlock() { | |
189 instance_ = NULL; | |
190 g_webplugin_ime_lock.Pointer()->Release(); | |
191 } | |
192 | |
193 // static | |
194 WebPluginIMEWin* WebPluginIMEWin::GetInstance(HIMC context) { | |
195 return instance_ && context == reinterpret_cast<HIMC>(instance_) ? | |
196 instance_ : NULL; | |
197 } | |
198 | |
199 // static | |
200 BOOL WINAPI WebPluginIMEWin::ImmAssociateContextEx(HWND window, | |
201 HIMC context, | |
202 DWORD flags) { | |
203 WebPluginIMEWin* instance = GetInstance(context); | |
204 if (!instance) | |
205 return ::ImmAssociateContextEx(window, context, flags); | |
206 | |
207 int input_type = !context && !flags; | |
208 instance->input_type_ = input_type; | |
209 instance->status_updated_ = true; | |
210 return TRUE; | |
211 } | |
212 | |
213 // static | |
214 LONG WINAPI WebPluginIMEWin::ImmGetCompositionStringW(HIMC context, | |
215 DWORD index, | |
216 LPVOID dst_data, | |
217 DWORD dst_size) { | |
218 WebPluginIMEWin* instance = GetInstance(context); | |
219 if (!instance) | |
220 return ::ImmGetCompositionStringW(context, index, dst_data, dst_size); | |
221 | |
222 const void* src_data = NULL; | |
223 DWORD src_size = 0; | |
224 switch (index) { | |
225 case GCS_COMPSTR: | |
226 src_data = instance->composition_text_.c_str(); | |
227 src_size = instance->composition_text_.length() * sizeof(wchar_t); | |
228 break; | |
229 | |
230 case GCS_COMPATTR: | |
231 src_data = instance->composition_attributes_.c_str(); | |
232 src_size = instance->composition_attributes_.length(); | |
233 break; | |
234 | |
235 case GCS_COMPCLAUSE: | |
236 src_data = &instance->composition_clauses_[0]; | |
237 src_size = instance->composition_clauses_.size() * sizeof(uint32_t); | |
238 break; | |
239 | |
240 case GCS_CURSORPOS: | |
241 return instance->cursor_position_; | |
242 | |
243 case GCS_DELTASTART: | |
244 return instance->delta_start_; | |
245 | |
246 case GCS_RESULTSTR: | |
247 src_data = instance->result_text_.c_str(); | |
248 src_size = instance->result_text_.length() * sizeof(wchar_t); | |
249 break; | |
250 | |
251 case GCS_RESULTCLAUSE: | |
252 src_data = &instance->result_clauses_[0]; | |
253 src_size = sizeof(instance->result_clauses_); | |
254 break; | |
255 | |
256 default: | |
257 break; | |
258 } | |
259 if (!src_data || !src_size) | |
260 return IMM_ERROR_NODATA; | |
261 | |
262 if (dst_size >= src_size) | |
263 memcpy(dst_data, src_data, src_size); | |
264 | |
265 return src_size; | |
266 } | |
267 | |
268 // static | |
269 HIMC WINAPI WebPluginIMEWin::ImmGetContext(HWND window) { | |
270 WebPluginIMEWin* instance = instance_; | |
271 if (instance) | |
272 instance->support_ime_messages_ = true; | |
273 return reinterpret_cast<HIMC>(instance); | |
274 } | |
275 | |
276 // static | |
277 BOOL WINAPI WebPluginIMEWin::ImmReleaseContext(HWND window, HIMC context) { | |
278 if (!GetInstance(context)) | |
279 return ::ImmReleaseContext(window, context); | |
280 return TRUE; | |
281 } | |
282 | |
283 // static | |
284 BOOL WINAPI WebPluginIMEWin::ImmSetCandidateWindow(HIMC context, | |
285 CANDIDATEFORM* candidate) { | |
286 WebPluginIMEWin* instance = GetInstance(context); | |
287 if (!instance) | |
288 return ::ImmSetCandidateWindow(context, candidate); | |
289 | |
290 gfx::Rect caret_rect(candidate->rcArea); | |
291 if ((candidate->dwStyle & CFS_EXCLUDE) && | |
292 instance->caret_rect_ != caret_rect) { | |
293 instance->caret_rect_ = caret_rect; | |
294 instance->status_updated_ = true; | |
295 } | |
296 return TRUE; | |
297 } | |
298 | |
299 // static | |
300 BOOL WINAPI WebPluginIMEWin::ImmSetOpenStatus(HIMC context, BOOL open) { | |
301 WebPluginIMEWin* instance = GetInstance(context); | |
302 if (!instance) | |
303 return ::ImmSetOpenStatus(context, open); | |
304 | |
305 int input_type = open ? 1 : 0; | |
306 if (instance->input_type_ != input_type) { | |
307 instance->input_type_ = input_type; | |
308 instance->status_updated_ = true; | |
309 } | |
310 | |
311 return TRUE; | |
312 } | |
313 | |
314 } // namespace content | |
OLD | NEW |