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