// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/base/ime/remote_input_method_win.h" #include "base/command_line.h" #include "base/observer_list.h" #include "base/strings/utf_string_conversions.h" #include "base/win/metro.h" #include "base/win/scoped_handle.h" #include "ui/base/ime/input_method.h" #include "ui/base/ime/input_method_delegate.h" #include "ui/base/ime/input_method_observer.h" #include "ui/base/ime/remote_input_method_delegate_win.h" #include "ui/base/ime/text_input_client.h" #include "ui/base/ime/win/tsf_input_scope.h" #include "ui/base/ui_base_switches.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" #include "ui/gfx/geometry/rect.h" namespace ui { namespace { const LANGID kFallbackLangID = MAKELANGID(LANG_NEUTRAL, SUBLANG_UI_CUSTOM_DEFAULT); InputMethod* g_public_interface_ = NULL; RemoteInputMethodPrivateWin* g_private_interface_ = NULL; void RegisterInstance(InputMethod* public_interface, RemoteInputMethodPrivateWin* private_interface) { CHECK(g_public_interface_ == NULL) << "Only one instance is supported at the same time"; CHECK(g_private_interface_ == NULL) << "Only one instance is supported at the same time"; g_public_interface_ = public_interface; g_private_interface_ = private_interface; } RemoteInputMethodPrivateWin* GetPrivate(InputMethod* public_interface) { if (g_public_interface_ != public_interface) return NULL; return g_private_interface_; } void UnregisterInstance(InputMethod* public_interface) { RemoteInputMethodPrivateWin* private_interface = GetPrivate(public_interface); if (g_public_interface_ == public_interface && g_private_interface_ == private_interface) { g_public_interface_ = NULL; g_private_interface_ = NULL; } } std::string GetLocaleString(LCID Locale_id, LCTYPE locale_type) { wchar_t buffer[16] = {}; //|chars_written| includes NUL terminator. const int chars_written = GetLocaleInfo(Locale_id, locale_type, buffer, arraysize(buffer)); if (chars_written <= 1 || arraysize(buffer) < chars_written) return std::string(); std::string result; base::WideToUTF8(buffer, chars_written - 1, &result); return result; } std::vector GetInputScopesAsInt(TextInputType text_input_type, TextInputMode text_input_mode) { std::vector result; // An empty vector represents |text_input_type| is TEXT_INPUT_TYPE_NONE. if (text_input_type == TEXT_INPUT_TYPE_NONE) return result; const std::vector& input_scopes = tsf_inputscope::GetInputScopes(text_input_type, text_input_mode); result.reserve(input_scopes.size()); for (size_t i = 0; i < input_scopes.size(); ++i) result.push_back(static_cast(input_scopes[i])); return result; } std::vector GetCompositionCharacterBounds( const TextInputClient* client) { if (!client) return std::vector(); std::vector bounds; if (client->HasCompositionText()) { gfx::Range range; if (client->GetCompositionTextRange(&range)) { for (uint32 i = 0; i < range.length(); ++i) { gfx::Rect rect; if (!client->GetCompositionCharacterBounds(i, &rect)) break; bounds.push_back(rect); } } } // Use the caret bounds as a fallback if no composition character bounds is // available. One typical use case is PPAPI Flash, which does not support // GetCompositionCharacterBounds at all. crbug.com/133472 if (bounds.empty()) bounds.push_back(client->GetCaretBounds()); return bounds; } class RemoteInputMethodWin : public InputMethod, public RemoteInputMethodPrivateWin { public: explicit RemoteInputMethodWin(internal::InputMethodDelegate* delegate) : delegate_(delegate), remote_delegate_(NULL), text_input_client_(NULL), is_candidate_popup_open_(false), is_ime_(false), langid_(kFallbackLangID) { RegisterInstance(this, this); } virtual ~RemoteInputMethodWin() { FOR_EACH_OBSERVER(InputMethodObserver, observer_list_, OnInputMethodDestroyed(this)); UnregisterInstance(this); } private: // Overridden from InputMethod: virtual void SetDelegate(internal::InputMethodDelegate* delegate) override { delegate_ = delegate; } virtual void Init(bool focused) override { } virtual void OnFocus() override { } virtual void OnBlur() override { } virtual bool OnUntranslatedIMEMessage(const base::NativeEvent& event, NativeEventResult* result) override { return false; } virtual void SetFocusedTextInputClient(TextInputClient* client) override { std::vector prev_input_scopes; std::swap(input_scopes_, prev_input_scopes); std::vector prev_bounds; std::swap(composition_character_bounds_, prev_bounds); if (client) { input_scopes_ = GetInputScopesAsInt(client->GetTextInputType(), client->GetTextInputMode()); composition_character_bounds_ = GetCompositionCharacterBounds(client); } const bool text_input_client_changed = text_input_client_ != client; text_input_client_ = client; if (text_input_client_changed) { FOR_EACH_OBSERVER(InputMethodObserver, observer_list_, OnTextInputStateChanged(client)); } if (!remote_delegate_ || (prev_input_scopes == input_scopes_ && prev_bounds == composition_character_bounds_)) return; remote_delegate_->OnTextInputClientUpdated(input_scopes_, composition_character_bounds_); } virtual void DetachTextInputClient(TextInputClient* client) override { if (text_input_client_ != client) return; SetFocusedTextInputClient(NULL); } virtual TextInputClient* GetTextInputClient() const override { return text_input_client_; } virtual bool DispatchKeyEvent(const ui::KeyEvent& event) override { if (event.HasNativeEvent()) { const base::NativeEvent& native_key_event = event.native_event(); if (native_key_event.message != WM_CHAR) return false; if (!text_input_client_) return false; text_input_client_->InsertChar( static_cast(native_key_event.wParam), ui::GetModifiersFromKeyState()); return true; } if (event.is_char()) { if (text_input_client_) { text_input_client_->InsertChar( event.GetCharacter(), ui::GetModifiersFromKeyState()); } return true; } if (!delegate_) return false; return delegate_->DispatchKeyEventPostIME(event); } virtual void OnTextInputTypeChanged(const TextInputClient* client) override { if (!text_input_client_ || text_input_client_ != client) return; std::vector prev_input_scopes; std::swap(input_scopes_, prev_input_scopes); input_scopes_ = GetInputScopesAsInt(client->GetTextInputType(), client->GetTextInputMode()); if (input_scopes_ != prev_input_scopes && remote_delegate_) { remote_delegate_->OnTextInputClientUpdated( input_scopes_, composition_character_bounds_); } } virtual void OnCaretBoundsChanged(const TextInputClient* client) override { if (!text_input_client_ || text_input_client_ != client) return; std::vector prev_rects; std::swap(composition_character_bounds_, prev_rects); composition_character_bounds_ = GetCompositionCharacterBounds(client); if (composition_character_bounds_ != prev_rects && remote_delegate_) { remote_delegate_->OnTextInputClientUpdated( input_scopes_, composition_character_bounds_); } } virtual void CancelComposition(const TextInputClient* client) override { if (CanSendRemoteNotification(client)) remote_delegate_->CancelComposition(); } virtual void OnInputLocaleChanged() override { } virtual std::string GetInputLocale() override { const LCID locale_id = MAKELCID(langid_, SORT_DEFAULT); std::string language = GetLocaleString(locale_id, LOCALE_SISO639LANGNAME); if (SUBLANGID(langid_) == SUBLANG_NEUTRAL || language.empty()) return language; const std::string& region = GetLocaleString(locale_id, LOCALE_SISO3166CTRYNAME); if (region.empty()) return language; return language.append(1, '-').append(region); } virtual bool IsActive() override { return true; // always turned on } virtual TextInputType GetTextInputType() const override { return text_input_client_ ? text_input_client_->GetTextInputType() : TEXT_INPUT_TYPE_NONE; } virtual TextInputMode GetTextInputMode() const override { return text_input_client_ ? text_input_client_->GetTextInputMode() : TEXT_INPUT_MODE_DEFAULT; } virtual int GetTextInputFlags() const override { return text_input_client_ ? text_input_client_->GetTextInputFlags() : 0; } virtual bool CanComposeInline() const override { return text_input_client_ ? text_input_client_->CanComposeInline() : true; } virtual bool IsCandidatePopupOpen() const override { return is_candidate_popup_open_; } virtual void ShowImeIfNeeded() override { } virtual void AddObserver(InputMethodObserver* observer) override { observer_list_.AddObserver(observer); } virtual void RemoveObserver(InputMethodObserver* observer) override { observer_list_.RemoveObserver(observer); } // Overridden from RemoteInputMethodPrivateWin: virtual void SetRemoteDelegate( internal::RemoteInputMethodDelegateWin* delegate) override{ remote_delegate_ = delegate; // Sync initial state. if (remote_delegate_) { remote_delegate_->OnTextInputClientUpdated( input_scopes_, composition_character_bounds_); } } virtual void OnCandidatePopupChanged(bool visible) override { is_candidate_popup_open_ = visible; if (!text_input_client_) return; // TODO(kochi): Support 'update' case, in addition to show/hide. // http://crbug.com/238585 if (visible) text_input_client_->OnCandidateWindowShown(); else text_input_client_->OnCandidateWindowHidden(); } virtual void OnInputSourceChanged(LANGID langid, bool /*is_ime*/) override { // Note: Currently |is_ime| is not utilized yet. const bool changed = (langid_ != langid); langid_ = langid; if (changed && GetTextInputClient()) GetTextInputClient()->OnInputMethodChanged(); } virtual void OnCompositionChanged( const CompositionText& composition_text) override { if (!text_input_client_) return; text_input_client_->SetCompositionText(composition_text); } virtual void OnTextCommitted(const base::string16& text) override { if (!text_input_client_) return; if (text_input_client_->GetTextInputType() == TEXT_INPUT_TYPE_NONE) { // According to the comment in text_input_client.h, // TextInputClient::InsertText should never be called when the // text input type is TEXT_INPUT_TYPE_NONE. for (size_t i = 0; i < text.size(); ++i) text_input_client_->InsertChar(text[i], 0); return; } text_input_client_->InsertText(text); } bool CanSendRemoteNotification( const TextInputClient* text_input_client) const { return text_input_client_ && text_input_client_ == text_input_client && remote_delegate_; } ObserverList observer_list_; internal::InputMethodDelegate* delegate_; internal::RemoteInputMethodDelegateWin* remote_delegate_; TextInputClient* text_input_client_; std::vector input_scopes_; std::vector composition_character_bounds_; bool is_candidate_popup_open_; bool is_ime_; LANGID langid_; DISALLOW_COPY_AND_ASSIGN(RemoteInputMethodWin); }; } // namespace bool IsRemoteInputMethodWinRequired(gfx::AcceleratedWidget widget) { // If the remote input method is already registered then don't do it again. if (ui::g_public_interface_ && ui::g_private_interface_) return false; DWORD process_id = 0; if (GetWindowThreadProcessId(widget, &process_id) == 0) return false; base::win::ScopedHandle process_handle(::OpenProcess( PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id)); if (!process_handle.IsValid()) return false; return base::win::IsProcessImmersive(process_handle.Get()) || base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kViewerConnect); } RemoteInputMethodPrivateWin::RemoteInputMethodPrivateWin() {} scoped_ptr CreateRemoteInputMethodWin( internal::InputMethodDelegate* delegate) { return make_scoped_ptr(new RemoteInputMethodWin(delegate)); } // static RemoteInputMethodPrivateWin* RemoteInputMethodPrivateWin::Get( InputMethod* input_method) { return GetPrivate(input_method); } } // namespace ui