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