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