1 // Copyright 2018 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 "chromeos/services/ime/input_engine.h"
6
7 #include "base/notreached.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/values.h"
11 #include "chromeos/services/ime/public/cpp/rulebased/engine.h"
12
13 namespace chromeos {
14 namespace ime {
15
16 namespace {
17
GetIdFromImeSpec(const std::string & ime_spec)18 std::string GetIdFromImeSpec(const std::string& ime_spec) {
19 static const std::string kPrefix("m17n:");
20 return base::StartsWith(ime_spec, kPrefix, base::CompareCase::SENSITIVE)
21 ? ime_spec.substr(kPrefix.length())
22 : std::string();
23 }
24
GenerateModifierValueForRulebased(const mojom::ModifierStatePtr & modifier_state,bool isAltRightDown)25 uint8_t GenerateModifierValueForRulebased(
26 const mojom::ModifierStatePtr& modifier_state,
27 bool isAltRightDown) {
28 uint8_t modifiers = 0;
29 if (modifier_state->shift)
30 modifiers |= rulebased::MODIFIER_SHIFT;
31 if (modifier_state->alt_graph || isAltRightDown)
32 modifiers |= rulebased::MODIFIER_ALTGR;
33 if (modifier_state->caps_lock)
34 modifiers |= rulebased::MODIFIER_CAPSLOCK;
35 return modifiers;
36 }
37
GenerateKeypressResponseForRulebased(rulebased::ProcessKeyResult & process_key_result)38 mojom::KeypressResponseForRulebasedPtr GenerateKeypressResponseForRulebased(
39 rulebased::ProcessKeyResult& process_key_result) {
40 mojom::KeypressResponseForRulebasedPtr keypress_response =
41 mojom::KeypressResponseForRulebased::New();
42 keypress_response->result = process_key_result.key_handled;
43 if (!process_key_result.commit_text.empty()) {
44 keypress_response->operations.push_back(mojom::OperationForRulebased::New(
45 mojom::OperationMethodForRulebased::COMMIT_TEXT,
46 process_key_result.commit_text));
47 }
48 // Need to add the setComposition operation to the result when the key is
49 // handled and commit_text and composition_text are both empty.
50 // That is the case of using Backspace to delete the last character in
51 // composition.
52 if (!process_key_result.composition_text.empty() ||
53 (process_key_result.key_handled &&
54 process_key_result.commit_text.empty())) {
55 keypress_response->operations.push_back(mojom::OperationForRulebased::New(
56 mojom::OperationMethodForRulebased::SET_COMPOSITION,
57 process_key_result.composition_text));
58 }
59 return keypress_response;
60 }
61
IsModifierKey(const std::string & key_code)62 bool IsModifierKey(const std::string& key_code) {
63 return key_code == "AltLeft" || key_code == "AltRight" ||
64 key_code == "ShiftLeft" || key_code == "ShiftRight" ||
65 key_code == "ControlLeft" || key_code == "ControlRight" ||
66 key_code == "CapsLock";
67 }
68
69 } // namespace
70
InputEngineContext(const std::string & ime)71 InputEngineContext::InputEngineContext(const std::string& ime) : ime_spec(ime) {
72 // The |ime_spec|'s format for rule based imes is: "m17n:<id>".
73 std::string id = GetIdFromImeSpec(ime_spec);
74 if (rulebased::Engine::IsImeSupported(id)) {
75 engine = std::make_unique<rulebased::Engine>();
76 engine->Activate(id);
77 }
78 }
79
~InputEngineContext()80 InputEngineContext::~InputEngineContext() {}
81
InputEngine()82 InputEngine::InputEngine() {}
83
~InputEngine()84 InputEngine::~InputEngine() {}
85
BindRequest(const std::string & ime_spec,mojo::PendingReceiver<mojom::InputChannel> receiver,mojo::PendingRemote<mojom::InputChannel> remote,const std::vector<uint8_t> & extra)86 bool InputEngine::BindRequest(
87 const std::string& ime_spec,
88 mojo::PendingReceiver<mojom::InputChannel> receiver,
89 mojo::PendingRemote<mojom::InputChannel> remote,
90 const std::vector<uint8_t>& extra) {
91 if (!IsImeSupportedByRulebased(ime_spec))
92 return false;
93
94 channel_receivers_.Add(this, std::move(receiver),
95 std::make_unique<InputEngineContext>(ime_spec));
96
97 return true;
98 // TODO(https://crbug.com/837156): Registry connection error handler.
99 }
100
IsImeSupportedByRulebased(const std::string & ime_spec)101 bool InputEngine::IsImeSupportedByRulebased(const std::string& ime_spec) {
102 return rulebased::Engine::IsImeSupported(GetIdFromImeSpec(ime_spec));
103 }
104
ProcessMessage(const std::vector<uint8_t> & message,ProcessMessageCallback callback)105 void InputEngine::ProcessMessage(const std::vector<uint8_t>& message,
106 ProcessMessageCallback callback) {
107 NOTIMPLEMENTED(); // Protobuf message is not used in the rulebased engine.
108 }
109
OnInputMethodChanged(const std::string & engine_id)110 void InputEngine::OnInputMethodChanged(const std::string& engine_id) {
111 NOTIMPLEMENTED(); // Not used in the rulebased engine.
112 }
113
OnFocus(mojom::InputFieldInfoPtr input_field_info)114 void InputEngine::OnFocus(mojom::InputFieldInfoPtr input_field_info) {
115 NOTIMPLEMENTED(); // Not used in the rulebased engine.
116 }
117
OnBlur()118 void InputEngine::OnBlur() {
119 NOTIMPLEMENTED(); // Not used in the rulebased engine.
120 }
121
OnSurroundingTextChanged(const std::string & text,uint32_t offset,mojom::SelectionRangePtr selection_range)122 void InputEngine::OnSurroundingTextChanged(
123 const std::string& text,
124 uint32_t offset,
125 mojom::SelectionRangePtr selection_range) {
126 NOTIMPLEMENTED(); // Not used in the rulebased engine.
127 }
128
OnCompositionCanceled()129 void InputEngine::OnCompositionCanceled() {
130 NOTIMPLEMENTED(); // Not used in the rulebased engine.
131 }
132
ProcessKeypressForRulebased(mojom::PhysicalKeyEventPtr event,ProcessKeypressForRulebasedCallback callback)133 void InputEngine::ProcessKeypressForRulebased(
134 mojom::PhysicalKeyEventPtr event,
135 ProcessKeypressForRulebasedCallback callback) {
136 auto& context = channel_receivers_.current_context();
137 auto& engine = context.get()->engine;
138
139 // According to the W3C spec, |altKey| is false if the AltGr key
140 // is pressed [1]. However, all rule-based input methods on Chrome OS use
141 // the US QWERTY layout as a base layout, with AltGr implemented at this
142 // layer. This means the right Alt key reports as being a normal Alt key, so
143 // |altKey| is true. Thus, we need to take |altKey| and exclude the
144 // right Alt key to determine the status of the "true" Alt key.
145 // [1] https://www.w3.org/TR/uievents-key/#keys-modifier
146 // TODO(https://crbug.com/1014778): Change the base layouts for the
147 // rule-based input methods so that |altKey| is false when AltGr is pressed.
148 if (event->code == "AltRight") {
149 isAltRightDown_ = event->type == mojom::KeyEventType::kKeyDown;
150 }
151
152 const bool isAltDown = event->modifier_state->alt && !isAltRightDown_;
153
154 // - Shift/AltRight/Caps/Ctrl are modifier keys for the characters which the
155 // Mojo service may accept, but don't send the keys themselves to Mojo.
156 // - Ctrl+? and Alt+? are shortcut keys, so don't send them to the rule based
157 // engine.
158 if (!engine || event->type != mojom::KeyEventType::kKeyDown ||
159 (IsModifierKey(event->code) || event->modifier_state->control ||
160 isAltDown)) {
161 std::move(callback).Run(mojom::KeypressResponseForRulebased::New(
162 false, std::vector<mojom::OperationForRulebasedPtr>(0)));
163 return;
164 }
165
166 rulebased::ProcessKeyResult process_key_result = engine->ProcessKey(
167 event->code, GenerateModifierValueForRulebased(event->modifier_state,
168 isAltRightDown_));
169 mojom::KeypressResponseForRulebasedPtr keypress_response =
170 GenerateKeypressResponseForRulebased(process_key_result);
171
172 std::move(callback).Run(std::move(keypress_response));
173 }
174
OnKeyEvent(mojom::PhysicalKeyEventPtr event,OnKeyEventCallback callback)175 void InputEngine::OnKeyEvent(mojom::PhysicalKeyEventPtr event,
176 OnKeyEventCallback callback) {
177 NOTIMPLEMENTED(); // Not used in the rulebased engine.
178 }
179
ResetForRulebased()180 void InputEngine::ResetForRulebased() {
181 auto& context = channel_receivers_.current_context();
182 auto& engine = context.get()->engine;
183 // TODO(https://crbug.com/1633694) Handle the case when the engine is not
184 // defined
185 if (engine) {
186 engine->Reset();
187 }
188 isAltRightDown_ = false;
189 }
190
GetRulebasedKeypressCountForTesting(GetRulebasedKeypressCountForTestingCallback callback)191 void InputEngine::GetRulebasedKeypressCountForTesting(
192 GetRulebasedKeypressCountForTestingCallback callback) {
193 auto& context = channel_receivers_.current_context();
194 auto& engine = context.get()->engine;
195 std::move(callback).Run(engine ? engine->process_key_count() : -1);
196 }
197
CommitText(const std::string & text)198 void InputEngine::CommitText(const std::string& text) {
199 NOTIMPLEMENTED(); // Not used in the rulebased engine.
200 }
201
SetComposition(const std::string & text)202 void InputEngine::SetComposition(const std::string& text) {
203 NOTIMPLEMENTED(); // Not used in the rulebased engine.
204 }
205
SetCompositionRange(uint32_t start,uint32_t end)206 void InputEngine::SetCompositionRange(uint32_t start, uint32_t end) {
207 NOTIMPLEMENTED(); // Not used in the rulebased engine.
208 }
209
FinishComposition()210 void InputEngine::FinishComposition() {
211 NOTIMPLEMENTED(); // Not used in the rulebased engine.
212 }
213
DeleteSurroundingText(uint32_t num_bytes_before_cursor,uint32_t num_bytes_after_cursor)214 void InputEngine::DeleteSurroundingText(uint32_t num_bytes_before_cursor,
215 uint32_t num_bytes_after_cursor) {
216 NOTIMPLEMENTED(); // Not used in the rulebased engine.
217 }
218
219 } // namespace ime
220 } // namespace chromeos
221