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