• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
RegisterInstance(InputMethod * public_interface,RemoteInputMethodPrivateWin * private_interface)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 
GetPrivate(InputMethod * public_interface)40 RemoteInputMethodPrivateWin* GetPrivate(InputMethod* public_interface) {
41   if (g_public_interface_ != public_interface)
42     return NULL;
43   return g_private_interface_;
44 }
45 
UnregisterInstance(InputMethod * public_interface)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 
GetLocaleString(LCID Locale_id,LCTYPE locale_type)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 
GetInputScopesAsInt(TextInputType text_input_type,TextInputMode text_input_mode)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 
GetCompositionCharacterBounds(const TextInputClient * client)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:
RemoteInputMethodWin(internal::InputMethodDelegate * delegate)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 
~RemoteInputMethodWin()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:
SetDelegate(internal::InputMethodDelegate * delegate)131   virtual void SetDelegate(internal::InputMethodDelegate* delegate) OVERRIDE {
132     delegate_ = delegate;
133   }
134 
Init(bool focused)135   virtual void Init(bool focused) OVERRIDE {
136   }
137 
OnFocus()138   virtual void OnFocus() OVERRIDE {
139   }
140 
OnBlur()141   virtual void OnBlur() OVERRIDE {
142   }
143 
OnUntranslatedIMEMessage(const base::NativeEvent & event,NativeEventResult * result)144   virtual bool OnUntranslatedIMEMessage(const base::NativeEvent& event,
145                                         NativeEventResult* result) OVERRIDE {
146     return false;
147   }
148 
SetFocusedTextInputClient(TextInputClient * client)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 
DetachTextInputClient(TextInputClient * client)175   virtual void DetachTextInputClient(TextInputClient* client) OVERRIDE {
176     if (text_input_client_ != client)
177       return;
178     SetFocusedTextInputClient(NULL);
179   }
180 
GetTextInputClient() const181   virtual TextInputClient* GetTextInputClient() const OVERRIDE {
182     return text_input_client_;
183   }
184 
DispatchKeyEvent(const ui::KeyEvent & event)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 
OnTextInputTypeChanged(const TextInputClient * client)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 
OnCaretBoundsChanged(const TextInputClient * client)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 
CancelComposition(const TextInputClient * client)235   virtual void CancelComposition(const TextInputClient* client) OVERRIDE {
236     if (CanSendRemoteNotification(client))
237       remote_delegate_->CancelComposition();
238   }
239 
OnInputLocaleChanged()240   virtual void OnInputLocaleChanged() OVERRIDE {
241   }
242 
GetInputLocale()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 
IsActive()256   virtual bool IsActive() OVERRIDE {
257     return true;  // always turned on
258   }
259 
GetTextInputType() const260   virtual TextInputType GetTextInputType() const OVERRIDE {
261     return text_input_client_ ? text_input_client_->GetTextInputType()
262                               : TEXT_INPUT_TYPE_NONE;
263   }
264 
GetTextInputMode() const265   virtual TextInputMode GetTextInputMode() const OVERRIDE {
266     return text_input_client_ ? text_input_client_->GetTextInputMode()
267                               : TEXT_INPUT_MODE_DEFAULT;
268   }
269 
CanComposeInline() const270   virtual bool CanComposeInline() const OVERRIDE {
271     return text_input_client_ ? text_input_client_->CanComposeInline() : true;
272   }
273 
IsCandidatePopupOpen() const274   virtual bool IsCandidatePopupOpen() const OVERRIDE {
275     return is_candidate_popup_open_;
276   }
277 
ShowImeIfNeeded()278   virtual void ShowImeIfNeeded() OVERRIDE {
279   }
280 
AddObserver(InputMethodObserver * observer)281   virtual void AddObserver(InputMethodObserver* observer) OVERRIDE {
282     observer_list_.AddObserver(observer);
283   }
284 
RemoveObserver(InputMethodObserver * observer)285   virtual void RemoveObserver(InputMethodObserver* observer) OVERRIDE {
286     observer_list_.RemoveObserver(observer);
287   }
288 
289   // Overridden from RemoteInputMethodPrivateWin:
SetRemoteDelegate(internal::RemoteInputMethodDelegateWin * delegate)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 
OnCandidatePopupChanged(bool visible)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 
OnInputSourceChanged(LANGID langid,bool)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 
OnCompositionChanged(const CompositionText & composition_text)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 
OnTextCommitted(const base::string16 & text)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 
CanSendRemoteNotification(const TextInputClient * text_input_client) const342   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 
IsRemoteInputMethodWinRequired(gfx::AcceleratedWidget widget)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 
RemoteInputMethodPrivateWin()377 RemoteInputMethodPrivateWin::RemoteInputMethodPrivateWin() {}
378 
CreateRemoteInputMethodWin(internal::InputMethodDelegate * delegate)379 scoped_ptr<InputMethod> CreateRemoteInputMethodWin(
380     internal::InputMethodDelegate* delegate) {
381   return scoped_ptr<InputMethod>(new RemoteInputMethodWin(delegate));
382 }
383 
384 // static
Get(InputMethod * input_method)385 RemoteInputMethodPrivateWin* RemoteInputMethodPrivateWin::Get(
386     InputMethod* input_method) {
387   return GetPrivate(input_method);
388 }
389 
390 }  // namespace ui
391