diff options
Diffstat (limited to 'chromium/ui/base/ime/win')
11 files changed, 169 insertions, 112 deletions
diff --git a/chromium/ui/base/ime/win/input_method_win_base.cc b/chromium/ui/base/ime/win/input_method_win_base.cc index 83850840d83..fc9c8d3a0f2 100644 --- a/chromium/ui/base/ime/win/input_method_win_base.cc +++ b/chromium/ui/base/ime/win/input_method_win_base.cc @@ -13,8 +13,6 @@ #include "base/command_line.h" #include "base/memory/ptr_util.h" #include "base/win/windows_version.h" -#include "ui/base/ime/ime_bridge.h" -#include "ui/base/ime/ime_engine_handler_interface.h" #include "ui/base/ime/text_input_client.h" #include "ui/base/ime/win/on_screen_keyboard_display_manager_input_pane.h" #include "ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.h" @@ -237,21 +235,6 @@ ui::EventDispatchDetails InputMethodWinBase::DispatchKeyEvent( !std::iswcntrl(static_cast<wint_t>(char_msgs[0].wParam))) event->set_character(static_cast<base::char16>(char_msgs[0].wParam)); - // Dispatches the key events to the Chrome IME extension which is listening to - // key events on the following two situations: - // 1) |char_msgs| is empty when the event is non-character key. - // 2) |char_msgs|.size() == 1 when the event is character key and the WM_CHAR - // messages have been combined in the event processing flow. - if (char_msgs.size() <= 1 && GetEngine()) { - ui::IMEEngineHandlerInterface::KeyEventDoneCallback callback = - base::BindOnce(&InputMethodWinBase::ProcessKeyEventDone, - weak_ptr_factory_.GetWeakPtr(), - base::Owned(new ui::KeyEvent(*event)), - base::Owned(new std::vector<MSG>(char_msgs))); - GetEngine()->ProcessKeyEvent(*event, std::move(callback)); - return ui::EventDispatchDetails(); - } - return ProcessUnhandledKeyEvent(event, &char_msgs); } @@ -506,48 +489,4 @@ ui::EventDispatchDetails InputMethodWinBase::ProcessUnhandledKeyEvent( return details; } -void InputMethodWinBase::UpdateCompositionBoundsForEngine( - const TextInputClient* client) { - TextInputType text_input_type = GetTextInputType(); - if (client == GetTextInputClient() && - text_input_type != TEXT_INPUT_TYPE_NONE && - text_input_type != TEXT_INPUT_TYPE_PASSWORD && GetEngine()) { - GetEngine()->SetCompositionBounds(GetCompositionBounds(client)); - } -} - -void InputMethodWinBase::ResetEngine() { - if (GetEngine()) - GetEngine()->Reset(); -} - -void InputMethodWinBase::CancelCompositionForEngine() { - TextInputType text_input_type = GetTextInputType(); - if (text_input_type != TEXT_INPUT_TYPE_NONE && - text_input_type != TEXT_INPUT_TYPE_PASSWORD) { - InputMethodWinBase::ResetEngine(); - } -} - -void InputMethodWinBase::UpdateEngineFocusAndInputContext() { - if (!ui::IMEBridge::Get()) // IMEBridge could be null for tests. - return; - - const TextInputType old_text_input_type = - ui::IMEBridge::Get()->GetCurrentInputContext().type; - ui::IMEEngineHandlerInterface::InputContext context( - GetTextInputType(), GetTextInputMode(), GetTextInputFlags(), - ui::TextInputClient::FOCUS_REASON_OTHER, GetClientShouldDoLearning()); - ui::IMEBridge::Get()->SetCurrentInputContext(context); - - // Update IME Engine state. - ui::IMEEngineHandlerInterface* engine = GetEngine(); - if (engine) { - if (old_text_input_type != ui::TEXT_INPUT_TYPE_NONE) - engine->FocusOut(); - if (GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE) - engine->FocusIn(context); - } -} - } // namespace ui diff --git a/chromium/ui/base/ime/win/input_method_win_base.h b/chromium/ui/base/ime/win/input_method_win_base.h index 139d966dc0e..c788c6b5f35 100644 --- a/chromium/ui/base/ime/win/input_method_win_base.h +++ b/chromium/ui/base/ime/win/input_method_win_base.h @@ -62,19 +62,6 @@ class COMPONENT_EXPORT(UI_BASE_IME_WIN) InputMethodWinBase ui::KeyEvent* event, const std::vector<MSG>* char_msgs); - // Update composition bounds for Chromium IME extension. - void UpdateCompositionBoundsForEngine(const TextInputClient* client); - - // Reset composition status for Chromium IME extension. - void ResetEngine(); - - // Cancel composition for Chromium IME extension. - void CancelCompositionForEngine(); - - // Update focus state for Chromium IME extension and update input context in - // ui::IMEBridge. - void UpdateEngineFocusAndInputContext(); - // The toplevel window handle. const HWND toplevel_window_handle_; diff --git a/chromium/ui/base/ime/win/input_method_win_imm32.cc b/chromium/ui/base/ime/win/input_method_win_imm32.cc index 7c0ee3ee966..324a6bc91fb 100644 --- a/chromium/ui/base/ime/win/input_method_win_imm32.cc +++ b/chromium/ui/base/ime/win/input_method_win_imm32.cc @@ -12,8 +12,6 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/metrics/histogram_macros.h" -#include "ui/base/ime/ime_bridge.h" -#include "ui/base/ime/ime_engine_handler_interface.h" #include "ui/base/ime/text_input_client.h" #include "ui/base/ime/win/tsf_input_scope.h" #include "ui/display/win/screen_win.h" @@ -101,7 +99,6 @@ void InputMethodWinImm32::OnCaretBoundsChanged(const TextInputClient* client) { if (!IsTextInputClientFocused(client) || !IsWindowFocused(client)) return; NotifyTextInputCaretBoundsChanged(client); - InputMethodWinBase::UpdateCompositionBoundsForEngine(client); if (!enabled_) return; @@ -126,13 +123,8 @@ void InputMethodWinImm32::OnCaretBoundsChanged(const TextInputClient* client) { } void InputMethodWinImm32::CancelComposition(const TextInputClient* client) { - if (IsTextInputClientFocused(client)) { - // |enabled_| == false could be faked, and the engine should rely on the - // real type get from GetTextInputType(). - InputMethodWinBase::CancelCompositionForEngine(); - - if (enabled_) - imm32_manager_.CancelIME(toplevel_window_handle_); + if (IsTextInputClientFocused(client) && enabled_) { + imm32_manager_.CancelIME(toplevel_window_handle_); } } @@ -326,8 +318,6 @@ void InputMethodWinImm32::RefreshInputLanguage() { void InputMethodWinImm32::ConfirmCompositionText(bool reset_engine, bool keep_selection) { InputMethodBase::ConfirmCompositionText(reset_engine, keep_selection); - if (reset_engine) - InputMethodWinBase::ResetEngine(); // Makes sure the native IME app can be informed about the composition is // cleared, so that it can clean up its internal states. @@ -339,8 +329,7 @@ void InputMethodWinImm32::UpdateIMEState() { // Use switch here in case we are going to add more text input types. // We disable input method in password field. const HWND window_handle = toplevel_window_handle_; - const TextInputType text_input_type = - GetEngine() ? TEXT_INPUT_TYPE_NONE : GetTextInputType(); + const TextInputType text_input_type = GetTextInputType(); const TextInputMode text_input_mode = GetTextInputMode(); switch (text_input_type) { case ui::TEXT_INPUT_TYPE_NONE: @@ -357,8 +346,6 @@ void InputMethodWinImm32::UpdateIMEState() { imm32_manager_.SetTextInputMode(window_handle, text_input_mode); tsf_inputscope::SetInputScopeForTsfUnawareWindow( window_handle, text_input_type, text_input_mode); - - InputMethodWinBase::UpdateEngineFocusAndInputContext(); } } // namespace ui diff --git a/chromium/ui/base/ime/win/input_method_win_tsf.cc b/chromium/ui/base/ime/win/input_method_win_tsf.cc index 58926e51a7f..b4e4fcc2e67 100644 --- a/chromium/ui/base/ime/win/input_method_win_tsf.cc +++ b/chromium/ui/base/ime/win/input_method_win_tsf.cc @@ -100,7 +100,6 @@ void InputMethodWinTSF::OnTextInputTypeChanged(const TextInputClient* client) { } ui::TSFBridge::GetInstance()->CancelComposition(); ui::TSFBridge::GetInstance()->OnTextInputTypeChanged(client); - InputMethodWinBase::UpdateEngineFocusAndInputContext(); } void InputMethodWinTSF::OnCaretBoundsChanged(const TextInputClient* client) { @@ -110,14 +109,12 @@ void InputMethodWinTSF::OnCaretBoundsChanged(const TextInputClient* client) { } NotifyTextInputCaretBoundsChanged(client); ui::TSFBridge::GetInstance()->OnTextLayoutChanged(); - InputMethodWinBase::UpdateCompositionBoundsForEngine(client); } void InputMethodWinTSF::CancelComposition(const TextInputClient* client) { if (ui::TSFBridge::GetInstance() && IsTextInputClientFocused(client) && IsWindowFocused(client)) { ui::TSFBridge::GetInstance()->CancelComposition(); - InputMethodWinBase::CancelCompositionForEngine(); } } @@ -181,8 +178,6 @@ void InputMethodWinTSF::ConfirmCompositionText(bool reset_engine, if (IsTextInputTypeNone()) return; - if (reset_engine && GetTextInputClient()->HasCompositionText()) - InputMethodWinBase::ResetEngine(); if (ui::TSFBridge::GetInstance()) ui::TSFBridge::GetInstance()->ConfirmComposition(); } diff --git a/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_input_pane.cc b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_input_pane.cc index cf2caabef7d..5c19abd64d6 100644 --- a/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_input_pane.cc +++ b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_input_pane.cc @@ -222,7 +222,7 @@ void OnScreenKeyboardDisplayManagerInputPane::Run() { // Execute show() or hide() on the background thread after the debounce // expires. switch (last_vk_visibility_request_) { - case VirtualKeyboardVisibilityRequest::SHOW: { + case mojom::VirtualKeyboardVisibilityRequest::SHOW: { background_task_runner_->PostTask( FROM_HERE, base::BindOnce( @@ -231,7 +231,7 @@ void OnScreenKeyboardDisplayManagerInputPane::Run() { base::RetainedRef(virtual_keyboard_input_pane_), hwnd_)); break; } - case VirtualKeyboardVisibilityRequest::HIDE: { + case mojom::VirtualKeyboardVisibilityRequest::HIDE: { background_task_runner_->PostTask( FROM_HERE, base::BindOnce( @@ -240,18 +240,18 @@ void OnScreenKeyboardDisplayManagerInputPane::Run() { base::RetainedRef(virtual_keyboard_input_pane_), hwnd_)); break; } - case VirtualKeyboardVisibilityRequest::NONE: { + case mojom::VirtualKeyboardVisibilityRequest::NONE: { break; } } // Reset the VK visibility state to none so we can keep track of subsequent // API calls. - last_vk_visibility_request_ = VirtualKeyboardVisibilityRequest::NONE; + last_vk_visibility_request_ = mojom::VirtualKeyboardVisibilityRequest::NONE; } bool OnScreenKeyboardDisplayManagerInputPane::DisplayVirtualKeyboard() { DCHECK(main_task_runner_->BelongsToCurrentThread()); - last_vk_visibility_request_ = VirtualKeyboardVisibilityRequest::SHOW; + last_vk_visibility_request_ = mojom::VirtualKeyboardVisibilityRequest::SHOW; debouncer_->RequestRun(base::BindOnce( &OnScreenKeyboardDisplayManagerInputPane::Run, base::Unretained(this))); return true; @@ -259,7 +259,7 @@ bool OnScreenKeyboardDisplayManagerInputPane::DisplayVirtualKeyboard() { void OnScreenKeyboardDisplayManagerInputPane::DismissVirtualKeyboard() { DCHECK(main_task_runner_->BelongsToCurrentThread()); - last_vk_visibility_request_ = VirtualKeyboardVisibilityRequest::HIDE; + last_vk_visibility_request_ = mojom::VirtualKeyboardVisibilityRequest::HIDE; debouncer_->RequestRun(base::BindOnce( &OnScreenKeyboardDisplayManagerInputPane::Run, base::Unretained(this))); } diff --git a/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_input_pane.h b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_input_pane.h index 40c8578cb79..68b15c27f21 100644 --- a/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_input_pane.h +++ b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_input_pane.h @@ -18,7 +18,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/win/windows_types.h" #include "ui/base/ime/input_method_keyboard_controller.h" -#include "ui/base/ime/virtual_keyboard_visibility_request.h" +#include "ui/base/ime/mojom/virtual_keyboard_types.mojom-shared.h" #include "ui/base/ime/win/virtual_keyboard_debounce_timer.h" #include "ui/gfx/geometry/rect.h" @@ -47,8 +47,8 @@ class COMPONENT_EXPORT(UI_BASE_IME_WIN) pane); // Returns whether show/hide VK API is called from // InputMethodKeyboardController or not. - VirtualKeyboardVisibilityRequest GetLastVirtualKeyboardVisibilityRequest() - const { + mojom::VirtualKeyboardVisibilityRequest + GetLastVirtualKeyboardVisibilityRequest() const { return last_vk_visibility_request_; } @@ -69,8 +69,8 @@ class COMPONENT_EXPORT(UI_BASE_IME_WIN) const scoped_refptr<base::SingleThreadTaskRunner> background_task_runner_; scoped_refptr<VirtualKeyboardInputPane> virtual_keyboard_input_pane_; bool is_keyboard_visible_; - VirtualKeyboardVisibilityRequest last_vk_visibility_request_ = - VirtualKeyboardVisibilityRequest::NONE; + mojom::VirtualKeyboardVisibilityRequest last_vk_visibility_request_ = + mojom::VirtualKeyboardVisibilityRequest::NONE; std::unique_ptr<VirtualKeyboardDebounceTimer> debouncer_; base::WeakPtrFactory<OnScreenKeyboardDisplayManagerInputPane> weak_factory_{ this}; diff --git a/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.h b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.h index c32989e65b7..f718926d662 100644 --- a/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.h +++ b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.h @@ -11,7 +11,6 @@ #include "base/observer_list.h" #include "base/strings/string16.h" #include "ui/base/ime/input_method_keyboard_controller.h" -#include "ui/base/ui_base_export.h" #include "ui/gfx/geometry/rect.h" namespace ui { diff --git a/chromium/ui/base/ime/win/tsf_bridge.cc b/chromium/ui/base/ime/win/tsf_bridge.cc index 778f19bb853..f5c8c7155d4 100644 --- a/chromium/ui/base/ime/win/tsf_bridge.cc +++ b/chromium/ui/base/ime/win/tsf_bridge.cc @@ -111,6 +111,10 @@ class TSFBridgeImpl : public TSFBridge { // An ITfThreadMgr object to be used in focus and document management. Microsoft::WRL::ComPtr<ITfThreadMgr> thread_manager_; + // An ITfInputProcessorProfiles object to be used to get current language + // locale profile. + Microsoft::WRL::ComPtr<ITfInputProcessorProfiles> input_processor_profiles_; + // A map from TextInputType to an editable document for TSF. We use multiple // TSF documents that have different InputScopes and TSF attributes based on // the TextInputType associated with the target document. For a TextInputType @@ -134,6 +138,9 @@ class TSFBridgeImpl : public TSFBridge { // Handle to ITfKeyTraceEventSink. DWORD key_trace_sink_cookie_ = 0; + // Handle to ITfLanguageProfileNotifySink + DWORD language_profile_cookie_ = 0; + DISALLOW_COPY_AND_ASSIGN(TSFBridgeImpl); }; @@ -149,6 +156,11 @@ TSFBridgeImpl::~TSFBridgeImpl() { if (SUCCEEDED(thread_manager_->QueryInterface(IID_PPV_ARGS(&source)))) { source->UnadviseSink(key_trace_sink_cookie_); } + Microsoft::WRL::ComPtr<ITfSource> language_source; + if (SUCCEEDED(input_processor_profiles_->QueryInterface( + IID_PPV_ARGS(&language_source)))) { + language_source->UnadviseSink(language_profile_cookie_); + } } for (TSFDocumentMap::iterator it = tsf_document_map_.begin(); @@ -174,6 +186,13 @@ bool TSFBridgeImpl::Initialize() { return false; } + if (FAILED(::CoCreateInstance(CLSID_TF_InputProcessorProfiles, nullptr, + CLSCTX_ALL, + IID_PPV_ARGS(&input_processor_profiles_)))) { + DVLOG(1) << "Failed to create InputProcessorProfiles instance."; + return false; + } + if (FAILED(::CoCreateInstance(CLSID_TF_ThreadMgr, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&thread_manager_)))) { DVLOG(1) << "Failed to create ThreadManager instance."; @@ -309,7 +328,8 @@ void TSFBridgeImpl::SetFocusedClient(HWND focused_window, void TSFBridgeImpl::RemoveFocusedClient(TextInputClient* client) { DCHECK(base::MessageLoopCurrentForUI::IsSet()); - DCHECK(IsInitialized()); + if (!IsInitialized()) + return; if (client_ != client) return; ClearAssociateFocus(); @@ -422,6 +442,21 @@ bool TSFBridgeImpl::CreateDocumentManager(TSFTextStore* text_store, return false; } + Microsoft::WRL::ComPtr<ITfSource> language_source; + if (FAILED(input_processor_profiles_->QueryInterface( + IID_PPV_ARGS(&language_source)))) { + DVLOG(1) << "Failed to get source_ITfInputProcessorProfiles."; + return false; + } + + if (FAILED( + language_source->AdviseSink(IID_ITfLanguageProfileNotifySink, + static_cast<ITfTextEditSink*>(text_store), + &language_profile_cookie_))) { + DVLOG(1) << "AdviseSink for language profile notify sink failed."; + return false; + } + if (*source_cookie == TF_INVALID_COOKIE) { DVLOG(1) << "The result of cookie is invalid."; return false; diff --git a/chromium/ui/base/ime/win/tsf_text_store.cc b/chromium/ui/base/ime/win/tsf_text_store.cc index 22f43ff72b0..98ca302320d 100644 --- a/chromium/ui/base/ime/win/tsf_text_store.cc +++ b/chromium/ui/base/ime/win/tsf_text_store.cc @@ -11,6 +11,7 @@ #include <algorithm> +#include "base/logging.h" #include "base/numerics/ranges.h" #include "base/win/scoped_variant.h" #include "ui/base/ime/text_input_client.h" @@ -80,6 +81,8 @@ HRESULT TSFTextStore::QueryInterface(REFIID iid, void** result) { *result = static_cast<ITextStoreACP*>(this); } else if (iid == IID_ITfContextOwnerCompositionSink) { *result = static_cast<ITfContextOwnerCompositionSink*>(this); + } else if (iid == IID_ITfLanguageProfileNotifySink) { + *result = static_cast<ITfLanguageProfileNotifySink*>(this); } else if (iid == IID_ITfTextEditSink) { *result = static_cast<ITfTextEditSink*>(this); } else if (iid == IID_ITfKeyTraceEventSink) { @@ -647,9 +650,7 @@ HRESULT TSFTextStore::RequestLock(DWORD lock_flags, HRESULT* result) { // 3. User commits current composition text. if (((new_composition_start > last_composition_start && text_input_client_->HasCompositionText()) || - (wparam_keydown_fired_ == 0 && !has_composition_range_ && - !text_input_client_->HasCompositionText()) || - (wparam_keydown_fired_ != 0 && !has_composition_range_)) && + !has_composition_range_) && text_input_client_) { CommitTextAndEndCompositionIfAny(last_composition_start, new_composition_start); @@ -694,6 +695,7 @@ HRESULT TSFTextStore::RequestLock(DWORD lock_flags, HRESULT* result) { // reset the flag since we've already inserted/replaced the text. new_text_inserted_ = false; + is_selection_interim_char_ = false; // reset string_buffer_ if composition is no longer active. if (!text_input_client_->HasCompositionText()) { @@ -760,6 +762,7 @@ HRESULT TSFTextStore::SetSelection(ULONG selection_buffer_size, } selection_.set_start(start_pos); selection_.set_end(end_pos); + is_selection_interim_char_ = selection_buffer[0].style.fInterimChar; } return S_OK; } @@ -829,6 +832,15 @@ HRESULT TSFTextStore::OnEndComposition(ITfCompositionView* composition_view) { return S_OK; } +HRESULT TSFTextStore::OnLanguageChange(LANGID langid, BOOL* pfAccept) { + return S_OK; +} +HRESULT TSFTextStore::OnLanguageChanged() { + if (text_input_client_) + text_input_client_->OnInputMethodChanged(); + return S_OK; +} + HRESULT TSFTextStore::OnKeyTraceDown(WPARAM wParam, LPARAM lParam) { // fire the event right away if we're in composition if (has_composition_range_) { @@ -1052,6 +1064,10 @@ bool TSFTextStore::GetCompositionStatus( span.end_offset = start_pos + length; span.underline_color = SK_ColorBLACK; span.background_color = SK_ColorTRANSPARENT; + if (selection_.EqualsIgnoringDirection( + gfx::Range(span.start_offset, span.end_offset))) { + span.interim_char_selection = is_selection_interim_char_; + } if (has_display_attribute) GetStyle(display_attribute, &span); spans->push_back(span); @@ -1355,8 +1371,11 @@ void TSFTextStore::CommitTextAndEndCompositionIfAny(size_t old_size, : new_committed_string_size); // TODO(crbug.com/978678): Unify the behavior of // |TextInputClient::InsertText(text)| for the empty text. - if (!new_committed_string.empty()) + if (!new_committed_string.empty()) { text_input_client_->InsertText(new_committed_string); + } else { + text_input_client_->ClearCompositionText(); + } // Notify accessibility about this committed composition text_input_client_->SetActiveCompositionForAccessibility( replace_text_range_, new_committed_string, diff --git a/chromium/ui/base/ime/win/tsf_text_store.h b/chromium/ui/base/ime/win/tsf_text_store.h index 10bbae5a50e..bf02704ffff 100644 --- a/chromium/ui/base/ime/win/tsf_text_store.h +++ b/chromium/ui/base/ime/win/tsf_text_store.h @@ -102,6 +102,7 @@ class TextInputClient; class COMPONENT_EXPORT(UI_BASE_IME_WIN) TSFTextStore : public ITextStoreACP, public ITfContextOwnerCompositionSink, + public ITfLanguageProfileNotifySink, public ITfKeyTraceEventSink, public ITfTextEditSink { public: @@ -216,6 +217,10 @@ class COMPONENT_EXPORT(UI_BASE_IME_WIN) TSFTextStore IFACEMETHODIMP OnEndComposition( ITfCompositionView* composition_view) override; + // ITfLanguageProfileNotifySink: + IFACEMETHODIMP OnLanguageChange(LANGID langid, BOOL* pfAccept) override; + IFACEMETHODIMP OnLanguageChanged() override; + // ITfTextEditSink: IFACEMETHODIMP OnEndEdit(ITfContext* context, TfEditCookie read_only_edit_cookie, @@ -391,6 +396,10 @@ class COMPONENT_EXPORT(UI_BASE_IME_WIN) TSFTextStore // |selection_.end()|: 4 gfx::Range selection_; + // Indicates if the selection is an interim character. Please refer to + // https://docs.microsoft.com/en-us/windows/win32/api/textstor/ns-textstor-ts_selectionstyle + bool is_selection_interim_char_ = false; + // |start_offset| and |end_offset| of |text_spans_| indicates // the offsets in |string_buffer_document_|. // Example: "aoi" is committed. There are two underlines in "umi" and "no". diff --git a/chromium/ui/base/ime/win/tsf_text_store_unittest.cc b/chromium/ui/base/ime/win/tsf_text_store_unittest.cc index 081809d75a1..4e81d7c1012 100644 --- a/chromium/ui/base/ime/win/tsf_text_store_unittest.cc +++ b/chromium/ui/base/ime/win/tsf_text_store_unittest.cc @@ -3394,5 +3394,92 @@ TEST_F(TSFTextStoreTest, RegressionTest7) { EXPECT_EQ(S_OK, result); } +// regression tests for crbug.com/1091069. +// We should allow inserting empty compositon string to cancel composition. +class RegressionTest8Callback : public TSFTextStoreTestCallback { + public: + explicit RegressionTest8Callback(TSFTextStore* text_store) + : TSFTextStoreTestCallback(text_store) {} + + HRESULT LockGranted1(DWORD flags) { + SetTextTest(0, 0, L"bbbb", S_OK); + SetSelectionTest(0, 4, S_OK); + + text_spans()->clear(); + ImeTextSpan text_span_1; + text_span_1.start_offset = 0; + text_span_1.end_offset = 4; + text_span_1.underline_color = SK_ColorBLACK; + text_span_1.thickness = ImeTextSpan::Thickness::kThin; + text_span_1.background_color = SK_ColorTRANSPARENT; + text_spans()->push_back(text_span_1); + + *edit_flag() = true; + *composition_start() = 0; + composition_range()->set_start(0); + composition_range()->set_end(4); + *has_composition_range() = true; + + text_store_->OnKeyTraceDown(65u, 1966081u); + return S_OK; + } + + void SetCompositionText1(const ui::CompositionText& composition) { + EXPECT_EQ(L"bbbb", composition.text); + EXPECT_EQ(0u, composition.selection.start()); + EXPECT_EQ(4u, composition.selection.end()); + ASSERT_EQ(1u, composition.ime_text_spans.size()); + EXPECT_EQ(0u, composition.ime_text_spans[0].start_offset); + EXPECT_EQ(4u, composition.ime_text_spans[0].end_offset); + SetHasCompositionText(true); + } + + HRESULT LockGranted2(DWORD flags) { + GetTextTest(0, -1, L"bbbb", 4); + SetTextTest(0, 4, L"", S_OK); + + text_spans()->clear(); + *edit_flag() = true; + *composition_start() = 0; + composition_range()->set_start(0); + composition_range()->set_end(0); + + *has_composition_range() = false; + text_store_->OnKeyTraceUp(65u, 1966081u); + return S_OK; + } + + void ClearCompositionText2() { EXPECT_EQ(false, *has_composition_range()); } + + private: + DISALLOW_COPY_AND_ASSIGN(RegressionTest8Callback); +}; + +TEST_F(TSFTextStoreTest, RegressionTest8) { + RegressionTest8Callback callback(text_store_.get()); + EXPECT_CALL(text_input_client_, SetCompositionText(_)) + .WillOnce( + Invoke(&callback, &RegressionTest8Callback::SetCompositionText1)); + + EXPECT_CALL(text_input_client_, ClearCompositionText()) + .WillOnce( + Invoke(&callback, &RegressionTest8Callback::ClearCompositionText2)); + + EXPECT_CALL(*sink_, OnLockGranted(_)) + .WillOnce(Invoke(&callback, &RegressionTest8Callback::LockGranted1)) + .WillOnce(Invoke(&callback, &RegressionTest8Callback::LockGranted2)); + + ON_CALL(text_input_client_, HasCompositionText()) + .WillByDefault( + Invoke(&callback, &TSFTextStoreTestCallback::HasCompositionText)); + + HRESULT result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); + result = kInvalidResult; + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); + EXPECT_EQ(S_OK, result); +} + } // namespace } // namespace ui |