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.
5 #include "ui/base/ime/remote_input_method_win.h"
7 #include "base/command_line.h"
8 #include "base/observer_list.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/win/metro.h"
11 #include "base/win/scoped_handle.h"
12 #include "ui/base/ime/input_method.h"
13 #include "ui/base/ime/input_method_delegate.h"
14 #include "ui/base/ime/input_method_observer.h"
15 #include "ui/base/ime/remote_input_method_delegate_win.h"
16 #include "ui/base/ime/text_input_client.h"
17 #include "ui/base/ime/win/tsf_input_scope.h"
18 #include "ui/base/ui_base_switches.h"
19 #include "ui/events/event.h"
20 #include "ui/events/event_utils.h"
21 #include "ui/gfx/geometry/rect.h"
26 const LANGID kFallbackLangID
=
27 MAKELANGID(LANG_NEUTRAL
, SUBLANG_UI_CUSTOM_DEFAULT
);
29 InputMethod
* g_public_interface_
= NULL
;
30 RemoteInputMethodPrivateWin
* g_private_interface_
= NULL
;
32 void RegisterInstance(InputMethod
* public_interface
,
33 RemoteInputMethodPrivateWin
* private_interface
) {
34 CHECK(g_public_interface_
== NULL
)
35 << "Only one instance is supported at the same time";
36 CHECK(g_private_interface_
== NULL
)
37 << "Only one instance is supported at the same time";
38 g_public_interface_
= public_interface
;
39 g_private_interface_
= private_interface
;
42 RemoteInputMethodPrivateWin
* GetPrivate(InputMethod
* public_interface
) {
43 if (g_public_interface_
!= public_interface
)
45 return g_private_interface_
;
48 void UnregisterInstance(InputMethod
* public_interface
) {
49 RemoteInputMethodPrivateWin
* private_interface
= GetPrivate(public_interface
);
50 if (g_public_interface_
== public_interface
&&
51 g_private_interface_
== private_interface
) {
52 g_public_interface_
= NULL
;
53 g_private_interface_
= NULL
;
57 std::string
GetLocaleString(LCID Locale_id
, LCTYPE locale_type
) {
58 wchar_t buffer
[16] = {};
60 //|chars_written| includes NUL terminator.
61 const int chars_written
=
62 GetLocaleInfo(Locale_id
, locale_type
, buffer
, arraysize(buffer
));
63 if (chars_written
<= 1 || arraysize(buffer
) < chars_written
)
66 base::WideToUTF8(buffer
, chars_written
- 1, &result
);
70 std::vector
<int32
> GetInputScopesAsInt(TextInputType text_input_type
,
71 TextInputMode text_input_mode
) {
72 std::vector
<int32
> result
;
73 // An empty vector represents |text_input_type| is TEXT_INPUT_TYPE_NONE.
74 if (text_input_type
== TEXT_INPUT_TYPE_NONE
)
77 const std::vector
<InputScope
>& input_scopes
=
78 tsf_inputscope::GetInputScopes(text_input_type
, text_input_mode
);
79 result
.reserve(input_scopes
.size());
80 for (size_t i
= 0; i
< input_scopes
.size(); ++i
)
81 result
.push_back(static_cast<int32
>(input_scopes
[i
]));
85 std::vector
<gfx::Rect
> GetCompositionCharacterBounds(
86 const TextInputClient
* client
) {
88 return std::vector
<gfx::Rect
>();
90 std::vector
<gfx::Rect
> bounds
;
91 if (client
->HasCompositionText()) {
93 if (client
->GetCompositionTextRange(&range
)) {
94 for (uint32 i
= 0; i
< range
.length(); ++i
) {
96 if (!client
->GetCompositionCharacterBounds(i
, &rect
))
98 bounds
.push_back(rect
);
103 // Use the caret bounds as a fallback if no composition character bounds is
104 // available. One typical use case is PPAPI Flash, which does not support
105 // GetCompositionCharacterBounds at all. crbug.com/133472
107 bounds
.push_back(client
->GetCaretBounds());
111 class RemoteInputMethodWin
: public InputMethod
,
112 public RemoteInputMethodPrivateWin
{
114 explicit RemoteInputMethodWin(internal::InputMethodDelegate
* delegate
)
115 : delegate_(delegate
),
116 remote_delegate_(NULL
),
117 text_input_client_(NULL
),
118 is_candidate_popup_open_(false),
120 langid_(kFallbackLangID
) {
121 RegisterInstance(this, this);
124 ~RemoteInputMethodWin() override
{
125 FOR_EACH_OBSERVER(InputMethodObserver
,
127 OnInputMethodDestroyed(this));
128 UnregisterInstance(this);
132 // Overridden from InputMethod:
133 void SetDelegate(internal::InputMethodDelegate
* delegate
) override
{
134 delegate_
= delegate
;
137 void Init(bool focused
) override
{}
139 void OnFocus() override
{}
141 void OnBlur() override
{}
143 bool OnUntranslatedIMEMessage(const base::NativeEvent
& event
,
144 NativeEventResult
* result
) override
{
148 void SetFocusedTextInputClient(TextInputClient
* client
) override
{
149 std::vector
<int32
> prev_input_scopes
;
150 std::swap(input_scopes_
, prev_input_scopes
);
151 std::vector
<gfx::Rect
> prev_bounds
;
152 std::swap(composition_character_bounds_
, prev_bounds
);
154 input_scopes_
= GetInputScopesAsInt(client
->GetTextInputType(),
155 client
->GetTextInputMode());
156 composition_character_bounds_
= GetCompositionCharacterBounds(client
);
159 const bool text_input_client_changed
= text_input_client_
!= client
;
160 text_input_client_
= client
;
161 if (text_input_client_changed
) {
162 FOR_EACH_OBSERVER(InputMethodObserver
,
164 OnTextInputStateChanged(client
));
167 if (!remote_delegate_
|| (prev_input_scopes
== input_scopes_
&&
168 prev_bounds
== composition_character_bounds_
))
170 remote_delegate_
->OnTextInputClientUpdated(input_scopes_
,
171 composition_character_bounds_
);
174 void DetachTextInputClient(TextInputClient
* client
) override
{
175 if (text_input_client_
!= client
)
177 SetFocusedTextInputClient(NULL
);
180 TextInputClient
* GetTextInputClient() const override
{
181 return text_input_client_
;
184 bool DispatchKeyEvent(const ui::KeyEvent
& event
) override
{
185 if (event
.HasNativeEvent()) {
186 const base::NativeEvent
& native_key_event
= event
.native_event();
187 if (native_key_event
.message
!= WM_CHAR
)
189 if (!text_input_client_
)
191 text_input_client_
->InsertChar(
192 static_cast<base::char16
>(native_key_event
.wParam
),
193 ui::GetModifiersFromKeyState());
197 if (event
.is_char()) {
198 if (text_input_client_
) {
199 text_input_client_
->InsertChar(
200 event
.GetCharacter(),
201 ui::GetModifiersFromKeyState());
207 return delegate_
->DispatchKeyEventPostIME(event
);
210 void OnTextInputTypeChanged(const TextInputClient
* client
) override
{
211 if (!text_input_client_
|| text_input_client_
!= client
)
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_
);
223 void OnCaretBoundsChanged(const TextInputClient
* client
) override
{
224 if (!text_input_client_
|| text_input_client_
!= client
)
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_
);
235 void CancelComposition(const TextInputClient
* client
) override
{
236 if (CanSendRemoteNotification(client
))
237 remote_delegate_
->CancelComposition();
240 void OnInputLocaleChanged() override
{}
242 std::string
GetInputLocale() override
{
243 const LCID locale_id
= MAKELCID(langid_
, SORT_DEFAULT
);
244 std::string language
=
245 GetLocaleString(locale_id
, LOCALE_SISO639LANGNAME
);
246 if (SUBLANGID(langid_
) == SUBLANG_NEUTRAL
|| language
.empty())
248 const std::string
& region
=
249 GetLocaleString(locale_id
, LOCALE_SISO3166CTRYNAME
);
252 return language
.append(1, '-').append(region
);
255 bool IsActive() override
{
256 return true; // always turned on
259 TextInputType
GetTextInputType() const override
{
260 return text_input_client_
? text_input_client_
->GetTextInputType()
261 : TEXT_INPUT_TYPE_NONE
;
264 TextInputMode
GetTextInputMode() const override
{
265 return text_input_client_
? text_input_client_
->GetTextInputMode()
266 : TEXT_INPUT_MODE_DEFAULT
;
269 int GetTextInputFlags() const override
{
270 return text_input_client_
? text_input_client_
->GetTextInputFlags()
274 bool CanComposeInline() const override
{
275 return text_input_client_
? text_input_client_
->CanComposeInline() : true;
278 bool IsCandidatePopupOpen() const override
{
279 return is_candidate_popup_open_
;
282 void ShowImeIfNeeded() override
{}
284 void AddObserver(InputMethodObserver
* observer
) override
{
285 observer_list_
.AddObserver(observer
);
288 void RemoveObserver(InputMethodObserver
* observer
) override
{
289 observer_list_
.RemoveObserver(observer
);
292 // Overridden from RemoteInputMethodPrivateWin:
293 void SetRemoteDelegate(
294 internal::RemoteInputMethodDelegateWin
* delegate
) override
{
295 remote_delegate_
= delegate
;
297 // Sync initial state.
298 if (remote_delegate_
) {
299 remote_delegate_
->OnTextInputClientUpdated(
300 input_scopes_
, composition_character_bounds_
);
304 void OnCandidatePopupChanged(bool visible
) override
{
305 is_candidate_popup_open_
= visible
;
306 if (!text_input_client_
)
308 // TODO(kochi): Support 'update' case, in addition to show/hide.
309 // http://crbug.com/238585
311 text_input_client_
->OnCandidateWindowShown();
313 text_input_client_
->OnCandidateWindowHidden();
316 void OnInputSourceChanged(LANGID langid
, bool /*is_ime*/) override
{
317 // Note: Currently |is_ime| is not utilized yet.
318 const bool changed
= (langid_
!= langid
);
320 if (changed
&& GetTextInputClient())
321 GetTextInputClient()->OnInputMethodChanged();
324 void OnCompositionChanged(const CompositionText
& composition_text
) override
{
325 if (!text_input_client_
)
327 text_input_client_
->SetCompositionText(composition_text
);
330 void OnTextCommitted(const base::string16
& text
) override
{
331 if (!text_input_client_
)
333 if (text_input_client_
->GetTextInputType() == TEXT_INPUT_TYPE_NONE
) {
334 // According to the comment in text_input_client.h,
335 // TextInputClient::InsertText should never be called when the
336 // text input type is TEXT_INPUT_TYPE_NONE.
337 for (size_t i
= 0; i
< text
.size(); ++i
)
338 text_input_client_
->InsertChar(text
[i
], 0);
341 text_input_client_
->InsertText(text
);
344 bool CanSendRemoteNotification(
345 const TextInputClient
* text_input_client
) const {
346 return text_input_client_
&&
347 text_input_client_
== text_input_client
&&
351 ObserverList
<InputMethodObserver
> observer_list_
;
353 internal::InputMethodDelegate
* delegate_
;
354 internal::RemoteInputMethodDelegateWin
* remote_delegate_
;
356 TextInputClient
* text_input_client_
;
357 std::vector
<int32
> input_scopes_
;
358 std::vector
<gfx::Rect
> composition_character_bounds_
;
359 bool is_candidate_popup_open_
;
363 DISALLOW_COPY_AND_ASSIGN(RemoteInputMethodWin
);
368 bool IsRemoteInputMethodWinRequired(gfx::AcceleratedWidget widget
) {
369 // If the remote input method is already registered then don't do it again.
370 if (ui::g_public_interface_
&& ui::g_private_interface_
)
373 DWORD process_id
= 0;
374 if (GetWindowThreadProcessId(widget
, &process_id
) == 0)
376 base::win::ScopedHandle
process_handle(::OpenProcess(
377 PROCESS_QUERY_LIMITED_INFORMATION
, FALSE
, process_id
));
378 if (!process_handle
.IsValid())
380 return base::win::IsProcessImmersive(process_handle
.Get()) ||
381 base::CommandLine::ForCurrentProcess()->HasSwitch(
382 switches::kViewerConnect
);
385 RemoteInputMethodPrivateWin::RemoteInputMethodPrivateWin() {}
387 scoped_ptr
<InputMethod
> CreateRemoteInputMethodWin(
388 internal::InputMethodDelegate
* delegate
) {
389 return make_scoped_ptr(new RemoteInputMethodWin(delegate
));
393 RemoteInputMethodPrivateWin
* RemoteInputMethodPrivateWin::Get(
394 InputMethod
* input_method
) {
395 return GetPrivate(input_method
);