• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
WebPluginIMEWin()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 
~WebPluginIMEWin()39 WebPluginIMEWin::~WebPluginIMEWin() {
40 }
41 
CompositionUpdated(const base::string16 & text,std::vector<int> clauses,std::vector<int> target,int cursor_position)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 
CompositionCompleted(const base::string16 & text)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 
SendEvents(PluginInstance * instance)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 
GetStatus(int * input_type,gfx::Rect * caret_rect)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
GetProcAddress(LPCSTR name)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 
Lock()179 void WebPluginIMEWin::Lock() {
180   g_webplugin_ime_lock.Pointer()->Acquire();
181   instance_ = this;
182 }
183 
Unlock()184 void WebPluginIMEWin::Unlock() {
185   instance_ = NULL;
186   g_webplugin_ime_lock.Pointer()->Release();
187 }
188 
189 // static
GetInstance(HIMC context)190 WebPluginIMEWin* WebPluginIMEWin::GetInstance(HIMC context) {
191   return instance_ && context == reinterpret_cast<HIMC>(instance_) ?
192       instance_ : NULL;
193 }
194 
195 // static
ImmAssociateContextEx(HWND window,HIMC context,DWORD flags)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
ImmGetCompositionStringW(HIMC context,DWORD index,LPVOID dst_data,DWORD dst_size)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
ImmGetContext(HWND window)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
ImmReleaseContext(HWND window,HIMC context)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
ImmSetCandidateWindow(HIMC context,CANDIDATEFORM * candidate)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
ImmSetOpenStatus(HIMC context,BOOL open)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
324