1 /*
2 * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "js_get_input_method_controller.h"
16
17 #include <set>
18
19 #include "event_checker.h"
20 #include "inputmethod_trace.h"
21 #include "input_method_controller.h"
22 #include "input_method_utils.h"
23 #include "js_callback_handler.h"
24 #include "js_get_input_method_textchange_listener.h"
25 #include "js_util.h"
26 #include "napi/native_api.h"
27 #include "napi/native_node_api.h"
28 #include "string_ex.h"
29
30 namespace OHOS {
31 namespace MiscServices {
32 constexpr size_t ARGC_ZERO = 0;
33 constexpr size_t ARGC_ONE = 1;
34 constexpr size_t ARGC_TWO = 2;
35 const std::set<std::string> EVENT_TYPE{
36 "selectByRange",
37 "selectByMovement",
38 };
39 const std::set<std::string> JsGetInputMethodController::TEXT_EVENT_TYPE{
40 "insertText",
41 "deleteLeft",
42 "deleteRight",
43 "sendKeyboardStatus",
44 "sendFunctionKey",
45 "moveCursor",
46 "handleExtendAction",
47 "getLeftTextOfCursor",
48 "getRightTextOfCursor",
49 "getTextIndexAtCursor",
50 };
51 thread_local napi_ref JsGetInputMethodController::IMCRef_ = nullptr;
52 const std::string JsGetInputMethodController::IMC_CLASS_NAME = "InputMethodController";
53 std::mutex JsGetInputMethodController::controllerMutex_;
54 std::shared_ptr<JsGetInputMethodController> JsGetInputMethodController::controller_{ nullptr };
55 std::mutex JsGetInputMethodController::eventHandlerMutex_;
56 std::shared_ptr<AppExecFwk::EventHandler> JsGetInputMethodController::handler_{ nullptr };
Init(napi_env env,napi_value info)57 napi_value JsGetInputMethodController::Init(napi_env env, napi_value info)
58 {
59 napi_property_descriptor descriptor[] = {
60 DECLARE_NAPI_FUNCTION("getInputMethodController", GetInputMethodController),
61 DECLARE_NAPI_FUNCTION("getController", GetController),
62 DECLARE_NAPI_STATIC_PROPERTY("KeyboardStatus", GetJsKeyboardStatusProperty(env)),
63 DECLARE_NAPI_STATIC_PROPERTY("EnterKeyType", GetJsEnterKeyTypeProperty(env)),
64 DECLARE_NAPI_STATIC_PROPERTY("TextInputType", GetJsTextInputTypeProperty(env)),
65 DECLARE_NAPI_STATIC_PROPERTY("Direction", GetJsDirectionProperty(env)),
66 DECLARE_NAPI_STATIC_PROPERTY("ExtendAction", GetJsExtendActionProperty(env)),
67 };
68 NAPI_CALL(
69 env, napi_define_properties(env, info, sizeof(descriptor) / sizeof(napi_property_descriptor), descriptor));
70
71 napi_property_descriptor properties[] = {
72 DECLARE_NAPI_FUNCTION("attach", Attach),
73 DECLARE_NAPI_FUNCTION("detach", Detach),
74 DECLARE_NAPI_FUNCTION("showTextInput", ShowTextInput),
75 DECLARE_NAPI_FUNCTION("hideTextInput", HideTextInput),
76 DECLARE_NAPI_FUNCTION("setCallingWindow", SetCallingWindow),
77 DECLARE_NAPI_FUNCTION("updateCursor", UpdateCursor),
78 DECLARE_NAPI_FUNCTION("changeSelection", ChangeSelection),
79 DECLARE_NAPI_FUNCTION("updateAttribute", UpdateAttribute),
80 DECLARE_NAPI_FUNCTION("stopInput", StopInput),
81 DECLARE_NAPI_FUNCTION("stopInputSession", StopInputSession),
82 DECLARE_NAPI_FUNCTION("hideSoftKeyboard", HideSoftKeyboard),
83 DECLARE_NAPI_FUNCTION("showSoftKeyboard", ShowSoftKeyboard),
84 DECLARE_NAPI_FUNCTION("on", Subscribe),
85 DECLARE_NAPI_FUNCTION("off", UnSubscribe),
86 };
87 napi_value cons = nullptr;
88 NAPI_CALL(env, napi_define_class(env, IMC_CLASS_NAME.c_str(), IMC_CLASS_NAME.size(), JsConstructor, nullptr,
89 sizeof(properties) / sizeof(napi_property_descriptor), properties, &cons));
90 NAPI_CALL(env, napi_create_reference(env, cons, 1, &IMCRef_));
91 NAPI_CALL(env, napi_set_named_property(env, info, IMC_CLASS_NAME.c_str(), cons));
92
93 return info;
94 }
95
GetJsKeyboardStatusProperty(napi_env env)96 napi_value JsGetInputMethodController::GetJsKeyboardStatusProperty(napi_env env)
97 {
98 napi_value keyboardStatus = nullptr;
99 napi_value statusNone = nullptr;
100 napi_value statusHide = nullptr;
101 napi_value statusShow = nullptr;
102 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(KeyboardStatus::NONE), &statusNone));
103 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(KeyboardStatus::HIDE), &statusHide));
104 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(KeyboardStatus::SHOW), &statusShow));
105 NAPI_CALL(env, napi_create_object(env, &keyboardStatus));
106 NAPI_CALL(env, napi_set_named_property(env, keyboardStatus, "NONE", statusNone));
107 NAPI_CALL(env, napi_set_named_property(env, keyboardStatus, "HIDE", statusHide));
108 NAPI_CALL(env, napi_set_named_property(env, keyboardStatus, "SHOW", statusShow));
109 return keyboardStatus;
110 }
111
GetJsEnterKeyTypeProperty(napi_env env)112 napi_value JsGetInputMethodController::GetJsEnterKeyTypeProperty(napi_env env)
113 {
114 napi_value enterKeyType = nullptr;
115 napi_value typeUnspecified = nullptr;
116 napi_value typeNone = nullptr;
117 napi_value typeGo = nullptr;
118 napi_value typeSearch = nullptr;
119 napi_value typeSend = nullptr;
120 napi_value typeNext = nullptr;
121 napi_value typeDone = nullptr;
122 napi_value typePrevious = nullptr;
123 napi_value typeNewline = nullptr;
124 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnterKeyType::UNSPECIFIED), &typeUnspecified));
125 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnterKeyType::NONE), &typeNone));
126 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnterKeyType::GO), &typeGo));
127 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnterKeyType::SEARCH), &typeSearch));
128 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnterKeyType::SEND), &typeSend));
129 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnterKeyType::NEXT), &typeNext));
130 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnterKeyType::DONE), &typeDone));
131 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnterKeyType::PREVIOUS), &typePrevious));
132 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnterKeyType::NEW_LINE), &typeNewline));
133 NAPI_CALL(env, napi_create_object(env, &enterKeyType));
134 NAPI_CALL(env, napi_set_named_property(env, enterKeyType, "UNSPECIFIED", typeUnspecified));
135 NAPI_CALL(env, napi_set_named_property(env, enterKeyType, "NONE", typeNone));
136 NAPI_CALL(env, napi_set_named_property(env, enterKeyType, "GO", typeGo));
137 NAPI_CALL(env, napi_set_named_property(env, enterKeyType, "SEARCH", typeSearch));
138 NAPI_CALL(env, napi_set_named_property(env, enterKeyType, "SEND", typeSend));
139 NAPI_CALL(env, napi_set_named_property(env, enterKeyType, "NEXT", typeNext));
140 NAPI_CALL(env, napi_set_named_property(env, enterKeyType, "DONE", typeDone));
141 NAPI_CALL(env, napi_set_named_property(env, enterKeyType, "PREVIOUS", typePrevious));
142 NAPI_CALL(env, napi_set_named_property(env, enterKeyType, "NEWLINE", typeNewline));
143 return enterKeyType;
144 }
145
GetJsTextInputTypeProperty(napi_env env)146 napi_value JsGetInputMethodController::GetJsTextInputTypeProperty(napi_env env)
147 {
148 napi_value textInputType = nullptr;
149 napi_value typeNone = nullptr;
150 napi_value typeText = nullptr;
151 napi_value typeMultiline = nullptr;
152 napi_value typeNumber = nullptr;
153 napi_value typePhone = nullptr;
154 napi_value typeDatatime = nullptr;
155 napi_value typeEmailAddress = nullptr;
156 napi_value typeUrl = nullptr;
157 napi_value typeVisiblePassword = nullptr;
158 napi_value typeNumberPassword = nullptr;
159 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(TextInputType::NONE), &typeNone));
160 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(TextInputType::TEXT), &typeText));
161 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(TextInputType::MULTILINE), &typeMultiline));
162 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(TextInputType::NUMBER), &typeNumber));
163 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(TextInputType::PHONE), &typePhone));
164 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(TextInputType::DATETIME), &typeDatatime));
165 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(TextInputType::EMAIL_ADDRESS), &typeEmailAddress));
166 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(TextInputType::URL), &typeUrl));
167 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(TextInputType::VISIBLE_PASSWORD), &typeVisiblePassword));
168 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(TextInputType::NUMBER_PASSWORD), &typeNumberPassword));
169 NAPI_CALL(env, napi_create_object(env, &textInputType));
170 NAPI_CALL(env, napi_set_named_property(env, textInputType, "NONE", typeNone));
171 NAPI_CALL(env, napi_set_named_property(env, textInputType, "TEXT", typeText));
172 NAPI_CALL(env, napi_set_named_property(env, textInputType, "MULTILINE", typeMultiline));
173 NAPI_CALL(env, napi_set_named_property(env, textInputType, "NUMBER", typeNumber));
174 NAPI_CALL(env, napi_set_named_property(env, textInputType, "PHONE", typePhone));
175 NAPI_CALL(env, napi_set_named_property(env, textInputType, "DATETIME", typeDatatime));
176 NAPI_CALL(env, napi_set_named_property(env, textInputType, "EMAIL_ADDRESS", typeEmailAddress));
177 NAPI_CALL(env, napi_set_named_property(env, textInputType, "URL", typeUrl));
178 NAPI_CALL(env, napi_set_named_property(env, textInputType, "VISIBLE_PASSWORD", typeVisiblePassword));
179 NAPI_CALL(env, napi_set_named_property(env, textInputType, "NUMBER_PASSWORD", typeNumberPassword));
180 return textInputType;
181 }
182
GetJsDirectionProperty(napi_env env)183 napi_value JsGetInputMethodController::GetJsDirectionProperty(napi_env env)
184 {
185 napi_value direction = nullptr;
186 napi_value cursorUp = nullptr;
187 napi_value cursorDown = nullptr;
188 napi_value cursorLeft = nullptr;
189 napi_value cursorRight = nullptr;
190 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(Direction::UP), &cursorUp));
191 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(Direction::DOWN), &cursorDown));
192 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(Direction::LEFT), &cursorLeft));
193 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(Direction::RIGHT), &cursorRight));
194 NAPI_CALL(env, napi_create_object(env, &direction));
195 NAPI_CALL(env, napi_set_named_property(env, direction, "CURSOR_UP", cursorUp));
196 NAPI_CALL(env, napi_set_named_property(env, direction, "CURSOR_DOWN", cursorDown));
197 NAPI_CALL(env, napi_set_named_property(env, direction, "CURSOR_LEFT", cursorLeft));
198 NAPI_CALL(env, napi_set_named_property(env, direction, "CURSOR_RIGHT", cursorRight));
199 return direction;
200 }
201
GetJsExtendActionProperty(napi_env env)202 napi_value JsGetInputMethodController::GetJsExtendActionProperty(napi_env env)
203 {
204 napi_value action = nullptr;
205 napi_value actionSelectAll = nullptr;
206 napi_value actionCut = nullptr;
207 napi_value actionCopy = nullptr;
208 napi_value actionPaste = nullptr;
209 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(ExtendAction::SELECT_ALL), &actionSelectAll));
210 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(ExtendAction::CUT), &actionCut));
211 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(ExtendAction::COPY), &actionCopy));
212 NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(ExtendAction::PASTE), &actionPaste));
213 NAPI_CALL(env, napi_create_object(env, &action));
214 NAPI_CALL(env, napi_set_named_property(env, action, "SELECT_ALL", actionSelectAll));
215 NAPI_CALL(env, napi_set_named_property(env, action, "CUT", actionCut));
216 NAPI_CALL(env, napi_set_named_property(env, action, "COPY", actionCopy));
217 NAPI_CALL(env, napi_set_named_property(env, action, "PASTE", actionPaste));
218 return action;
219 }
220
JsConstructor(napi_env env,napi_callback_info cbinfo)221 napi_value JsGetInputMethodController::JsConstructor(napi_env env, napi_callback_info cbinfo)
222 {
223 {
224 std::lock_guard<std::mutex> lock(eventHandlerMutex_);
225 handler_ = AppExecFwk::EventHandler::Current();
226 }
227 napi_value thisVar = nullptr;
228 NAPI_CALL(env, napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr));
229
230 auto controllerObject = GetInstance();
231 if (controllerObject == nullptr) {
232 IMSA_HILOGE("controllerObject is nullptr!");
233 napi_value result = nullptr;
234 napi_get_null(env, &result);
235 return result;
236 }
237 napi_status status = napi_wrap(
238 env, thisVar, controllerObject.get(), [](napi_env env, void *data, void *hint) {}, nullptr, nullptr);
239 if (status != napi_ok) {
240 IMSA_HILOGE("failed to wrap: %{public}d!", status);
241 return nullptr;
242 }
243
244 if (controllerObject->loop_ == nullptr) {
245 napi_get_uv_event_loop(env, &controllerObject->loop_);
246 }
247
248 return thisVar;
249 }
250
GetInputMethodController(napi_env env,napi_callback_info cbInfo)251 napi_value JsGetInputMethodController::GetInputMethodController(napi_env env, napi_callback_info cbInfo)
252 {
253 return GetIMController(env, cbInfo, false);
254 }
255
GetController(napi_env env,napi_callback_info cbInfo)256 napi_value JsGetInputMethodController::GetController(napi_env env, napi_callback_info cbInfo)
257 {
258 return GetIMController(env, cbInfo, true);
259 }
260
GetIMController(napi_env env,napi_callback_info cbInfo,bool needThrowException)261 napi_value JsGetInputMethodController::GetIMController(napi_env env, napi_callback_info cbInfo, bool needThrowException)
262 {
263 napi_value instance = nullptr;
264 napi_value cons = nullptr;
265 if (napi_get_reference_value(env, IMCRef_, &cons) != napi_ok) {
266 IMSA_HILOGE("failed to get reference value!");
267 if (needThrowException) {
268 JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_CONTROLLER, "", TYPE_OBJECT);
269 }
270 return nullptr;
271 }
272
273 if (napi_new_instance(env, cons, 0, nullptr, &instance) != napi_ok) {
274 IMSA_HILOGE("failed to create new instance!");
275 if (needThrowException) {
276 JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_CONTROLLER, "", TYPE_OBJECT);
277 }
278 return nullptr;
279 }
280 return instance;
281 }
282
GetInstance()283 std::shared_ptr<JsGetInputMethodController> JsGetInputMethodController::GetInstance()
284 {
285 if (controller_ == nullptr) {
286 std::lock_guard<std::mutex> lock(controllerMutex_);
287 if (controller_ == nullptr) {
288 auto controller = std::make_shared<JsGetInputMethodController>();
289 controller_ = controller;
290 InputMethodController::GetInstance()->SetControllerListener(controller_);
291 }
292 }
293 return controller_;
294 }
295
RegisterListener(napi_value callback,std::string type,std::shared_ptr<JSCallbackObject> callbackObj)296 void JsGetInputMethodController::RegisterListener(
297 napi_value callback, std::string type, std::shared_ptr<JSCallbackObject> callbackObj)
298 {
299 IMSA_HILOGD("start, type: %{public}s", type.c_str());
300 std::lock_guard<std::recursive_mutex> lock(mutex_);
301 if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) {
302 IMSA_HILOGD("methodName: %{public}s is not registered!", type.c_str());
303 }
304
305 auto callbacks = jsCbMap_[type];
306 bool ret = std::any_of(callbacks.begin(), callbacks.end(), [&callback](std::shared_ptr<JSCallbackObject> cb) {
307 return JsUtils::Equals(cb->env_, callback, cb->callback_, cb->threadId_);
308 });
309 if (ret) {
310 IMSA_HILOGD("callback already registered.");
311 return;
312 }
313
314 IMSA_HILOGI("add %{public}s callbackObj into jsCbMap_.", type.c_str());
315 jsCbMap_[type].push_back(std::move(callbackObj));
316 }
317
UnRegisterListener(napi_value callback,std::string type)318 void JsGetInputMethodController::UnRegisterListener(napi_value callback, std::string type)
319 {
320 IMSA_HILOGI("unregister listener: %{public}s.", type.c_str());
321 std::lock_guard<std::recursive_mutex> lock(mutex_);
322 if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) {
323 IMSA_HILOGE("methodName: %{public}s already unRegistered!", type.c_str());
324 return;
325 }
326 if (callback == nullptr) {
327 jsCbMap_.erase(type);
328 IMSA_HILOGE("callback is nullptr!");
329 return;
330 }
331
332 for (auto item = jsCbMap_[type].begin(); item != jsCbMap_[type].end(); item++) {
333 if ((JsUtils::Equals((*item)->env_, callback, (*item)->callback_, (*item)->threadId_))) {
334 jsCbMap_[type].erase(item);
335 break;
336 }
337 }
338 if (jsCbMap_[type].empty()) {
339 jsCbMap_.erase(type);
340 }
341 }
342
Subscribe(napi_env env,napi_callback_info info)343 napi_value JsGetInputMethodController::Subscribe(napi_env env, napi_callback_info info)
344 {
345 size_t argc = ARGC_TWO;
346 napi_value argv[ARGC_TWO] = { nullptr };
347 napi_value thisVar = nullptr;
348 void *data = nullptr;
349 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
350 std::string type;
351 // 2 means least param num.
352 PARAM_CHECK_RETURN(env, argc >= 2, "at least two parameters is required!", TYPE_NONE, nullptr);
353 PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], type), "type must be string", TYPE_NONE, nullptr);
354 PARAM_CHECK_RETURN(env, EventChecker::IsValidEventType(EventSubscribeModule::INPUT_METHOD_CONTROLLER, type),
355 "type verification failed, review the instructions and fill in the fixed values!", TYPE_NONE, nullptr);
356 PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[1]) == napi_function, "callback", TYPE_FUNCTION, nullptr);
357 IMSA_HILOGD("subscribe type: %{public}s.", type.c_str());
358 if (TEXT_EVENT_TYPE.find(type) != TEXT_EVENT_TYPE.end()) {
359 if (!InputMethodController::GetInstance()->WasAttached()) {
360 JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_DETACHED, "need to be attached first", TYPE_NONE);
361 return nullptr;
362 }
363 }
364
365 auto engine = reinterpret_cast<JsGetInputMethodController *>(JsUtils::GetNativeSelf(env, info));
366 if (engine == nullptr) {
367 return nullptr;
368 }
369 std::shared_ptr<JSCallbackObject> callback =
370 std::make_shared<JSCallbackObject>(env, argv[ARGC_ONE], std::this_thread::get_id());
371 engine->RegisterListener(argv[ARGC_ONE], type, callback);
372
373 napi_value result = nullptr;
374 napi_get_null(env, &result);
375 return result;
376 }
377
UnSubscribe(napi_env env,napi_callback_info info)378 napi_value JsGetInputMethodController::UnSubscribe(napi_env env, napi_callback_info info)
379 {
380 size_t argc = ARGC_TWO;
381 napi_value argv[ARGC_TWO] = { nullptr };
382 napi_value thisVar = nullptr;
383 void *data = nullptr;
384 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
385 std::string type;
386 // 1 means least param num.
387 if (argc < 1 || !JsUtil::GetValue(env, argv[0], type) ||
388 !EventChecker::IsValidEventType(EventSubscribeModule::INPUT_METHOD_CONTROLLER, type)) {
389 IMSA_HILOGE("unsubscribe failed, type: %{public}s!", type.c_str());
390 return nullptr;
391 }
392
393 // if the second param is not napi_function/napi_null/napi_undefined, return
394 auto paramType = JsUtil::GetType(env, argv[1]);
395 if (paramType != napi_function && paramType != napi_null && paramType != napi_undefined) {
396 return nullptr;
397 }
398 // if the second param is napi_function, delete it, else delete all
399 argv[1] = paramType == napi_function ? argv[1] : nullptr;
400
401 IMSA_HILOGD("unsubscribe type: %{public}s.", type.c_str());
402 auto engine = reinterpret_cast<JsGetInputMethodController *>(JsUtils::GetNativeSelf(env, info));
403 if (engine == nullptr) {
404 return nullptr;
405 }
406 engine->UnRegisterListener(argv[1], type);
407
408 napi_value result = nullptr;
409 napi_get_null(env, &result);
410 return result;
411 }
412
CreateSelectRange(napi_env env,int32_t start,int32_t end)413 napi_value JsGetInputMethodController::CreateSelectRange(napi_env env, int32_t start, int32_t end)
414 {
415 napi_value range = nullptr;
416 napi_create_object(env, &range);
417
418 napi_value value = nullptr;
419 napi_create_int32(env, start, &value);
420 napi_set_named_property(env, range, "start", value);
421
422 napi_create_int32(env, end, &value);
423 napi_set_named_property(env, range, "end", value);
424
425 return range;
426 }
427
CreateSelectMovement(napi_env env,int32_t direction)428 napi_value JsGetInputMethodController::CreateSelectMovement(napi_env env, int32_t direction)
429 {
430 napi_value movement = nullptr;
431 napi_create_object(env, &movement);
432
433 napi_value value = nullptr;
434 napi_create_int32(env, direction, &value);
435 napi_set_named_property(env, movement, "direction", value);
436
437 return movement;
438 }
439
HandleSoftKeyboard(napi_env env,napi_callback_info info,std::function<int32_t ()> callback,bool isOutput,bool needThrowException)440 napi_value JsGetInputMethodController::HandleSoftKeyboard(
441 napi_env env, napi_callback_info info, std::function<int32_t()> callback, bool isOutput, bool needThrowException)
442 {
443 auto ctxt = std::make_shared<HandleContext>();
444 auto input = [ctxt](
445 napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { return napi_ok; };
446 auto output = [ctxt, isOutput](napi_env env, napi_value *result) -> napi_status {
447 if (!isOutput) {
448 return napi_ok;
449 }
450 napi_status status = napi_get_boolean(env, ctxt->isHandle, result);
451 IMSA_HILOGE("output get boolean != nullptr[%{public}d]", result != nullptr);
452 return status;
453 };
454 auto exec = [ctxt, callback, needThrowException](AsyncCall::Context *ctx) {
455 int errCode = callback();
456 if (errCode == ErrorCode::NO_ERROR) {
457 IMSA_HILOGI("exec success.");
458 ctxt->status = napi_ok;
459 ctxt->isHandle = true;
460 ctxt->SetState(ctxt->status);
461 return;
462 }
463 if (needThrowException) {
464 ctxt->SetErrorCode(errCode);
465 }
466 };
467 ctxt->SetAction(std::move(input), std::move(output));
468 // 1 means JsAPI has 1 param at most.
469 AsyncCall asyncCall(env, info, ctxt, 1);
470 return asyncCall.Call(env, exec, "handleSoftKeyboard");
471 }
472
GetValue(napi_env env,napi_value in,Range & out)473 bool JsGetInputMethodController::GetValue(napi_env env, napi_value in, Range &out)
474 {
475 auto ret = JsUtil::Object::ReadProperty(env, in, "start", out.start);
476 return ret && JsUtil::Object::ReadProperty(env, in, "end", out.end);
477 }
478
479 /**
480 * let textConfig: TextConfig = {
481 * inputAttribute: InputAttribute = {
482 * textInputType: TextInputType = TextInputType.TEXT,
483 * enterKeyType: EnterKeyType = EnterKeyType.NONE
484 * },
485 * cursorInfo?: CursorInfo = {
486 * left: number,
487 * top: number,
488 * width: number,
489 * height: number,
490 * },
491 * selection?: Range = {
492 * start: number,
493 * end: number
494 * },
495 * windowId?: number
496 * }
497 */
GetValue(napi_env env,napi_value in,TextConfig & out)498 bool JsGetInputMethodController::GetValue(napi_env env, napi_value in, TextConfig &out)
499 {
500 napi_value attributeResult = nullptr;
501 napi_status status = JsUtils::GetValue(env, in, "inputAttribute", attributeResult);
502 CHECK_RETURN(status == napi_ok, "inputAttribute must be InputAttribute!", false);
503 bool ret = JsGetInputMethodController::GetValue(env, attributeResult, out.inputAttribute);
504 CHECK_RETURN(ret, "inputAttribute of TextConfig must be valid!", ret);
505
506 napi_value cursorInfoResult = nullptr;
507 status = JsUtils::GetValue(env, in, "cursorInfo", cursorInfoResult);
508 bool result = false;
509 if (status == napi_ok) {
510 result = JsGetInputMethodController::GetValue(env, cursorInfoResult, out.cursorInfo);
511 IMSA_HILOGE("get cursorInfo end, ret: %{public}d", result);
512 }
513
514 napi_value rangeResult = nullptr;
515 status = JsUtils::GetValue(env, in, "selection", rangeResult);
516 if (status == napi_ok) {
517 result = JsGetInputMethodController::GetValue(env, rangeResult, out.range);
518 IMSA_HILOGE("get selectionRange end, ret: %{public}d", result);
519 }
520
521 result = JsUtil::Object::ReadProperty(env, in, "windowId", out.windowId);
522 IMSA_HILOGE("get windowId end, ret: %{public}d", result);
523 return ret;
524 }
525
Attach(napi_env env,napi_callback_info info)526 napi_value JsGetInputMethodController::Attach(napi_env env, napi_callback_info info)
527 {
528 InputMethodSyncTrace tracer("JsGetInputMethodController_Attach");
529 auto ctxt = std::make_shared<AttachContext>();
530 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
531 PARAM_CHECK_RETURN(env, argc > 1, "at least two parameters is required!", TYPE_NONE, napi_generic_failure);
532 PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], ctxt->showKeyboard),
533 "showKeyboard covert failed, type must be boolean!", TYPE_NONE, napi_generic_failure);
534 PARAM_CHECK_RETURN(env, JsGetInputMethodController::GetValue(env, argv[1], ctxt->textConfig),
535 "textConfig covert failed, type must be TextConfig!", TYPE_NONE, napi_generic_failure);
536 return napi_ok;
537 };
538 auto exec = [ctxt, env](AsyncCall::Context *ctx) {
539 ctxt->textListener = JsGetInputMethodTextChangedListener::GetInstance();
540 auto status =
541 InputMethodController::GetInstance()->Attach(ctxt->textListener, ctxt->showKeyboard, ctxt->textConfig);
542 ctxt->SetErrorCode(status);
543 CHECK_RETURN_VOID(status == ErrorCode::NO_ERROR, "attach return error!");
544 ctxt->SetState(napi_ok);
545 };
546 ctxt->SetAction(std::move(input));
547 // 3 means JsAPI:attach has 3 params at most.
548 AsyncCall asyncCall(env, info, ctxt, 3);
549 return asyncCall.Call(env, exec, "attach");
550 }
551
Detach(napi_env env,napi_callback_info info)552 napi_value JsGetInputMethodController::Detach(napi_env env, napi_callback_info info)
553 {
554 return HandleSoftKeyboard(
555 env, info, [] { return InputMethodController::GetInstance()->Close(); }, false, true);
556 }
557
ShowTextInput(napi_env env,napi_callback_info info)558 napi_value JsGetInputMethodController::ShowTextInput(napi_env env, napi_callback_info info)
559 {
560 InputMethodSyncTrace tracer("JsGetInputMethodController_ShowTextInput");
561 return HandleSoftKeyboard(
562 env, info, [] { return InputMethodController::GetInstance()->ShowTextInput(); }, false, true);
563 }
564
HideTextInput(napi_env env,napi_callback_info info)565 napi_value JsGetInputMethodController::HideTextInput(napi_env env, napi_callback_info info)
566 {
567 InputMethodSyncTrace tracer("JsGetInputMethodController_HideTextInput");
568 return HandleSoftKeyboard(
569 env, info, [] { return InputMethodController::GetInstance()->HideTextInput(); }, false, true);
570 }
571
SetCallingWindow(napi_env env,napi_callback_info info)572 napi_value JsGetInputMethodController::SetCallingWindow(napi_env env, napi_callback_info info)
573 {
574 auto ctxt = std::make_shared<SetCallingWindowContext>();
575 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
576 PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
577 // 0 means the first parameter: windowId
578 napi_status status = JsUtils::GetValue(env, argv[0], ctxt->windID);
579 PARAM_CHECK_RETURN(env, status == napi_ok, "windowId type must be number", TYPE_NONE, status);
580 return status;
581 };
582 auto exec = [ctxt](AsyncCall::Context *ctx) {
583 auto errcode = InputMethodController::GetInstance()->SetCallingWindow(ctxt->windID);
584 ctxt->SetErrorCode(errcode);
585 CHECK_RETURN_VOID(errcode == ErrorCode::NO_ERROR, "setCallingWindow return error!");
586 ctxt->SetState(napi_ok);
587 };
588 ctxt->SetAction(std::move(input));
589 // 2 means JsAPI:setCallingWindow has 2 params at most.
590 AsyncCall asyncCall(env, info, ctxt, 2);
591 return asyncCall.Call(env, exec, "setCallingWindow");
592 }
593
GetValue(napi_env env,napi_value in,CursorInfo & out)594 bool JsGetInputMethodController::GetValue(napi_env env, napi_value in, CursorInfo &out)
595 {
596 auto ret = JsUtil::Object::ReadProperty(env, in, "left", out.left);
597 ret = ret && JsUtil::Object::ReadProperty(env, in, "top", out.top);
598 ret = ret && JsUtil::Object::ReadProperty(env, in, "width", out.width);
599 return ret && JsUtil::Object::ReadProperty(env, in, "height", out.height);
600 }
601
UpdateCursor(napi_env env,napi_callback_info info)602 napi_value JsGetInputMethodController::UpdateCursor(napi_env env, napi_callback_info info)
603 {
604 auto ctxt = std::make_shared<UpdateCursorContext>();
605 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
606 PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
607 // 0 means the first parameter: cursorInfo
608 PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_object,
609 "cursorInfo type must be CursorInfo", TYPE_NONE, napi_generic_failure);
610 bool ret = JsGetInputMethodController::GetValue(env, argv[0], ctxt->cursorInfo);
611 PARAM_CHECK_RETURN(env, ret, "cursorInfo covert failed, must contain four numbers!", TYPE_NONE,
612 napi_generic_failure);
613 return napi_ok;
614 };
615 auto exec = [ctxt](AsyncCall::Context *ctx) {
616 auto errcode = InputMethodController::GetInstance()->OnCursorUpdate(ctxt->cursorInfo);
617 ctxt->SetErrorCode(errcode);
618 CHECK_RETURN_VOID(errcode == ErrorCode::NO_ERROR, "updateCursor return error!");
619 ctxt->SetState(napi_ok);
620 };
621 ctxt->SetAction(std::move(input));
622 // 2 means JsAPI:updateCursor has 2 params at most.
623 AsyncCall asyncCall(env, info, ctxt, 2);
624 return asyncCall.Call(env, exec, "updateCursor");
625 }
626
ChangeSelection(napi_env env,napi_callback_info info)627 napi_value JsGetInputMethodController::ChangeSelection(napi_env env, napi_callback_info info)
628 {
629 std::shared_ptr<ChangeSelectionContext> ctxt = std::make_shared<ChangeSelectionContext>();
630 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
631 PARAM_CHECK_RETURN(env, argc > 2, "at least three parameters is required!", TYPE_NONE, napi_generic_failure);
632 PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], ctxt->text), "text type must be string!",
633 TYPE_NONE, napi_generic_failure);
634 PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[1], ctxt->start), "start type must be number!",
635 TYPE_NONE, napi_generic_failure);
636 PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[2], ctxt->end), "end type must be number!", TYPE_NONE,
637 napi_generic_failure);
638 return napi_ok;
639 };
640 auto exec = [ctxt](AsyncCall::Context *ctx) {
641 auto errcode = InputMethodController::GetInstance()->OnSelectionChange(ctxt->text, ctxt->start, ctxt->end);
642 ctxt->SetErrorCode(errcode);
643 CHECK_RETURN_VOID(errcode == ErrorCode::NO_ERROR, "changeSelection return error!");
644 ctxt->SetState(napi_ok);
645 };
646 ctxt->SetAction(std::move(input));
647 // 4 means JsAPI:changeSelection has 4 params at most.
648 AsyncCall asyncCall(env, info, ctxt, 4);
649 return asyncCall.Call(env, exec, "changeSelection");
650 }
651
GetValue(napi_env env,napi_value in,InputAttribute & out)652 bool JsGetInputMethodController::GetValue(napi_env env, napi_value in, InputAttribute &out)
653 {
654 auto ret = JsUtil::Object::ReadProperty(env, in, "textInputType", out.inputPattern);
655 return ret && JsUtil::Object::ReadProperty(env, in, "enterKeyType", out.enterKeyType);
656 }
657
UpdateAttribute(napi_env env,napi_callback_info info)658 napi_value JsGetInputMethodController::UpdateAttribute(napi_env env, napi_callback_info info)
659 {
660 auto ctxt = std::make_shared<UpdateAttributeContext>();
661 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
662 PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
663 bool ret = JsGetInputMethodController::GetValue(env, argv[0], ctxt->attribute);
664 PARAM_CHECK_RETURN(env, ret, "attribute type must be InputAttribute!", TYPE_NONE, napi_generic_failure);
665 ctxt->configuration.SetTextInputType(static_cast<TextInputType>(ctxt->attribute.inputPattern));
666 ctxt->configuration.SetEnterKeyType(static_cast<EnterKeyType>(ctxt->attribute.enterKeyType));
667 return napi_ok;
668 };
669 auto exec = [ctxt](AsyncCall::Context *ctx) {
670 auto errcode = InputMethodController::GetInstance()->OnConfigurationChange(ctxt->configuration);
671 ctxt->SetErrorCode(errcode);
672 CHECK_RETURN_VOID(errcode == ErrorCode::NO_ERROR, "updateAttribute return error!");
673 ctxt->SetState(napi_ok);
674 };
675 ctxt->SetAction(std::move(input));
676 // 2 means JsAPI:updateAttribute has 2 params at most.
677 AsyncCall asyncCall(env, info, ctxt, 2);
678 return asyncCall.Call(env, exec, "updateAttribute");
679 }
680
ShowSoftKeyboard(napi_env env,napi_callback_info info)681 napi_value JsGetInputMethodController::ShowSoftKeyboard(napi_env env, napi_callback_info info)
682 {
683 InputMethodSyncTrace tracer("JsGetInputMethodController_ShowSoftKeyboard");
684 return HandleSoftKeyboard(
685 env, info, [] { return InputMethodController::GetInstance()->ShowSoftKeyboard(); }, false, true);
686 }
687
HideSoftKeyboard(napi_env env,napi_callback_info info)688 napi_value JsGetInputMethodController::HideSoftKeyboard(napi_env env, napi_callback_info info)
689 {
690 InputMethodSyncTrace tracer("JsGetInputMethodController_HideSoftKeyboard");
691 return HandleSoftKeyboard(
692 env, info, [] { return InputMethodController::GetInstance()->HideSoftKeyboard(); }, false, true);
693 }
694
StopInputSession(napi_env env,napi_callback_info info)695 napi_value JsGetInputMethodController::StopInputSession(napi_env env, napi_callback_info info)
696 {
697 return HandleSoftKeyboard(
698 env, info, [] { return InputMethodController::GetInstance()->StopInputSession(); }, true, true);
699 }
700
StopInput(napi_env env,napi_callback_info info)701 napi_value JsGetInputMethodController::StopInput(napi_env env, napi_callback_info info)
702 {
703 return HandleSoftKeyboard(
704 env, info, [] { return InputMethodController::GetInstance()->HideCurrentInput(); }, true, false);
705 }
706
OnSelectByRange(int32_t start,int32_t end)707 void JsGetInputMethodController::OnSelectByRange(int32_t start, int32_t end)
708 {
709 std::string type = "selectByRange";
710 auto entry = GetEntry("selectByRange", [start, end](UvEntry &entry) {
711 entry.start = start;
712 entry.end = end;
713 });
714 if (entry == nullptr) {
715 IMSA_HILOGD("entry is nullptr.");
716 return;
717 }
718 auto eventHandler = GetEventHandler();
719 if (eventHandler == nullptr) {
720 IMSA_HILOGE("eventHandler is nullptr!");
721 return;
722 }
723 auto task = [entry]() {
724 auto getProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
725 if (argc < ARGC_ONE) {
726 return false;
727 }
728 napi_value range = CreateSelectRange(env, entry->start, entry->end);
729 if (range == nullptr) {
730 IMSA_HILOGE("set select range failed!");
731 return false;
732 }
733 // 0 means the first param of callback.
734 args[0] = range;
735 return true;
736 };
737 // 1 means the callback has one param.
738 JsCallbackHandler::Traverse(entry->vecCopy, { 1, getProperty });
739 };
740 eventHandler->PostTask(task, type);
741 }
742
OnSelectByMovement(int32_t direction)743 void JsGetInputMethodController::OnSelectByMovement(int32_t direction)
744 {
745 std::string type = "selectByMovement";
746 auto entry = GetEntry(type, [direction](UvEntry &entry) { entry.direction = direction; });
747 if (entry == nullptr) {
748 IMSA_HILOGE("failed to get uv entry!");
749 return;
750 }
751 auto eventHandler = GetEventHandler();
752 if (eventHandler == nullptr) {
753 IMSA_HILOGE("eventHandler is nullptr!");
754 return;
755 }
756 IMSA_HILOGI("direction: %{public}d.", direction);
757 auto task = [entry]() {
758 auto getProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
759 if (argc < 1) {
760 return false;
761 }
762 napi_value movement = CreateSelectMovement(env, entry->direction);
763 if (movement == nullptr) {
764 IMSA_HILOGE("set select movement failed!");
765 return false;
766 }
767 // 0 means the first param of callback.
768 args[0] = movement;
769 return true;
770 };
771 // 1 means the callback has one param.
772 JsCallbackHandler::Traverse(entry->vecCopy, { 1, getProperty });
773 };
774 eventHandler->PostTask(task, type);
775 }
776
InsertText(const std::u16string & text)777 void JsGetInputMethodController::InsertText(const std::u16string &text)
778 {
779 std::string insertText = Str16ToStr8(text);
780 std::string type = "insertText";
781 auto entry = GetEntry(type, [&insertText](UvEntry &entry) { entry.text = insertText; });
782 if (entry == nullptr) {
783 IMSA_HILOGD("failed to get uv entry.");
784 return;
785 }
786 auto eventHandler = GetEventHandler();
787 if (eventHandler == nullptr) {
788 IMSA_HILOGE("eventHandler is nullptr!");
789 return;
790 }
791 IMSA_HILOGI("start.");
792 auto task = [entry]() {
793 auto getInsertTextProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
794 if (argc == ARGC_ZERO) {
795 IMSA_HILOGE("getInsertTextProperty the number of argc is invalid.");
796 return false;
797 }
798 // 0 means the first param of callback.
799 napi_create_string_utf8(env, entry->text.c_str(), NAPI_AUTO_LENGTH, &args[0]);
800 return true;
801 };
802 // 1 means the callback has one param.
803 JsCallbackHandler::Traverse(entry->vecCopy, { 1, getInsertTextProperty });
804 };
805 eventHandler->PostTask(task, type);
806 }
807
DeleteRight(int32_t length)808 void JsGetInputMethodController::DeleteRight(int32_t length)
809 {
810 std::string type = "deleteRight";
811 auto entry = GetEntry(type, [&length](UvEntry &entry) { entry.length = length; });
812 if (entry == nullptr) {
813 IMSA_HILOGD("failed to get uv entry.");
814 return;
815 }
816 auto eventHandler = GetEventHandler();
817 if (eventHandler == nullptr) {
818 IMSA_HILOGE("eventHandler is nullptr!");
819 return;
820 }
821 IMSA_HILOGI("length: %{public}d", length);
822
823 auto task = [entry]() {
824 auto getDeleteForwardProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
825 if (argc == ARGC_ZERO) {
826 IMSA_HILOGE("getDeleteForwardProperty the number of argc is invalid.");
827 return false;
828 }
829 // 0 means the first param of callback.
830 napi_create_int32(env, entry->length, &args[0]);
831 return true;
832 };
833 // 1 means the callback has one param.
834 JsCallbackHandler::Traverse(entry->vecCopy, { 1, getDeleteForwardProperty });
835 };
836 eventHandler->PostTask(task, type);
837 }
838
DeleteLeft(int32_t length)839 void JsGetInputMethodController::DeleteLeft(int32_t length)
840 {
841 std::string type = "deleteLeft";
842 auto entry = GetEntry(type, [&length](UvEntry &entry) { entry.length = length; });
843 if (entry == nullptr) {
844 IMSA_HILOGD("failed to get uv entry.");
845 return;
846 }
847 auto eventHandler = GetEventHandler();
848 if (eventHandler == nullptr) {
849 IMSA_HILOGE("eventHandler is nullptr!");
850 return;
851 }
852 IMSA_HILOGI("length: %{public}d", length);
853 auto task = [entry]() {
854 auto getDeleteBackwardProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
855 if (argc == ARGC_ZERO) {
856 IMSA_HILOGE("getDeleteBackwardProperty the number of argc is invalid.");
857 return false;
858 }
859 // 0 means the first param of callback.
860 napi_create_int32(env, entry->length, &args[0]);
861 return true;
862 };
863 // 1 means the callback has one param.
864 JsCallbackHandler::Traverse(entry->vecCopy, { 1, getDeleteBackwardProperty });
865 };
866 eventHandler->PostTask(task, type);
867 }
868
SendKeyboardStatus(const KeyboardStatus & status)869 void JsGetInputMethodController::SendKeyboardStatus(const KeyboardStatus &status)
870 {
871 std::string type = "sendKeyboardStatus";
872 auto entry = GetEntry(type, [&status](UvEntry &entry) { entry.keyboardStatus = static_cast<int32_t>(status); });
873 if (entry == nullptr) {
874 IMSA_HILOGD("failed to get uv entry.");
875 return;
876 }
877 auto eventHandler = GetEventHandler();
878 if (eventHandler == nullptr) {
879 IMSA_HILOGE("eventHandler is nullptr!");
880 return;
881 }
882 IMSA_HILOGI("status: %{public}d", static_cast<int32_t>(status));
883 auto task = [entry]() {
884 auto getSendKeyboardStatusProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
885 if (argc == ARGC_ZERO) {
886 IMSA_HILOGE("getSendKeyboardStatusProperty the number of argc is invalid.");
887 return false;
888 }
889 // 0 means the first param of callback.
890 napi_create_int32(env, entry->keyboardStatus, &args[0]);
891 return true;
892 };
893 // 1 means the callback has one param.
894 JsCallbackHandler::Traverse(entry->vecCopy, { 1, getSendKeyboardStatusProperty });
895 };
896 eventHandler->PostTask(task, type);
897 }
898
CreateSendFunctionKey(napi_env env,int32_t functionKey)899 napi_value JsGetInputMethodController::CreateSendFunctionKey(napi_env env, int32_t functionKey)
900 {
901 napi_value functionkey = nullptr;
902 napi_create_object(env, &functionkey);
903
904 napi_value value = nullptr;
905 napi_create_int32(env, functionKey, &value);
906 napi_set_named_property(env, functionkey, "enterKeyType", value);
907
908 return functionkey;
909 }
910
SendFunctionKey(const FunctionKey & functionKey)911 void JsGetInputMethodController::SendFunctionKey(const FunctionKey &functionKey)
912 {
913 std::string type = "sendFunctionKey";
914 auto entry = GetEntry(type,
915 [&functionKey](UvEntry &entry) { entry.enterKeyType = static_cast<int32_t>(functionKey.GetEnterKeyType()); });
916 if (entry == nullptr) {
917 IMSA_HILOGD("failed to get uv entry.");
918 return;
919 }
920 auto eventHandler = GetEventHandler();
921 if (eventHandler == nullptr) {
922 IMSA_HILOGE("eventHandler is nullptr!");
923 return;
924 }
925 IMSA_HILOGI("functionKey: %{public}d", static_cast<int32_t>(functionKey.GetEnterKeyType()));
926 auto task = [entry]() {
927 auto getSendFunctionKeyProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
928 if (argc == ARGC_ZERO) {
929 IMSA_HILOGE("getSendFunctionKeyProperty the number of argc is invalid.");
930 return false;
931 }
932 napi_value functionKey = CreateSendFunctionKey(env, entry->enterKeyType);
933 if (functionKey == nullptr) {
934 IMSA_HILOGE("set select movement failed");
935 return false;
936 }
937 // 0 means the first param of callback.
938 args[0] = functionKey;
939 return true;
940 };
941 // 1 means the callback has one param.
942 JsCallbackHandler::Traverse(entry->vecCopy, { 1, getSendFunctionKeyProperty });
943 };
944 eventHandler->PostTask(task, type);
945 }
946
MoveCursor(const Direction direction)947 void JsGetInputMethodController::MoveCursor(const Direction direction)
948 {
949 std::string type = "moveCursor";
950 auto entry = GetEntry(type, [&direction](UvEntry &entry) { entry.direction = static_cast<int32_t>(direction); });
951 if (entry == nullptr) {
952 IMSA_HILOGD("failed to get uv entry.");
953 return;
954 }
955 auto eventHandler = GetEventHandler();
956 if (eventHandler == nullptr) {
957 IMSA_HILOGE("eventHandler is nullptr!");
958 return;
959 }
960 IMSA_HILOGI("direction: %{public}d", static_cast<int32_t>(direction));
961 auto task = [entry]() {
962 auto getMoveCursorProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
963 if (argc == ARGC_ZERO) {
964 IMSA_HILOGE("getMoveCursorProperty the number of argc is invalid.");
965 return false;
966 }
967 // 0 means the first param of callback.
968 napi_create_int32(env, static_cast<int32_t>(entry->direction), &args[0]);
969 return true;
970 };
971 // 1 means the callback has one param.
972 JsCallbackHandler::Traverse(entry->vecCopy, { 1, getMoveCursorProperty });
973 };
974 eventHandler->PostTask(task, type);
975 }
976
HandleExtendAction(int32_t action)977 void JsGetInputMethodController::HandleExtendAction(int32_t action)
978 {
979 std::string type = "handleExtendAction";
980 auto entry = GetEntry(type, [&action](UvEntry &entry) { entry.action = action; });
981 if (entry == nullptr) {
982 IMSA_HILOGD("failed to get uv entry.");
983 return;
984 }
985 auto eventHandler = GetEventHandler();
986 if (eventHandler == nullptr) {
987 IMSA_HILOGE("eventHandler is nullptr!");
988 return;
989 }
990 IMSA_HILOGI("action: %{public}d", action);
991 auto task = [entry]() {
992 auto getHandleExtendActionProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
993 if (argc == ARGC_ZERO) {
994 IMSA_HILOGE("getHandleExtendActionProperty the number of argc is invalid.");
995 return false;
996 }
997 // 0 means the first param of callback.
998 napi_create_int32(env, entry->action, &args[0]);
999 return true;
1000 };
1001 // 1 means the callback has one param.
1002 JsCallbackHandler::Traverse(entry->vecCopy, { 1, getHandleExtendActionProperty });
1003 };
1004 eventHandler->PostTask(task, type);
1005 }
1006
GetText(const std::string & type,int32_t number)1007 std::u16string JsGetInputMethodController::GetText(const std::string &type, int32_t number)
1008 {
1009 auto textResultHandler = std::make_shared<BlockData<std::string>>(MAX_TIMEOUT, "");
1010 auto entry = GetEntry(type, [&number, textResultHandler](UvEntry &entry) {
1011 entry.number = number;
1012 entry.textResultHandler = textResultHandler;
1013 });
1014 if (entry == nullptr) {
1015 IMSA_HILOGE("failed to get uv entry.");
1016 return u"";
1017 }
1018 auto eventHandler = GetEventHandler();
1019 if (eventHandler == nullptr) {
1020 IMSA_HILOGE("eventHandler is nullptr!");
1021 return u"";
1022 }
1023 IMSA_HILOGI("type: %{public}s, number: %{public}d.", type.c_str(), number);
1024 auto task = [entry]() {
1025 auto fillArguments = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
1026 if (argc < 1) {
1027 IMSA_HILOGE("argc is err.");
1028 return false;
1029 }
1030 // 0 means the first param of callback.
1031 napi_create_int32(env, entry->number, &args[0]);
1032 return true;
1033 };
1034 std::string text;
1035 // 1 means callback has one param.
1036 JsCallbackHandler::Traverse(entry->vecCopy, { 1, fillArguments }, text);
1037 entry->textResultHandler->SetValue(text);
1038 };
1039 eventHandler->PostTask(task, type);
1040 return Str8ToStr16(textResultHandler->GetValue());
1041 }
1042
GetTextIndexAtCursor()1043 int32_t JsGetInputMethodController::GetTextIndexAtCursor()
1044 {
1045 std::string type = "getTextIndexAtCursor";
1046 auto indexResultHandler = std::make_shared<BlockData<int32_t>>(MAX_TIMEOUT, -1);
1047 auto entry =
1048 GetEntry(type, [indexResultHandler](UvEntry &entry) { entry.indexResultHandler = indexResultHandler; });
1049 if (entry == nullptr) {
1050 IMSA_HILOGE("failed to get uv entry!");
1051 return -1;
1052 }
1053 auto eventHandler = GetEventHandler();
1054 if (eventHandler == nullptr) {
1055 IMSA_HILOGE("eventHandler is nullptr!");
1056 return -1;
1057 }
1058 IMSA_HILOGI("run in");
1059 auto task = [entry]() {
1060 int32_t index = -1;
1061 // 0 means callback has no params.
1062 JsCallbackHandler::Traverse(entry->vecCopy, { 0, nullptr }, index);
1063 entry->indexResultHandler->SetValue(index);
1064 };
1065 eventHandler->PostTask(task, type);
1066 return indexResultHandler->GetValue();
1067 }
1068
GetEventHandler()1069 std::shared_ptr<AppExecFwk::EventHandler> JsGetInputMethodController::GetEventHandler()
1070 {
1071 std::lock_guard<std::mutex> lock(eventHandlerMutex_);
1072 return handler_;
1073 }
1074
GetEntry(const std::string & type,EntrySetter entrySetter)1075 std::shared_ptr<JsGetInputMethodController::UvEntry> JsGetInputMethodController::GetEntry(
1076 const std::string &type, EntrySetter entrySetter)
1077 {
1078 IMSA_HILOGD("type: %{public}s", type.c_str());
1079 std::shared_ptr<UvEntry> entry = nullptr;
1080 {
1081 std::lock_guard<std::recursive_mutex> lock(mutex_);
1082 if (jsCbMap_[type].empty()) {
1083 IMSA_HILOGD("%{public}s cb-vector is empty.", type.c_str());
1084 return nullptr;
1085 }
1086 entry = std::make_shared<UvEntry>(jsCbMap_[type], type);
1087 }
1088 if (entrySetter != nullptr) {
1089 entrySetter(*entry);
1090 }
1091 return entry;
1092 }
1093 } // namespace MiscServices
1094 } // namespace OHOS