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