• 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 "inputmethod_trace.h"
21 #include "input_method_controller.h"
22 #include "input_method_utils.h"
23 #include "js_callback_handler.h"
24 #include "js_get_input_method_textchange_listener.h"
25 #include "js_util.h"
26 #include "napi/native_api.h"
27 #include "napi/native_node_api.h"
28 #include "string_ex.h"
29 
30 namespace OHOS {
31 namespace MiscServices {
32 constexpr size_t ARGC_ZERO = 0;
33 constexpr size_t ARGC_ONE = 1;
34 constexpr size_t ARGC_TWO = 2;
35 constexpr int32_t MAX_WAIT_TIME_MESSAGE_HANDLER = 2000;
36 const std::set<std::string> EVENT_TYPE{
37     "selectByRange",
38     "selectByMovement",
39 };
40 const std::set<std::string> JsGetInputMethodController::TEXT_EVENT_TYPE{
41     "insertText",
42     "deleteLeft",
43     "deleteRight",
44     "sendKeyboardStatus",
45     "sendFunctionKey",
46     "moveCursor",
47     "handleExtendAction",
48     "getLeftTextOfCursor",
49     "getRightTextOfCursor",
50     "getTextIndexAtCursor",
51 };
52 thread_local napi_ref JsGetInputMethodController::IMCRef_ = nullptr;
53 const std::string JsGetInputMethodController::IMC_CLASS_NAME = "InputMethodController";
54 std::mutex JsGetInputMethodController::controllerMutex_;
55 std::shared_ptr<JsGetInputMethodController> JsGetInputMethodController::controller_{ nullptr };
56 std::mutex JsGetInputMethodController::eventHandlerMutex_;
57 std::shared_ptr<AppExecFwk::EventHandler> JsGetInputMethodController::handler_{ nullptr };
58 BlockQueue<MessageHandlerInfo> JsGetInputMethodController::messageHandlerQueue_{ MAX_WAIT_TIME_MESSAGE_HANDLER };
Init(napi_env env,napi_value info)59 napi_value JsGetInputMethodController::Init(napi_env env, napi_value info)
60 {
61     napi_property_descriptor descriptor[] = {
62         DECLARE_NAPI_FUNCTION("getInputMethodController", GetInputMethodController),
63         DECLARE_NAPI_FUNCTION("getController", GetController),
64         DECLARE_NAPI_STATIC_PROPERTY("KeyboardStatus", GetJsKeyboardStatusProperty(env)),
65         DECLARE_NAPI_STATIC_PROPERTY("EnterKeyType", GetJsEnterKeyTypeProperty(env)),
66         DECLARE_NAPI_STATIC_PROPERTY("TextInputType", GetJsTextInputTypeProperty(env)),
67         DECLARE_NAPI_STATIC_PROPERTY("Direction", GetJsDirectionProperty(env)),
68         DECLARE_NAPI_STATIC_PROPERTY("ExtendAction", GetJsExtendActionProperty(env)),
69         DECLARE_NAPI_STATIC_PROPERTY("EnabledState", GetJsEnabledStateProperty(env)),
70         DECLARE_NAPI_STATIC_PROPERTY("RequestKeyboardReason", GetJsRequestKeyboardReasonProperty(env))
71     };
72     NAPI_CALL(env,
73         napi_define_properties(env, info, sizeof(descriptor) / sizeof(napi_property_descriptor), descriptor));
74 
75     napi_property_descriptor properties[] = {
76         DECLARE_NAPI_FUNCTION("attach", Attach),
77         DECLARE_NAPI_FUNCTION("detach", Detach),
78         DECLARE_NAPI_FUNCTION("showTextInput", ShowTextInput),
79         DECLARE_NAPI_FUNCTION("hideTextInput", HideTextInput),
80         DECLARE_NAPI_FUNCTION("setCallingWindow", SetCallingWindow),
81         DECLARE_NAPI_FUNCTION("updateCursor", UpdateCursor),
82         DECLARE_NAPI_FUNCTION("changeSelection", ChangeSelection),
83         DECLARE_NAPI_FUNCTION("updateAttribute", UpdateAttribute),
84         DECLARE_NAPI_FUNCTION("stopInput", StopInput),
85         DECLARE_NAPI_FUNCTION("stopInputSession", StopInputSession),
86         DECLARE_NAPI_FUNCTION("hideSoftKeyboard", HideSoftKeyboard),
87         DECLARE_NAPI_FUNCTION("showSoftKeyboard", ShowSoftKeyboard),
88         DECLARE_NAPI_FUNCTION("on", Subscribe),
89         DECLARE_NAPI_FUNCTION("off", UnSubscribe),
90         DECLARE_NAPI_FUNCTION("sendMessage", SendMessage),
91         DECLARE_NAPI_FUNCTION("recvMessage", RecvMessage),
92     };
93     napi_value cons = nullptr;
94     NAPI_CALL(env, napi_define_class(env, IMC_CLASS_NAME.c_str(), IMC_CLASS_NAME.size(), JsConstructor, nullptr,
95                        sizeof(properties) / sizeof(napi_property_descriptor), properties, &cons));
96     NAPI_CALL(env, napi_create_reference(env, cons, 1, &IMCRef_));
97     NAPI_CALL(env, napi_set_named_property(env, info, IMC_CLASS_NAME.c_str(), cons));
98 
99     return info;
100 }
101 
GetJsKeyboardStatusProperty(napi_env env)102 napi_value JsGetInputMethodController::GetJsKeyboardStatusProperty(napi_env env)
103 {
104     napi_value keyboardStatus = nullptr;
105     napi_value statusNone = nullptr;
106     napi_value statusHide = nullptr;
107     napi_value statusShow = nullptr;
108     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(KeyboardStatus::NONE), &statusNone));
109     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(KeyboardStatus::HIDE), &statusHide));
110     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(KeyboardStatus::SHOW), &statusShow));
111     NAPI_CALL(env, napi_create_object(env, &keyboardStatus));
112     NAPI_CALL(env, napi_set_named_property(env, keyboardStatus, "NONE", statusNone));
113     NAPI_CALL(env, napi_set_named_property(env, keyboardStatus, "HIDE", statusHide));
114     NAPI_CALL(env, napi_set_named_property(env, keyboardStatus, "SHOW", statusShow));
115     return keyboardStatus;
116 }
117 
GetJsEnterKeyTypeProperty(napi_env env)118 napi_value JsGetInputMethodController::GetJsEnterKeyTypeProperty(napi_env env)
119 {
120     napi_value enterKeyType = nullptr;
121     napi_value typeUnspecified = nullptr;
122     napi_value typeNone = nullptr;
123     napi_value typeGo = nullptr;
124     napi_value typeSearch = nullptr;
125     napi_value typeSend = nullptr;
126     napi_value typeNext = nullptr;
127     napi_value typeDone = nullptr;
128     napi_value typePrevious = nullptr;
129     napi_value typeNewline = nullptr;
130     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnterKeyType::UNSPECIFIED), &typeUnspecified));
131     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnterKeyType::NONE), &typeNone));
132     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnterKeyType::GO), &typeGo));
133     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnterKeyType::SEARCH), &typeSearch));
134     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnterKeyType::SEND), &typeSend));
135     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnterKeyType::NEXT), &typeNext));
136     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnterKeyType::DONE), &typeDone));
137     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnterKeyType::PREVIOUS), &typePrevious));
138     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnterKeyType::NEW_LINE), &typeNewline));
139     NAPI_CALL(env, napi_create_object(env, &enterKeyType));
140     NAPI_CALL(env, napi_set_named_property(env, enterKeyType, "UNSPECIFIED", typeUnspecified));
141     NAPI_CALL(env, napi_set_named_property(env, enterKeyType, "NONE", typeNone));
142     NAPI_CALL(env, napi_set_named_property(env, enterKeyType, "GO", typeGo));
143     NAPI_CALL(env, napi_set_named_property(env, enterKeyType, "SEARCH", typeSearch));
144     NAPI_CALL(env, napi_set_named_property(env, enterKeyType, "SEND", typeSend));
145     NAPI_CALL(env, napi_set_named_property(env, enterKeyType, "NEXT", typeNext));
146     NAPI_CALL(env, napi_set_named_property(env, enterKeyType, "DONE", typeDone));
147     NAPI_CALL(env, napi_set_named_property(env, enterKeyType, "PREVIOUS", typePrevious));
148     NAPI_CALL(env, napi_set_named_property(env, enterKeyType, "NEWLINE", typeNewline));
149     return enterKeyType;
150 }
151 
GetJsTextInputTypeProperty(napi_env env)152 napi_value JsGetInputMethodController::GetJsTextInputTypeProperty(napi_env env)
153 {
154     napi_value textInputType = nullptr;
155     napi_value typeNone = nullptr;
156     napi_value typeText = nullptr;
157     napi_value typeMultiline = nullptr;
158     napi_value typeNumber = nullptr;
159     napi_value typePhone = nullptr;
160     napi_value typeDatatime = nullptr;
161     napi_value typeEmailAddress = nullptr;
162     napi_value typeUrl = nullptr;
163     napi_value typeVisiblePassword = nullptr;
164     napi_value typeNumberPassword = nullptr;
165     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(TextInputType::NONE), &typeNone));
166     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(TextInputType::TEXT), &typeText));
167     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(TextInputType::MULTILINE), &typeMultiline));
168     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(TextInputType::NUMBER), &typeNumber));
169     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(TextInputType::PHONE), &typePhone));
170     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(TextInputType::DATETIME), &typeDatatime));
171     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(TextInputType::EMAIL_ADDRESS), &typeEmailAddress));
172     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(TextInputType::URL), &typeUrl));
173     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(TextInputType::VISIBLE_PASSWORD), &typeVisiblePassword));
174     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(TextInputType::NUMBER_PASSWORD), &typeNumberPassword));
175     NAPI_CALL(env, napi_create_object(env, &textInputType));
176     NAPI_CALL(env, napi_set_named_property(env, textInputType, "NONE", typeNone));
177     NAPI_CALL(env, napi_set_named_property(env, textInputType, "TEXT", typeText));
178     NAPI_CALL(env, napi_set_named_property(env, textInputType, "MULTILINE", typeMultiline));
179     NAPI_CALL(env, napi_set_named_property(env, textInputType, "NUMBER", typeNumber));
180     NAPI_CALL(env, napi_set_named_property(env, textInputType, "PHONE", typePhone));
181     NAPI_CALL(env, napi_set_named_property(env, textInputType, "DATETIME", typeDatatime));
182     NAPI_CALL(env, napi_set_named_property(env, textInputType, "EMAIL_ADDRESS", typeEmailAddress));
183     NAPI_CALL(env, napi_set_named_property(env, textInputType, "URL", typeUrl));
184     NAPI_CALL(env, napi_set_named_property(env, textInputType, "VISIBLE_PASSWORD", typeVisiblePassword));
185     NAPI_CALL(env, napi_set_named_property(env, textInputType, "NUMBER_PASSWORD", typeNumberPassword));
186     return textInputType;
187 }
188 
GetJsDirectionProperty(napi_env env)189 napi_value JsGetInputMethodController::GetJsDirectionProperty(napi_env env)
190 {
191     napi_value direction = nullptr;
192     napi_value cursorUp = nullptr;
193     napi_value cursorDown = nullptr;
194     napi_value cursorLeft = nullptr;
195     napi_value cursorRight = nullptr;
196     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(Direction::UP), &cursorUp));
197     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(Direction::DOWN), &cursorDown));
198     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(Direction::LEFT), &cursorLeft));
199     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(Direction::RIGHT), &cursorRight));
200     NAPI_CALL(env, napi_create_object(env, &direction));
201     NAPI_CALL(env, napi_set_named_property(env, direction, "CURSOR_UP", cursorUp));
202     NAPI_CALL(env, napi_set_named_property(env, direction, "CURSOR_DOWN", cursorDown));
203     NAPI_CALL(env, napi_set_named_property(env, direction, "CURSOR_LEFT", cursorLeft));
204     NAPI_CALL(env, napi_set_named_property(env, direction, "CURSOR_RIGHT", cursorRight));
205     return direction;
206 }
207 
GetJsExtendActionProperty(napi_env env)208 napi_value JsGetInputMethodController::GetJsExtendActionProperty(napi_env env)
209 {
210     napi_value action = nullptr;
211     napi_value actionSelectAll = nullptr;
212     napi_value actionCut = nullptr;
213     napi_value actionCopy = nullptr;
214     napi_value actionPaste = nullptr;
215     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(ExtendAction::SELECT_ALL), &actionSelectAll));
216     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(ExtendAction::CUT), &actionCut));
217     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(ExtendAction::COPY), &actionCopy));
218     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(ExtendAction::PASTE), &actionPaste));
219     NAPI_CALL(env, napi_create_object(env, &action));
220     NAPI_CALL(env, napi_set_named_property(env, action, "SELECT_ALL", actionSelectAll));
221     NAPI_CALL(env, napi_set_named_property(env, action, "CUT", actionCut));
222     NAPI_CALL(env, napi_set_named_property(env, action, "COPY", actionCopy));
223     NAPI_CALL(env, napi_set_named_property(env, action, "PASTE", actionPaste));
224     return action;
225 }
226 
GetJsEnabledStateProperty(napi_env env)227 napi_value JsGetInputMethodController::GetJsEnabledStateProperty(napi_env env)
228 {
229     napi_value status = nullptr;
230     napi_value disabled = nullptr;
231     napi_value basicMode = nullptr;
232     napi_value fullExperience = nullptr;
233     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnabledStatus::DISABLED), &disabled));
234     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnabledStatus::BASIC_MODE), &basicMode));
235     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(EnabledStatus::FULL_EXPERIENCE_MODE), &fullExperience));
236     NAPI_CALL(env, napi_create_object(env, &status));
237     NAPI_CALL(env, napi_set_named_property(env, status, "DISABLED", disabled));
238     NAPI_CALL(env, napi_set_named_property(env, status, "BASIC_MODE", basicMode));
239     NAPI_CALL(env, napi_set_named_property(env, status, "FULL_EXPERIENCE_MODE", fullExperience));
240     return status;
241 }
242 
GetJsRequestKeyboardReasonProperty(napi_env env)243 napi_value JsGetInputMethodController::GetJsRequestKeyboardReasonProperty(napi_env env)
244 {
245     napi_value requestKeyboardReason = nullptr;
246     napi_value none = nullptr;
247     napi_value mouse = nullptr;
248     napi_value touch = nullptr;
249     napi_value other = nullptr;
250     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(RequestKeyboardReason::NONE), &none));
251     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(RequestKeyboardReason::MOUSE), &mouse));
252     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(RequestKeyboardReason::TOUCH), &touch));
253     NAPI_CALL(env, napi_create_int32(env, static_cast<int32_t>(RequestKeyboardReason::OTHER), &other));
254     NAPI_CALL(env, napi_create_object(env, &requestKeyboardReason));
255     NAPI_CALL(env, napi_set_named_property(env, requestKeyboardReason, "NONE", none));
256     NAPI_CALL(env, napi_set_named_property(env, requestKeyboardReason, "MOUSE", mouse));
257     NAPI_CALL(env, napi_set_named_property(env, requestKeyboardReason, "TOUCH", touch));
258     NAPI_CALL(env, napi_set_named_property(env, requestKeyboardReason, "OTHER", other));
259     return requestKeyboardReason;
260 }
261 
JsConstructor(napi_env env,napi_callback_info cbinfo)262 napi_value JsGetInputMethodController::JsConstructor(napi_env env, napi_callback_info cbinfo)
263 {
264     {
265         std::lock_guard<std::mutex> lock(eventHandlerMutex_);
266         handler_ = AppExecFwk::EventHandler::Current();
267     }
268     napi_value thisVar = nullptr;
269     NAPI_CALL(env, napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr));
270 
271     auto controllerObject = GetInstance();
272     if (controllerObject == nullptr) {
273         IMSA_HILOGE("controllerObject is nullptr!");
274         napi_value result = nullptr;
275         napi_get_null(env, &result);
276         return result;
277     }
278     napi_status status = napi_wrap(
279         env, thisVar, controllerObject.get(), [](napi_env env, void *data, void *hint) {}, nullptr, nullptr);
280     if (status != napi_ok) {
281         IMSA_HILOGE("failed to wrap: %{public}d!", status);
282         return nullptr;
283     }
284 
285     if (controllerObject->loop_ == nullptr) {
286         napi_get_uv_event_loop(env, &controllerObject->loop_);
287     }
288 
289     return thisVar;
290 }
291 
GetInputMethodController(napi_env env,napi_callback_info cbInfo)292 napi_value JsGetInputMethodController::GetInputMethodController(napi_env env, napi_callback_info cbInfo)
293 {
294     return GetIMController(env, cbInfo, false);
295 }
296 
GetController(napi_env env,napi_callback_info cbInfo)297 napi_value JsGetInputMethodController::GetController(napi_env env, napi_callback_info cbInfo)
298 {
299     return GetIMController(env, cbInfo, true);
300 }
301 
GetIMController(napi_env env,napi_callback_info cbInfo,bool needThrowException)302 napi_value JsGetInputMethodController::GetIMController(napi_env env, napi_callback_info cbInfo, bool needThrowException)
303 {
304     napi_value instance = nullptr;
305     napi_value cons = nullptr;
306     if (napi_get_reference_value(env, IMCRef_, &cons) != napi_ok) {
307         IMSA_HILOGE("failed to get reference value!");
308         if (needThrowException) {
309             JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_CONTROLLER, "", TYPE_OBJECT);
310         }
311         return nullptr;
312     }
313 
314     if (napi_new_instance(env, cons, 0, nullptr, &instance) != napi_ok) {
315         IMSA_HILOGE("failed to create new instance!");
316         if (needThrowException) {
317             JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_CONTROLLER, "", TYPE_OBJECT);
318         }
319         return nullptr;
320     }
321     return instance;
322 }
323 
GetInstance()324 std::shared_ptr<JsGetInputMethodController> JsGetInputMethodController::GetInstance()
325 {
326     if (controller_ == nullptr) {
327         std::lock_guard<std::mutex> lock(controllerMutex_);
328         if (controller_ == nullptr) {
329             auto controller = std::make_shared<JsGetInputMethodController>();
330             controller_ = controller;
331             InputMethodController::GetInstance()->SetControllerListener(controller_);
332         }
333     }
334     return controller_;
335 }
336 
RegisterListener(napi_value callback,std::string type,std::shared_ptr<JSCallbackObject> callbackObj)337 void JsGetInputMethodController::RegisterListener(napi_value callback, std::string type,
338     std::shared_ptr<JSCallbackObject> callbackObj)
339 {
340     IMSA_HILOGD("start, type: %{public}s", type.c_str());
341     std::lock_guard<std::recursive_mutex> lock(mutex_);
342     if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) {
343         IMSA_HILOGD("methodName: %{public}s is not registered!", type.c_str());
344     }
345 
346     auto callbacks = jsCbMap_[type];
347     bool ret = std::any_of(callbacks.begin(), callbacks.end(), [&callback](std::shared_ptr<JSCallbackObject> cb) {
348         return JsUtils::Equals(cb->env_, callback, cb->callback_, cb->threadId_);
349     });
350     if (ret) {
351         IMSA_HILOGD("callback already registered.");
352         return;
353     }
354 
355     IMSA_HILOGI("add %{public}s callbackObj into jsCbMap_.", type.c_str());
356     jsCbMap_[type].push_back(std::move(callbackObj));
357 }
358 
UnRegisterListener(napi_value callback,std::string type)359 void JsGetInputMethodController::UnRegisterListener(napi_value callback, std::string type)
360 {
361     IMSA_HILOGI("unregister listener: %{public}s.", type.c_str());
362     std::lock_guard<std::recursive_mutex> lock(mutex_);
363     if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) {
364         IMSA_HILOGE("methodName: %{public}s already unRegistered!", type.c_str());
365         return;
366     }
367     if (callback == nullptr) {
368         jsCbMap_.erase(type);
369         UpdateTextPreviewState(type);
370         IMSA_HILOGE("callback is nullptr!");
371         return;
372     }
373 
374     for (auto item = jsCbMap_[type].begin(); item != jsCbMap_[type].end(); item++) {
375         if ((JsUtils::Equals((*item)->env_, callback, (*item)->callback_, (*item)->threadId_))) {
376             jsCbMap_[type].erase(item);
377             break;
378         }
379     }
380     if (jsCbMap_[type].empty()) {
381         jsCbMap_.erase(type);
382         UpdateTextPreviewState(type);
383     }
384 }
385 
Subscribe(napi_env env,napi_callback_info info)386 napi_value JsGetInputMethodController::Subscribe(napi_env env, napi_callback_info info)
387 {
388     size_t argc = ARGC_TWO;
389     napi_value argv[ARGC_TWO] = { nullptr };
390     napi_value thisVar = nullptr;
391     void *data = nullptr;
392     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
393     std::string type;
394     // 2 means least param num.
395     PARAM_CHECK_RETURN(env, argc >= 2, "at least two parameters is required!", TYPE_NONE, nullptr);
396     PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], type), "type must be string", TYPE_NONE, nullptr);
397     PARAM_CHECK_RETURN(env, EventChecker::IsValidEventType(EventSubscribeModule::INPUT_METHOD_CONTROLLER, type),
398         "type verification failed, review the instructions and fill in the fixed values!", TYPE_NONE, nullptr);
399     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[1]) == napi_function, "callback", TYPE_FUNCTION, nullptr);
400     IMSA_HILOGD("subscribe type: %{public}s.", type.c_str());
401     if (TEXT_EVENT_TYPE.find(type) != TEXT_EVENT_TYPE.end()) {
402         if (!InputMethodController::GetInstance()->WasAttached()) {
403             JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_DETACHED, "need to be attached first", TYPE_NONE);
404             return nullptr;
405         }
406     }
407 
408     auto engine = reinterpret_cast<JsGetInputMethodController *>(JsUtils::GetNativeSelf(env, info));
409     if (engine == nullptr) {
410         return nullptr;
411     }
412     std::shared_ptr<JSCallbackObject> callback =
413         std::make_shared<JSCallbackObject>(env, argv[ARGC_ONE], std::this_thread::get_id(),
414             AppExecFwk::EventHandler::Current());
415     engine->RegisterListener(argv[ARGC_ONE], type, callback);
416 
417     napi_value result = nullptr;
418     napi_get_null(env, &result);
419     return result;
420 }
421 
UnSubscribe(napi_env env,napi_callback_info info)422 napi_value JsGetInputMethodController::UnSubscribe(napi_env env, napi_callback_info info)
423 {
424     size_t argc = ARGC_TWO;
425     napi_value argv[ARGC_TWO] = { nullptr };
426     napi_value thisVar = nullptr;
427     void *data = nullptr;
428     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
429     std::string type;
430     // 1 means least param num.
431     if (argc < 1 || !JsUtil::GetValue(env, argv[0], type) ||
432         !EventChecker::IsValidEventType(EventSubscribeModule::INPUT_METHOD_CONTROLLER, type)) {
433         IMSA_HILOGE("unsubscribe failed, type: %{public}s!", type.c_str());
434         return nullptr;
435     }
436 
437     // if the second param is not napi_function/napi_null/napi_undefined, return
438     auto paramType = JsUtil::GetType(env, argv[1]);
439     if (paramType != napi_function && paramType != napi_null && paramType != napi_undefined) {
440         return nullptr;
441     }
442     // if the second param is napi_function, delete it, else delete all
443     argv[1] = paramType == napi_function ? argv[1] : nullptr;
444 
445     IMSA_HILOGD("unsubscribe type: %{public}s.", type.c_str());
446     auto engine = reinterpret_cast<JsGetInputMethodController *>(JsUtils::GetNativeSelf(env, info));
447     if (engine == nullptr) {
448         return nullptr;
449     }
450     engine->UnRegisterListener(argv[1], type);
451 
452     napi_value result = nullptr;
453     napi_get_null(env, &result);
454     return result;
455 }
456 
CreateSelectRange(napi_env env,int32_t start,int32_t end)457 napi_value JsGetInputMethodController::CreateSelectRange(napi_env env, int32_t start, int32_t end)
458 {
459     napi_value range = nullptr;
460     napi_create_object(env, &range);
461 
462     napi_value value = nullptr;
463     napi_create_int32(env, start, &value);
464     napi_set_named_property(env, range, "start", value);
465 
466     napi_create_int32(env, end, &value);
467     napi_set_named_property(env, range, "end", value);
468 
469     return range;
470 }
471 
CreateSelectMovement(napi_env env,int32_t direction)472 napi_value JsGetInputMethodController::CreateSelectMovement(napi_env env, int32_t direction)
473 {
474     napi_value movement = nullptr;
475     napi_create_object(env, &movement);
476 
477     napi_value value = nullptr;
478     napi_create_int32(env, direction, &value);
479     napi_set_named_property(env, movement, "direction", value);
480 
481     return movement;
482 }
483 
HandleSoftKeyboard(napi_env env,napi_callback_info info,std::function<int32_t ()> callback,bool isOutput,bool needThrowException)484 napi_value JsGetInputMethodController::HandleSoftKeyboard(napi_env env, napi_callback_info info,
485     std::function<int32_t()> callback, bool isOutput, bool needThrowException)
486 {
487     auto ctxt = std::make_shared<HandleContext>();
488     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
489         return napi_ok;
490     };
491     auto output = [ctxt, isOutput](napi_env env, napi_value *result) -> napi_status {
492         if (!isOutput) {
493             return napi_ok;
494         }
495         napi_status status = napi_get_boolean(env, ctxt->isHandle, result);
496         IMSA_HILOGE("output get boolean != nullptr[%{public}d]", result != nullptr);
497         return status;
498     };
499     auto exec = [ctxt, callback, needThrowException](AsyncCall::Context *ctx) {
500         int errCode = callback();
501         if (errCode == ErrorCode::NO_ERROR) {
502             IMSA_HILOGI("exec success.");
503             ctxt->status = napi_ok;
504             ctxt->isHandle = true;
505             ctxt->SetState(ctxt->status);
506             return;
507         }
508         if (needThrowException) {
509             ctxt->SetErrorCode(errCode);
510         }
511     };
512     ctxt->SetAction(std::move(input), std::move(output));
513     // 1 means JsAPI has 1 param at most.
514     AsyncCall asyncCall(env, info, ctxt, 1);
515     return asyncCall.Call(env, exec, "handleSoftKeyboard");
516 }
517 
GetValue(napi_env env,napi_value in,Range & out)518 bool JsGetInputMethodController::GetValue(napi_env env, napi_value in, Range &out)
519 {
520     auto ret = JsUtil::Object::ReadProperty(env, in, "start", out.start);
521     return ret && JsUtil::Object::ReadProperty(env, in, "end", out.end);
522 }
523 
524 /**
525  * let textConfig: TextConfig = {
526  *   inputAttribute: InputAttribute = {
527  *     textInputType: TextInputType = TextInputType.TEXT,
528  *     enterKeyType: EnterKeyType = EnterKeyType.NONE
529  *   },
530  *   cursorInfo?: CursorInfo = {
531  *     left: number,
532  *     top: number,
533  *     width: number,
534  *     height: number,
535  *   },
536  *   selection?: Range = {
537  *     start: number,
538  *     end: number
539  *   },
540  *   windowId?: number
541  * }
542  */
GetValue(napi_env env,napi_value in,TextConfig & out)543 bool JsGetInputMethodController::GetValue(napi_env env, napi_value in, TextConfig &out)
544 {
545     napi_value attributeResult = nullptr;
546     napi_status status = JsUtils::GetValue(env, in, "inputAttribute", attributeResult);
547     CHECK_RETURN(status == napi_ok, "inputAttribute must be InputAttribute!", false);
548     bool ret = JsGetInputMethodController::GetValue(env, attributeResult, out.inputAttribute);
549     CHECK_RETURN(ret, "inputAttribute of TextConfig must be valid!", ret);
550 
551     napi_value cursorInfoResult = nullptr;
552     status = JsUtils::GetValue(env, in, "cursorInfo", cursorInfoResult);
553     bool result = false;
554     if (status == napi_ok) {
555         result = JsGetInputMethodController::GetValue(env, cursorInfoResult, out.cursorInfo);
556         if (!result) {
557             IMSA_HILOGE("get cursorInfo failed.");
558         }
559     }
560 
561     napi_value rangeResult = nullptr;
562     status = JsUtils::GetValue(env, in, "selection", rangeResult);
563     if (status == napi_ok) {
564         result = JsGetInputMethodController::GetValue(env, rangeResult, out.range);
565         if (!result) {
566             IMSA_HILOGE("get selectionRange failed.");
567         }
568     }
569 
570     result = JsUtil::Object::ReadProperty(env, in, "windowId", out.windowId);
571     if (!result) {
572         IMSA_HILOGE("get windowId failed.");
573     }
574     return ret;
575 }
576 
Attach(napi_env env,napi_callback_info info)577 napi_value JsGetInputMethodController::Attach(napi_env env, napi_callback_info info)
578 {
579     IMSA_HILOGI("run in.");
580     InputMethodSyncTrace tracer("JsGetInputMethodController_Attach");
581     auto ctxt = std::make_shared<AttachContext>();
582     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
583         PARAM_CHECK_RETURN(env, argc > 1, "at least two parameters is required!", TYPE_NONE, napi_generic_failure);
584         PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], ctxt->showKeyboard),
585             "showKeyboard covert failed, type must be boolean!", TYPE_NONE, napi_generic_failure);
586         PARAM_CHECK_RETURN(env, JsGetInputMethodController::GetValue(env, argv[1], ctxt->textConfig),
587             "textConfig covert failed, type must be TextConfig!", TYPE_NONE, napi_generic_failure);
588         if (JsGetInputMethodController::IsTextPreviewSupported()) {
589             ctxt->textConfig.inputAttribute.isTextPreviewSupported = true;
590         }
591         // requestKeyboardReason not must
592         if (argc > 2) {
593             napi_valuetype valueType = napi_undefined;
594             napi_typeof(env, argv[2], &valueType);
595             if (valueType != napi_function) {
596                 JsUtil::GetValue(env, argv[2], ctxt->requestKeyboardReason);
597             }
598         }
599         return napi_ok;
600     };
601     auto exec = [ctxt, env](AsyncCall::Context *ctx) {
602         ctxt->textListener = JsGetInputMethodTextChangedListener::GetInstance();
603         OHOS::MiscServices::AttachOptions attachOptions;
604         attachOptions.isShowKeyboard = ctxt->showKeyboard;
605         attachOptions.requestKeyboardReason =
606               static_cast<OHOS::MiscServices::RequestKeyboardReason>(ctxt->requestKeyboardReason);
607         auto status = InputMethodController::GetInstance()->Attach(
608             ctxt->textListener, attachOptions, ctxt->textConfig, ClientType::JS);
609         ctxt->SetErrorCode(status);
610         CHECK_RETURN_VOID(status == ErrorCode::NO_ERROR, "attach return error!");
611         ctxt->SetState(napi_ok);
612     };
613     ctxt->SetAction(std::move(input));
614     // 3 means JsAPI:attach has 3 params at most.
615     AsyncCall asyncCall(env, info, ctxt, 3);
616     return asyncCall.Call(env, exec, "attach");
617 }
618 
Detach(napi_env env,napi_callback_info info)619 napi_value JsGetInputMethodController::Detach(napi_env env, napi_callback_info info)
620 {
621     return HandleSoftKeyboard(
622         env, info, [] { return InputMethodController::GetInstance()->Close(); }, false, true);
623 }
624 
ShowTextInput(napi_env env,napi_callback_info info)625 napi_value JsGetInputMethodController::ShowTextInput(napi_env env, napi_callback_info info)
626 {
627     IMSA_HILOGI("run in.");
628     AttachOptions attachOptions;
629     JsGetInputMethodController::GetAttachOptionsValue(env, info, attachOptions);
630     InputMethodSyncTrace tracer("JsGetInputMethodController_ShowTextInput");
631     return HandleSoftKeyboard(
632         env, info,
633         [attachOptions] {
634             return InputMethodController::GetInstance()->ShowTextInput(attachOptions, ClientType::JS);
635         },
636         false, true);
637 }
638 
GetAttachOptionsValue(napi_env env,napi_callback_info cbinfo,AttachOptions & attachOptions)639 napi_value JsGetInputMethodController::GetAttachOptionsValue(
640     napi_env env, napi_callback_info cbinfo, AttachOptions &attachOptions)
641 {
642     napi_value result = nullptr;
643     NAPI_CALL(env, napi_create_int32(env, 0, &result));
644     size_t argc = ARGC_ONE;
645     napi_value argv[ARGC_ONE] = { nullptr };
646     napi_value thisVar = nullptr;
647     void *data = nullptr;
648     NAPI_CALL(env, napi_get_cb_info(env, cbinfo, &argc, argv, &thisVar, &data));
649     int32_t requestKeyboardReason = 0;
650     if (argc > 0) {
651         napi_valuetype valueType = napi_undefined;
652         napi_typeof(env, argv[0], &valueType);
653         if (valueType != napi_function) {
654             JsUtil::GetValue(env, argv[0], requestKeyboardReason);
655         }
656     }
657     IMSA_HILOGI("run in. requestKeyboardReason=%{public}d", requestKeyboardReason);
658     attachOptions.requestKeyboardReason = static_cast<OHOS::MiscServices::RequestKeyboardReason>(requestKeyboardReason);
659 
660     return result;
661 }
662 
HideTextInput(napi_env env,napi_callback_info info)663 napi_value JsGetInputMethodController::HideTextInput(napi_env env, napi_callback_info info)
664 {
665     InputMethodSyncTrace tracer("JsGetInputMethodController_HideTextInput");
666     return HandleSoftKeyboard(
667         env, info, [] { return InputMethodController::GetInstance()->HideTextInput(); }, false, true);
668 }
669 
SetCallingWindow(napi_env env,napi_callback_info info)670 napi_value JsGetInputMethodController::SetCallingWindow(napi_env env, napi_callback_info info)
671 {
672     auto ctxt = std::make_shared<SetCallingWindowContext>();
673     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
674         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
675         // 0 means the first parameter: windowId
676         napi_status status = JsUtils::GetValue(env, argv[0], ctxt->windID);
677         PARAM_CHECK_RETURN(env, status == napi_ok, "windowId type must be number", TYPE_NONE, status);
678         return status;
679     };
680     auto exec = [ctxt](AsyncCall::Context *ctx) {
681         auto errcode = InputMethodController::GetInstance()->SetCallingWindow(ctxt->windID);
682         ctxt->SetErrorCode(errcode);
683         CHECK_RETURN_VOID(errcode == ErrorCode::NO_ERROR, "setCallingWindow return error!");
684         ctxt->SetState(napi_ok);
685     };
686     ctxt->SetAction(std::move(input));
687     // 2 means JsAPI:setCallingWindow has 2 params at most.
688     AsyncCall asyncCall(env, info, ctxt, 2);
689     return asyncCall.Call(env, exec, "setCallingWindow");
690 }
691 
GetValue(napi_env env,napi_value in,CursorInfo & out)692 bool JsGetInputMethodController::GetValue(napi_env env, napi_value in, CursorInfo &out)
693 {
694     auto ret = JsUtil::Object::ReadProperty(env, in, "left", out.left);
695     ret = ret && JsUtil::Object::ReadProperty(env, in, "top", out.top);
696     ret = ret && JsUtil::Object::ReadProperty(env, in, "width", out.width);
697     return ret && JsUtil::Object::ReadProperty(env, in, "height", out.height);
698 }
699 
UpdateCursor(napi_env env,napi_callback_info info)700 napi_value JsGetInputMethodController::UpdateCursor(napi_env env, napi_callback_info info)
701 {
702     auto ctxt = std::make_shared<UpdateCursorContext>();
703     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
704         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
705         // 0 means the first parameter: cursorInfo
706         PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_object,
707             "cursorInfo type must be CursorInfo", TYPE_NONE, napi_generic_failure);
708         bool ret = JsGetInputMethodController::GetValue(env, argv[0], ctxt->cursorInfo);
709         PARAM_CHECK_RETURN(env, ret, "cursorInfo covert failed, must contain four numbers!", TYPE_NONE,
710             napi_generic_failure);
711         return napi_ok;
712     };
713     auto exec = [ctxt](AsyncCall::Context *ctx) {
714         auto errcode = InputMethodController::GetInstance()->OnCursorUpdate(ctxt->cursorInfo);
715         ctxt->SetErrorCode(errcode);
716         CHECK_RETURN_VOID(errcode == ErrorCode::NO_ERROR, "updateCursor return error!");
717         ctxt->SetState(napi_ok);
718     };
719     ctxt->SetAction(std::move(input));
720     // 2 means JsAPI:updateCursor has 2 params at most.
721     AsyncCall asyncCall(env, info, ctxt, 2);
722     return asyncCall.Call(env, exec, "updateCursor");
723 }
724 
ChangeSelection(napi_env env,napi_callback_info info)725 napi_value JsGetInputMethodController::ChangeSelection(napi_env env, napi_callback_info info)
726 {
727     std::shared_ptr<ChangeSelectionContext> ctxt = std::make_shared<ChangeSelectionContext>();
728     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
729         PARAM_CHECK_RETURN(env, argc > 2, "at least three parameters is required!", TYPE_NONE, napi_generic_failure);
730         PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], ctxt->text), "text type must be string!",
731             TYPE_NONE, napi_generic_failure);
732         PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[1], ctxt->start), "start type must be number!",
733             TYPE_NONE, napi_generic_failure);
734         PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[2], ctxt->end), "end type must be number!", TYPE_NONE,
735             napi_generic_failure);
736         return napi_ok;
737     };
738     auto exec = [ctxt](AsyncCall::Context *ctx) {
739         auto errcode = InputMethodController::GetInstance()->OnSelectionChange(ctxt->text, ctxt->start, ctxt->end);
740         ctxt->SetErrorCode(errcode);
741         CHECK_RETURN_VOID(errcode == ErrorCode::NO_ERROR, "changeSelection return error!");
742         ctxt->SetState(napi_ok);
743     };
744     ctxt->SetAction(std::move(input));
745     // 4 means JsAPI:changeSelection has 4 params at most.
746     AsyncCall asyncCall(env, info, ctxt, 4);
747     return asyncCall.Call(env, exec, "changeSelection");
748 }
749 
GetValue(napi_env env,napi_value in,InputAttribute & out)750 bool JsGetInputMethodController::GetValue(napi_env env, napi_value in, InputAttribute &out)
751 {
752     auto ret = JsUtil::Object::ReadProperty(env, in, "textInputType", out.inputPattern);
753     return ret && JsUtil::Object::ReadProperty(env, in, "enterKeyType", out.enterKeyType);
754 }
755 
UpdateAttribute(napi_env env,napi_callback_info info)756 napi_value JsGetInputMethodController::UpdateAttribute(napi_env env, napi_callback_info info)
757 {
758     auto ctxt = std::make_shared<UpdateAttributeContext>();
759     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
760         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
761         bool ret = JsGetInputMethodController::GetValue(env, argv[0], ctxt->attribute);
762         PARAM_CHECK_RETURN(env, ret, "attribute type must be InputAttribute!", TYPE_NONE, napi_generic_failure);
763         ctxt->configuration.SetTextInputType(static_cast<TextInputType>(ctxt->attribute.inputPattern));
764         ctxt->configuration.SetEnterKeyType(static_cast<EnterKeyType>(ctxt->attribute.enterKeyType));
765         return napi_ok;
766     };
767     auto exec = [ctxt](AsyncCall::Context *ctx) {
768         auto errcode = InputMethodController::GetInstance()->OnConfigurationChange(ctxt->configuration);
769         ctxt->SetErrorCode(errcode);
770         CHECK_RETURN_VOID(errcode == ErrorCode::NO_ERROR, "updateAttribute return error!");
771         ctxt->SetState(napi_ok);
772     };
773     ctxt->SetAction(std::move(input));
774     // 2 means JsAPI:updateAttribute has 2 params at most.
775     AsyncCall asyncCall(env, info, ctxt, 2);
776     return asyncCall.Call(env, exec, "updateAttribute");
777 }
778 
ShowSoftKeyboard(napi_env env,napi_callback_info info)779 napi_value JsGetInputMethodController::ShowSoftKeyboard(napi_env env, napi_callback_info info)
780 {
781     InputMethodSyncTrace tracer("JsGetInputMethodController_ShowSoftKeyboard");
782     return HandleSoftKeyboard(
783         env, info, [] { return InputMethodController::GetInstance()->ShowSoftKeyboard(ClientType::JS); }, false, true);
784 }
785 
HideSoftKeyboard(napi_env env,napi_callback_info info)786 napi_value JsGetInputMethodController::HideSoftKeyboard(napi_env env, napi_callback_info info)
787 {
788     InputMethodSyncTrace tracer("JsGetInputMethodController_HideSoftKeyboard");
789     return HandleSoftKeyboard(
790         env, info, [] { return InputMethodController::GetInstance()->HideSoftKeyboard(); }, false, true);
791 }
792 
StopInputSession(napi_env env,napi_callback_info info)793 napi_value JsGetInputMethodController::StopInputSession(napi_env env, napi_callback_info info)
794 {
795     return HandleSoftKeyboard(
796         env, info, [] { return InputMethodController::GetInstance()->StopInputSession(); }, true, true);
797 }
798 
StopInput(napi_env env,napi_callback_info info)799 napi_value JsGetInputMethodController::StopInput(napi_env env, napi_callback_info info)
800 {
801     return HandleSoftKeyboard(
802         env, info, [] { return InputMethodController::GetInstance()->HideCurrentInput(); }, true, false);
803 }
804 
OnSelectByRange(int32_t start,int32_t end)805 void JsGetInputMethodController::OnSelectByRange(int32_t start, int32_t end)
806 {
807     std::string type = "selectByRange";
808     auto entry = GetEntry("selectByRange", [start, end](UvEntry &entry) {
809         entry.start = start;
810         entry.end = end;
811     });
812     if (entry == nullptr) {
813         IMSA_HILOGD("entry is nullptr.");
814         return;
815     }
816     auto eventHandler = GetEventHandler();
817     if (eventHandler == nullptr) {
818         IMSA_HILOGE("eventHandler is nullptr!");
819         return;
820     }
821     auto task = [entry]() {
822         auto getProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
823             if (argc < ARGC_ONE) {
824                 return false;
825             }
826             napi_value range = CreateSelectRange(env, entry->start, entry->end);
827             if (range == nullptr) {
828                 IMSA_HILOGE("set select range failed!");
829                 return false;
830             }
831             // 0 means the first param of callback.
832             args[0] = range;
833             return true;
834         };
835         // 1 means the callback has one param.
836         JsCallbackHandler::Traverse(entry->vecCopy, { 1, getProperty });
837     };
838     eventHandler->PostTask(task, type, 0, AppExecFwk::EventQueue::Priority::VIP);
839 }
840 
OnSelectByMovement(int32_t direction)841 void JsGetInputMethodController::OnSelectByMovement(int32_t direction)
842 {
843     std::string type = "selectByMovement";
844     auto entry = GetEntry(type, [direction](UvEntry &entry) { entry.direction = direction; });
845     if (entry == nullptr) {
846         IMSA_HILOGE("failed to get uv entry!");
847         return;
848     }
849     auto eventHandler = GetEventHandler();
850     if (eventHandler == nullptr) {
851         IMSA_HILOGE("eventHandler is nullptr!");
852         return;
853     }
854     IMSA_HILOGI("direction: %{public}d.", direction);
855     auto task = [entry]() {
856         auto getProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
857             if (argc < 1) {
858                 return false;
859             }
860             napi_value movement = CreateSelectMovement(env, entry->direction);
861             if (movement == nullptr) {
862                 IMSA_HILOGE("set select movement failed!");
863                 return false;
864             }
865             // 0 means the first param of callback.
866             args[0] = movement;
867             return true;
868         };
869         // 1 means the callback has one param.
870         JsCallbackHandler::Traverse(entry->vecCopy, { 1, getProperty });
871     };
872     eventHandler->PostTask(task, type, 0, AppExecFwk::EventQueue::Priority::VIP);
873 }
874 
InsertText(const std::u16string & text)875 void JsGetInputMethodController::InsertText(const std::u16string &text)
876 {
877     std::string insertText = Str16ToStr8(text);
878     std::string type = "insertText";
879     auto entry = GetEntry(type, [&insertText](UvEntry &entry) { entry.text = insertText; });
880     if (entry == nullptr) {
881         IMSA_HILOGD("failed to get uv entry.");
882         InputMethodController::GetInstance()->ReportBaseTextOperation(
883             IInputDataChannel::INSERT_TEXT, ErrorCode::ERROR_JS_CB_NOT_REGISTER);
884         return;
885     }
886     auto eventHandler = GetEventHandler();
887     if (eventHandler == nullptr) {
888         IMSA_HILOGE("eventHandler is nullptr!");
889         return;
890     }
891     IMSA_HILOGI("start.");
892     auto task = [entry]() {
893         auto getInsertTextProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
894             if (argc == ARGC_ZERO) {
895                 IMSA_HILOGE("getInsertTextProperty the number of argc is invalid.");
896                 return false;
897             }
898             // 0 means the first param of callback.
899             napi_create_string_utf8(env, entry->text.c_str(), NAPI_AUTO_LENGTH, &args[0]);
900             return true;
901         };
902         // 1 means the callback has one param.
903         JsCallbackHandler::Traverse(entry->vecCopy, { 1, getInsertTextProperty });
904     };
905     eventHandler->PostTask(task, type, 0, AppExecFwk::EventQueue::Priority::VIP);
906 }
907 
DeleteRight(int32_t length)908 void JsGetInputMethodController::DeleteRight(int32_t length)
909 {
910     std::string type = "deleteRight";
911     auto entry = GetEntry(type, [&length](UvEntry &entry) { entry.length = length; });
912     if (entry == nullptr) {
913         IMSA_HILOGD("failed to get uv entry.");
914         InputMethodController::GetInstance()->ReportBaseTextOperation(
915             IInputDataChannel::DELETE_FORWARD, ErrorCode::ERROR_JS_CB_NOT_REGISTER);
916         return;
917     }
918     auto eventHandler = GetEventHandler();
919     if (eventHandler == nullptr) {
920         IMSA_HILOGE("eventHandler is nullptr!");
921         return;
922     }
923     IMSA_HILOGI("length: %{public}d", length);
924 
925     auto task = [entry]() {
926         auto getDeleteForwardProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
927             if (argc == ARGC_ZERO) {
928                 IMSA_HILOGE("getDeleteForwardProperty the number of argc is invalid.");
929                 return false;
930             }
931             // 0 means the first param of callback.
932             napi_create_int32(env, entry->length, &args[0]);
933             return true;
934         };
935         // 1 means the callback has one param.
936         JsCallbackHandler::Traverse(entry->vecCopy, { 1, getDeleteForwardProperty });
937     };
938     eventHandler->PostTask(task, type, 0, AppExecFwk::EventQueue::Priority::VIP);
939 }
940 
DeleteLeft(int32_t length)941 void JsGetInputMethodController::DeleteLeft(int32_t length)
942 {
943     std::string type = "deleteLeft";
944     auto entry = GetEntry(type, [&length](UvEntry &entry) { entry.length = length; });
945     if (entry == nullptr) {
946         IMSA_HILOGD("failed to get uv entry.");
947         InputMethodController::GetInstance()->ReportBaseTextOperation(
948             IInputDataChannel::DELETE_BACKWARD, ErrorCode::ERROR_JS_CB_NOT_REGISTER);
949         return;
950     }
951     auto eventHandler = GetEventHandler();
952     if (eventHandler == nullptr) {
953         IMSA_HILOGE("eventHandler is nullptr!");
954         return;
955     }
956     IMSA_HILOGI("length: %{public}d", length);
957     auto task = [entry]() {
958         auto getDeleteBackwardProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
959             if (argc == ARGC_ZERO) {
960                 IMSA_HILOGE("getDeleteBackwardProperty the number of argc is invalid.");
961                 return false;
962             }
963             // 0 means the first param of callback.
964             napi_create_int32(env, entry->length, &args[0]);
965             return true;
966         };
967         // 1 means the callback has one param.
968         JsCallbackHandler::Traverse(entry->vecCopy, { 1, getDeleteBackwardProperty });
969     };
970     eventHandler->PostTask(task, type, 0, AppExecFwk::EventQueue::Priority::VIP);
971 }
972 
SendKeyboardStatus(const KeyboardStatus & status)973 void JsGetInputMethodController::SendKeyboardStatus(const KeyboardStatus &status)
974 {
975     std::string type = "sendKeyboardStatus";
976     auto entry = GetEntry(type, [&status](UvEntry &entry) { entry.keyboardStatus = static_cast<int32_t>(status); });
977     if (entry == nullptr) {
978         IMSA_HILOGD("failed to get uv entry.");
979         return;
980     }
981     auto eventHandler = GetEventHandler();
982     if (eventHandler == nullptr) {
983         IMSA_HILOGE("eventHandler is nullptr!");
984         return;
985     }
986     IMSA_HILOGI("status: %{public}d", static_cast<int32_t>(status));
987     auto task = [entry]() {
988         auto getSendKeyboardStatusProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
989             if (argc == ARGC_ZERO) {
990                 IMSA_HILOGE("getSendKeyboardStatusProperty the number of argc is invalid.");
991                 return false;
992             }
993             // 0 means the first param of callback.
994             napi_create_int32(env, entry->keyboardStatus, &args[0]);
995             return true;
996         };
997         // 1 means the callback has one param.
998         JsCallbackHandler::Traverse(entry->vecCopy, { 1, getSendKeyboardStatusProperty });
999     };
1000     eventHandler->PostTask(task, type, 0, AppExecFwk::EventQueue::Priority::VIP);
1001 }
1002 
CreateSendFunctionKey(napi_env env,int32_t functionKey)1003 napi_value JsGetInputMethodController::CreateSendFunctionKey(napi_env env, int32_t functionKey)
1004 {
1005     napi_value functionkey = nullptr;
1006     napi_create_object(env, &functionkey);
1007 
1008     napi_value value = nullptr;
1009     napi_create_int32(env, functionKey, &value);
1010     napi_set_named_property(env, functionkey, "enterKeyType", value);
1011 
1012     return functionkey;
1013 }
1014 
SendFunctionKey(const FunctionKey & functionKey)1015 void JsGetInputMethodController::SendFunctionKey(const FunctionKey &functionKey)
1016 {
1017     std::string type = "sendFunctionKey";
1018     auto entry = GetEntry(type,
1019         [&functionKey](UvEntry &entry) { entry.enterKeyType = static_cast<int32_t>(functionKey.GetEnterKeyType()); });
1020     if (entry == nullptr) {
1021         IMSA_HILOGD("failed to get uv entry.");
1022         return;
1023     }
1024     auto eventHandler = GetEventHandler();
1025     if (eventHandler == nullptr) {
1026         IMSA_HILOGE("eventHandler is nullptr!");
1027         return;
1028     }
1029     IMSA_HILOGI("functionKey: %{public}d", static_cast<int32_t>(functionKey.GetEnterKeyType()));
1030     auto task = [entry]() {
1031         auto getSendFunctionKeyProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
1032             if (argc == ARGC_ZERO) {
1033                 IMSA_HILOGE("getSendFunctionKeyProperty the number of argc is invalid.");
1034                 return false;
1035             }
1036             napi_value functionKey = CreateSendFunctionKey(env, entry->enterKeyType);
1037             if (functionKey == nullptr) {
1038                 IMSA_HILOGE("set select movement failed");
1039                 return false;
1040             }
1041             // 0 means the first param of callback.
1042             args[0] = functionKey;
1043             return true;
1044         };
1045         // 1 means the callback has one param.
1046         JsCallbackHandler::Traverse(entry->vecCopy, { 1, getSendFunctionKeyProperty });
1047     };
1048     eventHandler->PostTask(task, type, 0, AppExecFwk::EventQueue::Priority::VIP);
1049 }
1050 
MoveCursor(const Direction direction)1051 void JsGetInputMethodController::MoveCursor(const Direction direction)
1052 {
1053     std::string type = "moveCursor";
1054     auto entry = GetEntry(type, [&direction](UvEntry &entry) { entry.direction = static_cast<int32_t>(direction); });
1055     if (entry == nullptr) {
1056         IMSA_HILOGD("failed to get uv entry.");
1057         return;
1058     }
1059     auto eventHandler = GetEventHandler();
1060     if (eventHandler == nullptr) {
1061         IMSA_HILOGE("eventHandler is nullptr!");
1062         return;
1063     }
1064     IMSA_HILOGI("direction: %{public}d", static_cast<int32_t>(direction));
1065     auto task = [entry]() {
1066         auto getMoveCursorProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
1067             if (argc == ARGC_ZERO) {
1068                 IMSA_HILOGE("getMoveCursorProperty the number of argc is invalid.");
1069                 return false;
1070             }
1071             // 0 means the first param of callback.
1072             napi_create_int32(env, static_cast<int32_t>(entry->direction), &args[0]);
1073             return true;
1074         };
1075         // 1 means the callback has one param.
1076         JsCallbackHandler::Traverse(entry->vecCopy, { 1, getMoveCursorProperty });
1077     };
1078     eventHandler->PostTask(task, type, 0, AppExecFwk::EventQueue::Priority::VIP);
1079 }
1080 
HandleExtendAction(int32_t action)1081 void JsGetInputMethodController::HandleExtendAction(int32_t action)
1082 {
1083     std::string type = "handleExtendAction";
1084     auto entry = GetEntry(type, [&action](UvEntry &entry) { entry.action = action; });
1085     if (entry == nullptr) {
1086         IMSA_HILOGD("failed to get uv entry.");
1087         return;
1088     }
1089     auto eventHandler = GetEventHandler();
1090     if (eventHandler == nullptr) {
1091         IMSA_HILOGE("eventHandler is nullptr!");
1092         return;
1093     }
1094     IMSA_HILOGI("action: %{public}d", action);
1095     auto task = [entry]() {
1096         auto getHandleExtendActionProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
1097             if (argc == ARGC_ZERO) {
1098                 IMSA_HILOGE("getHandleExtendActionProperty the number of argc is invalid.");
1099                 return false;
1100             }
1101             // 0 means the first param of callback.
1102             napi_create_int32(env, entry->action, &args[0]);
1103             return true;
1104         };
1105         // 1 means the callback has one param.
1106         JsCallbackHandler::Traverse(entry->vecCopy, { 1, getHandleExtendActionProperty });
1107     };
1108     eventHandler->PostTask(task, type, 0, AppExecFwk::EventQueue::Priority::VIP);
1109 }
1110 
GetText(const std::string & type,int32_t number)1111 std::u16string JsGetInputMethodController::GetText(const std::string &type, int32_t number)
1112 {
1113     auto textResultHandler = std::make_shared<BlockData<std::string>>(MAX_TIMEOUT, "");
1114     auto entry = GetEntry(type, [&number, textResultHandler](UvEntry &entry) {
1115         entry.number = number;
1116         entry.textResultHandler = textResultHandler;
1117     });
1118     if (entry == nullptr) {
1119         IMSA_HILOGE("failed to get uv entry.");
1120         return u"";
1121     }
1122     auto eventHandler = GetEventHandler();
1123     if (eventHandler == nullptr) {
1124         IMSA_HILOGE("eventHandler is nullptr!");
1125         return u"";
1126     }
1127     IMSA_HILOGI("type: %{public}s, number: %{public}d.", type.c_str(), number);
1128     auto task = [entry]() {
1129         auto fillArguments = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
1130             if (argc < 1) {
1131                 IMSA_HILOGE("argc is err.");
1132                 return false;
1133             }
1134             // 0 means the first param of callback.
1135             napi_create_int32(env, entry->number, &args[0]);
1136             return true;
1137         };
1138         std::string text;
1139         // 1 means callback has one param.
1140         JsCallbackHandler::Traverse(entry->vecCopy, { 1, fillArguments }, text);
1141         entry->textResultHandler->SetValue(text);
1142     };
1143     eventHandler->PostTask(task, type, 0, AppExecFwk::EventQueue::Priority::VIP);
1144     return Str8ToStr16(textResultHandler->GetValue());
1145 }
1146 
GetTextIndexAtCursor()1147 int32_t JsGetInputMethodController::GetTextIndexAtCursor()
1148 {
1149     std::string type = "getTextIndexAtCursor";
1150     auto indexResultHandler = std::make_shared<BlockData<int32_t>>(MAX_TIMEOUT, -1);
1151     auto entry =
1152         GetEntry(type, [indexResultHandler](UvEntry &entry) { entry.indexResultHandler = indexResultHandler; });
1153     if (entry == nullptr) {
1154         IMSA_HILOGE("failed to get uv entry!");
1155         return -1;
1156     }
1157     auto eventHandler = GetEventHandler();
1158     if (eventHandler == nullptr) {
1159         IMSA_HILOGE("eventHandler is nullptr!");
1160         return -1;
1161     }
1162     IMSA_HILOGI("run in");
1163     auto task = [entry]() {
1164         int32_t index = -1;
1165         // 0 means callback has no params.
1166         JsCallbackHandler::Traverse(entry->vecCopy, { 0, nullptr }, index);
1167         entry->indexResultHandler->SetValue(index);
1168     };
1169     eventHandler->PostTask(task, type, 0, AppExecFwk::EventQueue::Priority::VIP);
1170     return indexResultHandler->GetValue();
1171 }
1172 
SetPreviewText(const std::u16string & text,const Range & range)1173 int32_t JsGetInputMethodController::SetPreviewText(const std::u16string &text, const Range &range)
1174 {
1175     std::string previewText = Str16ToStr8(text);
1176     std::string type = "setPreviewText";
1177     auto entry = GetEntry(type, [&previewText, &range](UvEntry &entry) {
1178         entry.text = previewText;
1179         entry.start = range.start;
1180         entry.end = range.end;
1181     });
1182     if (entry == nullptr) {
1183         IMSA_HILOGD("failed to get uv entry!");
1184         return ErrorCode::ERROR_NULL_POINTER;
1185     }
1186     auto eventHandler = GetEventHandler();
1187     if (eventHandler == nullptr) {
1188         IMSA_HILOGE("eventHandler is nullptr!");
1189         return ErrorCode::ERROR_NULL_POINTER;
1190     }
1191     IMSA_HILOGI("previewText start.");
1192     auto task = [entry]() {
1193         auto getPreviewTextProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
1194             // 2 means the callback has two params.
1195             if (argc < 2) {
1196                 return false;
1197             }
1198             // 0 means the first param of callback.
1199             args[0] = JsUtil::GetValue(env, entry->text);
1200             // 1 means the second param of callback.
1201             args[1] = CreateSelectRange(env, entry->start, entry->end);
1202             return true;
1203         };
1204 
1205         // 2 means the callback has two param.
1206         JsCallbackHandler::Traverse(entry->vecCopy, { 2, getPreviewTextProperty });
1207     };
1208     eventHandler->PostTask(task, type, 0, AppExecFwk::EventQueue::Priority::VIP);
1209     return ErrorCode::NO_ERROR;
1210 }
1211 
FinishTextPreview()1212 void JsGetInputMethodController::FinishTextPreview()
1213 {
1214     std::string type = "finishTextPreview";
1215     auto entry = GetEntry(type, nullptr);
1216     if (entry == nullptr) {
1217         IMSA_HILOGE("failed to get uv entry!");
1218         return;
1219     }
1220     auto eventHandler = GetEventHandler();
1221     if (eventHandler == nullptr) {
1222         IMSA_HILOGE("eventHandler is nullptr!");
1223         return;
1224     }
1225     IMSA_HILOGI("run in");
1226     auto task = [entry]() {
1227         // 0 means callback has no params.
1228         JsCallbackHandler::Traverse(entry->vecCopy, { 0, nullptr });
1229     };
1230     eventHandler->PostTask(task, type, 0, AppExecFwk::EventQueue::Priority::VIP);
1231 }
1232 
GetEventHandler()1233 std::shared_ptr<AppExecFwk::EventHandler> JsGetInputMethodController::GetEventHandler()
1234 {
1235     std::lock_guard<std::mutex> lock(eventHandlerMutex_);
1236     return handler_;
1237 }
1238 
GetEntry(const std::string & type,EntrySetter entrySetter)1239 std::shared_ptr<JsGetInputMethodController::UvEntry> JsGetInputMethodController::GetEntry(const std::string &type,
1240     EntrySetter entrySetter)
1241 {
1242     IMSA_HILOGD("type: %{public}s", type.c_str());
1243     std::shared_ptr<UvEntry> entry = nullptr;
1244     {
1245         std::lock_guard<std::recursive_mutex> lock(mutex_);
1246         if (jsCbMap_.find(type) == jsCbMap_.end() || jsCbMap_[type].empty()) {
1247             IMSA_HILOGD("%{public}s cb-vector is empty.", type.c_str());
1248             return nullptr;
1249         }
1250         entry = std::make_shared<UvEntry>(jsCbMap_[type], type);
1251     }
1252     if (entrySetter != nullptr) {
1253         entrySetter(*entry);
1254     }
1255     return entry;
1256 }
1257 
SendMessage(napi_env env,napi_callback_info info)1258 napi_value JsGetInputMethodController::SendMessage(napi_env env, napi_callback_info info)
1259 {
1260     auto ctxt = std::make_shared<SendMessageContext>();
1261     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
1262         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
1263         PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_string, "msgId",
1264             TYPE_STRING, napi_generic_failure);
1265         CHECK_RETURN(JsUtils::GetValue(env, argv[0], ctxt->arrayBuffer.msgId) == napi_ok,
1266             "msgId covert failed!", napi_generic_failure);
1267         ctxt->arrayBuffer.jsArgc = argc;
1268         // 1 means first param msgId.
1269         if (argc > 1) {
1270             bool isArryBuffer = false;
1271             //  1 means second param msgParam index.
1272             CHECK_RETURN(napi_is_arraybuffer(env, argv[1], &isArryBuffer) == napi_ok,
1273                 "napi_is_arraybuffer failed!", napi_generic_failure);
1274             PARAM_CHECK_RETURN(env, isArryBuffer, "msgParam", TYPE_ARRAY_BUFFER, napi_generic_failure);
1275             CHECK_RETURN(JsUtils::GetValue(env, argv[1], ctxt->arrayBuffer.msgParam) == napi_ok,
1276                 "msgParam covert failed!", napi_generic_failure);
1277         }
1278         PARAM_CHECK_RETURN(env, ArrayBuffer::IsSizeValid(ctxt->arrayBuffer),
1279             "msgId limit 256B and msgParam limit 128KB.", TYPE_NONE, napi_generic_failure);
1280         ctxt->info = { std::chrono::system_clock::now(), ctxt->arrayBuffer };
1281         messageHandlerQueue_.Push(ctxt->info);
1282         return napi_ok;
1283     };
1284     auto exec = [ctxt](AsyncCall::Context *ctx) {
1285         messageHandlerQueue_.Wait(ctxt->info);
1286         int32_t code = InputMethodController::GetInstance()->SendMessage(ctxt->arrayBuffer);
1287         messageHandlerQueue_.Pop();
1288         if (code == ErrorCode::NO_ERROR) {
1289             ctxt->status = napi_ok;
1290             ctxt->SetState(ctxt->status);
1291         } else {
1292             ctxt->SetErrorCode(code);
1293         }
1294     };
1295     ctxt->SetAction(std::move(input), nullptr);
1296     // 2 means JsAPI:sendMessage has 2 params at most.
1297     AsyncCall asyncCall(env, info, ctxt, 2);
1298     return asyncCall.Call(env, exec, "imcSendMessage");
1299 }
1300 
RecvMessage(napi_env env,napi_callback_info info)1301 napi_value JsGetInputMethodController::RecvMessage(napi_env env, napi_callback_info info)
1302 {
1303     size_t argc = ARGC_ONE;
1304     napi_value argv[ARGC_TWO] = {nullptr};
1305     napi_value thisVar = nullptr;
1306     void *data = nullptr;
1307     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
1308     std::string type;
1309     if (argc < 0) {
1310         IMSA_HILOGE("RecvMessage failed! argc abnormal.");
1311         return nullptr;
1312     }
1313     if (argc == 0) {
1314         IMSA_HILOGI("RecvMessage off.");
1315         InputMethodController::GetInstance()->RegisterMsgHandler();
1316         return nullptr;
1317     }
1318     IMSA_HILOGI("RecvMessage on.");
1319     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_object, "msgHnadler (MessageHandler)",
1320         TYPE_OBJECT, nullptr);
1321 
1322     napi_value onMessage = nullptr;
1323     CHECK_RETURN(napi_get_named_property(env, argv[0], "onMessage", &onMessage) == napi_ok,
1324         "Get onMessage property failed!", nullptr);
1325     CHECK_RETURN(JsUtil::GetType(env, onMessage) == napi_function, "onMessage is not napi_function!", nullptr);
1326     napi_value onTerminated = nullptr;
1327     CHECK_RETURN(napi_get_named_property(env, argv[0], "onTerminated", &onTerminated) == napi_ok,
1328         "Get onTerminated property failed!", nullptr);
1329     CHECK_RETURN(JsUtil::GetType(env, onTerminated) == napi_function, "onTerminated is not napi_function!", nullptr);
1330 
1331     std::shared_ptr<MsgHandlerCallbackInterface> callback =
1332         std::make_shared<JsGetInputMethodController::JsMessageHandler>(env, onTerminated, onMessage);
1333     InputMethodController::GetInstance()->RegisterMsgHandler(callback);
1334     napi_value result = nullptr;
1335     napi_get_null(env, &result);
1336     return result;
1337 }
1338 
OnTerminated()1339 int32_t JsGetInputMethodController::JsMessageHandler::OnTerminated()
1340 {
1341     std::lock_guard<decltype(callbackObjectMutex_)> lock(callbackObjectMutex_);
1342     if (jsMessageHandler_ == nullptr) {
1343         IMSA_HILOGI("jsCallbackObject is nullptr, can not call OnTerminated!.");
1344         return ErrorCode::ERROR_NULL_POINTER;
1345     }
1346     auto eventHandler = jsMessageHandler_->GetEventHandler();
1347     if (eventHandler == nullptr) {
1348         IMSA_HILOGI("EventHandler is nullptr!.");
1349         return ErrorCode::ERROR_NULL_POINTER;
1350     }
1351     // Ensure jsMessageHandler_ destructor run in current thread.
1352     auto task = [jsCallback = std::move(jsMessageHandler_)]() {
1353         napi_value callback = nullptr;
1354         napi_value global = nullptr;
1355         if (jsCallback == nullptr) {
1356             IMSA_HILOGI("jsCallback is nullptr!.");
1357             return;
1358         }
1359         napi_get_reference_value(jsCallback->env_, jsCallback->onTerminatedCallback_, &callback);
1360         if (callback != nullptr) {
1361             napi_get_global(jsCallback->env_, &global);
1362             napi_value output = nullptr;
1363             // 0 means the callback has no param.
1364             auto status = napi_call_function(jsCallback->env_, global, callback, 0, nullptr, &output);
1365             if (status != napi_ok) {
1366                 IMSA_HILOGI("Call js function failed!.");
1367                 output = nullptr;
1368             }
1369         }
1370     };
1371     eventHandler->PostTask(task, "IMC_MsgHandler_OnTerminated", 0, AppExecFwk::EventQueue::Priority::VIP);
1372     return ErrorCode::NO_ERROR;
1373 }
1374 
OnMessage(const ArrayBuffer & arrayBuffer)1375 int32_t JsGetInputMethodController::JsMessageHandler::OnMessage(const ArrayBuffer &arrayBuffer)
1376 {
1377     std::lock_guard<decltype(callbackObjectMutex_)> lock(callbackObjectMutex_);
1378     if (jsMessageHandler_ == nullptr) {
1379         IMSA_HILOGI("jsCallbackObject is nullptr, can not call OnTerminated!.");
1380         return ErrorCode::ERROR_NULL_POINTER;
1381     }
1382     auto eventHandler = jsMessageHandler_->GetEventHandler();
1383     if (eventHandler == nullptr) {
1384         IMSA_HILOGI("EventHandler is nullptr!.");
1385         return ErrorCode::ERROR_CLIENT_NULL_POINTER;
1386     }
1387     auto task = [jsCallbackObject = jsMessageHandler_, arrayBuffer]() {
1388         napi_value callback = nullptr;
1389         napi_value global = nullptr;
1390         if (jsCallbackObject == nullptr) {
1391             IMSA_HILOGI("jsCallbackObject is nullptr!.");
1392             return;
1393         }
1394         napi_get_reference_value(jsCallbackObject->env_, jsCallbackObject->onMessageCallback_, &callback);
1395         if (callback != nullptr) {
1396             napi_get_global(jsCallbackObject->env_, &global);
1397             napi_value output = nullptr;
1398             napi_value argv[ARGC_TWO] = { nullptr };
1399             // 2 means just use the first two parameters
1400             if (JsUtils::GetMessageHandlerCallbackParam(argv, jsCallbackObject, arrayBuffer, 2) != napi_ok) {
1401                 IMSA_HILOGE("Get message handler callback param failed!.");
1402                 return;
1403             }
1404             // The maximum valid parameters count of callback is 2.
1405             auto callbackArgc = arrayBuffer.jsArgc > ARGC_ONE ? ARGC_TWO : ARGC_ONE;
1406             auto status = napi_call_function(jsCallbackObject->env_, global, callback, callbackArgc, argv, &output);
1407             if (status != napi_ok) {
1408                 IMSA_HILOGI("Call js function failed!.");
1409                 output = nullptr;
1410             }
1411         }
1412     };
1413     eventHandler->PostTask(task, "IMC_MsgHandler_OnMessage", 0, AppExecFwk::EventQueue::Priority::VIP);
1414     return ErrorCode::NO_ERROR;
1415 }
1416 
IsRegister(const std::string & type)1417 bool JsGetInputMethodController::IsRegister(const std::string &type)
1418 {
1419     std::lock_guard<std::recursive_mutex> lock(mutex_);
1420     if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) {
1421         IMSA_HILOGD("methodName: %{public}s is not registered!", type.c_str());
1422         return false;
1423     }
1424     if (jsCbMap_[type].empty()) {
1425         IMSA_HILOGD("methodName: %{public}s cb-vector is empty!", type.c_str());
1426         return false;
1427     }
1428     return true;
1429 }
1430 
IsTextPreviewSupported()1431 bool JsGetInputMethodController::IsTextPreviewSupported()
1432 {
1433     auto engine = JsGetInputMethodController::GetInstance();
1434     if (engine == nullptr) {
1435         return false;
1436     }
1437     return engine->IsRegister("setPreviewText") && engine->IsRegister("finishTextPreview");
1438 }
1439 
UpdateTextPreviewState(const std::string & type)1440 void JsGetInputMethodController::UpdateTextPreviewState(const std::string &type)
1441 {
1442     if (type == "setPreviewText" || type == "finishTextPreview") {
1443         InputMethodController::GetInstance()->UpdateTextPreviewState(false);
1444     }
1445 }
1446 } // namespace MiscServices
1447 } // namespace OHOS
1448