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