• 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 "input_method_ability.h"
19 #include "inputmethod_trace.h"
20 #include "js_util.h"
21 #include "js_utils.h"
22 #include "napi/native_api.h"
23 #include "napi/native_node_api.h"
24 #include "string_ex.h"
25 #include "wm_common.h"
26 
27 namespace OHOS {
28 namespace MiscServices {
29 #define ASYNC_POST(env, ctx) asyncCall.Post((env), (ctx), taskQueue_, __FUNCTION__)
30 using namespace std::chrono;
31 thread_local napi_ref JsTextInputClientEngine::TICRef_ = nullptr;
32 const std::string JsTextInputClientEngine::TIC_CLASS_NAME = "TextInputClient";
33 constexpr int32_t MAX_WAIT_TIME = 5000;
34 constexpr int32_t MAX_WAIT_TIME_PRIVATE_COMMAND = 2000;
35 constexpr int32_t MAX_WAIT_TIME_MESSAGE_HANDLER = 2000;
36 constexpr size_t ARGC_TWO = 2;
37 constexpr size_t ARGC_ONE = 1;
38 std::shared_ptr<AsyncCall::TaskQueue> JsTextInputClientEngine::taskQueue_ = std::make_shared<AsyncCall::TaskQueue>();
39 BlockQueue<PrivateCommandInfo> JsTextInputClientEngine::privateCommandQueue_{ MAX_WAIT_TIME_PRIVATE_COMMAND };
40 BlockQueue<MessageHandlerInfo> JsTextInputClientEngine::messageHandlerQueue_{ MAX_WAIT_TIME_MESSAGE_HANDLER };
41 uint32_t JsTextInputClientEngine::traceId_{ 0 };
Init(napi_env env,napi_value info)42 napi_value JsTextInputClientEngine::Init(napi_env env, napi_value info)
43 {
44     IMSA_HILOGD("JsTextInputClientEngine init");
45     napi_property_descriptor properties[] = {
46         DECLARE_NAPI_FUNCTION("sendKeyFunction", SendKeyFunction),
47         DECLARE_NAPI_FUNCTION("deleteForward", DeleteForward),
48         DECLARE_NAPI_FUNCTION("deleteBackward", DeleteBackward),
49         DECLARE_NAPI_FUNCTION("insertText", InsertText),
50         DECLARE_NAPI_FUNCTION("getForward", GetForward),
51         DECLARE_NAPI_FUNCTION("getBackward", GetBackward),
52         DECLARE_NAPI_FUNCTION("getEditorAttribute", GetEditorAttribute),
53         DECLARE_NAPI_FUNCTION("getTextIndexAtCursor", GetTextIndexAtCursor),
54         DECLARE_NAPI_FUNCTION("moveCursor", MoveCursor),
55         DECLARE_NAPI_FUNCTION("selectByRange", SelectByRange),
56         DECLARE_NAPI_FUNCTION("selectByMovement", SelectByMovement),
57         DECLARE_NAPI_FUNCTION("sendExtendAction", SendExtendAction),
58         DECLARE_NAPI_FUNCTION("insertTextSync", InsertTextSync),
59         DECLARE_NAPI_FUNCTION("moveCursorSync", MoveCursorSync),
60         DECLARE_NAPI_FUNCTION("getEditorAttributeSync", GetEditorAttributeSync),
61         DECLARE_NAPI_FUNCTION("selectByRangeSync", SelectByRangeSync),
62         DECLARE_NAPI_FUNCTION("selectByMovementSync", SelectByMovementSync),
63         DECLARE_NAPI_FUNCTION("getTextIndexAtCursorSync", GetTextIndexAtCursorSync),
64         DECLARE_NAPI_FUNCTION("deleteForwardSync", DeleteForwardSync),
65         DECLARE_NAPI_FUNCTION("deleteBackwardSync", DeleteBackwardSync),
66         DECLARE_NAPI_FUNCTION("getForwardSync", GetForwardSync),
67         DECLARE_NAPI_FUNCTION("getBackwardSync", GetBackwardSync),
68         DECLARE_NAPI_FUNCTION("sendPrivateCommand", SendPrivateCommand),
69         DECLARE_NAPI_FUNCTION("getCallingWindowInfo", GetCallingWindowInfo),
70         DECLARE_NAPI_FUNCTION("setPreviewText", SetPreviewText),
71         DECLARE_NAPI_FUNCTION("setPreviewTextSync", SetPreviewTextSync),
72         DECLARE_NAPI_FUNCTION("finishTextPreview", FinishTextPreview),
73         DECLARE_NAPI_FUNCTION("finishTextPreviewSync", FinishTextPreviewSync),
74         DECLARE_NAPI_FUNCTION("sendMessage", SendMessage),
75         DECLARE_NAPI_FUNCTION("recvMessage", RecvMessage),
76     };
77     napi_value cons = nullptr;
78     NAPI_CALL(env, napi_define_class(env, TIC_CLASS_NAME.c_str(), TIC_CLASS_NAME.size(), JsConstructor, nullptr,
79                        sizeof(properties) / sizeof(napi_property_descriptor), properties, &cons));
80     NAPI_CALL(env, napi_create_reference(env, cons, 1, &TICRef_));
81     NAPI_CALL(env, napi_set_named_property(env, info, TIC_CLASS_NAME.c_str(), cons));
82 
83     return info;
84 }
85 
MoveCursor(napi_env env,napi_callback_info info)86 napi_value JsTextInputClientEngine::MoveCursor(napi_env env, napi_callback_info info)
87 {
88     auto ctxt = std::make_shared<MoveCursorContext>();
89     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
90         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
91         PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "direction type must be number!",
92             TYPE_NONE, napi_generic_failure);
93         auto status = JsUtils::GetValue(env, argv[0], ctxt->num);
94         // 1 means least param num.
95         PARAM_CHECK_RETURN(env, ctxt->num >= 0, "direction should be not less than 0!", TYPE_NONE,
96             napi_generic_failure);
97         return status;
98     };
99     auto exec = [ctxt](AsyncCall::Context *ctx) {
100         int32_t code = InputMethodAbility::GetInstance()->MoveCursor(ctxt->num);
101         if (code == ErrorCode::NO_ERROR) {
102             ctxt->status = napi_ok;
103             ctxt->SetState(ctxt->status);
104         } else {
105             ctxt->SetErrorCode(code);
106         }
107     };
108     ctxt->SetAction(std::move(input));
109     // 2 means JsAPI:moveCursor has 2 params at most.
110     AsyncCall asyncCall(env, info, ctxt, 2);
111     return ASYNC_POST(env, exec);
112 }
113 
MoveCursorSync(napi_env env,napi_callback_info info)114 napi_value JsTextInputClientEngine::MoveCursorSync(napi_env env, napi_callback_info info)
115 {
116     size_t argc = 1;
117     napi_value argv[1] = { nullptr };
118     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
119     int32_t direction = 0;
120     // 1 means least param num.
121     PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
122     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "direction must be number!", TYPE_NUMBER,
123         HandleParamCheckFailure(env));
124     PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], direction), "direction covert failed!", TYPE_NONE,
125         HandleParamCheckFailure(env));
126     PARAM_CHECK_RETURN(env, direction >= 0, "direction should be no less than 0!", TYPE_NONE,
127         HandleParamCheckFailure(env));
128     IMSA_HILOGD("moveCursor , direction: %{public}d", direction);
129     int32_t ret = InputMethodAbility::GetInstance()->MoveCursor(direction);
130     if (ret != ErrorCode::NO_ERROR) {
131         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to move cursor!", TYPE_NONE);
132     }
133     return JsUtil::Const::Null(env);
134 }
135 
JsConstructor(napi_env env,napi_callback_info info)136 napi_value JsTextInputClientEngine::JsConstructor(napi_env env, napi_callback_info info)
137 {
138     napi_value thisVar = nullptr;
139     NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr));
140 
141     JsTextInputClientEngine *clientObject = new (std::nothrow) JsTextInputClientEngine();
142     if (clientObject == nullptr) {
143         IMSA_HILOGE("clientObject is nullptr!");
144         napi_value result = nullptr;
145         napi_get_null(env, &result);
146         return result;
147     }
148     auto finalize = [](napi_env env, void *data, void *hint) {
149         IMSA_HILOGD("finalize.");
150         auto *objInfo = reinterpret_cast<JsTextInputClientEngine *>(data);
151         if (objInfo != nullptr) {
152             delete objInfo;
153         }
154     };
155     napi_status status = napi_wrap(env, thisVar, clientObject, finalize, nullptr, nullptr);
156     if (status != napi_ok) {
157         IMSA_HILOGE("failed to wrap: %{public}d!", status);
158         delete clientObject;
159         return nullptr;
160     }
161     return thisVar;
162 }
163 
GetTextInputClientInstance(napi_env env)164 napi_value JsTextInputClientEngine::GetTextInputClientInstance(napi_env env)
165 {
166     napi_value instance = nullptr;
167     napi_value cons = nullptr;
168     if (napi_get_reference_value(env, TICRef_, &cons) != napi_ok) {
169         IMSA_HILOGE("failed to get reference value!");
170         return nullptr;
171     }
172     if (napi_new_instance(env, cons, 0, nullptr, &instance) != napi_ok) {
173         IMSA_HILOGE("failed to new instance!");
174         return nullptr;
175     }
176     return instance;
177 }
178 
GetResult(napi_env env,std::string & text)179 napi_value JsTextInputClientEngine::GetResult(napi_env env, std::string &text)
180 {
181     napi_value jsText = nullptr;
182     napi_create_string_utf8(env, text.c_str(), NAPI_AUTO_LENGTH, &jsText);
183     return jsText;
184 }
185 
GetSelectRange(napi_env env,napi_value argv,std::shared_ptr<SelectContext> ctxt)186 napi_status JsTextInputClientEngine::GetSelectRange(napi_env env, napi_value argv, std::shared_ptr<SelectContext> ctxt)
187 {
188     napi_status status = napi_generic_failure;
189     napi_value napiValue = nullptr;
190     status = napi_get_named_property(env, argv, "start", &napiValue);
191     PARAM_CHECK_RETURN(env, status == napi_ok, "start of range cannot empty and must be number.", TYPE_NONE, status);
192     status = JsUtils::GetValue(env, napiValue, ctxt->start);
193     CHECK_RETURN(status == napi_ok, "failed to get start value!", status);
194 
195     status = napi_get_named_property(env, argv, "end", &napiValue);
196     PARAM_CHECK_RETURN(env, status == napi_ok, "end of range cannot empty and must be number.", TYPE_NONE, status);
197     status = JsUtils::GetValue(env, napiValue, ctxt->end);
198     if (status != napi_ok) {
199         IMSA_HILOGE("failed to get end value!");
200     }
201     return status;
202 }
203 
GetSelectMovement(napi_env env,napi_value argv,std::shared_ptr<SelectContext> ctxt)204 napi_status JsTextInputClientEngine::GetSelectMovement(
205     napi_env env, napi_value argv, std::shared_ptr<SelectContext> ctxt)
206 {
207     napi_status status = napi_generic_failure;
208     napi_value napiValue = nullptr;
209     status = napi_get_named_property(env, argv, "direction", &napiValue);
210     PARAM_CHECK_RETURN(env, status == napi_ok, "direction must be exist!", TYPE_NONE, status);
211     status = JsUtils::GetValue(env, napiValue, ctxt->direction);
212     if (status != napi_ok) {
213         IMSA_HILOGE("failed to get direction value!");
214     }
215     PARAM_CHECK_RETURN(env, status == napi_ok, "direction type must be Direction!", TYPE_NONE, status);
216     return status;
217 }
218 
SendKeyFunction(napi_env env,napi_callback_info info)219 napi_value JsTextInputClientEngine::SendKeyFunction(napi_env env, napi_callback_info info)
220 {
221     auto ctxt = std::make_shared<SendKeyFunctionContext>();
222     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
223         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
224         napi_status ret = JsUtils::GetValue(env, argv[0], ctxt->action);
225         PARAM_CHECK_RETURN(env, ret == napi_ok, "action type must be number!", TYPE_NONE, napi_generic_failure);
226         return napi_ok;
227     };
228     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
229         napi_status status = napi_get_boolean(env, ctxt->isSendKeyFunction, result);
230         return status;
231     };
232     auto exec = [ctxt](AsyncCall::Context *ctx) {
233         int32_t code = InputMethodAbility::GetInstance()->SendFunctionKey(ctxt->action);
234         if (code == ErrorCode::NO_ERROR) {
235             ctxt->status = napi_ok;
236             ctxt->SetState(ctxt->status);
237             ctxt->isSendKeyFunction = true;
238         } else {
239             ctxt->SetErrorCode(code);
240         }
241     };
242     ctxt->SetAction(std::move(input), std::move(output));
243     // 2 means JsAPI:sendKeyFunction has 2 params at most.
244     AsyncCall asyncCall(env, info, ctxt, 2);
245     return ASYNC_POST(env, exec);
246 }
247 
SendPrivateCommand(napi_env env,napi_callback_info info)248 napi_value JsTextInputClientEngine::SendPrivateCommand(napi_env env, napi_callback_info info)
249 {
250     auto ctxt = std::make_shared<SendPrivateCommandContext>();
251     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
252         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
253         napi_status status = JsUtils::GetValue(env, argv[0], ctxt->privateCommand);
254         CHECK_RETURN(status == napi_ok,
255             "commandData covert failed, type must be Record<string, CommandDataType>", status);
256         PARAM_CHECK_RETURN(env, TextConfig::IsPrivateCommandValid(ctxt->privateCommand),
257             "commandData size limit 32KB, count limit 5.", TYPE_NONE, napi_generic_failure);
258         ctxt->info = { std::chrono::system_clock::now(), ctxt->privateCommand };
259         privateCommandQueue_.Push(ctxt->info);
260         return status;
261     };
262     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status { return napi_ok; };
263     auto exec = [ctxt](AsyncCall::Context *ctx) {
264         privateCommandQueue_.Wait(ctxt->info);
265         int32_t code = InputMethodAbility::GetInstance()->SendPrivateCommand(ctxt->privateCommand);
266         privateCommandQueue_.Pop();
267         if (code == ErrorCode::NO_ERROR) {
268             ctxt->status = napi_ok;
269             ctxt->SetState(ctxt->status);
270         } else {
271             ctxt->SetErrorCode(code);
272         }
273     };
274     ctxt->SetAction(std::move(input), std::move(output));
275     // 1 means JsAPI:SendPrivateCommand has 1 param at most.
276     AsyncCall asyncCall(env, info, ctxt, 1);
277     return ASYNC_POST(env, exec);
278 }
279 
DeleteForwardSync(napi_env env,napi_callback_info info)280 napi_value JsTextInputClientEngine::DeleteForwardSync(napi_env env, napi_callback_info info)
281 {
282     InputMethodSyncTrace tracer("JS_DeleteForwardSync", GenerateTraceId());
283     size_t argc = 1;
284     napi_value argv[1] = { nullptr };
285     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
286     int32_t length = 0;
287     // 1 means least param num.
288     PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
289     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length must be number!", TYPE_NUMBER,
290         HandleParamCheckFailure(env));
291     PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], length), "length covert failed", TYPE_NONE,
292         HandleParamCheckFailure(env));
293     PARAM_CHECK_RETURN(env, length >= 0, "length should not less than 0!", TYPE_NONE, HandleParamCheckFailure(env));
294     IMSA_HILOGD("delete forward, length: %{public}d.", length);
295     int32_t ret = InputMethodAbility::GetInstance()->DeleteForward(length);
296     if (ret != ErrorCode::NO_ERROR) {
297         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to delete forward", TYPE_NONE);
298     }
299     return JsUtil::Const::Null(env);
300 }
301 
DeleteForward(napi_env env,napi_callback_info info)302 napi_value JsTextInputClientEngine::DeleteForward(napi_env env, napi_callback_info info)
303 {
304     auto traceId = GenerateTraceId();
305     InputMethodSyncTrace tracer("JS_DeleteForward_Start", traceId);
306     auto ctxt = std::make_shared<DeleteForwardContext>();
307     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
308         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
309         PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length type must be number!",
310             TYPE_NONE, napi_generic_failure);
311         auto status = JsUtils::GetValue(env, argv[0], ctxt->length);
312         PARAM_CHECK_RETURN(env, ctxt->length >= 0, "length should no less than 0!", TYPE_NONE, napi_generic_failure);
313         return status;
314     };
315     auto output = [ctxt, traceId](napi_env env, napi_value *result) -> napi_status {
316         InputMethodSyncTrace tracer("JS_DeleteForward_Complete", traceId);
317         napi_status status = napi_get_boolean(env, ctxt->isDeleteForward, result);
318         return status;
319     };
320     auto exec = [ctxt, traceId](AsyncCall::Context *ctx) {
321         InputMethodSyncTrace tracer("JS_DeleteForward_Exec", traceId);
322         int32_t code = InputMethodAbility::GetInstance()->DeleteForward(ctxt->length);
323         if (code == ErrorCode::NO_ERROR) {
324             ctxt->status = napi_ok;
325             ctxt->SetState(ctxt->status);
326             ctxt->isDeleteForward = true;
327         } else {
328             ctxt->SetErrorCode(code);
329         }
330     };
331     ctxt->SetAction(std::move(input), std::move(output));
332     // 2 means JsAPI:deleteForward has 2 params at most.
333     AsyncCall asyncCall(env, info, ctxt, 2);
334     return ASYNC_POST(env, exec);
335 }
336 
DeleteBackwardSync(napi_env env,napi_callback_info info)337 napi_value JsTextInputClientEngine::DeleteBackwardSync(napi_env env, napi_callback_info info)
338 {
339     size_t argc = 1;
340     napi_value argv[1] = { nullptr };
341     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
342     int32_t length = 0;
343     // 1 means least param num.
344     PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
345     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length must be number!", TYPE_NUMBER,
346         HandleParamCheckFailure(env));
347     PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], length), "length covert failed!", TYPE_NONE,
348         HandleParamCheckFailure(env));
349     PARAM_CHECK_RETURN(env, length >= 0, "length should no less than 0!", TYPE_NONE, HandleParamCheckFailure(env));
350     IMSA_HILOGD("delete backward, length: %{public}d.", length);
351     int32_t ret = InputMethodAbility::GetInstance()->DeleteBackward(length);
352     if (ret != ErrorCode::NO_ERROR) {
353         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to delete backward", TYPE_NONE);
354     }
355     return JsUtil::Const::Null(env);
356 }
357 
DeleteBackward(napi_env env,napi_callback_info info)358 napi_value JsTextInputClientEngine::DeleteBackward(napi_env env, napi_callback_info info)
359 {
360     auto ctxt = std::make_shared<DeleteBackwardContext>();
361     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
362         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
363         PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "param length type must be number!",
364             TYPE_NONE, napi_generic_failure);
365         auto status = JsUtils::GetValue(env, argv[0], ctxt->length);
366         return status;
367     };
368     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
369         napi_status status = napi_get_boolean(env, ctxt->isDeleteBackward, result);
370         return status;
371     };
372     auto exec = [ctxt](AsyncCall::Context *ctx) {
373         int32_t code = InputMethodAbility::GetInstance()->DeleteBackward(ctxt->length);
374         if (code == ErrorCode::NO_ERROR) {
375             ctxt->status = napi_ok;
376             ctxt->SetState(ctxt->status);
377             ctxt->isDeleteBackward = true;
378         } else {
379             ctxt->SetErrorCode(code);
380         }
381     };
382     ctxt->SetAction(std::move(input), std::move(output));
383     // 2 means JsAPI:deleteBackward has 2 params at most.
384     AsyncCall asyncCall(env, info, ctxt, 2);
385     return ASYNC_POST(env, exec);
386 }
387 
InsertText(napi_env env,napi_callback_info info)388 napi_value JsTextInputClientEngine::InsertText(napi_env env, napi_callback_info info)
389 {
390     auto traceId = GenerateTraceId();
391     InputMethodSyncTrace tracer("JS_InsertText_Start", traceId);
392     auto ctxt = std::make_shared<InsertTextContext>();
393     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
394         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
395         PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_string, "text type must be string",
396             TYPE_NONE, napi_generic_failure);
397         auto status = JsUtils::GetValue(env, argv[0], ctxt->text);
398         return status;
399     };
400     auto output = [ctxt, traceId](napi_env env, napi_value *result) -> napi_status {
401         InputMethodSyncTrace tracer("JS_InsertText_Complete", traceId);
402         napi_status status = napi_get_boolean(env, ctxt->isInsertText, result);
403         return status;
404     };
405     auto exec = [ctxt, traceId](AsyncCall::Context *ctx) {
406         InputMethodSyncTrace tracer("JS_InsertText_Exec", traceId);
407         int32_t code = InputMethodAbility::GetInstance()->InsertText(ctxt->text);
408         if (code == ErrorCode::NO_ERROR) {
409             ctxt->status = napi_ok;
410             ctxt->SetState(ctxt->status);
411             ctxt->isInsertText = true;
412         } else {
413             ctxt->SetErrorCode(code);
414         }
415     };
416     ctxt->SetAction(std::move(input), std::move(output));
417     // 2 means JsAPI:insertText has 2 params at most.
418     AsyncCall asyncCall(env, info, ctxt, 2);
419     return ASYNC_POST(env, exec);
420 }
421 
InsertTextSync(napi_env env,napi_callback_info info)422 napi_value JsTextInputClientEngine::InsertTextSync(napi_env env, napi_callback_info info)
423 {
424     InputMethodSyncTrace tracer("JS_InsertTextSync", GenerateTraceId());
425     size_t argc = 1;
426     napi_value argv[1] = { nullptr };
427     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
428     std::string text;
429     // 1 means least param num.
430     PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
431     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_string, "text must be string!", TYPE_STRING,
432         HandleParamCheckFailure(env));
433     PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], text), "text covert failed!", TYPE_NONE,
434         HandleParamCheckFailure(env));
435     IMSA_HILOGD("insert text, text: %{public}s.", text.c_str());
436     int32_t ret = InputMethodAbility::GetInstance()->InsertText(text);
437     if (ret != ErrorCode::NO_ERROR) {
438         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to insert text!", TYPE_NONE);
439     }
440     return JsUtil::Const::Null(env);
441 }
442 
GetForwardSync(napi_env env,napi_callback_info info)443 napi_value JsTextInputClientEngine::GetForwardSync(napi_env env, napi_callback_info info)
444 {
445     InputMethodSyncTrace tracer("JS_GetForwardSync", GenerateTraceId());
446     size_t argc = 1;
447     napi_value argv[1] = { nullptr };
448     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
449     int32_t length = 0;
450     // 1 means least param num.
451     PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
452     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length must be string!", TYPE_NUMBER,
453         HandleParamCheckFailure(env));
454     PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], length), "length covert failed!", TYPE_NONE,
455         HandleParamCheckFailure(env));
456     PARAM_CHECK_RETURN(env, length >= 0, "length should no less than 0!", TYPE_NONE, HandleParamCheckFailure(env));
457     IMSA_HILOGD("get forward, length: %{public}d.", length);
458     std::u16string text;
459     int32_t ret = InputMethodAbility::GetInstance()->GetTextBeforeCursor(length, text);
460     if (ret != ErrorCode::NO_ERROR) {
461         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to get forward!", TYPE_NONE);
462         return JsUtil::Const::Null(env);
463     }
464     napi_value result = nullptr;
465     auto status = JsUtils::GetValue(env, Str16ToStr8(text), result);
466     CHECK_RETURN(status == napi_ok, "GetValue failed", JsUtil::Const::Null(env));
467     return result;
468 }
469 
GetForward(napi_env env,napi_callback_info info)470 napi_value JsTextInputClientEngine::GetForward(napi_env env, napi_callback_info info)
471 {
472     auto traceId = GenerateTraceId();
473     InputMethodSyncTrace tracer("JS_GetForward_Start", traceId);
474     auto ctxt = std::make_shared<GetForwardContext>();
475     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
476         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
477         PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length type must be number!",
478             TYPE_NONE, napi_generic_failure);
479         auto status = JsUtils::GetValue(env, argv[0], ctxt->length);
480         return status;
481     };
482     auto output = [ctxt, traceId](napi_env env, napi_value *result) -> napi_status {
483         InputMethodSyncTrace tracer("JS_GetForward_Complete", traceId);
484         napi_value data = GetResult(env, ctxt->text);
485         *result = data;
486         return napi_ok;
487     };
488     auto exec = [ctxt, traceId](AsyncCall::Context *ctx) {
489         InputMethodSyncTrace tracer("JS_GetForward_Exec", traceId);
490         std::u16string temp;
491         int32_t code = InputMethodAbility::GetInstance()->GetTextBeforeCursor(ctxt->length, temp);
492         if (code == ErrorCode::NO_ERROR) {
493             ctxt->status = napi_ok;
494             ctxt->SetState(ctxt->status);
495             ctxt->text = Str16ToStr8(temp);
496         } else {
497             ctxt->SetErrorCode(code);
498         }
499     };
500     ctxt->SetAction(std::move(input), std::move(output));
501     // 2 means JsAPI:getForward has 2 params at most.
502     AsyncCall asyncCall(env, info, ctxt, 2);
503     return ASYNC_POST(env, exec);
504 }
505 
GetBackwardSync(napi_env env,napi_callback_info info)506 napi_value JsTextInputClientEngine::GetBackwardSync(napi_env env, napi_callback_info info)
507 {
508     size_t argc = 1;
509     napi_value argv[1] = { nullptr };
510     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
511     int32_t length = 0;
512     // 1 means least param num.
513     PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
514     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length must be string!", TYPE_NUMBER,
515         HandleParamCheckFailure(env));
516     PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], length), "length covert failed!", TYPE_NONE,
517         HandleParamCheckFailure(env));
518     PARAM_CHECK_RETURN(env, length >= 0, "length should not less than 0!", TYPE_NONE, HandleParamCheckFailure(env));
519     IMSA_HILOGD("get backward, length: %{public}d.", length);
520     std::u16string text;
521     int32_t ret = InputMethodAbility::GetInstance()->GetTextAfterCursor(length, text);
522     if (ret != ErrorCode::NO_ERROR) {
523         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to get backward!", TYPE_NONE);
524         return JsUtil::Const::Null(env);
525     }
526     napi_value result = nullptr;
527     auto status = JsUtils::GetValue(env, Str16ToStr8(text), result);
528     CHECK_RETURN(status == napi_ok, "GetValue failed", JsUtil::Const::Null(env));
529     return result;
530 }
531 
GetBackward(napi_env env,napi_callback_info info)532 napi_value JsTextInputClientEngine::GetBackward(napi_env env, napi_callback_info info)
533 {
534     auto ctxt = std::make_shared<GetBackwardContext>();
535     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
536         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
537         PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length type must be number!",
538             TYPE_NONE, napi_generic_failure);
539         auto status = JsUtils::GetValue(env, argv[0], ctxt->length);
540         return status;
541     };
542     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
543         napi_value data = GetResult(env, ctxt->text);
544         *result = data;
545         return napi_ok;
546     };
547     auto exec = [ctxt](AsyncCall::Context *ctx) {
548         std::u16string temp;
549         int32_t code = InputMethodAbility::GetInstance()->GetTextAfterCursor(ctxt->length, temp);
550         if (code == ErrorCode::NO_ERROR) {
551             ctxt->status = napi_ok;
552             ctxt->SetState(ctxt->status);
553             ctxt->text = Str16ToStr8(temp);
554         } else {
555             ctxt->SetErrorCode(code);
556         }
557     };
558     ctxt->SetAction(std::move(input), std::move(output));
559     // 2 means JsAPI:getBackward has 2 params at most.
560     AsyncCall asyncCall(env, info, ctxt, 2);
561     return ASYNC_POST(env, exec);
562 }
563 
GetEditorAttributeSync(napi_env env,napi_callback_info info)564 napi_value JsTextInputClientEngine::GetEditorAttributeSync(napi_env env, napi_callback_info info)
565 {
566     TextTotalConfig config;
567     int32_t ret = InputMethodAbility::GetInstance()->GetTextConfig(config);
568     if (ret != ErrorCode::NO_ERROR) {
569         IMSA_HILOGE("failed to get text config: %{public}d!", ret);
570         JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_IMCLIENT, "failed to get text config!", TYPE_NONE);
571     }
572     IMSA_HILOGD("inputPattern: %{public}d, enterKeyType: %{public}d, isTextPreviewSupported: %{public}d.",
573         config.inputAttribute.inputPattern, config.inputAttribute.enterKeyType,
574         config.inputAttribute.isTextPreviewSupported);
575     return JsInputAttribute::Write(env, config.inputAttribute);
576 }
577 
GetEditorAttribute(napi_env env,napi_callback_info info)578 napi_value JsTextInputClientEngine::GetEditorAttribute(napi_env env, napi_callback_info info)
579 {
580     auto ctxt = std::make_shared<GetEditorAttributeContext>();
581     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
582         *result = JsInputAttribute::Write(env, ctxt->inputAttribute);
583         return napi_ok;
584     };
585     auto exec = [ctxt](AsyncCall::Context *ctx) {
586         TextTotalConfig config;
587         int32_t ret = InputMethodAbility::GetInstance()->GetTextConfig(config);
588         ctxt->inputAttribute = config.inputAttribute;
589         if (ret == ErrorCode::NO_ERROR) {
590             ctxt->SetState(napi_ok);
591             IMSA_HILOGD("inputPattern: %{public}d, enterKeyType: %{public}d, isTextPreviewSupported: %{public}d",
592                 config.inputAttribute.inputPattern, config.inputAttribute.enterKeyType,
593                 config.inputAttribute.isTextPreviewSupported);
594         } else {
595             IMSA_HILOGE("failed to get text config: %{public}d!", ret);
596             ctxt->SetErrorCode(IMFErrorCode::EXCEPTION_IMCLIENT);
597             ctxt->SetErrorMessage("failed to get text config!");
598         }
599     };
600     ctxt->SetAction(nullptr, std::move(output));
601     // 1 means JsAPI:getEditorAttribute has 1 param at most.
602     AsyncCall asyncCall(env, info, ctxt, 1);
603     return ASYNC_POST(env, exec);
604 }
605 
SelectByRange(napi_env env,napi_callback_info info)606 napi_value JsTextInputClientEngine::SelectByRange(napi_env env, napi_callback_info info)
607 {
608     IMSA_HILOGD("run in");
609     auto ctxt = std::make_shared<SelectContext>();
610     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
611         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
612         napi_valuetype valueType = napi_undefined;
613         napi_typeof(env, argv[0], &valueType);
614         PARAM_CHECK_RETURN(env, valueType == napi_object, "range type must be Range!", TYPE_NONE,
615             napi_generic_failure);
616         auto status = GetSelectRange(env, argv[0], ctxt);
617         return status;
618     };
619     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status { return napi_ok; };
620     auto exec = [ctxt](AsyncCall::Context *ctx) {
621         int32_t code = InputMethodAbility::GetInstance()->SelectByRange(ctxt->start, ctxt->end);
622         if (code == ErrorCode::NO_ERROR) {
623             ctxt->status = napi_ok;
624             ctxt->SetState(ctxt->status);
625         } else {
626             ctxt->SetErrorCode(code);
627         }
628     };
629     ctxt->SetAction(std::move(input), std::move(output));
630     // 2 means JsAPI:selectByRange has 2 params at most.
631     AsyncCall asyncCall(env, info, ctxt, 2);
632     return ASYNC_POST(env, exec);
633 }
634 
SelectByRangeSync(napi_env env,napi_callback_info info)635 napi_value JsTextInputClientEngine::SelectByRangeSync(napi_env env, napi_callback_info info)
636 {
637     IMSA_HILOGD("SelectByRangeSync");
638     size_t argc = 1;
639     napi_value argv[1] = { nullptr };
640     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
641     PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
642     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_object, "range type must be Range!", TYPE_NONE,
643         HandleParamCheckFailure(env));
644     auto ctxt = std::make_shared<SelectContext>();
645     auto status = GetSelectRange(env, argv[0], ctxt);
646     if (status != napi_ok) {
647         JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK,
648             "failed to get start or end, should have start and end number!", TYPE_NONE);
649         return JsUtil::Const::Null(env);
650     }
651     IMSA_HILOGD("start: %{public}d, end: %{public}d.", ctxt->start, ctxt->end);
652     int32_t ret = InputMethodAbility::GetInstance()->SelectByRange(ctxt->start, ctxt->end);
653     if (ret != ErrorCode::NO_ERROR) {
654         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to select by range!", TYPE_NONE);
655     }
656     return JsUtil::Const::Null(env);
657 }
658 
SelectByMovementSync(napi_env env,napi_callback_info info)659 napi_value JsTextInputClientEngine::SelectByMovementSync(napi_env env, napi_callback_info info)
660 {
661     IMSA_HILOGD("run in");
662     size_t argc = 1;
663     napi_value argv[1] = { nullptr };
664     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
665     PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
666     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_object, "movement type must be Movement!",
667         TYPE_NONE, HandleParamCheckFailure(env));
668     auto ctxt = std::make_shared<SelectContext>();
669     auto status = GetSelectMovement(env, argv[0], ctxt);
670     if (status != napi_ok) {
671         JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK, "direction covert failed!", TYPE_NONE);
672         return JsUtil::Const::Null(env);
673     }
674     IMSA_HILOGD("direction: %{public}d.", ctxt->direction);
675     int32_t ret = InputMethodAbility::GetInstance()->SelectByMovement(ctxt->direction);
676     if (ret != ErrorCode::NO_ERROR) {
677         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to select by movement!", TYPE_NONE);
678     }
679     return JsUtil::Const::Null(env);
680 }
681 
SelectByMovement(napi_env env,napi_callback_info info)682 napi_value JsTextInputClientEngine::SelectByMovement(napi_env env, napi_callback_info info)
683 {
684     IMSA_HILOGD("run in");
685     auto ctxt = std::make_shared<SelectContext>();
686     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
687         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
688         napi_valuetype valueType = napi_undefined;
689         napi_typeof(env, argv[0], &valueType);
690         PARAM_CHECK_RETURN(env, valueType == napi_object, "movement type must be Movement!", TYPE_NONE,
691             napi_generic_failure);
692         auto status = GetSelectMovement(env, argv[0], ctxt);
693         return status;
694     };
695     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status { return napi_ok; };
696     auto exec = [ctxt](AsyncCall::Context *ctx) {
697         int32_t code = InputMethodAbility::GetInstance()->SelectByMovement(ctxt->direction);
698         if (code == ErrorCode::NO_ERROR) {
699             ctxt->status = napi_ok;
700             ctxt->SetState(ctxt->status);
701         } else {
702             ctxt->SetErrorCode(code);
703         }
704     };
705     ctxt->SetAction(std::move(input), std::move(output));
706     // 2 means JsAPI:selectByMovement has 2 params at most.
707     AsyncCall asyncCall(env, info, ctxt, 2);
708     return ASYNC_POST(env, exec);
709 }
710 
SendExtendAction(napi_env env,napi_callback_info info)711 napi_value JsTextInputClientEngine::SendExtendAction(napi_env env, napi_callback_info info)
712 {
713     auto ctxt = std::make_shared<SendExtendActionContext>();
714     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
715         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
716         auto status = JsUtils::GetValue(env, argv[0], ctxt->action);
717         if (status != napi_ok) {
718             ctxt->SetErrorMessage("action must be number and should in ExtendAction");
719         }
720         return status;
721     };
722     auto exec = [ctxt](AsyncCall::Context *ctx) {
723         int32_t code = InputMethodAbility::GetInstance()->SendExtendAction(ctxt->action);
724         if (code == ErrorCode::NO_ERROR) {
725             ctxt->SetState(napi_ok);
726             return;
727         }
728         ctxt->SetErrorCode(code);
729     };
730     ctxt->SetAction(std::move(input));
731     // 2 means JsAPI:sendExtendAction has 2 params at most.
732     AsyncCall asyncCall(env, info, ctxt, 2);
733     return ASYNC_POST(env, exec);
734 }
735 
GetTextIndexAtCursor(napi_env env,napi_callback_info info)736 napi_value JsTextInputClientEngine::GetTextIndexAtCursor(napi_env env, napi_callback_info info)
737 {
738     IMSA_HILOGD("GetTextIndexAtCursor");
739     auto ctxt = std::make_shared<GetTextIndexAtCursorContext>();
740     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
741         return napi_ok;
742     };
743     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
744         return napi_create_int32(env, ctxt->index, result);
745     };
746     auto exec = [ctxt](AsyncCall::Context *ctx) {
747         int32_t code = InputMethodAbility::GetInstance()->GetTextIndexAtCursor(ctxt->index);
748         if (code == ErrorCode::NO_ERROR) {
749             ctxt->status = napi_ok;
750             ctxt->SetState(ctxt->status);
751         } else {
752             ctxt->SetErrorCode(code);
753         }
754     };
755     ctxt->SetAction(std::move(input), std::move(output));
756     // 1 means JsAPI:getTextIndexAtCursor has 1 param at most.
757     AsyncCall asyncCall(env, info, ctxt, 1);
758     return ASYNC_POST(env, exec);
759 }
760 
SetPreviewText(napi_env env,napi_callback_info info)761 napi_value JsTextInputClientEngine::SetPreviewText(napi_env env, napi_callback_info info)
762 {
763     auto traceId = GenerateTraceId();
764     InputMethodSyncTrace tracer("JS_SetPreviewText_Start", traceId);
765     IMSA_HILOGD("JsTextInputClientEngine in");
766     auto ctxt = std::make_shared<SetPreviewTextContext>();
767     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
768         if (GetPreviewTextParam(env, argc, argv, ctxt->text, ctxt->range) != napi_ok) {
769             return napi_generic_failure;
770         }
771         return napi_ok;
772     };
773     auto output = [ctxt, traceId](napi_env env, napi_value *result) -> napi_status {
774         InputMethodSyncTrace tracer("JS_SetPreviewText_Complete", traceId);
775         return napi_ok;
776     };
777     auto exec = [ctxt, traceId](AsyncCall::Context *ctx) {
778         InputMethodSyncTrace tracer("JS_SetPreviewText_Exec", traceId);
779         int32_t code = InputMethodAbility::GetInstance()->SetPreviewText(ctxt->text, ctxt->range);
780         if (code == ErrorCode::NO_ERROR) {
781             IMSA_HILOGD("exec setPreviewText success");
782             ctxt->SetState(napi_ok);
783         } else if (code == ErrorCode::ERROR_INVALID_RANGE) {
784             ctxt->SetErrorCode(code);
785             ctxt->SetErrorMessage("range should be included in preview text range, otherwise should be included in "
786                                   "total text range!");
787         } else {
788             ctxt->SetErrorCode(code);
789         }
790     };
791     ctxt->SetAction(std::move(input), std::move(output));
792     // 2 means JsAPI:setPreviewText needs 2 params at most
793     AsyncCall asyncCall(env, info, ctxt, 2);
794     return ASYNC_POST(env, exec);
795 }
796 
SetPreviewTextSync(napi_env env,napi_callback_info info)797 napi_value JsTextInputClientEngine::SetPreviewTextSync(napi_env env, napi_callback_info info)
798 {
799     InputMethodSyncTrace tracer("JS_SetPreviewTextSync", GenerateTraceId());
800     IMSA_HILOGD("start.");
801     // 2 means JsAPI:setPreviewText needs 2 params at most
802     size_t argc = 2;
803     napi_value argv[2] = { nullptr };
804     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
805     std::string text;
806     Range range;
807     if (GetPreviewTextParam(env, argc, argv, text, range) != napi_ok) {
808         return JsUtil::Const::Null(env);
809     }
810     int32_t ret = InputMethodAbility::GetInstance()->SetPreviewText(text, range);
811     if (ret == ErrorCode::ERROR_INVALID_RANGE) {
812         JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK,
813             "range should be included in preview text range, otherwise should be included in total text range",
814             TYPE_NONE);
815     } else if (ret != ErrorCode::NO_ERROR) {
816         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to set preview text!", TYPE_NONE);
817     }
818     return JsUtil::Const::Null(env);
819 }
820 
FinishTextPreview(napi_env env,napi_callback_info info)821 napi_value JsTextInputClientEngine::FinishTextPreview(napi_env env, napi_callback_info info)
822 {
823     auto traceId = GenerateTraceId();
824     InputMethodSyncTrace tracer("JS_FinishTextPreview_Start", traceId);
825     IMSA_HILOGD("start.");
826     auto ctxt = std::make_shared<FinishTextPreviewContext>();
827     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
828         return napi_ok;
829     };
830     auto output = [ctxt, traceId](napi_env env, napi_value *result) -> napi_status {
831         InputMethodSyncTrace tracer("JS_FinishTextPreview_Complete", traceId);
832         return napi_ok;
833     };
834     auto exec = [ctxt, traceId](AsyncCall::Context *ctx) {
835         InputMethodSyncTrace tracer("JS_FinishTextPreview_Exec", traceId);
836         int32_t code = InputMethodAbility::GetInstance()->FinishTextPreview(false);
837         if (code == ErrorCode::NO_ERROR) {
838             IMSA_HILOGI("exec finishTextPreview success.");
839             ctxt->SetState(napi_ok);
840         } else {
841             ctxt->SetErrorCode(code);
842         }
843     };
844     ctxt->SetAction(std::move(input), std::move(output));
845     // 0 means JsAPI:finishTextPreview needs no param
846     AsyncCall asyncCall(env, info, ctxt, 0);
847     return ASYNC_POST(env, exec);
848 }
849 
FinishTextPreviewSync(napi_env env,napi_callback_info info)850 napi_value JsTextInputClientEngine::FinishTextPreviewSync(napi_env env, napi_callback_info info)
851 {
852     InputMethodSyncTrace tracer("JS_FinishTextPreviewSync", GenerateTraceId());
853     IMSA_HILOGD("JsTextInputClientEngine in");
854     int32_t ret = InputMethodAbility::GetInstance()->FinishTextPreview(false);
855     if (ret != ErrorCode::NO_ERROR) {
856         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to finish text preview!", TYPE_NONE);
857     }
858     return JsUtil::Const::Null(env);
859 }
860 
GetTextIndexAtCursorSync(napi_env env,napi_callback_info info)861 napi_value JsTextInputClientEngine::GetTextIndexAtCursorSync(napi_env env, napi_callback_info info)
862 {
863     IMSA_HILOGD("start.");
864     int32_t index = 0;
865     int32_t ret = InputMethodAbility::GetInstance()->GetTextIndexAtCursor(index);
866     if (ret != ErrorCode::NO_ERROR) {
867         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to get text index at cursor!", TYPE_NONE);
868     }
869     return JsUtil::GetValue(env, index);
870 }
871 
GetCallingWindowInfo(napi_env env,napi_callback_info info)872 napi_value JsTextInputClientEngine::GetCallingWindowInfo(napi_env env, napi_callback_info info)
873 {
874     IMSA_HILOGD("start.");
875     auto ctxt = std::make_shared<GetCallingWindowInfoContext>();
876     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
877         *result = JsCallingWindowInfo::Write(env, ctxt->windowInfo);
878         return napi_ok;
879     };
880     auto exec = [ctxt](AsyncCall::Context *ctx) {
881         int32_t ret = InputMethodAbility::GetInstance()->GetCallingWindowInfo(ctxt->windowInfo);
882         if (ret == ErrorCode::NO_ERROR) {
883             IMSA_HILOGI("exec GetCallingWindowInfo success.");
884             ctxt->SetState(napi_ok);
885             return;
886         }
887         ctxt->SetErrorCode(ret);
888     };
889     ctxt->SetAction(nullptr, std::move(output));
890     // 0 means JsAPI:getCallingWindowInfo needs no parameter.
891     AsyncCall asyncCall(env, info, ctxt, 0);
892     return ASYNC_POST(env, exec);
893 }
894 
GetPreviewTextParam(napi_env env,size_t argc,napi_value * argv,std::string & text,Range & range)895 napi_status JsTextInputClientEngine::GetPreviewTextParam(
896     napi_env env, size_t argc, napi_value *argv, std::string &text, Range &range)
897 {
898     // 2 means JsAPI:setPreviewText needs 2 params at least.
899     PARAM_CHECK_RETURN(env, argc >= 2, "at least two parameters is required!", TYPE_NONE, napi_generic_failure);
900     PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], text), "text covert failed, must be string!",
901         TYPE_NONE, napi_generic_failure);
902     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[1]) == napi_object, "range type must be Range!", TYPE_NONE,
903         napi_generic_failure);
904     PARAM_CHECK_RETURN(env, JsRange::Read(env, argv[1], range),
905         "range covert failed, the range should have numbers start and end", TYPE_NONE, napi_generic_failure);
906     return napi_ok;
907 }
908 
Write(napi_env env,const Rosen::Rect & nativeObject)909 napi_value JsRect::Write(napi_env env, const Rosen::Rect &nativeObject)
910 {
911     napi_value jsObject = nullptr;
912     napi_create_object(env, &jsObject);
913     bool ret = JsUtil::Object::WriteProperty(env, jsObject, "left", nativeObject.posX_);
914     ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "top", nativeObject.posY_);
915     ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "width", nativeObject.width_);
916     ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "height", nativeObject.height_);
917     return ret ? jsObject : JsUtil::Const::Null(env);
918 }
919 
Read(napi_env env,napi_value jsObject,Rosen::Rect & nativeObject)920 bool JsRect::Read(napi_env env, napi_value jsObject, Rosen::Rect &nativeObject)
921 {
922     auto ret = JsUtil::Object::ReadProperty(env, jsObject, "left", nativeObject.posX_);
923     ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "top", nativeObject.posY_);
924     ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "width", nativeObject.width_);
925     ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "height", nativeObject.height_);
926     return ret;
927 }
928 
Write(napi_env env,const CallingWindowInfo & nativeObject)929 napi_value JsCallingWindowInfo::Write(napi_env env, const CallingWindowInfo &nativeObject)
930 {
931     napi_value jsObject = nullptr;
932     napi_create_object(env, &jsObject);
933     bool ret = JsUtil::Object::WriteProperty(env, jsObject, "rect", JsRect::Write(env, nativeObject.rect));
934     ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "status", static_cast<uint32_t>(nativeObject.status));
935     return ret ? jsObject : JsUtil::Const::Null(env);
936 }
937 
Read(napi_env env,napi_value object,CallingWindowInfo & nativeObject)938 bool JsCallingWindowInfo::Read(napi_env env, napi_value object, CallingWindowInfo &nativeObject)
939 {
940     napi_value rectObject = nullptr;
941     napi_get_named_property(env, object, "rect", &rectObject);
942     auto ret = JsRect::Read(env, rectObject, nativeObject.rect);
943     uint32_t status = 0;
944     ret = ret && JsUtil::Object::ReadProperty(env, object, "status", status);
945     nativeObject.status = static_cast<Rosen::WindowStatus>(status);
946     return ret;
947 }
948 
HandleParamCheckFailure(napi_env env)949 napi_value JsTextInputClientEngine::HandleParamCheckFailure(napi_env env)
950 {
951     return JsUtil::Const::Null(env);
952 }
953 
Write(napi_env env,const Range & nativeObject)954 napi_value JsRange::Write(napi_env env, const Range &nativeObject)
955 {
956     napi_value jsObject = nullptr;
957     napi_create_object(env, &jsObject);
958     bool ret = JsUtil::Object::WriteProperty(env, jsObject, "start", nativeObject.start);
959     ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "end", nativeObject.end);
960     return ret ? jsObject : JsUtil::Const::Null(env);
961 }
962 
Read(napi_env env,napi_value jsObject,Range & nativeObject)963 bool JsRange::Read(napi_env env, napi_value jsObject, Range &nativeObject)
964 {
965     auto ret = JsUtil::Object::ReadProperty(env, jsObject, "start", nativeObject.start);
966     ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "end", nativeObject.end);
967     return ret;
968 }
969 
Write(napi_env env,const InputAttribute & nativeObject)970 napi_value JsInputAttribute::Write(napi_env env, const InputAttribute &nativeObject)
971 {
972     napi_value jsObject = nullptr;
973     napi_create_object(env, &jsObject);
974     auto ret = JsUtil::Object::WriteProperty(env, jsObject, "inputPattern", nativeObject.inputPattern);
975     ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "enterKeyType", nativeObject.enterKeyType);
976     ret =
977         ret
978         && JsUtil::Object::WriteProperty(env, jsObject, "isTextPreviewSupported", nativeObject.isTextPreviewSupported);
979     // not care write bundleName fail
980     JsUtil::Object::WriteProperty(env, jsObject, "bundleName", nativeObject.bundleName);
981     ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "immersiveMode", nativeObject.immersiveMode);
982     return ret ? jsObject : JsUtil::Const::Null(env);
983 }
984 
Read(napi_env env,napi_value jsObject,InputAttribute & nativeObject)985 bool JsInputAttribute::Read(napi_env env, napi_value jsObject, InputAttribute &nativeObject)
986 {
987     auto ret = JsUtil::Object::ReadProperty(env, jsObject, "inputPattern", nativeObject.inputPattern);
988     ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "enterKeyType", nativeObject.enterKeyType);
989     ret = ret
990           && JsUtil::Object::ReadProperty(env, jsObject, "isTextPreviewSupported", nativeObject.isTextPreviewSupported);
991     // not care read bundleName fail
992     JsUtil::Object::ReadProperty(env, jsObject, "bundleName", nativeObject.bundleName);
993     ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "immersiveMode", nativeObject.immersiveMode);
994     return ret;
995 }
996 
SendMessage(napi_env env,napi_callback_info info)997 napi_value JsTextInputClientEngine::SendMessage(napi_env env, napi_callback_info info)
998 {
999     auto ctxt = std::make_shared<SendMessageContext>();
1000     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
1001         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
1002         PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_string, "msgId",
1003             TYPE_STRING, napi_generic_failure);
1004         CHECK_RETURN(JsUtils::GetValue(env, argv[0], ctxt->arrayBuffer.msgId) == napi_ok,
1005             "msgId covert failed!", napi_generic_failure);
1006         ctxt->arrayBuffer.jsArgc = argc;
1007         // 1 means first param msgId.
1008         if (argc > 1) {
1009             bool isArryBuffer = false;
1010             //  1 means second param msgParam index.
1011             CHECK_RETURN(napi_is_arraybuffer(env, argv[1], &isArryBuffer) == napi_ok,
1012                 "napi_is_arraybuffer failed!", napi_generic_failure);
1013             PARAM_CHECK_RETURN(env, isArryBuffer, "msgParam", TYPE_ARRAY_BUFFER, napi_generic_failure);
1014             CHECK_RETURN(JsUtils::GetValue(env, argv[1], ctxt->arrayBuffer.msgParam) == napi_ok,
1015                 "msgParam covert failed!", napi_generic_failure);
1016         }
1017         PARAM_CHECK_RETURN(env, ArrayBuffer::IsSizeValid(ctxt->arrayBuffer),
1018             "msgId limit 256B and msgParam limit 128KB.", TYPE_NONE, napi_generic_failure);
1019         ctxt->info = { std::chrono::system_clock::now(), ctxt->arrayBuffer };
1020         messageHandlerQueue_.Push(ctxt->info);
1021         return napi_ok;
1022     };
1023     auto exec = [ctxt](AsyncCall::Context *ctx) {
1024         messageHandlerQueue_.Wait(ctxt->info);
1025         int32_t code = InputMethodAbility::GetInstance()->SendMessage(ctxt->arrayBuffer);
1026         messageHandlerQueue_.Pop();
1027         if (code == ErrorCode::NO_ERROR) {
1028             ctxt->status = napi_ok;
1029             ctxt->SetState(ctxt->status);
1030         } else {
1031             ctxt->SetErrorCode(code);
1032         }
1033     };
1034     ctxt->SetAction(std::move(input), nullptr);
1035     // 2 means JsAPI:sendMessage has 2 params at most.
1036     AsyncCall asyncCall(env, info, ctxt, 2);
1037     return asyncCall.Call(env, exec, "imaSendMessage");
1038 }
1039 
RecvMessage(napi_env env,napi_callback_info info)1040 napi_value JsTextInputClientEngine::RecvMessage(napi_env env, napi_callback_info info)
1041 {
1042     size_t argc = ARGC_TWO;
1043     napi_value argv[ARGC_TWO] = {nullptr};
1044     napi_value thisVar = nullptr;
1045     void *data = nullptr;
1046     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
1047     if (argc < 0) {
1048         IMSA_HILOGE("RecvMessage failed! argc abnormal.");
1049         return nullptr;
1050     }
1051     void *native = nullptr;
1052     auto status = napi_unwrap(env, thisVar, &native);
1053     CHECK_RETURN((status == napi_ok && native != nullptr), "napi_unwrap failed!", nullptr);
1054     auto inputClient = reinterpret_cast<JsTextInputClientEngine *>(native);
1055     if (inputClient == nullptr) {
1056         IMSA_HILOGI("Unwrap js object self is nullptr.");
1057         return nullptr;
1058     }
1059     if (argc == 0) {
1060         IMSA_HILOGI("RecvMessage off.");
1061         InputMethodAbility::GetInstance()->RegisterMsgHandler();
1062         return nullptr;
1063     }
1064     IMSA_HILOGI("RecvMessage on.");
1065     PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_object, "msgHnadler (MessageHandler)",
1066         TYPE_OBJECT, nullptr);
1067 
1068     napi_value onMessage = nullptr;
1069     CHECK_RETURN(napi_get_named_property(env, argv[0], "onMessage", &onMessage) == napi_ok,
1070         "Get onMessage property failed!", nullptr);
1071     CHECK_RETURN(JsUtil::GetType(env, onMessage) == napi_function, "onMessage is not napi_function!", nullptr);
1072     napi_value onTerminated = nullptr;
1073     CHECK_RETURN(napi_get_named_property(env, argv[0], "onTerminated", &onTerminated) == napi_ok,
1074         "Get onMessage property failed!", nullptr);
1075     CHECK_RETURN(JsUtil::GetType(env, onTerminated) == napi_function, "onTerminated is not napi_function!", nullptr);
1076 
1077     std::shared_ptr<MsgHandlerCallbackInterface> callback =
1078         std::make_shared<JsTextInputClientEngine::JsMessageHandler>(env, onTerminated, onMessage);
1079     InputMethodAbility::GetInstance()->RegisterMsgHandler(callback);
1080     napi_value result = nullptr;
1081     napi_get_null(env, &result);
1082     return result;
1083 }
1084 
OnTerminated()1085 int32_t JsTextInputClientEngine::JsMessageHandler::OnTerminated()
1086 {
1087     std::lock_guard<decltype(callbackObjectMutex_)> lock(callbackObjectMutex_);
1088     if (jsMessageHandler_ == nullptr) {
1089         IMSA_HILOGI("jsCallbackObject is nullptr, can not call OnTerminated!.");
1090         return ErrorCode::ERROR_NULL_POINTER;
1091     }
1092     auto eventHandler = jsMessageHandler_->GetEventHandler();
1093     if (eventHandler == nullptr) {
1094         IMSA_HILOGI("EventHandler is nullptr!.");
1095         return ErrorCode::ERROR_NULL_POINTER;
1096     }
1097     auto task = [jsCallback = std::move(jsMessageHandler_)]() {
1098         napi_value callback = nullptr;
1099         napi_value global = nullptr;
1100         if (jsCallback == nullptr) {
1101             IMSA_HILOGI("jsCallback is nullptr!.");
1102             return;
1103         }
1104         napi_get_reference_value(jsCallback->env_, jsCallback->onTerminatedCallback_, &callback);
1105         if (callback != nullptr) {
1106             napi_get_global(jsCallback->env_, &global);
1107             napi_value output = nullptr;
1108             // 0 means the callback has no param.
1109             auto status = napi_call_function(jsCallback->env_, global, callback, 0, nullptr, &output);
1110             if (status != napi_ok) {
1111                 IMSA_HILOGI("Call js function failed!.");
1112                 output = nullptr;
1113             }
1114         }
1115     };
1116     eventHandler->PostTask(task, "IMA_MsgHandler_OnTerminated", 0, AppExecFwk::EventQueue::Priority::VIP);
1117     return ErrorCode::NO_ERROR;
1118 }
1119 
OnMessage(const ArrayBuffer & arrayBuffer)1120 int32_t JsTextInputClientEngine::JsMessageHandler::OnMessage(const ArrayBuffer &arrayBuffer)
1121 {
1122     std::lock_guard<decltype(callbackObjectMutex_)> lock(callbackObjectMutex_);
1123     if (jsMessageHandler_ == nullptr) {
1124         IMSA_HILOGE("MessageHandler was not regist!.");
1125         return ErrorCode::ERROR_MSG_HANDLER_NOT_REGIST;
1126     }
1127     auto eventHandler = jsMessageHandler_->GetEventHandler();
1128     if (eventHandler == nullptr) {
1129         IMSA_HILOGI("EventHandler is nullptr!.");
1130         return ErrorCode::ERROR_NULL_POINTER;
1131     }
1132     auto task = [jsCallbackObject = jsMessageHandler_, arrayBuffer]() {
1133         napi_value callback = nullptr;
1134         napi_value global = nullptr;
1135         if (jsCallbackObject == nullptr) {
1136             IMSA_HILOGI("jsCallbackObject is nullptr!.");
1137             return;
1138         }
1139         napi_get_reference_value(jsCallbackObject->env_, jsCallbackObject->onMessageCallback_, &callback);
1140         if (callback != nullptr) {
1141             napi_get_global(jsCallbackObject->env_, &global);
1142             napi_value output = nullptr;
1143             napi_value argv[ARGC_TWO] = { nullptr };
1144             if (JsUtils::GetMessageHandlerCallbackParam(argv, jsCallbackObject, arrayBuffer) != napi_ok) {
1145                 IMSA_HILOGE("Get message handler callback param failed!.");
1146                 return;
1147             }
1148             // The maximum valid parameters count of callback is 2.
1149             auto callbackArgc = arrayBuffer.jsArgc > ARGC_ONE ? ARGC_TWO : ARGC_ONE;
1150             auto status = napi_call_function(jsCallbackObject->env_, global, callback, callbackArgc, argv, &output);
1151             if (status != napi_ok) {
1152                 IMSA_HILOGI("Call js function failed!.");
1153                 output = nullptr;
1154             }
1155         }
1156     };
1157     eventHandler->PostTask(task, "IMC_MsgHandler_OnMessage", 0, AppExecFwk::EventQueue::Priority::VIP);
1158     return ErrorCode::NO_ERROR;
1159 }
1160 } // namespace MiscServices
1161 } // namespace OHOS