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