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