1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
|
// Copyright (c) 2012 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 "content/child/npapi/webplugin_ime_win.h"
#include <cstring>
#include <string>
#include <vector>
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
#include "content/child/npapi/plugin_instance.h"
#include "content/common/plugin_constants_win.h"
#pragma comment(lib, "imm32.lib")
namespace content {
// A critical section that prevents two or more plug-ins from accessing a
// WebPluginIMEWin instance through our patch function.
base::LazyInstance<base::Lock>::Leaky
g_webplugin_ime_lock = LAZY_INSTANCE_INITIALIZER;
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) {
memset(result_clauses_, 0, sizeof(result_clauses_));
}
WebPluginIMEWin::~WebPluginIMEWin() {
}
void WebPluginIMEWin::CompositionUpdated(const base::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 base::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) {
*input_type = input_type_;
*caret_rect = caret_rect_;
return true;
}
// 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) {
WebPluginIMEWin* instance = GetInstance(context);
if (!instance)
return ::ImmAssociateContextEx(window, context, flags);
int input_type = !context && !flags;
instance->input_type_ = input_type;
instance->status_updated_ = true;
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 content
|