• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2024 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 
16 #include "js_text_input_client_engine.h"
17 
18 #include "event_checker.h"
19 #include "input_method_ability.h"
20 #include "inputmethod_trace.h"
21 #include "js_callback_handler.h"
22 #include "js_util.h"
23 #include "js_utils.h"
24 #include "napi/native_api.h"
25 #include "napi/native_node_api.h"
26 #include "string_ex.h"
27 #include "string_utils.h"
28 #include "wm_common.h"
29 #include "res_config.h"
30 #include "resource_manager.h"
31 #include "variant_util.h"
32 
33 namespace OHOS {
34 namespace MiscServices {
35 #define ASYNC_POST(env, ctx) asyncCall.Post((env), (ctx), taskQueue_, __FUNCTION__)
36 using namespace std::chrono;
37 thread_local napi_ref JsTextInputClientEngine::TICRef_ = nullptr;
38 const std::string JsTextInputClientEngine::TIC_CLASS_NAME = "TextInputClient";
39 constexpr int32_t DEVICE_TYPE_2IN1 = 7;
40 constexpr int32_t MAX_WAIT_TIME = 5000;
41 constexpr int32_t MAX_WAIT_TIME_PRIVATE_COMMAND = 2000;
42 constexpr int32_t MAX_WAIT_TIME_MESSAGE_HANDLER = 2000;
43 constexpr size_t ARGC_TWO = 2;
44 constexpr size_t ARGC_ONE = 1;
45 std::shared_ptr<AsyncCall::TaskQueue> JsTextInputClientEngine::taskQueue_ = std::make_shared<AsyncCall::TaskQueue>();
46 BlockQueue<MessageHandlerInfo> JsTextInputClientEngine::messageHandlerQueue_{ MAX_WAIT_TIME_MESSAGE_HANDLER };
47 std::mutex JsTextInputClientEngine::engineMutex_;
48 std::shared_ptr<JsTextInputClientEngine> JsTextInputClientEngine::textInputClientEngine_{ nullptr };
49 std::mutex JsTextInputClientEngine::eventHandlerMutex_;
50 std::shared_ptr<AppExecFwk::EventHandler> JsTextInputClientEngine::handler_{ nullptr };
51 uint32_t JsTextInputClientEngine::traceId_{ 0 };
52 int32_t JsTextInputClientEngine::deviceTypeCache_{ -1 };
Init(napi_env env,napi_value info)53 napi_value JsTextInputClientEngine::Init(napi_env env, napi_value info)
54 {
55     IMSA_HILOGD("JsTextInputClientEngine init");
56     napi_property_descriptor properties[] = { DECLARE_NAPI_FUNCTION("sendKeyFunction", SendKeyFunction),
57         DECLARE_NAPI_FUNCTION("deleteForward", DeleteForward), DECLARE_NAPI_FUNCTION("deleteBackward", DeleteBackward),
58         DECLARE_NAPI_FUNCTION("insertText", InsertText), DECLARE_NAPI_FUNCTION("getForward", GetForward),
59         DECLARE_NAPI_FUNCTION("getBackward", GetBackward),
60         DECLARE_NAPI_FUNCTION("getEditorAttribute", GetEditorAttribute),
61         DECLARE_NAPI_FUNCTION("getTextIndexAtCursor", GetTextIndexAtCursor),
62         DECLARE_NAPI_FUNCTION("moveCursor", MoveCursor), DECLARE_NAPI_FUNCTION("selectByRange", SelectByRange),
63         DECLARE_NAPI_FUNCTION("selectByMovement", SelectByMovement),
64         DECLARE_NAPI_FUNCTION("sendExtendAction", SendExtendAction),
65         DECLARE_NAPI_FUNCTION("insertTextSync", InsertTextSync),
66         DECLARE_NAPI_FUNCTION("moveCursorSync", MoveCursorSync),
67         DECLARE_NAPI_FUNCTION("getEditorAttributeSync", GetEditorAttributeSync),
68         DECLARE_NAPI_FUNCTION("selectByRangeSync", SelectByRangeSync),
69         DECLARE_NAPI_FUNCTION("selectByMovementSync", SelectByMovementSync),
70         DECLARE_NAPI_FUNCTION("getTextIndexAtCursorSync", GetTextIndexAtCursorSync),
71         DECLARE_NAPI_FUNCTION("deleteForwardSync", DeleteForwardSync),
72         DECLARE_NAPI_FUNCTION("deleteBackwardSync", DeleteBackwardSync),
73         DECLARE_NAPI_FUNCTION("getForwardSync", GetForwardSync),
74         DECLARE_NAPI_FUNCTION("getBackwardSync", GetBackwardSync),
75         DECLARE_NAPI_FUNCTION("sendPrivateCommand", SendPrivateCommand),
76         DECLARE_NAPI_FUNCTION("getCallingWindowInfo", GetCallingWindowInfo),
77         DECLARE_NAPI_FUNCTION("setPreviewText", SetPreviewText),
78         DECLARE_NAPI_FUNCTION("setPreviewTextSync", SetPreviewTextSync),
79         DECLARE_NAPI_FUNCTION("finishTextPreview", FinishTextPreview),
80         DECLARE_NAPI_FUNCTION("finishTextPreviewSync", FinishTextPreviewSync),
81         DECLARE_NAPI_FUNCTION("sendMessage", SendMessage),
82         DECLARE_NAPI_FUNCTION("recvMessage", RecvMessage),
83         DECLARE_NAPI_FUNCTION("getAttachOptions", GetAttachOptions),
84         DECLARE_NAPI_FUNCTION("on", Subscribe),
85         DECLARE_NAPI_FUNCTION("off", UnSubscribe) };
86     napi_value cons = nullptr;
87     NAPI_CALL(env, napi_define_class(env, TIC_CLASS_NAME.c_str(), TIC_CLASS_NAME.size(), JsConstructor, nullptr,
88                        sizeof(properties) / sizeof(napi_property_descriptor), properties, &cons));
89     NAPI_CALL(env, napi_create_reference(env, cons, 1, &TICRef_));
90     NAPI_CALL(env, napi_set_named_property(env, info, TIC_CLASS_NAME.c_str(), cons));
91 
92     return info;
93 }
94 
IsTargetDeviceType(int32_t resDeviceType)95 bool JsTextInputClientEngine::IsTargetDeviceType(int32_t resDeviceType)
96 {
97     if (deviceTypeCache_ != -1) {
98         if (deviceTypeCache_ == resDeviceType) {
99             return true;
100         }
101         return false;
102     }
103     std::shared_ptr<AbilityRuntime::ApplicationContext> context =
104         AbilityRuntime::ApplicationContext::GetApplicationContext();
105     if (context == nullptr) {
106         return false;
107     }
108     auto resourceManager = context->GetResourceManager();
109     auto resConfig = std::unique_ptr<Global::Resource::ResConfig>(Global::Resource::CreateResConfig());
110     if (resourceManager == nullptr || resConfig == nullptr) {
111         return false;
112     }
113     resourceManager->GetResConfig(*resConfig);
114 
115     Global::Resource::DeviceType deviceType = resConfig->GetDeviceType();
116     deviceTypeCache_ = static_cast<int32_t>(deviceType);
117     if (static_cast<int32_t>(deviceType) == resDeviceType) {
118         return true;
119     }
120     return false;
121 }
122 
MoveCursor(napi_env env,napi_callback_info info)123 napi_value JsTextInputClientEngine::MoveCursor(napi_env env, napi_callback_info info)
124 {
125     auto ctxt = std::make_shared<MoveCursorContext>();
126     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
127         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
128         PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "direction type must be number!",
129             TYPE_NONE, napi_generic_failure);
130         auto status = JsUtils::GetValue(env, argv[0], ctxt->num);
131         // 1 means least param num.
132         PARAM_CHECK_RETURN(env, ctxt->num >= 0, "direction should be not less than 0!", TYPE_NONE,
133             napi_generic_failure);
134         return status;
135     };
136     auto exec = [ctxt](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
137         auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
138             if (code == ErrorCode::NO_ERROR) {
139                 ctxt->status = napi_ok;
140                 ctxt->SetState(ctxt->status);
141             } else {
142                 ctxt->SetErrorCode(code);
143             }
144             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
145         };
146         int32_t code = InputMethodAbility::GetInstance().MoveCursor(ctxt->num, rspCallBack);
147         if (code != ErrorCode::NO_ERROR) {
148             ctxt->SetErrorCode(code);
149             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
150         }
151     };
152     ctxt->SetAction(std::move(input));
153     // 2 means JsAPI:moveCursor has 2 params at most.
154     EditAsyncCall asyncCall(env, info, ctxt, 2);
155     return asyncCall.Call(env, exec, __FUNCTION__);
156 }
157 
MoveCursorSync(napi_env env,napi_callback_info info)158 napi_value JsTextInputClientEngine::MoveCursorSync(napi_env env, napi_callback_info info)
159 {
160     size_t argc = 1;
161     napi_value argv[1] = { nullptr };
162     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
163     int32_t direction = 0;
164     // 1 means least param num.
165     PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
166     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "direction must be number!", TYPE_NUMBER,
167         HandleParamCheckFailure(env));
168     PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], direction), "direction covert failed!", TYPE_NONE,
169         HandleParamCheckFailure(env));
170     PARAM_CHECK_RETURN(env, direction >= 0, "direction should be no less than 0!", TYPE_NONE,
171         HandleParamCheckFailure(env));
172     IMSA_HILOGD("moveCursor , direction: %{public}d", direction);
173     int32_t ret = InputMethodAbility::GetInstance().MoveCursor(direction);
174     if (ret != ErrorCode::NO_ERROR) {
175         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to move cursor!", TYPE_NONE);
176     }
177     return JsUtil::Const::Null(env);
178 }
179 
JsConstructor(napi_env env,napi_callback_info info)180 napi_value JsTextInputClientEngine::JsConstructor(napi_env env, napi_callback_info info)
181 {
182     napi_value thisVar = nullptr;
183     NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr));
184 
185     auto clientObject = GetTextInputClientEngine();
186     if (clientObject == nullptr || !InitTextInputClientEngine()) {
187         IMSA_HILOGE("clientObject is nullptr!");
188         napi_value result = nullptr;
189         napi_get_null(env, &result);
190         return result;
191     }
192     napi_status status =
193         napi_wrap(env, thisVar, clientObject.get(), [](napi_env env, void *data, void *hint) { }, nullptr, nullptr);
194     if (status != napi_ok) {
195         IMSA_HILOGE("failed to wrap: %{public}d!", status);
196         return nullptr;
197     }
198     return thisVar;
199 }
200 
GetTextInputClientEngine()201 std::shared_ptr<JsTextInputClientEngine> JsTextInputClientEngine::GetTextInputClientEngine()
202 {
203     if (textInputClientEngine_ == nullptr) {
204         std::lock_guard<std::mutex> lock(engineMutex_);
205         if (textInputClientEngine_ == nullptr) {
206             textInputClientEngine_ = std::make_shared<JsTextInputClientEngine>();
207         }
208     }
209     return textInputClientEngine_;
210 }
211 
InitTextInputClientEngine()212 bool JsTextInputClientEngine::InitTextInputClientEngine()
213 {
214     if (!InputMethodAbility::GetInstance().IsCurrentIme()) {
215         return false;
216     }
217     auto engine = GetTextInputClientEngine();
218     if (engine == nullptr) {
219         return false;
220     }
221     InputMethodAbility::GetInstance().SetTextInputClientListener(engine);
222     {
223         std::lock_guard<std::mutex> lock(eventHandlerMutex_);
224         handler_ = AppExecFwk::EventHandler::Current();
225     }
226     return true;
227 }
228 
GetTextInputClientInstance(napi_env env)229 napi_value JsTextInputClientEngine::GetTextInputClientInstance(napi_env env)
230 {
231     napi_value instance = nullptr;
232     napi_value cons = nullptr;
233     if (napi_get_reference_value(env, TICRef_, &cons) != napi_ok) {
234         IMSA_HILOGE("failed to get reference value!");
235         return nullptr;
236     }
237     if (napi_new_instance(env, cons, 0, nullptr, &instance) != napi_ok) {
238         IMSA_HILOGE("failed to new instance!");
239         return nullptr;
240     }
241     return instance;
242 }
243 
GetResult(napi_env env,std::string & text)244 napi_value JsTextInputClientEngine::GetResult(napi_env env, std::string &text)
245 {
246     napi_value jsText = nullptr;
247     napi_create_string_utf8(env, text.c_str(), NAPI_AUTO_LENGTH, &jsText);
248     return jsText;
249 }
250 
GetSelectRange(napi_env env,napi_value argv,std::shared_ptr<SelectContext> ctxt)251 napi_status JsTextInputClientEngine::GetSelectRange(napi_env env, napi_value argv, std::shared_ptr<SelectContext> ctxt)
252 {
253     napi_status status = napi_generic_failure;
254     napi_value napiValue = nullptr;
255     status = napi_get_named_property(env, argv, "start", &napiValue);
256     PARAM_CHECK_RETURN(env, status == napi_ok, "start of range cannot empty and must be number.", TYPE_NONE, status);
257     status = JsUtils::GetValue(env, napiValue, ctxt->start);
258     CHECK_RETURN(status == napi_ok, "failed to get start value!", status);
259 
260     status = napi_get_named_property(env, argv, "end", &napiValue);
261     PARAM_CHECK_RETURN(env, status == napi_ok, "end of range cannot empty and must be number.", TYPE_NONE, status);
262     status = JsUtils::GetValue(env, napiValue, ctxt->end);
263     if (status != napi_ok) {
264         IMSA_HILOGE("failed to get end value!");
265     }
266     return status;
267 }
268 
GetSelectMovement(napi_env env,napi_value argv,std::shared_ptr<SelectContext> ctxt)269 napi_status JsTextInputClientEngine::GetSelectMovement(napi_env env, napi_value argv,
270     std::shared_ptr<SelectContext> ctxt)
271 {
272     napi_status status = napi_generic_failure;
273     napi_value napiValue = nullptr;
274     status = napi_get_named_property(env, argv, "direction", &napiValue);
275     PARAM_CHECK_RETURN(env, status == napi_ok, "direction must be exist!", TYPE_NONE, status);
276     status = JsUtils::GetValue(env, napiValue, ctxt->direction);
277     if (status != napi_ok) {
278         IMSA_HILOGE("failed to get direction value!");
279     }
280     PARAM_CHECK_RETURN(env, status == napi_ok, "direction type must be Direction!", TYPE_NONE, status);
281     return status;
282 }
283 
SendKeyFunction(napi_env env,napi_callback_info info)284 napi_value JsTextInputClientEngine::SendKeyFunction(napi_env env, napi_callback_info info)
285 {
286     auto ctxt = std::make_shared<SendKeyFunctionContext>();
287     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
288         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
289         napi_status ret = JsUtils::GetValue(env, argv[0], ctxt->action);
290         PARAM_CHECK_RETURN(env, ret == napi_ok, "action type must be number!", TYPE_NONE, napi_generic_failure);
291         return napi_ok;
292     };
293     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
294         napi_status status = napi_get_boolean(env, ctxt->isSendKeyFunction, result);
295         return status;
296     };
297     auto exec = [ctxt](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
298         auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
299             if (code == ErrorCode::NO_ERROR) {
300                 ctxt->status = napi_ok;
301                 ctxt->SetState(ctxt->status);
302                 ctxt->isSendKeyFunction = true;
303             } else {
304                 ctxt->SetErrorCode(code);
305             }
306             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
307         };
308         int32_t code = InputMethodAbility::GetInstance().SendFunctionKey(ctxt->action, rspCallBack);
309         if (code != ErrorCode::NO_ERROR) {
310             ctxt->SetErrorCode(code);
311             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
312         }
313     };
314     ctxt->SetAction(std::move(input), std::move(output));
315     // 2 means JsAPI:sendKeyFunction has 2 params at most.
316     EditAsyncCall asyncCall(env, info, ctxt, 2);
317     return asyncCall.Call(env, exec, __FUNCTION__);
318 }
319 
SendPrivateCommand(napi_env env,napi_callback_info info)320 napi_value JsTextInputClientEngine::SendPrivateCommand(napi_env env, napi_callback_info info)
321 {
322     auto ctxt = std::make_shared<SendPrivateCommandContext>();
323     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
324         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
325         napi_status status = JsUtils::GetValue(env, argv[0], ctxt->privateCommand);
326         CHECK_RETURN(status == napi_ok,
327             "commandData covert failed, type must be Record<string, CommandDataType>", status);
328         PARAM_CHECK_RETURN(env, TextConfig::IsPrivateCommandValid(ctxt->privateCommand),
329             "commandData size limit 32KB, count limit 5.", TYPE_NONE, napi_generic_failure);
330         ctxt->info = { std::chrono::system_clock::now(), ctxt->privateCommand };
331         return status;
332     };
333     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status { return napi_ok; };
334     auto exec = [ctxt](AsyncCall::Context *ctx) {
335         int32_t code = InputMethodAbility::GetInstance().SendPrivateCommand(ctxt->privateCommand);
336         if (code == ErrorCode::NO_ERROR) {
337             ctxt->status = napi_ok;
338             ctxt->SetState(ctxt->status);
339         } else {
340             ctxt->SetErrorCode(code);
341         }
342     };
343     ctxt->SetAction(std::move(input), std::move(output));
344     // 1 means JsAPI:SendPrivateCommand has 1 param at most.
345     AsyncCall asyncCall(env, info, ctxt, 1);
346     return ASYNC_POST(env, exec);
347 }
348 
DeleteForwardSync(napi_env env,napi_callback_info info)349 napi_value JsTextInputClientEngine::DeleteForwardSync(napi_env env, napi_callback_info info)
350 {
351     InputMethodSyncTrace tracer("JS_DeleteForwardSync", GenerateTraceId());
352     size_t argc = 1;
353     napi_value argv[1] = { nullptr };
354     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
355     int32_t length = 0;
356     // 1 means least param num.
357     PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
358     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length must be number!", TYPE_NUMBER,
359         HandleParamCheckFailure(env));
360     PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], length), "length covert failed", TYPE_NONE,
361         HandleParamCheckFailure(env));
362     PARAM_CHECK_RETURN(env, length >= 0, "length should not less than 0!", TYPE_NONE, HandleParamCheckFailure(env));
363     IMSA_HILOGD("delete forward, length: %{public}d.", length);
364     int32_t ret = InputMethodAbility::GetInstance().DeleteForward(length);
365     if (ret != ErrorCode::NO_ERROR) {
366         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to delete forward", TYPE_NONE);
367     }
368     return JsUtil::Const::Null(env);
369 }
370 
DeleteForward(napi_env env,napi_callback_info info)371 napi_value JsTextInputClientEngine::DeleteForward(napi_env env, napi_callback_info info)
372 {
373     auto traceId = GenerateTraceId();
374     InputMethodSyncTrace tracer("JS_DeleteForward_Start", traceId);
375     auto ctxt = std::make_shared<DeleteForwardContext>();
376     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
377         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
378         PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length type must be number!",
379             TYPE_NONE, napi_generic_failure);
380         auto status = JsUtils::GetValue(env, argv[0], ctxt->length);
381         PARAM_CHECK_RETURN(env, ctxt->length >= 0, "length should no less than 0!", TYPE_NONE, napi_generic_failure);
382         return status;
383     };
384     auto output = [ctxt, traceId](napi_env env, napi_value *result) -> napi_status {
385         InputMethodSyncTrace tracer("JS_DeleteForward_Complete", traceId);
386         napi_status status = napi_get_boolean(env, ctxt->isDeleteForward, result);
387         return status;
388     };
389     auto exec = [ctxt, traceId](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
390         InputMethodSyncTrace tracer("JS_DeleteForward_Exec", traceId);
391         auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
392             if (code == ErrorCode::NO_ERROR) {
393                 ctxt->status = napi_ok;
394                 ctxt->SetState(ctxt->status);
395                 ctxt->isDeleteForward = true;
396             } else {
397                 ctxt->SetErrorCode(code);
398             }
399             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
400         };
401         int32_t code = InputMethodAbility::GetInstance().DeleteForward(ctxt->length, rspCallBack);
402         if (code != ErrorCode::NO_ERROR) {
403             ctxt->SetErrorCode(code);
404             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
405         }
406     };
407     ctxt->SetAction(std::move(input), std::move(output));
408     // 2 means JsAPI:deleteForward has 2 params at most.
409     EditAsyncCall asyncCall(env, info, ctxt, 2);
410     return asyncCall.Call(env, exec, __FUNCTION__);
411 }
412 
DeleteBackwardSync(napi_env env,napi_callback_info info)413 napi_value JsTextInputClientEngine::DeleteBackwardSync(napi_env env, napi_callback_info info)
414 {
415     size_t argc = 1;
416     napi_value argv[1] = { nullptr };
417     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
418     int32_t length = 0;
419     // 1 means least param num.
420     PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
421     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length must be number!", TYPE_NUMBER,
422         HandleParamCheckFailure(env));
423     PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], length), "length covert failed!", TYPE_NONE,
424         HandleParamCheckFailure(env));
425     PARAM_CHECK_RETURN(env, length >= 0, "length should no less than 0!", TYPE_NONE, HandleParamCheckFailure(env));
426     IMSA_HILOGD("delete backward, length: %{public}d.", length);
427     int32_t ret = InputMethodAbility::GetInstance().DeleteBackward(length);
428     if (ret != ErrorCode::NO_ERROR) {
429         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to delete backward", TYPE_NONE);
430     }
431     return JsUtil::Const::Null(env);
432 }
433 
DeleteBackward(napi_env env,napi_callback_info info)434 napi_value JsTextInputClientEngine::DeleteBackward(napi_env env, napi_callback_info info)
435 {
436     auto ctxt = std::make_shared<DeleteBackwardContext>();
437     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
438         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
439         PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "param length type must be number!",
440             TYPE_NONE, napi_generic_failure);
441         auto status = JsUtils::GetValue(env, argv[0], ctxt->length);
442         return status;
443     };
444     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
445         napi_status status = napi_get_boolean(env, ctxt->isDeleteBackward, result);
446         return status;
447     };
448     auto exec = [ctxt](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
449         auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
450             if (code == ErrorCode::NO_ERROR) {
451                 ctxt->status = napi_ok;
452                 ctxt->SetState(ctxt->status);
453                 ctxt->isDeleteBackward = true;
454             } else {
455                 ctxt->SetErrorCode(code);
456             }
457             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
458         };
459         int32_t code = InputMethodAbility::GetInstance().DeleteBackward(ctxt->length, rspCallBack);
460         if (code != ErrorCode::NO_ERROR) {
461             ctxt->SetErrorCode(code);
462             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
463         }
464     };
465     ctxt->SetAction(std::move(input), std::move(output));
466     // 2 means JsAPI:deleteBackward has 2 params at most.
467     EditAsyncCall asyncCall(env, info, ctxt, 2);
468     return asyncCall.Call(env, exec, __FUNCTION__);
469 }
470 
InsertText(napi_env env,napi_callback_info info)471 napi_value JsTextInputClientEngine::InsertText(napi_env env, napi_callback_info info)
472 {
473     auto traceId = GenerateTraceId();
474     InputMethodSyncTrace tracer("JS_InsertText_Start", traceId);
475     auto ctxt = std::make_shared<InsertTextContext>();
476     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
477         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
478         PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_string, "text type must be string",
479             TYPE_NONE, napi_generic_failure);
480         auto status = JsUtils::GetValue(env, argv[0], ctxt->text);
481         return status;
482     };
483     auto output = [ctxt, traceId](napi_env env, napi_value *result) -> napi_status {
484         InputMethodSyncTrace tracer("JS_InsertText_Complete", traceId);
485         napi_status status = napi_get_boolean(env, ctxt->isInsertText, result);
486         return status;
487     };
488     auto exec = [ctxt, traceId](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
489         InputMethodSyncTrace tracer("JS_InsertText_Exec", traceId);
490         auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
491             if (code == ErrorCode::NO_ERROR) {
492                 ctxt->status = napi_ok;
493                 ctxt->SetState(ctxt->status);
494                 ctxt->isInsertText = true;
495             } else {
496                 ctxt->SetErrorCode(code);
497             }
498             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
499         };
500 
501         int32_t code = InputMethodAbility::GetInstance().InsertText(ctxt->text, rspCallBack);
502         if (code != ErrorCode::NO_ERROR) {
503             ctxt->SetErrorCode(code);
504             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
505         }
506     };
507     ctxt->SetAction(std::move(input), std::move(output));
508     // 2 means JsAPI:insertText has 2 params at most.
509     EditAsyncCall asyncCall(env, info, ctxt, 2);
510     return asyncCall.Call(env, exec, __FUNCTION__);
511 }
512 
InsertTextSync(napi_env env,napi_callback_info info)513 napi_value JsTextInputClientEngine::InsertTextSync(napi_env env, napi_callback_info info)
514 {
515     InputMethodSyncTrace tracer("JS_InsertTextSync", GenerateTraceId());
516     size_t argc = 1;
517     napi_value argv[1] = { nullptr };
518     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
519     std::string text;
520     // 1 means least param num.
521     PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
522     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_string, "text must be string!", TYPE_STRING,
523         HandleParamCheckFailure(env));
524     PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], text), "text covert failed!", TYPE_NONE,
525         HandleParamCheckFailure(env));
526     IMSA_HILOGD("insert text, text: %{public}s.", text.c_str());
527     int32_t ret = InputMethodAbility::GetInstance().InsertText(text);
528     if (ret != ErrorCode::NO_ERROR) {
529         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to insert text!", TYPE_NONE);
530     }
531     return JsUtil::Const::Null(env);
532 }
533 
GetForwardSync(napi_env env,napi_callback_info info)534 napi_value JsTextInputClientEngine::GetForwardSync(napi_env env, napi_callback_info info)
535 {
536     InputMethodSyncTrace tracer("JS_GetForwardSync", GenerateTraceId());
537     size_t argc = 1;
538     napi_value argv[1] = { nullptr };
539     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
540     int32_t length = 0;
541     // 1 means least param num.
542     PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
543     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length must be string!", TYPE_NUMBER,
544         HandleParamCheckFailure(env));
545     PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], length), "length covert failed!", TYPE_NONE,
546         HandleParamCheckFailure(env));
547     PARAM_CHECK_RETURN(env, length >= 0, "length should no less than 0!", TYPE_NONE, HandleParamCheckFailure(env));
548     IMSA_HILOGD("get forward, length: %{public}d.", length);
549     std::u16string text;
550     int32_t ret = InputMethodAbility::GetInstance().GetTextBeforeCursor(length, text);
551     if (ret != ErrorCode::NO_ERROR) {
552         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to get forward!", TYPE_NONE);
553         return JsUtil::Const::Null(env);
554     }
555     napi_value result = nullptr;
556     auto status = JsUtils::GetValue(env, Str16ToStr8(text), result);
557     CHECK_RETURN(status == napi_ok, "GetValue failed", JsUtil::Const::Null(env));
558     return result;
559 }
560 
GetForward(napi_env env,napi_callback_info info)561 napi_value JsTextInputClientEngine::GetForward(napi_env env, napi_callback_info info)
562 {
563     auto traceId = GenerateTraceId();
564     InputMethodSyncTrace tracer("JS_GetForward_Start", traceId);
565     auto ctxt = std::make_shared<GetForwardContext>();
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, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
568         PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length type must be number!",
569             TYPE_NONE, napi_generic_failure);
570         auto status = JsUtils::GetValue(env, argv[0], ctxt->length);
571         return status;
572     };
573     auto output = [ctxt, traceId](napi_env env, napi_value *result) -> napi_status {
574         InputMethodSyncTrace tracer("JS_GetForward_Complete", traceId);
575         napi_value data = GetResult(env, ctxt->text);
576         *result = data;
577         return napi_ok;
578     };
579     auto exec = [ctxt, traceId](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
580         InputMethodSyncTrace tracer("JS_GetForward_Exec", traceId);
581         auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
582             if (code == ErrorCode::NO_ERROR) {
583                 VariantUtil::GetValue(data, ctxt->text);
584                 ctxt->status = napi_ok;
585                 ctxt->SetState(ctxt->status);
586             } else {
587                 ctxt->SetErrorCode(code);
588             }
589             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
590         };
591         std::u16string temp;
592         int32_t code = InputMethodAbility::GetInstance().GetTextBeforeCursor(ctxt->length, temp, rspCallBack);
593         if (code != ErrorCode::NO_ERROR) {
594             ctxt->SetErrorCode(code);
595             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
596         }
597     };
598     ctxt->SetAction(std::move(input), std::move(output));
599     // 2 means JsAPI:getForward has 2 params at most.
600     EditAsyncCall asyncCall(env, info, ctxt, 2);
601     return asyncCall.Call(env, exec, __FUNCTION__);
602 }
603 
GetBackwardSync(napi_env env,napi_callback_info info)604 napi_value JsTextInputClientEngine::GetBackwardSync(napi_env env, napi_callback_info info)
605 {
606     size_t argc = 1;
607     napi_value argv[1] = { nullptr };
608     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
609     int32_t length = 0;
610     // 1 means least param num.
611     PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
612     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length must be string!", TYPE_NUMBER,
613         HandleParamCheckFailure(env));
614     PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], length), "length covert failed!", TYPE_NONE,
615         HandleParamCheckFailure(env));
616     PARAM_CHECK_RETURN(env, length >= 0, "length should not less than 0!", TYPE_NONE, HandleParamCheckFailure(env));
617     IMSA_HILOGD("get backward, length: %{public}d.", length);
618     std::u16string text;
619     int32_t ret = InputMethodAbility::GetInstance().GetTextAfterCursor(length, text);
620     if (ret != ErrorCode::NO_ERROR) {
621         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to get backward!", TYPE_NONE);
622         return JsUtil::Const::Null(env);
623     }
624     napi_value result = nullptr;
625     auto status = JsUtils::GetValue(env, Str16ToStr8(text), result);
626     CHECK_RETURN(status == napi_ok, "GetValue failed", JsUtil::Const::Null(env));
627     return result;
628 }
629 
GetBackward(napi_env env,napi_callback_info info)630 napi_value JsTextInputClientEngine::GetBackward(napi_env env, napi_callback_info info)
631 {
632     auto ctxt = std::make_shared<GetBackwardContext>();
633     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
634         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
635         PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length type must be number!",
636             TYPE_NONE, napi_generic_failure);
637         auto status = JsUtils::GetValue(env, argv[0], ctxt->length);
638         return status;
639     };
640     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
641         napi_value data = GetResult(env, ctxt->text);
642         *result = data;
643         return napi_ok;
644     };
645     auto exec = [ctxt](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
646         auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
647             if (code == ErrorCode::NO_ERROR) {
648                 VariantUtil::GetValue(data, ctxt->text);
649                 ctxt->status = napi_ok;
650                 ctxt->SetState(ctxt->status);
651             } else {
652                 ctxt->SetErrorCode(code);
653             }
654             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
655         };
656         std::u16string temp;
657         int32_t code = InputMethodAbility::GetInstance().GetTextAfterCursor(ctxt->length, temp, rspCallBack);
658         if (code != ErrorCode::NO_ERROR) {
659             ctxt->SetErrorCode(code);
660             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
661         }
662     };
663     ctxt->SetAction(std::move(input), std::move(output));
664     // 2 means JsAPI:getBackward has 2 params at most.
665     EditAsyncCall asyncCall(env, info, ctxt, 2);
666     return asyncCall.Call(env, exec, __FUNCTION__);
667 }
668 
GetEditorAttributeSync(napi_env env,napi_callback_info info)669 napi_value JsTextInputClientEngine::GetEditorAttributeSync(napi_env env, napi_callback_info info)
670 {
671     TextTotalConfig config;
672     int32_t ret = InputMethodAbility::GetInstance().GetTextConfig(config);
673     if (ret != ErrorCode::NO_ERROR) {
674         IMSA_HILOGE("failed to get text config: %{public}d!", ret);
675         JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_IMCLIENT, "failed to get text config!", TYPE_NONE);
676     }
677     IMSA_HILOGD("inputPattern: %{public}d, enterKeyType: %{public}d, isTextPreviewSupported: %{public}d.",
678         config.inputAttribute.inputPattern, config.inputAttribute.enterKeyType,
679         config.inputAttribute.isTextPreviewSupported);
680     return JsInputAttribute::Write(env, config.inputAttribute);
681 }
682 
GetEditorAttribute(napi_env env,napi_callback_info info)683 napi_value JsTextInputClientEngine::GetEditorAttribute(napi_env env, napi_callback_info info)
684 {
685     auto ctxt = std::make_shared<GetEditorAttributeContext>();
686     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
687         *result = JsInputAttribute::Write(env, ctxt->inputAttribute);
688         return napi_ok;
689     };
690     auto exec = [ctxt](AsyncCall::Context *ctx) {
691         TextTotalConfig config;
692         int32_t ret = InputMethodAbility::GetInstance().GetTextConfig(config);
693         ctxt->inputAttribute = config.inputAttribute;
694         if (ret == ErrorCode::NO_ERROR) {
695             ctxt->SetState(napi_ok);
696             IMSA_HILOGD("inputPattern: %{public}d, enterKeyType: %{public}d, isTextPreviewSupported: %{public}d",
697                 config.inputAttribute.inputPattern, config.inputAttribute.enterKeyType,
698                 config.inputAttribute.isTextPreviewSupported);
699         } else {
700             IMSA_HILOGE("failed to get text config: %{public}d!", ret);
701             ctxt->SetErrorCode(IMFErrorCode::EXCEPTION_IMCLIENT);
702             ctxt->SetErrorMessage("failed to get text config!");
703         }
704     };
705     ctxt->SetAction(nullptr, std::move(output));
706     // 1 means JsAPI:getEditorAttribute has 1 param at most.
707     AsyncCall asyncCall(env, info, ctxt, 1);
708     return ASYNC_POST(env, exec);
709 }
710 
SelectByRange(napi_env env,napi_callback_info info)711 napi_value JsTextInputClientEngine::SelectByRange(napi_env env, napi_callback_info info)
712 {
713     IMSA_HILOGD("run in");
714     auto ctxt = std::make_shared<SelectContext>();
715     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
716         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
717         napi_valuetype valueType = napi_undefined;
718         napi_typeof(env, argv[0], &valueType);
719         PARAM_CHECK_RETURN(env, valueType == napi_object, "range type must be Range!", TYPE_NONE,
720             napi_generic_failure);
721         auto status = GetSelectRange(env, argv[0], ctxt);
722         return status;
723     };
724     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status { return napi_ok; };
725     auto exec = [ctxt](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
726         auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
727             if (code == ErrorCode::NO_ERROR) {
728                 ctxt->status = napi_ok;
729                 ctxt->SetState(ctxt->status);
730             } else {
731                 ctxt->SetErrorCode(code);
732             }
733             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
734         };
735         int32_t code = InputMethodAbility::GetInstance().SelectByRange(ctxt->start, ctxt->end, rspCallBack);
736         if (code != ErrorCode::NO_ERROR) {
737             ctxt->SetErrorCode(code);
738             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
739         }
740     };
741     ctxt->SetAction(std::move(input), std::move(output));
742     // 2 means JsAPI:selectByRange has 2 params at most.
743     EditAsyncCall asyncCall(env, info, ctxt, 2);
744     return asyncCall.Call(env, exec, __FUNCTION__);
745 }
746 
SelectByRangeSync(napi_env env,napi_callback_info info)747 napi_value JsTextInputClientEngine::SelectByRangeSync(napi_env env, napi_callback_info info)
748 {
749     IMSA_HILOGD("SelectByRangeSync");
750     size_t argc = 1;
751     napi_value argv[1] = { nullptr };
752     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
753     PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
754     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_object, "range type must be Range!", TYPE_NONE,
755         HandleParamCheckFailure(env));
756     auto ctxt = std::make_shared<SelectContext>();
757     auto status = GetSelectRange(env, argv[0], ctxt);
758     if (status != napi_ok) {
759         JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK,
760             "failed to get start or end, should have start and end number!", TYPE_NONE);
761         return JsUtil::Const::Null(env);
762     }
763     IMSA_HILOGD("start: %{public}d, end: %{public}d.", ctxt->start, ctxt->end);
764     int32_t ret = InputMethodAbility::GetInstance().SelectByRange(ctxt->start, ctxt->end);
765     if (ret != ErrorCode::NO_ERROR) {
766         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to select by range!", TYPE_NONE);
767     }
768     return JsUtil::Const::Null(env);
769 }
770 
SelectByMovementSync(napi_env env,napi_callback_info info)771 napi_value JsTextInputClientEngine::SelectByMovementSync(napi_env env, napi_callback_info info)
772 {
773     IMSA_HILOGD("run in");
774     size_t argc = 1;
775     napi_value argv[1] = { nullptr };
776     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
777     PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
778     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_object, "movement type must be Movement!",
779         TYPE_NONE, HandleParamCheckFailure(env));
780     auto ctxt = std::make_shared<SelectContext>();
781     auto status = GetSelectMovement(env, argv[0], ctxt);
782     if (status != napi_ok) {
783         JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK, "direction covert failed!", TYPE_NONE);
784         return JsUtil::Const::Null(env);
785     }
786     IMSA_HILOGD("direction: %{public}d.", ctxt->direction);
787     int32_t ret = InputMethodAbility::GetInstance().SelectByMovement(ctxt->direction);
788     if (ret != ErrorCode::NO_ERROR) {
789         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to select by movement!", TYPE_NONE);
790     }
791     return JsUtil::Const::Null(env);
792 }
793 
SelectByMovement(napi_env env,napi_callback_info info)794 napi_value JsTextInputClientEngine::SelectByMovement(napi_env env, napi_callback_info info)
795 {
796     IMSA_HILOGD("run in");
797     auto ctxt = std::make_shared<SelectContext>();
798     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
799         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
800         napi_valuetype valueType = napi_undefined;
801         napi_typeof(env, argv[0], &valueType);
802         PARAM_CHECK_RETURN(env, valueType == napi_object, "movement type must be Movement!", TYPE_NONE,
803             napi_generic_failure);
804         auto status = GetSelectMovement(env, argv[0], ctxt);
805         return status;
806     };
807     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status { return napi_ok; };
808     auto exec = [ctxt](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
809         auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
810             if (code == ErrorCode::NO_ERROR) {
811                 ctxt->status = napi_ok;
812                 ctxt->SetState(ctxt->status);
813             } else {
814                 ctxt->SetErrorCode(code);
815             }
816             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
817         };
818         int32_t code = InputMethodAbility::GetInstance().SelectByMovement(ctxt->direction, rspCallBack);
819         if (code != ErrorCode::NO_ERROR) {
820             ctxt->SetErrorCode(code);
821             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
822         }
823     };
824     ctxt->SetAction(std::move(input), std::move(output));
825     // 2 means JsAPI:selectByMovement has 2 params at most.
826     EditAsyncCall asyncCall(env, info, ctxt, 2);
827     return asyncCall.Call(env, exec, __FUNCTION__);
828 }
829 
SendExtendAction(napi_env env,napi_callback_info info)830 napi_value JsTextInputClientEngine::SendExtendAction(napi_env env, napi_callback_info info)
831 {
832     auto ctxt = std::make_shared<SendExtendActionContext>();
833     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
834         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
835         auto status = JsUtils::GetValue(env, argv[0], ctxt->action);
836         PARAM_CHECK_RETURN(env, status == napi_ok, "action must be number and should in ExtendAction",
837             TYPE_NONE, napi_generic_failure);
838         return status;
839     };
840     auto exec = [ctxt](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
841         auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
842             if (code == ErrorCode::NO_ERROR) {
843                 ctxt->SetState(napi_ok);
844             } else {
845                 ctxt->SetErrorCode(code);
846             }
847             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
848         };
849         int32_t code = InputMethodAbility::GetInstance().SendExtendAction(ctxt->action, rspCallBack);
850         if (code != ErrorCode::NO_ERROR) {
851             ctxt->SetErrorCode(code);
852             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
853         }
854     };
855     ctxt->SetAction(std::move(input));
856     // 2 means JsAPI:sendExtendAction has 2 params at most.
857     EditAsyncCall asyncCall(env, info, ctxt, 2);
858     return asyncCall.Call(env, exec, __FUNCTION__);
859 }
860 
GetTextIndexAtCursor(napi_env env,napi_callback_info info)861 napi_value JsTextInputClientEngine::GetTextIndexAtCursor(napi_env env, napi_callback_info info)
862 {
863     IMSA_HILOGD("GetTextIndexAtCursor");
864     auto ctxt = std::make_shared<GetTextIndexAtCursorContext>();
865     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
866         return napi_ok;
867     };
868     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
869         return napi_create_int32(env, ctxt->index, result);
870     };
871     auto exec = [ctxt](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
872         auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
873             if (code == ErrorCode::NO_ERROR) {
874                 VariantUtil::GetValue(data, ctxt->index);
875                 ctxt->status = napi_ok;
876                 ctxt->SetState(ctxt->status);
877             } else {
878                 ctxt->SetErrorCode(code);
879             }
880             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
881         };
882         int32_t code = InputMethodAbility::GetInstance().GetTextIndexAtCursor(ctxt->index, rspCallBack);
883         if (code != ErrorCode::NO_ERROR) {
884             ctxt->SetErrorCode(code);
885             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
886         }
887     };
888     ctxt->SetAction(std::move(input), std::move(output));
889     // 1 means JsAPI:getTextIndexAtCursor has 1 param at most.
890     EditAsyncCall asyncCall(env, info, ctxt, 1);
891     return asyncCall.Call(env, exec, __FUNCTION__);
892 }
893 
SetPreviewText(napi_env env,napi_callback_info info)894 napi_value JsTextInputClientEngine::SetPreviewText(napi_env env, napi_callback_info info)
895 {
896     auto traceId = GenerateTraceId();
897     InputMethodSyncTrace tracer("JS_SetPreviewText_Start", traceId);
898     IMSA_HILOGD("JsTextInputClientEngine in");
899     auto ctxt = std::make_shared<SetPreviewTextContext>();
900     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
901         if (GetPreviewTextParam(env, argc, argv, ctxt->text, ctxt->range) != napi_ok) {
902             return napi_generic_failure;
903         }
904         return napi_ok;
905     };
906     auto output = [ctxt, traceId](napi_env env, napi_value *result) -> napi_status {
907         InputMethodSyncTrace tracer("JS_SetPreviewText_Complete", traceId);
908         return napi_ok;
909     };
910     auto exec = [ctxt, traceId](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
911         InputMethodSyncTrace tracer("JS_SetPreviewText_Exec", traceId);
912         auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
913             if (code == ErrorCode::NO_ERROR) {
914                 IMSA_HILOGD("exec setPreviewText success");
915                 ctxt->SetState(napi_ok);
916             } else if (code == ErrorCode::ERROR_INVALID_RANGE) {
917                 ctxt->SetErrorCode(code);
918                 ctxt->SetErrorMessage("range should be included in preview text range, otherwise should be included in "
919                                       "total text range!");
920             } else {
921                 ctxt->SetErrorCode(code);
922             }
923             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
924         };
925         int32_t code = InputMethodAbility::GetInstance().SetPreviewText(ctxt->text, ctxt->range, rspCallBack);
926         if (code != ErrorCode::NO_ERROR) {
927             ctxt->SetErrorCode(code);
928             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
929         }
930     };
931     ctxt->SetAction(std::move(input), std::move(output));
932     // 2 means JsAPI:setPreviewText needs 2 params at most
933     EditAsyncCall asyncCall(env, info, ctxt, 2);
934     return asyncCall.Call(env, exec, __FUNCTION__);
935 }
936 
SetPreviewTextSync(napi_env env,napi_callback_info info)937 napi_value JsTextInputClientEngine::SetPreviewTextSync(napi_env env, napi_callback_info info)
938 {
939     InputMethodSyncTrace tracer("JS_SetPreviewTextSync", GenerateTraceId());
940     IMSA_HILOGD("start.");
941     // 2 means JsAPI:setPreviewText needs 2 params at most
942     size_t argc = 2;
943     napi_value argv[2] = { nullptr };
944     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
945     std::string text;
946     Range range;
947     if (GetPreviewTextParam(env, argc, argv, text, range) != napi_ok) {
948         return JsUtil::Const::Null(env);
949     }
950     int32_t ret = InputMethodAbility::GetInstance().SetPreviewText(text, range);
951     if (ret == ErrorCode::ERROR_INVALID_RANGE) {
952         JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK,
953             "range should be included in preview text range, otherwise should be included in total text range",
954             TYPE_NONE);
955     } else if (ret != ErrorCode::NO_ERROR) {
956         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to set preview text!", TYPE_NONE);
957     }
958     return JsUtil::Const::Null(env);
959 }
960 
FinishTextPreview(napi_env env,napi_callback_info info)961 napi_value JsTextInputClientEngine::FinishTextPreview(napi_env env, napi_callback_info info)
962 {
963     auto traceId = GenerateTraceId();
964     InputMethodSyncTrace tracer("JS_FinishTextPreview_Start", traceId);
965     IMSA_HILOGD("start.");
966     auto ctxt = std::make_shared<FinishTextPreviewContext>();
967     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
968         return napi_ok;
969     };
970     auto output = [ctxt, traceId](napi_env env, napi_value *result) -> napi_status {
971         InputMethodSyncTrace tracer("JS_FinishTextPreview_Complete", traceId);
972         return napi_ok;
973     };
974     auto exec = [ctxt, traceId](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
975         InputMethodSyncTrace tracer("JS_FinishTextPreview_Exec", traceId);
976         auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
977             if (code == ErrorCode::NO_ERROR) {
978                 IMSA_HILOGI("exec finishTextPreview success.");
979                 ctxt->SetState(napi_ok);
980             } else {
981                 ctxt->SetErrorCode(code);
982             }
983             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
984         };
985         int32_t code = InputMethodAbility::GetInstance().FinishTextPreview(rspCallBack);
986         if (code != ErrorCode::NO_ERROR) {
987             ctxt->SetErrorCode(code);
988             completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
989         }
990     };
991     ctxt->SetAction(std::move(input), std::move(output));
992     // 0 means JsAPI:finishTextPreview needs no param
993     EditAsyncCall asyncCall(env, info, ctxt, 0);
994     return asyncCall.Call(env, exec, __FUNCTION__);
995 }
996 
FinishTextPreviewSync(napi_env env,napi_callback_info info)997 napi_value JsTextInputClientEngine::FinishTextPreviewSync(napi_env env, napi_callback_info info)
998 {
999     InputMethodSyncTrace tracer("JS_FinishTextPreviewSync", GenerateTraceId());
1000     IMSA_HILOGD("JsTextInputClientEngine in");
1001     int32_t ret = InputMethodAbility::GetInstance().FinishTextPreview();
1002     if (ret != ErrorCode::NO_ERROR) {
1003         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to finish text preview!", TYPE_NONE);
1004     }
1005     return JsUtil::Const::Null(env);
1006 }
1007 
GetTextIndexAtCursorSync(napi_env env,napi_callback_info info)1008 napi_value JsTextInputClientEngine::GetTextIndexAtCursorSync(napi_env env, napi_callback_info info)
1009 {
1010     IMSA_HILOGD("start.");
1011     int32_t index = 0;
1012     int32_t ret = InputMethodAbility::GetInstance().GetTextIndexAtCursor(index);
1013     if (ret != ErrorCode::NO_ERROR) {
1014         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to get text index at cursor!", TYPE_NONE);
1015     }
1016     return JsUtil::GetValue(env, index);
1017 }
1018 
GetCallingWindowInfo(napi_env env,napi_callback_info info)1019 napi_value JsTextInputClientEngine::GetCallingWindowInfo(napi_env env, napi_callback_info info)
1020 {
1021     IMSA_HILOGD("start.");
1022     auto ctxt = std::make_shared<GetCallingWindowInfoContext>();
1023     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
1024         *result = JsCallingWindowInfo::Write(env, ctxt->windowInfo);
1025         return napi_ok;
1026     };
1027     auto exec = [ctxt](AsyncCall::Context *ctx) {
1028         int32_t ret = InputMethodAbility::GetInstance().GetCallingWindowInfo(ctxt->windowInfo);
1029         if (ret == ErrorCode::NO_ERROR) {
1030             IMSA_HILOGI("exec GetCallingWindowInfo success.");
1031             ctxt->SetState(napi_ok);
1032             return;
1033         }
1034         ctxt->SetErrorCode(ret);
1035     };
1036     ctxt->SetAction(nullptr, std::move(output));
1037     // 0 means JsAPI:getCallingWindowInfo needs no parameter.
1038     AsyncCall asyncCall(env, info, ctxt, 0);
1039     return ASYNC_POST(env, exec);
1040 }
1041 
GetPreviewTextParam(napi_env env,size_t argc,napi_value * argv,std::string & text,Range & range)1042 napi_status JsTextInputClientEngine::GetPreviewTextParam(napi_env env, size_t argc, napi_value *argv,
1043     std::string &text, Range &range)
1044 {
1045     // 2 means JsAPI:setPreviewText needs 2 params at least.
1046     PARAM_CHECK_RETURN(env, argc >= 2, "at least two parameters is required!", TYPE_NONE, napi_generic_failure);
1047     PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], text), "text covert failed, must be string!",
1048         TYPE_NONE, napi_generic_failure);
1049     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[1]) == napi_object, "range type must be Range!", TYPE_NONE,
1050         napi_generic_failure);
1051     PARAM_CHECK_RETURN(env, JsRange::Read(env, argv[1], range),
1052         "range covert failed, the range should have numbers start and end", TYPE_NONE, napi_generic_failure);
1053     return napi_ok;
1054 }
1055 
RegisterListener(napi_value callback,std::string type,std::shared_ptr<JSCallbackObject> callbackObj)1056 void JsTextInputClientEngine::RegisterListener(napi_value callback, std::string type,
1057     std::shared_ptr<JSCallbackObject> callbackObj)
1058 {
1059     IMSA_HILOGD("register listener: %{public}s.", type.c_str());
1060     std::lock_guard<std::recursive_mutex> lock(mutex_);
1061     if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) {
1062         IMSA_HILOGD("methodName: %{public}s not registered!", type.c_str());
1063     }
1064     auto callbacks = jsCbMap_[type];
1065     bool ret = std::any_of(callbacks.begin(), callbacks.end(), [&callback](std::shared_ptr<JSCallbackObject> cb) {
1066         if (cb == nullptr) {
1067             return false;
1068         }
1069         return JsUtils::Equals(cb->env_, callback, cb->callback_, cb->threadId_);
1070     });
1071     if (ret) {
1072         IMSA_HILOGD("JsTextInputClientEngine callback already registered!");
1073         return;
1074     }
1075 
1076     IMSA_HILOGI("add %{public}s callbackObj into jsCbMap_.", type.c_str());
1077     jsCbMap_[type].push_back(std::move(callbackObj));
1078 }
1079 
UnRegisterListener(napi_value callback,std::string type)1080 void JsTextInputClientEngine::UnRegisterListener(napi_value callback, std::string type)
1081 {
1082     IMSA_HILOGI("unregister listener: %{public}s.", type.c_str());
1083     std::lock_guard<std::recursive_mutex> lock(mutex_);
1084     if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) {
1085         IMSA_HILOGE("methodName: %{public}s already unregistered!", type.c_str());
1086         return;
1087     }
1088 
1089     if (callback == nullptr) {
1090         jsCbMap_.erase(type);
1091         IMSA_HILOGE("callback is nullptr.");
1092         return;
1093     }
1094 
1095     for (auto item = jsCbMap_[type].begin(); item != jsCbMap_[type].end(); item++) {
1096         if (JsUtils::Equals((*item)->env_, callback, (*item)->callback_, (*item)->threadId_)) {
1097             jsCbMap_[type].erase(item);
1098             break;
1099         }
1100     }
1101 
1102     if (jsCbMap_[type].empty()) {
1103         jsCbMap_.erase(type);
1104     }
1105 }
1106 
Write(napi_env env,const Rosen::Rect & nativeObject)1107 napi_value JsRect::Write(napi_env env, const Rosen::Rect &nativeObject)
1108 {
1109     napi_value jsObject = nullptr;
1110     napi_create_object(env, &jsObject);
1111     bool ret = JsUtil::Object::WriteProperty(env, jsObject, "left", nativeObject.posX_);
1112     ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "top", nativeObject.posY_);
1113     ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "width", nativeObject.width_);
1114     ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "height", nativeObject.height_);
1115     return ret ? jsObject : JsUtil::Const::Null(env);
1116 }
1117 
Read(napi_env env,napi_value jsObject,Rosen::Rect & nativeObject)1118 bool JsRect::Read(napi_env env, napi_value jsObject, Rosen::Rect &nativeObject)
1119 {
1120     auto ret = JsUtil::Object::ReadProperty(env, jsObject, "left", nativeObject.posX_);
1121     ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "top", nativeObject.posY_);
1122     ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "width", nativeObject.width_);
1123     ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "height", nativeObject.height_);
1124     return ret;
1125 }
1126 
Write(napi_env env,const CallingWindowInfo & nativeObject)1127 napi_value JsCallingWindowInfo::Write(napi_env env, const CallingWindowInfo &nativeObject)
1128 {
1129     napi_value jsObject = nullptr;
1130     napi_create_object(env, &jsObject);
1131     bool ret = JsUtil::Object::WriteProperty(env, jsObject, "rect", JsRect::Write(env, nativeObject.rect));
1132     ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "status", static_cast<uint32_t>(nativeObject.status));
1133     return ret ? jsObject : JsUtil::Const::Null(env);
1134 }
1135 
Read(napi_env env,napi_value object,CallingWindowInfo & nativeObject)1136 bool JsCallingWindowInfo::Read(napi_env env, napi_value object, CallingWindowInfo &nativeObject)
1137 {
1138     napi_value rectObject = nullptr;
1139     napi_get_named_property(env, object, "rect", &rectObject);
1140     auto ret = JsRect::Read(env, rectObject, nativeObject.rect);
1141     uint32_t status = 0;
1142     ret = ret && JsUtil::Object::ReadProperty(env, object, "status", status);
1143     nativeObject.status = static_cast<Rosen::WindowStatus>(status);
1144     return ret;
1145 }
1146 
HandleParamCheckFailure(napi_env env)1147 napi_value JsTextInputClientEngine::HandleParamCheckFailure(napi_env env)
1148 {
1149     return JsUtil::Const::Null(env);
1150 }
1151 
Write(napi_env env,const Range & nativeObject)1152 napi_value JsRange::Write(napi_env env, const Range &nativeObject)
1153 {
1154     napi_value jsObject = nullptr;
1155     napi_create_object(env, &jsObject);
1156     bool ret = JsUtil::Object::WriteProperty(env, jsObject, "start", nativeObject.start);
1157     ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "end", nativeObject.end);
1158     return ret ? jsObject : JsUtil::Const::Null(env);
1159 }
1160 
Read(napi_env env,napi_value jsObject,Range & nativeObject)1161 bool JsRange::Read(napi_env env, napi_value jsObject, Range &nativeObject)
1162 {
1163     auto ret = JsUtil::Object::ReadProperty(env, jsObject, "start", nativeObject.start);
1164     ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "end", nativeObject.end);
1165     return ret;
1166 }
1167 
Write(napi_env env,const InputAttribute & nativeObject)1168 napi_value JsInputAttribute::Write(napi_env env, const InputAttribute &nativeObject)
1169 {
1170     napi_value jsObject = nullptr;
1171     napi_create_object(env, &jsObject);
1172     auto ret = JsUtil::Object::WriteProperty(env, jsObject, "inputPattern", nativeObject.inputPattern);
1173     ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "enterKeyType", nativeObject.enterKeyType);
1174     ret = ret &&
1175           JsUtil::Object::WriteProperty(env, jsObject, "isTextPreviewSupported", nativeObject.isTextPreviewSupported);
1176     // not care write bundleName fail
1177     JsUtil::Object::WriteProperty(env, jsObject, "bundleName", nativeObject.bundleName);
1178     ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "immersiveMode", nativeObject.immersiveMode);
1179     ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "windowId", nativeObject.windowId);
1180     ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "displayId",
1181         static_cast<uint32_t>(nativeObject.callingDisplayId));
1182     ret = ret && JsUtil::Object::WritePropertyU16String(env, jsObject, "placeholder", nativeObject.placeholder);
1183     ret = ret && JsUtil::Object::WritePropertyU16String(env, jsObject, "abilityName", nativeObject.abilityName);
1184     int32_t capitalizeMode = static_cast<int32_t>(nativeObject.capitalizeMode);
1185     ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "capitalizeMode", capitalizeMode);
1186     ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "gradientMode", nativeObject.gradientMode);
1187     if (InputMethodAbility::GetInstance().IsSystemApp()) {
1188         ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "fluidLightMode", nativeObject.fluidLightMode);
1189     }
1190     return ret ? jsObject : JsUtil::Const::Null(env);
1191 }
1192 
Read(napi_env env,napi_value jsObject,InputAttribute & nativeObject)1193 bool JsInputAttribute::Read(napi_env env, napi_value jsObject, InputAttribute &nativeObject)
1194 {
1195     auto ret = JsUtil::Object::ReadProperty(env, jsObject, "inputPattern", nativeObject.inputPattern);
1196     ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "enterKeyType", nativeObject.enterKeyType);
1197     ret = ret &&
1198           JsUtil::Object::ReadProperty(env, jsObject, "isTextPreviewSupported", nativeObject.isTextPreviewSupported);
1199     // not care read bundleName fail
1200     JsUtil::Object::ReadProperty(env, jsObject, "bundleName", nativeObject.bundleName);
1201     JsUtil::Object::ReadPropertyU16String(env, jsObject, "placeholder", nativeObject.placeholder);
1202     IMSA_HILOGD("placeholder:%{public}s", StringUtils::ToHex(nativeObject.placeholder).c_str());
1203     JsUtil::Object::ReadPropertyU16String(env, jsObject, "abilityName", nativeObject.abilityName);
1204     IMSA_HILOGD("abilityName:%{public}s", StringUtils::ToHex(nativeObject.abilityName).c_str());
1205     int32_t capitalizeMode;
1206     if (!JsUtil::Object::ReadProperty(env, jsObject, "capitalizeMode", capitalizeMode)) {
1207         nativeObject.capitalizeMode = CapitalizeMode::NONE;
1208     } else {
1209         nativeObject.capitalizeMode = static_cast<CapitalizeMode>(capitalizeMode);
1210     }
1211     ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "immersiveMode", nativeObject.immersiveMode);
1212     ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "gradientMode", nativeObject.gradientMode);
1213     if (InputMethodAbility::GetInstance().IsSystemApp()) {
1214         ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "fluidLightMode", nativeObject.fluidLightMode);
1215     }
1216     return ret;
1217 }
1218 
Write(napi_env env,const AttachOptions & attachOptions)1219 napi_value JsAttachOptions::Write(napi_env env, const AttachOptions &attachOptions)
1220 {
1221     napi_value jsObject = nullptr;
1222     napi_create_object(env, &jsObject);
1223     bool ret = JsUtil::Object::WriteProperty(
1224         env, jsObject, "requestKeyboardReason", static_cast<uint32_t>(attachOptions.requestKeyboardReason));
1225     ret = ret && JsUtil::Object::WriteProperty(
1226         env, jsObject, "isSimpleKeyboardEnabled", attachOptions.isSimpleKeyboardEnabled);
1227     return ret ? jsObject : JsUtil::Const::Null(env);
1228 }
1229 
Read(napi_env env,napi_value jsObject,AttachOptions & attachOptions)1230 bool JsAttachOptions::Read(napi_env env, napi_value jsObject, AttachOptions &attachOptions)
1231 {
1232     uint32_t requestKeyboardReason = static_cast<uint32_t>(RequestKeyboardReason::NONE);
1233     auto ret = JsUtil::Object::ReadProperty(env, jsObject, "requestKeyboardReason", requestKeyboardReason);
1234     attachOptions.requestKeyboardReason = static_cast<RequestKeyboardReason>(requestKeyboardReason);
1235     ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "isSimpleKeyboardEnabled",
1236         attachOptions.isSimpleKeyboardEnabled);
1237     return ret;
1238 }
1239 
SendMessage(napi_env env,napi_callback_info info)1240 napi_value JsTextInputClientEngine::SendMessage(napi_env env, napi_callback_info info)
1241 {
1242     auto ctxt = std::make_shared<SendMessageContext>();
1243     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
1244         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
1245         PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_string, "msgId",
1246             TYPE_STRING, napi_generic_failure);
1247         CHECK_RETURN(JsUtils::GetValue(env, argv[0], ctxt->arrayBuffer.msgId) == napi_ok,
1248             "msgId covert failed!", napi_generic_failure);
1249         ctxt->arrayBuffer.jsArgc = argc;
1250         // 1 means first param msgId.
1251         if (argc > 1) {
1252             bool isArryBuffer = false;
1253             //  1 means second param msgParam index.
1254             CHECK_RETURN(napi_is_arraybuffer(env, argv[1], &isArryBuffer) == napi_ok,
1255                 "napi_is_arraybuffer failed!", napi_generic_failure);
1256             PARAM_CHECK_RETURN(env, isArryBuffer, "msgParam", TYPE_ARRAY_BUFFER, napi_generic_failure);
1257             CHECK_RETURN(JsUtils::GetValue(env, argv[1], ctxt->arrayBuffer.msgParam) == napi_ok,
1258                 "msgParam covert failed!", napi_generic_failure);
1259         }
1260         PARAM_CHECK_RETURN(env, ArrayBuffer::IsSizeValid(ctxt->arrayBuffer),
1261             "msgId limit 256B and msgParam limit 128KB.", TYPE_NONE, napi_generic_failure);
1262         ctxt->info = { std::chrono::system_clock::now(), ctxt->arrayBuffer };
1263         messageHandlerQueue_.Push(ctxt->info);
1264         return napi_ok;
1265     };
1266     auto exec = [ctxt](AsyncCall::Context *ctx) {
1267         messageHandlerQueue_.Wait(ctxt->info);
1268         int32_t code = InputMethodAbility::GetInstance().SendMessage(ctxt->arrayBuffer);
1269         messageHandlerQueue_.Pop();
1270         if (code == ErrorCode::NO_ERROR) {
1271             ctxt->status = napi_ok;
1272             ctxt->SetState(ctxt->status);
1273         } else {
1274             ctxt->SetErrorCode(code);
1275         }
1276     };
1277     ctxt->SetAction(std::move(input), nullptr);
1278     // 2 means JsAPI:sendMessage has 2 params at most.
1279     AsyncCall asyncCall(env, info, ctxt, 2);
1280     return asyncCall.Call(env, exec, "imaSendMessage");
1281 }
1282 
RecvMessage(napi_env env,napi_callback_info info)1283 napi_value JsTextInputClientEngine::RecvMessage(napi_env env, napi_callback_info info)
1284 {
1285     size_t argc = ARGC_TWO;
1286     napi_value argv[ARGC_TWO] = {nullptr};
1287     napi_value thisVar = nullptr;
1288     void *data = nullptr;
1289     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
1290     if (argc < 0) {
1291         IMSA_HILOGE("RecvMessage failed! argc abnormal.");
1292         return nullptr;
1293     }
1294     void *native = nullptr;
1295     auto status = napi_unwrap(env, thisVar, &native);
1296     CHECK_RETURN((status == napi_ok && native != nullptr), "napi_unwrap failed!", nullptr);
1297     auto inputClient = reinterpret_cast<JsTextInputClientEngine *>(native);
1298     if (inputClient == nullptr) {
1299         IMSA_HILOGI("Unwrap js object self is nullptr.");
1300         return nullptr;
1301     }
1302     if (argc == 0) {
1303         IMSA_HILOGI("RecvMessage off.");
1304         InputMethodAbility::GetInstance().RegisterMsgHandler();
1305         return nullptr;
1306     }
1307     IMSA_HILOGI("RecvMessage on.");
1308     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_object, "msgHnadler (MessageHandler)",
1309         TYPE_OBJECT, nullptr);
1310 
1311     napi_value onMessage = nullptr;
1312     CHECK_RETURN(napi_get_named_property(env, argv[0], "onMessage", &onMessage) == napi_ok,
1313         "Get onMessage property failed!", nullptr);
1314     CHECK_RETURN(JsUtil::GetType(env, onMessage) == napi_function, "onMessage is not napi_function!", nullptr);
1315     napi_value onTerminated = nullptr;
1316     CHECK_RETURN(napi_get_named_property(env, argv[0], "onTerminated", &onTerminated) == napi_ok,
1317         "Get onMessage property failed!", nullptr);
1318     CHECK_RETURN(JsUtil::GetType(env, onTerminated) == napi_function, "onTerminated is not napi_function!", nullptr);
1319 
1320     std::shared_ptr<MsgHandlerCallbackInterface> callback =
1321         std::make_shared<JsTextInputClientEngine::JsMessageHandler>(env, onTerminated, onMessage);
1322     InputMethodAbility::GetInstance().RegisterMsgHandler(callback);
1323     napi_value result = nullptr;
1324     napi_get_null(env, &result);
1325     return result;
1326 }
1327 
OnAttachOptionsChanged(const AttachOptions & attachOptions)1328 void JsTextInputClientEngine::OnAttachOptionsChanged(const AttachOptions &attachOptions)
1329 {
1330     IMSA_HILOGD("OnAttachOptionsChanged requestKeyboardReason:%{public}d.", attachOptions.requestKeyboardReason);
1331     std::string type = "attachOptionsDidChange";
1332     auto entry = GetEntry(type, [&attachOptions](UvEntry &entry) {
1333         entry.attachOptions.requestKeyboardReason = attachOptions.requestKeyboardReason;
1334         entry.attachOptions.isSimpleKeyboardEnabled = attachOptions.isSimpleKeyboardEnabled;
1335     });
1336     if (entry == nullptr) {
1337         IMSA_HILOGE("failed to get uv entry!");
1338         return;
1339     }
1340     auto eventHandler = GetEventHandler();
1341     if (eventHandler == nullptr) {
1342         IMSA_HILOGE("eventHandler is nullptr!");
1343         return;
1344     }
1345     auto task = [entry]() {
1346         auto gitAttachOptionsParams = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
1347             if (argc == 0) {
1348                 return false;
1349             }
1350             napi_value attachOptions = JsAttachOptions::Write(env, entry->attachOptions);
1351             // 0 means the first param of callback.
1352             args[0] = attachOptions;
1353             return true;
1354         };
1355         JsCallbackHandler::Traverse({ entry->vecCopy }, { 1, gitAttachOptionsParams });
1356     };
1357     eventHandler->PostTask(task, type, 0, AppExecFwk::EventQueue::Priority::VIP);
1358 }
1359 
GetAttachOptions(napi_env env,napi_callback_info info)1360 napi_value JsTextInputClientEngine::GetAttachOptions(napi_env env, napi_callback_info info)
1361 {
1362     auto attachOptions = InputMethodAbility::GetInstance().GetAttachOptions();
1363     IMSA_HILOGD("GetAttachOptions:%{public}d/%{public}d.", attachOptions.requestKeyboardReason,
1364         attachOptions.isSimpleKeyboardEnabled);
1365     return JsAttachOptions::Write(env, attachOptions);
1366 }
1367 
Subscribe(napi_env env,napi_callback_info info)1368 napi_value JsTextInputClientEngine::Subscribe(napi_env env, napi_callback_info info)
1369 {
1370     size_t argc = ARGC_TWO;
1371     napi_value argv[ARGC_TWO] = { nullptr };
1372     napi_value thisVar = nullptr;
1373     void *data = nullptr;
1374     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
1375     std::string type;
1376     // 2 means least param num.
1377     if (argc < ARGC_TWO || !JsUtil::GetValue(env, argv[0], type) ||
1378         !EventChecker::IsValidEventType(EventSubscribeModule::TEXT_INPUT_CLIENT, type) ||
1379         JsUtil::GetType(env, argv[1]) != napi_function) {
1380         IMSA_HILOGE("subscribe failed, type: %{public}s.", type.c_str());
1381         return nullptr;
1382     }
1383     IMSA_HILOGD("subscribe type:%{public}s.", type.c_str());
1384     auto engine = reinterpret_cast<JsTextInputClientEngine *>(JsUtils::GetNativeSelf(env, info));
1385     if (engine == nullptr) {
1386         return nullptr;
1387     }
1388     std::shared_ptr<JSCallbackObject> callback =
1389         std::make_shared<JSCallbackObject>(env, argv[ARGC_ONE], std::this_thread::get_id(),
1390             AppExecFwk::EventHandler::Current());
1391     engine->RegisterListener(argv[ARGC_ONE], type, callback);
1392 
1393     napi_value result = nullptr;
1394     napi_get_null(env, &result);
1395     return result;
1396 }
1397 
UnSubscribe(napi_env env,napi_callback_info info)1398 napi_value JsTextInputClientEngine::UnSubscribe(napi_env env, napi_callback_info info)
1399 {
1400     size_t argc = ARGC_TWO;
1401     napi_value argv[ARGC_TWO] = { nullptr };
1402     napi_value thisVar = nullptr;
1403     void *data = nullptr;
1404     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
1405     std::string type;
1406     // 1 means least param num.
1407     if (argc < ARGC_ONE || !JsUtil::GetValue(env, argv[0], type) ||
1408         !EventChecker::IsValidEventType(EventSubscribeModule::TEXT_INPUT_CLIENT, type)) {
1409         IMSA_HILOGE("unsubscribe failed, type: %{public}s!", type.c_str());
1410         return nullptr;
1411     }
1412     // if the second param is not napi_function/napi_null/napi_undefined, return
1413     auto paramType = JsUtil::GetType(env, argv[1]);
1414     if (paramType != napi_function && paramType != napi_null && paramType != napi_undefined) {
1415         return nullptr;
1416     }
1417     // if the second param is napi_function, delete it, else delete all
1418     argv[1] = paramType == napi_function ? argv[1] : nullptr;
1419 
1420     IMSA_HILOGD("unsubscribe type: %{public}s.", type.c_str());
1421     auto setting = reinterpret_cast<JsTextInputClientEngine *>(JsUtils::GetNativeSelf(env, info));
1422     if (setting == nullptr) {
1423         return nullptr;
1424     }
1425     setting->UnRegisterListener(argv[ARGC_ONE], type);
1426     napi_value result = nullptr;
1427     napi_get_null(env, &result);
1428     return result;
1429 }
1430 
GetEventHandler()1431 std::shared_ptr<AppExecFwk::EventHandler> JsTextInputClientEngine::GetEventHandler()
1432 {
1433     std::lock_guard<std::mutex> lock(eventHandlerMutex_);
1434     return handler_;
1435 }
1436 
GetEntry(const std::string & type,EntrySetter entrySetter)1437 std::shared_ptr<JsTextInputClientEngine::UvEntry> JsTextInputClientEngine::GetEntry(
1438     const std::string &type, EntrySetter entrySetter)
1439 {
1440     IMSA_HILOGD("type: %{public}s.", type.c_str());
1441     std::shared_ptr<UvEntry> entry = nullptr;
1442     {
1443         std::lock_guard<std::recursive_mutex> lock(mutex_);
1444         if (jsCbMap_[type].empty()) {
1445             IMSA_HILOGD("%{public}s cb-vector is empty", type.c_str());
1446             return nullptr;
1447         }
1448         entry = std::make_shared<UvEntry>(jsCbMap_[type], type);
1449     }
1450     if (entrySetter != nullptr) {
1451         entrySetter(*entry);
1452     }
1453     return entry;
1454 }
1455 
OnTerminated()1456 int32_t JsTextInputClientEngine::JsMessageHandler::OnTerminated()
1457 {
1458     std::lock_guard<decltype(callbackObjectMutex_)> lock(callbackObjectMutex_);
1459     if (jsMessageHandler_ == nullptr) {
1460         IMSA_HILOGI("jsCallbackObject is nullptr, can not call OnTerminated!.");
1461         return ErrorCode::ERROR_NULL_POINTER;
1462     }
1463     auto eventHandler = jsMessageHandler_->GetEventHandler();
1464     if (eventHandler == nullptr) {
1465         IMSA_HILOGI("EventHandler is nullptr!.");
1466         return ErrorCode::ERROR_NULL_POINTER;
1467     }
1468     auto task = [jsCallback = std::move(jsMessageHandler_)]() {
1469         napi_value callback = nullptr;
1470         napi_value global = nullptr;
1471         if (jsCallback == nullptr) {
1472             IMSA_HILOGI("jsCallback is nullptr!.");
1473             return;
1474         }
1475         napi_get_reference_value(jsCallback->env_, jsCallback->onTerminatedCallback_, &callback);
1476         if (callback != nullptr) {
1477             napi_get_global(jsCallback->env_, &global);
1478             napi_value output = nullptr;
1479             // 0 means the callback has no param.
1480             auto status = napi_call_function(jsCallback->env_, global, callback, 0, nullptr, &output);
1481             if (status != napi_ok) {
1482                 IMSA_HILOGI("Call js function failed!.");
1483                 output = nullptr;
1484             }
1485         }
1486     };
1487     eventHandler->PostTask(task, "IMA_MsgHandler_OnTerminated", 0, AppExecFwk::EventQueue::Priority::VIP);
1488     return ErrorCode::NO_ERROR;
1489 }
1490 
OnMessage(const ArrayBuffer & arrayBuffer)1491 int32_t JsTextInputClientEngine::JsMessageHandler::OnMessage(const ArrayBuffer &arrayBuffer)
1492 {
1493     std::lock_guard<decltype(callbackObjectMutex_)> lock(callbackObjectMutex_);
1494     if (jsMessageHandler_ == nullptr) {
1495         IMSA_HILOGE("MessageHandler was not regist!.");
1496         return ErrorCode::ERROR_MSG_HANDLER_NOT_REGIST;
1497     }
1498     auto eventHandler = jsMessageHandler_->GetEventHandler();
1499     if (eventHandler == nullptr) {
1500         IMSA_HILOGI("EventHandler is nullptr!.");
1501         return ErrorCode::ERROR_NULL_POINTER;
1502     }
1503     auto task = [jsCallbackObject = jsMessageHandler_, arrayBuffer]() {
1504         napi_value callback = nullptr;
1505         napi_value global = nullptr;
1506         if (jsCallbackObject == nullptr) {
1507             IMSA_HILOGI("jsCallbackObject is nullptr!.");
1508             return;
1509         }
1510         napi_get_reference_value(jsCallbackObject->env_, jsCallbackObject->onMessageCallback_, &callback);
1511         if (callback != nullptr) {
1512             napi_get_global(jsCallbackObject->env_, &global);
1513             napi_value output = nullptr;
1514             napi_value argv[ARGC_TWO] = { nullptr };
1515             // 2 means just use the first two parameters
1516             if (JsUtils::GetMessageHandlerCallbackParam(argv, jsCallbackObject, arrayBuffer, 2) != napi_ok) {
1517                 IMSA_HILOGE("Get message handler callback param failed!.");
1518                 return;
1519             }
1520             // The maximum valid parameters count of callback is 2.
1521             auto callbackArgc = arrayBuffer.jsArgc > ARGC_ONE ? ARGC_TWO : ARGC_ONE;
1522             auto status = napi_call_function(jsCallbackObject->env_, global, callback, callbackArgc, argv, &output);
1523             if (status != napi_ok) {
1524                 IMSA_HILOGI("Call js function failed!.");
1525                 output = nullptr;
1526             }
1527         }
1528     };
1529     eventHandler->PostTask(task, "IMC_MsgHandler_OnMessage", 0, AppExecFwk::EventQueue::Priority::VIP);
1530     return ErrorCode::NO_ERROR;
1531 }
1532 } // namespace MiscServices
1533 } // namespace OHOS