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