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