- add sources.
[platform/framework/web/crosswalk.git] / src / content / child / npapi / webplugin_ime_win.cc
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 <cstring>
8 #include <string>
9 #include <vector>
10
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"
17
18 #pragma comment(lib, "imm32.lib")
19
20 namespace content {
21
22 // A critical section that prevents two or more plug-ins from accessing a
23 // WebPluginIMEWin instance through our patch function.
24 base::LazyInstance<base::Lock>::Leaky
25     g_webplugin_ime_lock = LAZY_INSTANCE_INITIALIZER;
26
27 WebPluginIMEWin* WebPluginIMEWin::instance_ = NULL;
28
29 WebPluginIMEWin::WebPluginIMEWin()
30     : cursor_position_(0),
31       delta_start_(0),
32       composing_text_(false),
33       support_ime_messages_(false),
34       status_updated_(false),
35       input_type_(1) {
36   memset(result_clauses_, 0, sizeof(result_clauses_));
37 }
38
39 WebPluginIMEWin::~WebPluginIMEWin() {
40 }
41
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.
47   NPEvent np_event;
48   if (!composing_text_) {
49     composing_text_ = true;
50     result_text_.clear();
51
52     np_event.event = WM_IME_STARTCOMPOSITION;
53     np_event.wParam = 0;
54     np_event.lParam = 0;
55     events_.push_back(np_event);
56   }
57
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;
62   np_event.wParam = 0;
63   np_event.lParam = GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE |
64       GCS_CURSORPOS | GCS_DELTASTART;
65   events_.push_back(np_event);
66
67   // Converts this event to the IMM32 data so we do not have to convert it every
68   // time when a plug-in call an IMM32 function.
69   composition_text_ = text;
70
71   // Create the composition clauses returned when a plug-in 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]);
76
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;
82   } else {
83     composition_attributes_.assign(text.length(), ATTR_INPUT);
84   }
85
86   cursor_position_ = cursor_position;
87   delta_start_ = cursor_position;
88 }
89
90 void WebPluginIMEWin::CompositionCompleted(const base::string16& text) {
91   composing_text_ = false;
92
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.
96   NPEvent np_event;
97   np_event.event = WM_IME_COMPOSITION;
98   np_event.wParam = 0;
99   np_event.lParam = GCS_CURSORPOS | GCS_DELTASTART | GCS_RESULTSTR |
100       GCS_RESULTCLAUSE;
101   events_.push_back(np_event);
102
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;
106   np_event.wParam = 0;
107   np_event.lParam = 0;
108   events_.push_back(np_event);
109
110   // If the target plug-in does not seem to support IME messages, we send
111   // each character in IME text with a WM_CHAR message so the plug-in can
112   // insert the IME text.
113   if (!support_ime_messages_) {
114     np_event.event = WM_CHAR;
115     np_event.wParam = 0;
116     np_event.lParam = 0;
117     for (size_t i = 0; i < result_text_.length(); ++i) {
118       np_event.wParam = result_text_[i];
119       events_.push_back(np_event);
120     }
121   }
122
123   // Updated the result text and its clause. (Unlike composition clauses, a
124   // result clause consists of only one region.)
125   result_text_ = text;
126
127   result_clauses_[0] = 0;
128   result_clauses_[1] = result_text_.length();
129
130   cursor_position_ = result_clauses_[1];
131   delta_start_ = result_clauses_[1];
132 }
133
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 plug-in.
137   ScopedLock lock(this);
138
139   bool ret = true;
140   for (std::vector<NPEvent>::iterator it = events_.begin();
141        it != events_.end(); ++it) {
142     if (!instance->NPP_HandleEvent(&(*it)))
143       ret = false;
144   }
145
146   events_.clear();
147   return ret;
148 }
149
150 bool WebPluginIMEWin::GetStatus(int* input_type, gfx::Rect* caret_rect) {
151   *input_type = input_type_;
152   *caret_rect = caret_rect_;
153   return true;
154 }
155
156 // static
157 FARPROC WebPluginIMEWin::GetProcAddress(LPCSTR name) {
158   static const struct {
159     const char* name;
160     FARPROC function;
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) },
171   };
172   for (int i = 0; i < arraysize(kImm32Functions); ++i) {
173     if (!lstrcmpiA(name, kImm32Functions[i].name))
174       return kImm32Functions[i].function;
175   }
176   return NULL;
177 }
178
179 void WebPluginIMEWin::Lock() {
180   g_webplugin_ime_lock.Pointer()->Acquire();
181   instance_ = this;
182 }
183
184 void WebPluginIMEWin::Unlock() {
185   instance_ = NULL;
186   g_webplugin_ime_lock.Pointer()->Release();
187 }
188
189 // static
190 WebPluginIMEWin* WebPluginIMEWin::GetInstance(HIMC context) {
191   return instance_ && context == reinterpret_cast<HIMC>(instance_) ?
192       instance_ : NULL;
193 }
194
195 // static
196 BOOL WINAPI WebPluginIMEWin::ImmAssociateContextEx(HWND window,
197                                                    HIMC context,
198                                                    DWORD flags) {
199   WebPluginIMEWin* instance = GetInstance(context);
200   if (!instance)
201     return ::ImmAssociateContextEx(window, context, flags);
202
203   int input_type = !context && !flags;
204   instance->input_type_ = input_type;
205   instance->status_updated_ = true;
206   return TRUE;
207 }
208
209 // static
210 LONG WINAPI WebPluginIMEWin::ImmGetCompositionStringW(HIMC context,
211                                                       DWORD index,
212                                                       LPVOID dst_data,
213                                                       DWORD dst_size) {
214   WebPluginIMEWin* instance = GetInstance(context);
215   if (!instance)
216     return ::ImmGetCompositionStringW(context, index, dst_data, dst_size);
217
218   const void* src_data = NULL;
219   DWORD src_size = 0;
220   switch (index) {
221     case GCS_COMPSTR:
222       src_data = instance->composition_text_.c_str();
223       src_size = instance->composition_text_.length() * sizeof(wchar_t);
224       break;
225
226     case GCS_COMPATTR:
227       src_data = instance->composition_attributes_.c_str();
228       src_size = instance->composition_attributes_.length();
229       break;
230
231     case GCS_COMPCLAUSE:
232       src_data = &instance->composition_clauses_[0];
233       src_size = instance->composition_clauses_.size() * sizeof(uint32);
234       break;
235
236     case GCS_CURSORPOS:
237       return instance->cursor_position_;
238
239     case GCS_DELTASTART:
240       return instance->delta_start_;
241
242     case GCS_RESULTSTR:
243       src_data = instance->result_text_.c_str();
244       src_size = instance->result_text_.length() * sizeof(wchar_t);
245       break;
246
247     case GCS_RESULTCLAUSE:
248       src_data = &instance->result_clauses_[0];
249       src_size = sizeof(instance->result_clauses_);
250       break;
251
252     default:
253       break;
254   }
255   if (!src_data || !src_size)
256     return IMM_ERROR_NODATA;
257
258   if (dst_size >= src_size)
259     memcpy(dst_data, src_data, src_size);
260
261   return src_size;
262 }
263
264 // static
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 plug-ins (such as Flash) call this function with a dummy
270   // window handle. We return our dummy IME context for these plug-ins so they
271   // can use our IME emulator.
272   if (IsWindow(window)) {
273     wchar_t name[128];
274     GetClassName(window, &name[0], arraysize(name));
275     if (!wcscmp(&name[0], kNativeWindowClassName))
276       return ::ImmGetContext(window);
277   }
278
279   WebPluginIMEWin* instance = instance_;
280   if (instance)
281     instance->support_ime_messages_ = true;
282   return reinterpret_cast<HIMC>(instance);
283 }
284
285 // static
286 BOOL WINAPI WebPluginIMEWin::ImmReleaseContext(HWND window, HIMC context) {
287   if (!GetInstance(context))
288     return ::ImmReleaseContext(window, context);
289   return TRUE;
290 }
291
292 // static
293 BOOL WINAPI WebPluginIMEWin::ImmSetCandidateWindow(HIMC context,
294                                                    CANDIDATEFORM* candidate) {
295   WebPluginIMEWin* instance = GetInstance(context);
296   if (!instance)
297     return ::ImmSetCandidateWindow(context, candidate);
298
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;
304   }
305   return TRUE;
306 }
307
308 // static
309 BOOL WINAPI WebPluginIMEWin::ImmSetOpenStatus(HIMC context, BOOL open) {
310   WebPluginIMEWin* instance = GetInstance(context);
311   if (!instance)
312     return ::ImmSetOpenStatus(context, open);
313
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;
318   }
319
320   return TRUE;
321 }
322
323 }  // namespace content