|
| 1 | +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Siye Liu <siliu@microsoft.com> |
| 3 | +Date: Mon, 8 Jun 2020 21:19:00 +0000 |
| 4 | +Subject: Allow IME to insert zero-length composition string. |
| 5 | + |
| 6 | +Some IMEs and Windows OS dictation will either insert zero-length text |
| 7 | +or delete existing composition text to commit active composition. |
| 8 | +Therefore, we should allow IMEs to cancel composition by committing zero |
| 9 | +length text. |
| 10 | + |
| 11 | +Bug: 1091069 |
| 12 | +Change-Id: I99cb213dc2ba1965abfa88ccf6858b89bd7e82df |
| 13 | +Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2233234 |
| 14 | +Commit-Queue: Siye Liu <siliu@microsoft.com> |
| 15 | +Reviewed-by: Yohei Yukawa <yukawa@chromium.org> |
| 16 | +Cr-Commit-Position: refs/heads/master@{#776198} |
| 17 | + |
| 18 | +diff --git a/ui/base/ime/win/tsf_text_store.cc b/ui/base/ime/win/tsf_text_store.cc |
| 19 | +index 4c8552a427c2a559ee1f3753af2ef000e9fe4a8c..7642bb5a92dad553044ace1e17e33b5815e6217f 100644 |
| 20 | +--- a/ui/base/ime/win/tsf_text_store.cc |
| 21 | ++++ b/ui/base/ime/win/tsf_text_store.cc |
| 22 | +@@ -612,9 +612,7 @@ STDMETHODIMP TSFTextStore::RequestLock(DWORD lock_flags, HRESULT* result) { |
| 23 | + // 3. User commits current composition text. |
| 24 | + if (((new_composition_start > last_composition_start && |
| 25 | + text_input_client_->HasCompositionText()) || |
| 26 | +- (wparam_keydown_fired_ == 0 && !has_composition_range_ && |
| 27 | +- !text_input_client_->HasCompositionText()) || |
| 28 | +- (wparam_keydown_fired_ != 0 && !has_composition_range_)) && |
| 29 | ++ !has_composition_range_) && |
| 30 | + text_input_client_) { |
| 31 | + CommitTextAndEndCompositionIfAny(last_composition_start, |
| 32 | + new_composition_start); |
| 33 | +@@ -1314,8 +1312,11 @@ void TSFTextStore::CommitTextAndEndCompositionIfAny(size_t old_size, |
| 34 | + : new_committed_string_size); |
| 35 | + // TODO(crbug.com/978678): Unify the behavior of |
| 36 | + // |TextInputClient::InsertText(text)| for the empty text. |
| 37 | +- if (!new_committed_string.empty()) |
| 38 | ++ if (!new_committed_string.empty()) { |
| 39 | + text_input_client_->InsertText(new_committed_string); |
| 40 | ++ } else { |
| 41 | ++ text_input_client_->ClearCompositionText(); |
| 42 | ++ } |
| 43 | + // Notify accessibility about this committed composition |
| 44 | + text_input_client_->SetActiveCompositionForAccessibility( |
| 45 | + replace_text_range_, new_committed_string, |
| 46 | +diff --git a/ui/base/ime/win/tsf_text_store_unittest.cc b/ui/base/ime/win/tsf_text_store_unittest.cc |
| 47 | +index 7909ade63ab752a3d0d450347bba15dc205ee466..77c5eb051e646401eaf22ca1753ef71c585fbc76 100644 |
| 48 | +--- a/ui/base/ime/win/tsf_text_store_unittest.cc |
| 49 | ++++ b/ui/base/ime/win/tsf_text_store_unittest.cc |
| 50 | +@@ -3139,5 +3139,92 @@ TEST_F(TSFTextStoreTest, RegressionTest5) { |
| 51 | + EXPECT_EQ(S_OK, result); |
| 52 | + } |
| 53 | + |
| 54 | ++// regression tests for crbug.com/1091069. |
| 55 | ++// We should allow inserting empty compositon string to cancel composition. |
| 56 | ++class RegressionTest8Callback : public TSFTextStoreTestCallback { |
| 57 | ++ public: |
| 58 | ++ explicit RegressionTest8Callback(TSFTextStore* text_store) |
| 59 | ++ : TSFTextStoreTestCallback(text_store) {} |
| 60 | ++ |
| 61 | ++ HRESULT LockGranted1(DWORD flags) { |
| 62 | ++ SetTextTest(0, 0, L"bbbb", S_OK); |
| 63 | ++ SetSelectionTest(0, 4, S_OK); |
| 64 | ++ |
| 65 | ++ text_spans()->clear(); |
| 66 | ++ ImeTextSpan text_span_1; |
| 67 | ++ text_span_1.start_offset = 0; |
| 68 | ++ text_span_1.end_offset = 4; |
| 69 | ++ text_span_1.underline_color = SK_ColorBLACK; |
| 70 | ++ text_span_1.thickness = ImeTextSpan::Thickness::kThin; |
| 71 | ++ text_span_1.background_color = SK_ColorTRANSPARENT; |
| 72 | ++ text_spans()->push_back(text_span_1); |
| 73 | ++ |
| 74 | ++ *edit_flag() = true; |
| 75 | ++ *composition_start() = 0; |
| 76 | ++ composition_range()->set_start(0); |
| 77 | ++ composition_range()->set_end(4); |
| 78 | ++ *has_composition_range() = true; |
| 79 | ++ |
| 80 | ++ text_store_->OnKeyTraceDown(65u, 1966081u); |
| 81 | ++ return S_OK; |
| 82 | ++ } |
| 83 | ++ |
| 84 | ++ void SetCompositionText1(const ui::CompositionText& composition) { |
| 85 | ++ EXPECT_EQ(L"bbbb", composition.text); |
| 86 | ++ EXPECT_EQ(0u, composition.selection.start()); |
| 87 | ++ EXPECT_EQ(4u, composition.selection.end()); |
| 88 | ++ ASSERT_EQ(1u, composition.ime_text_spans.size()); |
| 89 | ++ EXPECT_EQ(0u, composition.ime_text_spans[0].start_offset); |
| 90 | ++ EXPECT_EQ(4u, composition.ime_text_spans[0].end_offset); |
| 91 | ++ SetHasCompositionText(true); |
| 92 | ++ } |
| 93 | ++ |
| 94 | ++ HRESULT LockGranted2(DWORD flags) { |
| 95 | ++ GetTextTest(0, -1, L"bbbb", 4); |
| 96 | ++ SetTextTest(0, 4, L"", S_OK); |
| 97 | ++ |
| 98 | ++ text_spans()->clear(); |
| 99 | ++ *edit_flag() = true; |
| 100 | ++ *composition_start() = 0; |
| 101 | ++ composition_range()->set_start(0); |
| 102 | ++ composition_range()->set_end(0); |
| 103 | ++ |
| 104 | ++ *has_composition_range() = false; |
| 105 | ++ text_store_->OnKeyTraceUp(65u, 1966081u); |
| 106 | ++ return S_OK; |
| 107 | ++ } |
| 108 | ++ |
| 109 | ++ void ClearCompositionText2() { EXPECT_EQ(false, *has_composition_range()); } |
| 110 | ++ |
| 111 | ++ private: |
| 112 | ++ DISALLOW_COPY_AND_ASSIGN(RegressionTest8Callback); |
| 113 | ++}; |
| 114 | ++ |
| 115 | ++TEST_F(TSFTextStoreTest, RegressionTest8) { |
| 116 | ++ RegressionTest8Callback callback(text_store_.get()); |
| 117 | ++ EXPECT_CALL(text_input_client_, SetCompositionText(_)) |
| 118 | ++ .WillOnce( |
| 119 | ++ Invoke(&callback, &RegressionTest8Callback::SetCompositionText1)); |
| 120 | ++ |
| 121 | ++ EXPECT_CALL(text_input_client_, ClearCompositionText()) |
| 122 | ++ .WillOnce( |
| 123 | ++ Invoke(&callback, &RegressionTest8Callback::ClearCompositionText2)); |
| 124 | ++ |
| 125 | ++ EXPECT_CALL(*sink_, OnLockGranted(_)) |
| 126 | ++ .WillOnce(Invoke(&callback, &RegressionTest8Callback::LockGranted1)) |
| 127 | ++ .WillOnce(Invoke(&callback, &RegressionTest8Callback::LockGranted2)); |
| 128 | ++ |
| 129 | ++ ON_CALL(text_input_client_, HasCompositionText()) |
| 130 | ++ .WillByDefault( |
| 131 | ++ Invoke(&callback, &TSFTextStoreTestCallback::HasCompositionText)); |
| 132 | ++ |
| 133 | ++ HRESULT result = kInvalidResult; |
| 134 | ++ EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); |
| 135 | ++ EXPECT_EQ(S_OK, result); |
| 136 | ++ result = kInvalidResult; |
| 137 | ++ EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); |
| 138 | ++ EXPECT_EQ(S_OK, result); |
| 139 | ++} |
| 140 | ++ |
| 141 | + } // namespace |
| 142 | + } // namespace ui |
0 commit comments