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