Home | History | Annotate | Download | only in ime
      1 // Copyright 2013 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 "ui/base/ime/remote_input_method_win.h"
      6 
      7 #include "base/observer_list.h"
      8 #include "base/strings/utf_string_conversions.h"
      9 #include "base/win/metro.h"
     10 #include "base/win/scoped_handle.h"
     11 #include "ui/base/ime/input_method.h"
     12 #include "ui/base/ime/input_method_delegate.h"
     13 #include "ui/base/ime/input_method_observer.h"
     14 #include "ui/base/ime/remote_input_method_delegate_win.h"
     15 #include "ui/base/ime/text_input_client.h"
     16 #include "ui/base/ime/win/tsf_input_scope.h"
     17 #include "ui/events/event.h"
     18 #include "ui/events/event_utils.h"
     19 #include "ui/gfx/rect.h"
     20 
     21 namespace ui {
     22 namespace {
     23 
     24 const LANGID kFallbackLangID =
     25     MAKELANGID(LANG_NEUTRAL, SUBLANG_UI_CUSTOM_DEFAULT);
     26 
     27 InputMethod* g_public_interface_ = NULL;
     28 RemoteInputMethodPrivateWin* g_private_interface_ = NULL;
     29 
     30 void RegisterInstance(InputMethod* public_interface,
     31                       RemoteInputMethodPrivateWin* private_interface) {
     32   CHECK(g_public_interface_ == NULL)
     33       << "Only one instance is supported at the same time";
     34   CHECK(g_private_interface_ == NULL)
     35       << "Only one instance is supported at the same time";
     36   g_public_interface_ = public_interface;
     37   g_private_interface_ = private_interface;
     38 }
     39 
     40 RemoteInputMethodPrivateWin* GetPrivate(InputMethod* public_interface) {
     41   if (g_public_interface_ != public_interface)
     42     return NULL;
     43   return g_private_interface_;
     44 }
     45 
     46 void UnregisterInstance(InputMethod* public_interface) {
     47   RemoteInputMethodPrivateWin* private_interface = GetPrivate(public_interface);
     48   if (g_public_interface_ == public_interface &&
     49       g_private_interface_ == private_interface) {
     50     g_public_interface_ = NULL;
     51     g_private_interface_ = NULL;
     52   }
     53 }
     54 
     55 std::string GetLocaleString(LCID Locale_id, LCTYPE locale_type) {
     56   wchar_t buffer[16] = {};
     57 
     58   //|chars_written| includes NUL terminator.
     59   const int chars_written =
     60       GetLocaleInfo(Locale_id, locale_type, buffer, arraysize(buffer));
     61   if (chars_written <= 1 || arraysize(buffer) < chars_written)
     62     return std::string();
     63   std::string result;
     64   base::WideToUTF8(buffer, chars_written - 1, &result);
     65   return result;
     66 }
     67 
     68 std::vector<int32> GetInputScopesAsInt(TextInputType text_input_type,
     69                                        TextInputMode text_input_mode) {
     70   std::vector<int32> result;
     71   // An empty vector represents |text_input_type| is TEXT_INPUT_TYPE_NONE.
     72   if (text_input_type == TEXT_INPUT_TYPE_NONE)
     73     return result;
     74 
     75   const std::vector<InputScope>& input_scopes =
     76       tsf_inputscope::GetInputScopes(text_input_type, text_input_mode);
     77   result.reserve(input_scopes.size());
     78   for (size_t i = 0; i < input_scopes.size(); ++i)
     79     result.push_back(static_cast<int32>(input_scopes[i]));
     80   return result;
     81 }
     82 
     83 std::vector<gfx::Rect> GetCompositionCharacterBounds(
     84     const TextInputClient* client) {
     85   if (!client)
     86     return std::vector<gfx::Rect>();
     87 
     88   std::vector<gfx::Rect> bounds;
     89   if (client->HasCompositionText()) {
     90     gfx::Range range;
     91     if (client->GetCompositionTextRange(&range)) {
     92       for (uint32 i = 0; i < range.length(); ++i) {
     93         gfx::Rect rect;
     94         if (!client->GetCompositionCharacterBounds(i, &rect))
     95           break;
     96         bounds.push_back(rect);
     97       }
     98     }
     99   }
    100 
    101   // Use the caret bounds as a fallback if no composition character bounds is
    102   // available. One typical use case is PPAPI Flash, which does not support
    103   // GetCompositionCharacterBounds at all. crbug.com/133472
    104   if (bounds.empty())
    105     bounds.push_back(client->GetCaretBounds());
    106   return bounds;
    107 }
    108 
    109 class RemoteInputMethodWin : public InputMethod,
    110                              public RemoteInputMethodPrivateWin {
    111  public:
    112   explicit RemoteInputMethodWin(internal::InputMethodDelegate* delegate)
    113       : delegate_(delegate),
    114         remote_delegate_(NULL),
    115         text_input_client_(NULL),
    116         is_candidate_popup_open_(false),
    117         is_ime_(false),
    118         langid_(kFallbackLangID) {
    119     RegisterInstance(this, this);
    120   }
    121 
    122   virtual ~RemoteInputMethodWin() {
    123     FOR_EACH_OBSERVER(InputMethodObserver,
    124                       observer_list_,
    125                       OnInputMethodDestroyed(this));
    126     UnregisterInstance(this);
    127   }
    128 
    129  private:
    130   // Overridden from InputMethod:
    131   virtual void SetDelegate(internal::InputMethodDelegate* delegate) OVERRIDE {
    132     delegate_ = delegate;
    133   }
    134 
    135   virtual void Init(bool focused) OVERRIDE {
    136   }
    137 
    138   virtual void OnFocus() OVERRIDE {
    139   }
    140 
    141   virtual void OnBlur() OVERRIDE {
    142   }
    143 
    144   virtual bool OnUntranslatedIMEMessage(const base::NativeEvent& event,
    145                                         NativeEventResult* result) OVERRIDE {
    146     return false;
    147   }
    148 
    149   virtual void SetFocusedTextInputClient(TextInputClient* client) OVERRIDE {
    150     std::vector<int32> prev_input_scopes;
    151     std::swap(input_scopes_, prev_input_scopes);
    152     std::vector<gfx::Rect> prev_bounds;
    153     std::swap(composition_character_bounds_, prev_bounds);
    154     if (client) {
    155       input_scopes_ = GetInputScopesAsInt(client->GetTextInputType(),
    156                                           client->GetTextInputMode());
    157       composition_character_bounds_ = GetCompositionCharacterBounds(client);
    158     }
    159 
    160     const bool text_input_client_changed = text_input_client_ != client;
    161     text_input_client_ = client;
    162     if (text_input_client_changed) {
    163       FOR_EACH_OBSERVER(InputMethodObserver,
    164                         observer_list_,
    165                         OnTextInputStateChanged(client));
    166     }
    167 
    168     if (!remote_delegate_ || (prev_input_scopes == input_scopes_ &&
    169                               prev_bounds == composition_character_bounds_))
    170       return;
    171     remote_delegate_->OnTextInputClientUpdated(input_scopes_,
    172                                                composition_character_bounds_);
    173   }
    174 
    175   virtual void DetachTextInputClient(TextInputClient* client) OVERRIDE {
    176     if (text_input_client_ != client)
    177       return;
    178     SetFocusedTextInputClient(NULL);
    179   }
    180 
    181   virtual TextInputClient* GetTextInputClient() const OVERRIDE {
    182     return text_input_client_;
    183   }
    184 
    185   virtual bool DispatchKeyEvent(const ui::KeyEvent& event) OVERRIDE {
    186     if (event.HasNativeEvent()) {
    187       const base::NativeEvent& native_key_event = event.native_event();
    188       if (native_key_event.message != WM_CHAR)
    189         return false;
    190       if (!text_input_client_)
    191         return false;
    192       text_input_client_->InsertChar(
    193           static_cast<base::char16>(native_key_event.wParam),
    194           ui::GetModifiersFromKeyState());
    195       return true;
    196     }
    197 
    198     if (event.is_char()) {
    199       if (text_input_client_) {
    200         text_input_client_->InsertChar(event.key_code(),
    201                                        ui::GetModifiersFromKeyState());
    202       }
    203       return true;
    204     }
    205     if (!delegate_)
    206       return false;
    207     return delegate_->DispatchKeyEventPostIME(event);
    208   }
    209 
    210   virtual void OnTextInputTypeChanged(const TextInputClient* client) OVERRIDE {
    211     if (!text_input_client_ || text_input_client_ != client)
    212       return;
    213     std::vector<int32> prev_input_scopes;
    214     std::swap(input_scopes_, prev_input_scopes);
    215     input_scopes_ = GetInputScopesAsInt(client->GetTextInputType(),
    216                                         client->GetTextInputMode());
    217     if (input_scopes_ != prev_input_scopes && remote_delegate_) {
    218       remote_delegate_->OnTextInputClientUpdated(
    219           input_scopes_, composition_character_bounds_);
    220     }
    221   }
    222 
    223   virtual void OnCaretBoundsChanged(const TextInputClient* client) OVERRIDE {
    224     if (!text_input_client_ || text_input_client_ != client)
    225       return;
    226     std::vector<gfx::Rect> prev_rects;
    227     std::swap(composition_character_bounds_, prev_rects);
    228     composition_character_bounds_ = GetCompositionCharacterBounds(client);
    229     if (composition_character_bounds_ != prev_rects && remote_delegate_) {
    230       remote_delegate_->OnTextInputClientUpdated(
    231           input_scopes_, composition_character_bounds_);
    232     }
    233   }
    234 
    235   virtual void CancelComposition(const TextInputClient* client) OVERRIDE {
    236     if (CanSendRemoteNotification(client))
    237       remote_delegate_->CancelComposition();
    238   }
    239 
    240   virtual void OnInputLocaleChanged() OVERRIDE {
    241   }
    242 
    243   virtual std::string GetInputLocale() OVERRIDE {
    244     const LCID locale_id = MAKELCID(langid_, SORT_DEFAULT);
    245     std::string language =
    246         GetLocaleString(locale_id, LOCALE_SISO639LANGNAME);
    247     if (SUBLANGID(langid_) == SUBLANG_NEUTRAL || language.empty())
    248       return language;
    249     const std::string& region =
    250         GetLocaleString(locale_id, LOCALE_SISO3166CTRYNAME);
    251     if (region.empty())
    252       return language;
    253     return language.append(1, '-').append(region);
    254   }
    255 
    256   virtual bool IsActive() OVERRIDE {
    257     return true;  // always turned on
    258   }
    259 
    260   virtual TextInputType GetTextInputType() const OVERRIDE {
    261     return text_input_client_ ? text_input_client_->GetTextInputType()
    262                               : TEXT_INPUT_TYPE_NONE;
    263   }
    264 
    265   virtual TextInputMode GetTextInputMode() const OVERRIDE {
    266     return text_input_client_ ? text_input_client_->GetTextInputMode()
    267                               : TEXT_INPUT_MODE_DEFAULT;
    268   }
    269 
    270   virtual bool CanComposeInline() const OVERRIDE {
    271     return text_input_client_ ? text_input_client_->CanComposeInline() : true;
    272   }
    273 
    274   virtual bool IsCandidatePopupOpen() const OVERRIDE {
    275     return is_candidate_popup_open_;
    276   }
    277 
    278   virtual void ShowImeIfNeeded() OVERRIDE {
    279   }
    280 
    281   virtual void AddObserver(InputMethodObserver* observer) OVERRIDE {
    282     observer_list_.AddObserver(observer);
    283   }
    284 
    285   virtual void RemoveObserver(InputMethodObserver* observer) OVERRIDE {
    286     observer_list_.RemoveObserver(observer);
    287   }
    288 
    289   // Overridden from RemoteInputMethodPrivateWin:
    290   virtual void SetRemoteDelegate(
    291       internal::RemoteInputMethodDelegateWin* delegate) OVERRIDE{
    292     remote_delegate_ = delegate;
    293 
    294     // Sync initial state.
    295     if (remote_delegate_) {
    296       remote_delegate_->OnTextInputClientUpdated(
    297           input_scopes_, composition_character_bounds_);
    298     }
    299   }
    300 
    301   virtual void OnCandidatePopupChanged(bool visible) OVERRIDE {
    302     is_candidate_popup_open_ = visible;
    303     if (!text_input_client_)
    304       return;
    305     // TODO(kochi): Support 'update' case, in addition to show/hide.
    306     // http://crbug.com/238585
    307     if (visible)
    308       text_input_client_->OnCandidateWindowShown();
    309     else
    310       text_input_client_->OnCandidateWindowHidden();
    311   }
    312 
    313   virtual void OnInputSourceChanged(LANGID langid, bool /*is_ime*/) OVERRIDE {
    314     // Note: Currently |is_ime| is not utilized yet.
    315     const bool changed = (langid_ != langid);
    316     langid_ = langid;
    317     if (changed && GetTextInputClient())
    318       GetTextInputClient()->OnInputMethodChanged();
    319   }
    320 
    321   virtual void OnCompositionChanged(
    322       const CompositionText& composition_text) OVERRIDE {
    323     if (!text_input_client_)
    324       return;
    325     text_input_client_->SetCompositionText(composition_text);
    326   }
    327 
    328   virtual void OnTextCommitted(const base::string16& text) OVERRIDE {
    329     if (!text_input_client_)
    330       return;
    331     if (text_input_client_->GetTextInputType() == TEXT_INPUT_TYPE_NONE) {
    332       // According to the comment in text_input_client.h,
    333       // TextInputClient::InsertText should never be called when the
    334       // text input type is TEXT_INPUT_TYPE_NONE.
    335       for (size_t i = 0; i < text.size(); ++i)
    336         text_input_client_->InsertChar(text[i], 0);
    337       return;
    338     }
    339     text_input_client_->InsertText(text);
    340   }
    341 
    342   bool CanSendRemoteNotification(
    343       const TextInputClient* text_input_client) const {
    344     return text_input_client_ &&
    345            text_input_client_ == text_input_client &&
    346            remote_delegate_;
    347   }
    348 
    349   ObserverList<InputMethodObserver> observer_list_;
    350 
    351   internal::InputMethodDelegate* delegate_;
    352   internal::RemoteInputMethodDelegateWin* remote_delegate_;
    353 
    354   TextInputClient* text_input_client_;
    355   std::vector<int32> input_scopes_;
    356   std::vector<gfx::Rect> composition_character_bounds_;
    357   bool is_candidate_popup_open_;
    358   bool is_ime_;
    359   LANGID langid_;
    360 
    361   DISALLOW_COPY_AND_ASSIGN(RemoteInputMethodWin);
    362 };
    363 
    364 }  // namespace
    365 
    366 bool IsRemoteInputMethodWinRequired(gfx::AcceleratedWidget widget) {
    367   DWORD process_id = 0;
    368   if (GetWindowThreadProcessId(widget, &process_id) == 0)
    369     return false;
    370   base::win::ScopedHandle process_handle(::OpenProcess(
    371       PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id));
    372   if (!process_handle.IsValid())
    373     return false;
    374   return base::win::IsProcessImmersive(process_handle.Get());
    375 }
    376 
    377 RemoteInputMethodPrivateWin::RemoteInputMethodPrivateWin() {}
    378 
    379 scoped_ptr<InputMethod> CreateRemoteInputMethodWin(
    380     internal::InputMethodDelegate* delegate) {
    381   return scoped_ptr<InputMethod>(new RemoteInputMethodWin(delegate));
    382 }
    383 
    384 // static
    385 RemoteInputMethodPrivateWin* RemoteInputMethodPrivateWin::Get(
    386     InputMethod* input_method) {
    387   return GetPrivate(input_method);
    388 }
    389 
    390 }  // namespace ui
    391