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