Home | History | Annotate | Download | only in npapi
      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
    324