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