1 /*
2 * Copyright (c) 2022-2023 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 "js_util.h"
20 #include "js_utils.h"
21 #include "napi/native_api.h"
22 #include "napi/native_node_api.h"
23 #include "string_ex.h"
24
25 namespace OHOS {
26 namespace MiscServices {
27 thread_local napi_ref JsTextInputClientEngine::TICRef_ = nullptr;
28 const std::string JsTextInputClientEngine::TIC_CLASS_NAME = "TextInputClient";
29 constexpr int32_t MAX_WAIT_TIME = 5000;
30 BlockQueue<EditorEventInfo> JsTextInputClientEngine::editorQueue_{ MAX_WAIT_TIME };
Init(napi_env env,napi_value info)31 napi_value JsTextInputClientEngine::Init(napi_env env, napi_value info)
32 {
33 IMSA_HILOGI("JsTextInputClientEngine init");
34 napi_property_descriptor properties[] = {
35 DECLARE_NAPI_FUNCTION("sendKeyFunction", SendKeyFunction),
36 DECLARE_NAPI_FUNCTION("deleteForward", DeleteForward),
37 DECLARE_NAPI_FUNCTION("deleteBackward", DeleteBackward),
38 DECLARE_NAPI_FUNCTION("insertText", InsertText),
39 DECLARE_NAPI_FUNCTION("getForward", GetForward),
40 DECLARE_NAPI_FUNCTION("getBackward", GetBackward),
41 DECLARE_NAPI_FUNCTION("getEditorAttribute", GetEditorAttribute),
42 DECLARE_NAPI_FUNCTION("getTextIndexAtCursor", GetTextIndexAtCursor),
43 DECLARE_NAPI_FUNCTION("moveCursor", MoveCursor),
44 DECLARE_NAPI_FUNCTION("selectByRange", SelectByRange),
45 DECLARE_NAPI_FUNCTION("selectByMovement", SelectByMovement),
46 DECLARE_NAPI_FUNCTION("sendExtendAction", SendExtendAction),
47 DECLARE_NAPI_FUNCTION("insertTextSync", InsertTextSync),
48 DECLARE_NAPI_FUNCTION("moveCursorSync", MoveCursorSync),
49 DECLARE_NAPI_FUNCTION("getEditorAttributeSync", GetEditorAttributeSync),
50 DECLARE_NAPI_FUNCTION("selectByRangeSync", SelectByRangeSync),
51 DECLARE_NAPI_FUNCTION("selectByMovementSync", SelectByMovementSync),
52 DECLARE_NAPI_FUNCTION("getTextIndexAtCursorSync", GetTextIndexAtCursorSync),
53 DECLARE_NAPI_FUNCTION("deleteForwardSync", DeleteForwardSync),
54 DECLARE_NAPI_FUNCTION("deleteBackwardSync", DeleteBackwardSync),
55 DECLARE_NAPI_FUNCTION("getForwardSync", GetForwardSync),
56 DECLARE_NAPI_FUNCTION("getBackwardSync", GetBackwardSync)
57 };
58 napi_value cons = nullptr;
59 NAPI_CALL(env, napi_define_class(env, TIC_CLASS_NAME.c_str(), TIC_CLASS_NAME.size(), JsConstructor, nullptr,
60 sizeof(properties) / sizeof(napi_property_descriptor), properties, &cons));
61 NAPI_CALL(env, napi_create_reference(env, cons, 1, &TICRef_));
62 NAPI_CALL(env, napi_set_named_property(env, info, TIC_CLASS_NAME.c_str(), cons));
63
64 return info;
65 }
66
MoveCursor(napi_env env,napi_callback_info info)67 napi_value JsTextInputClientEngine::MoveCursor(napi_env env, napi_callback_info info)
68 {
69 auto ctxt = std::make_shared<MoveCursorContext>();
70 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
71 PARAM_CHECK_RETURN(env, argc > 0, " should 1 or 2 parameters! ", TYPE_NONE, napi_generic_failure);
72 auto status = JsUtils::GetValue(env, argv[0], ctxt->num);
73 if (status == napi_ok) {
74 ctxt->info = { std::chrono::system_clock::now(), EditorEvent::MOVE_CURSOR };
75 editorQueue_.Push(ctxt->info);
76 }
77 return status;
78 };
79 auto exec = [ctxt](AsyncCall::Context *ctx) {
80 editorQueue_.Wait(ctxt->info);
81 int32_t code = InputMethodAbility::GetInstance()->MoveCursor(ctxt->num);
82 editorQueue_.Pop();
83 if (code == ErrorCode::NO_ERROR) {
84 ctxt->status = napi_ok;
85 ctxt->SetState(ctxt->status);
86 } else {
87 ctxt->SetErrorCode(code);
88 }
89 };
90 ctxt->SetAction(std::move(input));
91 // 2 means JsAPI:moveCursor has 2 params at most.
92 AsyncCall asyncCall(env, info, ctxt, 2);
93 return asyncCall.Call(env, exec, "moveCursor");
94 }
95
MoveCursorSync(napi_env env,napi_callback_info info)96 napi_value JsTextInputClientEngine::MoveCursorSync(napi_env env, napi_callback_info info)
97 {
98 EditorEventInfo eventInfo = { std::chrono::system_clock::now(), EditorEvent::MOVE_CURSOR};
99 editorQueue_.Push(eventInfo);
100 editorQueue_.Wait(eventInfo);
101 size_t argc = 1;
102 napi_value argv[1] = { nullptr };
103 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
104 int32_t direction = 0;
105 // 1 means least param num.
106 if (argc < 1 || JsUtil::GetType(env, argv[0]) != napi_number || !JsUtil::GetValue(env, argv[0], direction)
107 || direction < 0) {
108 editorQueue_.Pop();
109 JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK, "please check the params", TYPE_NONE);
110 return JsUtil::Const::Null(env);
111 }
112 IMSA_HILOGD("moveCursor , direction: %{public}d", direction);
113 int32_t ret = InputMethodAbility::GetInstance()->MoveCursor(direction);
114 editorQueue_.Pop();
115 if (ret != ErrorCode::NO_ERROR) {
116 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to move cursor", TYPE_NONE);
117 }
118 return JsUtil::Const::Null(env);
119 }
120
JsConstructor(napi_env env,napi_callback_info cbinfo)121 napi_value JsTextInputClientEngine::JsConstructor(napi_env env, napi_callback_info cbinfo)
122 {
123 napi_value thisVar = nullptr;
124 NAPI_CALL(env, napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr));
125
126 JsTextInputClientEngine *clientObject = new (std::nothrow) JsTextInputClientEngine();
127 if (clientObject == nullptr) {
128 IMSA_HILOGE("clientObject is nullptr");
129 napi_value result = nullptr;
130 napi_get_null(env, &result);
131 return result;
132 }
133 auto finalize = [](napi_env env, void *data, void *hint) {
134 IMSA_HILOGE("JsTextInputClientEngine finalize");
135 auto *objInfo = reinterpret_cast<JsTextInputClientEngine *>(data);
136 if (objInfo != nullptr) {
137 delete objInfo;
138 }
139 };
140 napi_status status = napi_wrap(env, thisVar, clientObject, finalize, nullptr, nullptr);
141 if (status != napi_ok) {
142 IMSA_HILOGE("JsTextInputClientEngine napi_wrap failed: %{public}d", status);
143 delete clientObject;
144 return nullptr;
145 }
146 return thisVar;
147 }
148
GetTextInputClientInstance(napi_env env)149 napi_value JsTextInputClientEngine::GetTextInputClientInstance(napi_env env)
150 {
151 napi_value instance = nullptr;
152 napi_value cons = nullptr;
153 if (napi_get_reference_value(env, TICRef_, &cons) != napi_ok) {
154 IMSA_HILOGE("JsTextInputClientEngine::napi_get_reference_value not ok");
155 return nullptr;
156 }
157 if (napi_new_instance(env, cons, 0, nullptr, &instance) != napi_ok) {
158 IMSA_HILOGE("JsTextInputClientEngine::napi_new_instance not ok");
159 return nullptr;
160 }
161 return instance;
162 }
163
GetResult(napi_env env,std::string & text)164 napi_value JsTextInputClientEngine::GetResult(napi_env env, std::string &text)
165 {
166 napi_value jsText = nullptr;
167 napi_create_string_utf8(env, text.c_str(), NAPI_AUTO_LENGTH, &jsText);
168 return jsText;
169 }
170
GetResultEditorAttribute(napi_env env,std::shared_ptr<GetEditorAttributeContext> getEditorAttribute)171 napi_value JsTextInputClientEngine::GetResultEditorAttribute(
172 napi_env env, std::shared_ptr<GetEditorAttributeContext> getEditorAttribute)
173 {
174 napi_value editorAttribute = nullptr;
175 napi_create_object(env, &editorAttribute);
176
177 napi_value jsValue = nullptr;
178 napi_create_int32(env, getEditorAttribute->enterKeyType, &jsValue);
179 napi_set_named_property(env, editorAttribute, "enterKeyType", jsValue);
180
181 napi_value jsMethodId = nullptr;
182 napi_create_int32(env, getEditorAttribute->inputPattern, &jsMethodId);
183 napi_set_named_property(env, editorAttribute, "inputPattern", jsMethodId);
184
185 return editorAttribute;
186 }
187
GetSelectRange(napi_env env,napi_value argv,std::shared_ptr<SelectContext> ctxt)188 napi_status JsTextInputClientEngine::GetSelectRange(napi_env env, napi_value argv, std::shared_ptr<SelectContext> ctxt)
189 {
190 napi_status status = napi_generic_failure;
191 napi_value napiValue = nullptr;
192 status = napi_get_named_property(env, argv, "start", &napiValue);
193 PARAM_CHECK_RETURN(env, status == napi_ok, "missing start parameter.", TYPE_NONE, status);
194 status = JsUtils::GetValue(env, napiValue, ctxt->start);
195 CHECK_RETURN(status == napi_ok, "failed to get start value", status);
196
197 status = napi_get_named_property(env, argv, "end", &napiValue);
198 PARAM_CHECK_RETURN(env, status == napi_ok, "missing end parameter.", TYPE_NONE, status);
199 status = JsUtils::GetValue(env, napiValue, ctxt->end);
200 if (status != napi_ok) {
201 IMSA_HILOGE("failed to get end value");
202 }
203 return status;
204 }
205
GetSelectMovement(napi_env env,napi_value argv,std::shared_ptr<SelectContext> ctxt)206 napi_status JsTextInputClientEngine::GetSelectMovement(
207 napi_env env, napi_value argv, std::shared_ptr<SelectContext> ctxt)
208 {
209 napi_status status = napi_generic_failure;
210 napi_value napiValue = nullptr;
211 status = napi_get_named_property(env, argv, "direction", &napiValue);
212 PARAM_CHECK_RETURN(env, status == napi_ok, "missing direction parameter.", TYPE_NONE, status);
213 status = JsUtils::GetValue(env, napiValue, ctxt->direction);
214 if (status != napi_ok) {
215 IMSA_HILOGE("failed to get direction value");
216 }
217 return status;
218 }
219
SendKeyFunction(napi_env env,napi_callback_info info)220 napi_value JsTextInputClientEngine::SendKeyFunction(napi_env env, napi_callback_info info)
221 {
222 auto ctxt = std::make_shared<SendKeyFunctionContext>();
223 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
224 PARAM_CHECK_RETURN(env, argc > 0, "should 1 or 2 parameters!", TYPE_NONE, napi_generic_failure);
225 return JsUtils::GetValue(env, argv[0], ctxt->action);
226 };
227 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
228 napi_status status = napi_get_boolean(env, ctxt->isSendKeyFunction, result);
229 return status;
230 };
231 auto exec = [ctxt](AsyncCall::Context *ctx) {
232 int32_t code = InputMethodAbility::GetInstance()->SendFunctionKey(ctxt->action);
233 if (code == ErrorCode::NO_ERROR) {
234 ctxt->status = napi_ok;
235 ctxt->SetState(ctxt->status);
236 ctxt->isSendKeyFunction = true;
237 } else {
238 ctxt->SetErrorCode(code);
239 }
240 };
241 ctxt->SetAction(std::move(input), std::move(output));
242 // 2 means JsAPI:sendKeyFunction has 2 params at most.
243 AsyncCall asyncCall(env, info, ctxt, 2);
244 return asyncCall.Call(env, exec, "sendKeyFunction");
245 }
246
DeleteForwardSync(napi_env env,napi_callback_info info)247 napi_value JsTextInputClientEngine::DeleteForwardSync(napi_env env, napi_callback_info info)
248 {
249 EditorEventInfo eventInfo = { std::chrono::system_clock::now(), EditorEvent::DELETE_FORWARD };
250 editorQueue_.Push(eventInfo);
251 editorQueue_.Wait(eventInfo);
252 size_t argc = 1;
253 napi_value argv[1] = { nullptr };
254 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
255 int32_t length = 0;
256 // 1 means least param num.
257 if (argc < 1 || JsUtil::GetType(env, argv[0]) != napi_number || !JsUtil::GetValue(env, argv[0], length)
258 || length < 0) {
259 JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK, "please check the params", TYPE_NONE);
260 editorQueue_.Pop();
261 return JsUtil::Const::Null(env);
262 }
263 IMSA_HILOGD("Delete forward, length: %{public}d", length);
264 int32_t ret = InputMethodAbility::GetInstance()->DeleteForward(length);
265 editorQueue_.Pop();
266 if (ret != ErrorCode::NO_ERROR) {
267 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to delete forward", TYPE_NONE);
268 }
269 return JsUtil::Const::Null(env);
270 }
271
DeleteForward(napi_env env,napi_callback_info info)272 napi_value JsTextInputClientEngine::DeleteForward(napi_env env, napi_callback_info info)
273 {
274 auto ctxt = std::make_shared<DeleteForwardContext>();
275 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
276 PARAM_CHECK_RETURN(env, argc > 0, "should 1 or 2 parameters!", TYPE_NONE, napi_generic_failure);
277 auto status = JsUtils::GetValue(env, argv[0], ctxt->length);
278 if (status == napi_ok) {
279 ctxt->info = { std::chrono::system_clock::now(), EditorEvent::DELETE_FORWARD };
280 editorQueue_.Push(ctxt->info);
281 }
282 return status;
283 };
284 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
285 napi_status status = napi_get_boolean(env, ctxt->isDeleteForward, result);
286 return status;
287 };
288 auto exec = [ctxt](AsyncCall::Context *ctx) {
289 editorQueue_.Wait(ctxt->info);
290 int32_t code = InputMethodAbility::GetInstance()->DeleteForward(ctxt->length);
291 editorQueue_.Pop();
292 if (code == ErrorCode::NO_ERROR) {
293 ctxt->status = napi_ok;
294 ctxt->SetState(ctxt->status);
295 ctxt->isDeleteForward = true;
296 } else {
297 ctxt->SetErrorCode(code);
298 }
299 };
300 ctxt->SetAction(std::move(input), std::move(output));
301 // 2 means JsAPI:deleteForward has 2 params at most.
302 AsyncCall asyncCall(env, info, ctxt, 2);
303 return asyncCall.Call(env, exec, "deleteForward");
304 }
305
DeleteBackwardSync(napi_env env,napi_callback_info info)306 napi_value JsTextInputClientEngine::DeleteBackwardSync(napi_env env, napi_callback_info info)
307 {
308 EditorEventInfo eventInfo = { std::chrono::system_clock::now(), EditorEvent::DELETE_BACKWARD };
309 editorQueue_.Push(eventInfo);
310 editorQueue_.Wait(eventInfo);
311 size_t argc = 1;
312 napi_value argv[1] = { nullptr };
313 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
314 int32_t length = 0;
315 // 1 means least param num.
316 if (argc < 1 || JsUtil::GetType(env, argv[0]) != napi_number || !JsUtil::GetValue(env, argv[0], length)
317 || length < 0) {
318 editorQueue_.Pop();
319 JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK, "please check the params", TYPE_NONE);
320 return JsUtil::Const::Null(env);
321 }
322 IMSA_HILOGD("Delete backward, length: %{public}d", length);
323 int32_t ret = InputMethodAbility::GetInstance()->DeleteBackward(length);
324 editorQueue_.Pop();
325 if (ret != ErrorCode::NO_ERROR) {
326 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to delete backward", TYPE_NONE);
327 }
328 return JsUtil::Const::Null(env);
329 }
330
DeleteBackward(napi_env env,napi_callback_info info)331 napi_value JsTextInputClientEngine::DeleteBackward(napi_env env, napi_callback_info info)
332 {
333 auto ctxt = std::make_shared<DeleteBackwardContext>();
334 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
335 PARAM_CHECK_RETURN(env, argc > 0, "should 1 or 2 parameters!", TYPE_NONE, napi_generic_failure);
336 auto status = JsUtils::GetValue(env, argv[0], ctxt->length);
337 if (status == napi_ok) {
338 ctxt->info = { std::chrono::system_clock::now(), EditorEvent::DELETE_BACKWARD };
339 editorQueue_.Push(ctxt->info);
340 }
341 return status;
342 };
343 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
344 napi_status status = napi_get_boolean(env, ctxt->isDeleteBackward, result);
345 return status;
346 };
347 auto exec = [ctxt](AsyncCall::Context *ctx) {
348 editorQueue_.Wait(ctxt->info);
349 int32_t code = InputMethodAbility::GetInstance()->DeleteBackward(ctxt->length);
350 editorQueue_.Pop();
351 if (code == ErrorCode::NO_ERROR) {
352 ctxt->status = napi_ok;
353 ctxt->SetState(ctxt->status);
354 ctxt->isDeleteBackward = true;
355 } else {
356 ctxt->SetErrorCode(code);
357 }
358 };
359 ctxt->SetAction(std::move(input), std::move(output));
360 // 2 means JsAPI:deleteBackward has 2 params at most.
361 AsyncCall asyncCall(env, info, ctxt, 2);
362 return asyncCall.Call(env, exec, "deleteBackward");
363 }
364
InsertText(napi_env env,napi_callback_info info)365 napi_value JsTextInputClientEngine::InsertText(napi_env env, napi_callback_info info)
366 {
367 auto ctxt = std::make_shared<InsertTextContext>();
368 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
369 PARAM_CHECK_RETURN(env, argc > 0, "should 1 or 2 parameters!", TYPE_NONE, napi_generic_failure);
370 auto status = JsUtils::GetValue(env, argv[0], ctxt->text);
371 if (status == napi_ok) {
372 ctxt->info = { std::chrono::system_clock::now(), EditorEvent::INSERT_TEXT };
373 editorQueue_.Push(ctxt->info);
374 }
375 return status;
376 };
377 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
378 napi_status status = napi_get_boolean(env, ctxt->isInsertText, result);
379 return status;
380 };
381 auto exec = [ctxt](AsyncCall::Context *ctx) {
382 editorQueue_.Wait(ctxt->info);
383 int32_t code = InputMethodAbility::GetInstance()->InsertText(ctxt->text);
384 editorQueue_.Pop();
385 if (code == ErrorCode::NO_ERROR) {
386 ctxt->status = napi_ok;
387 ctxt->SetState(ctxt->status);
388 ctxt->isInsertText = true;
389 } else {
390 ctxt->SetErrorCode(code);
391 }
392 };
393 ctxt->SetAction(std::move(input), std::move(output));
394 // 2 means JsAPI:insertText has 2 params at most.
395 AsyncCall asyncCall(env, info, ctxt, 2);
396 return asyncCall.Call(env, exec, "insertText");
397 }
398
InsertTextSync(napi_env env,napi_callback_info info)399 napi_value JsTextInputClientEngine::InsertTextSync(napi_env env, napi_callback_info info)
400 {
401 EditorEventInfo eventInfo = { std::chrono::system_clock::now(), EditorEvent::INSERT_TEXT};
402 editorQueue_.Push(eventInfo);
403 editorQueue_.Wait(eventInfo);
404 size_t argc = 1;
405 napi_value argv[1] = { nullptr };
406 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
407 std::string text = "";
408 // 1 means least param num.
409 if (argc < 1 || JsUtil::GetType(env, argv[0]) != napi_string || !JsUtil::GetValue(env, argv[0], text)) {
410 editorQueue_.Pop();
411 JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK, "please check the params", TYPE_NONE);
412 return JsUtil::Const::Null(env);
413 }
414 IMSA_HILOGD("insert text , text: %{public}s", text.c_str());
415 int32_t ret = InputMethodAbility::GetInstance()->InsertText(text);
416 editorQueue_.Pop();
417 if (ret != ErrorCode::NO_ERROR) {
418 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to insert text", TYPE_NONE);
419 }
420 return JsUtil::Const::Null(env);
421 }
422
GetForwardSync(napi_env env,napi_callback_info info)423 napi_value JsTextInputClientEngine::GetForwardSync(napi_env env, napi_callback_info info)
424 {
425 EditorEventInfo eventInfo = { std::chrono::system_clock::now(), EditorEvent::GET_FORWARD };
426 editorQueue_.Push(eventInfo);
427 editorQueue_.Wait(eventInfo);
428 size_t argc = 1;
429 napi_value argv[1] = { nullptr };
430 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
431 int32_t length = 0;
432 // 1 means least param num.
433 if (argc < 1 || JsUtil::GetType(env, argv[0]) != napi_number || !JsUtil::GetValue(env, argv[0], length)
434 || length < 0) {
435 editorQueue_.Pop();
436 JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK, "please check the params", TYPE_NONE);
437 return JsUtil::Const::Null(env);
438 }
439 IMSA_HILOGD("Get forward, length: %{public}d", length);
440 std::u16string text;
441 int32_t ret = InputMethodAbility::GetInstance()->GetTextBeforeCursor(length, text);
442 editorQueue_.Pop();
443 if (ret != ErrorCode::NO_ERROR) {
444 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to get forward", TYPE_NONE);
445 return JsUtil::Const::Null(env);
446 }
447 napi_value result = nullptr;
448 auto status = JsUtils::GetValue(env, Str16ToStr8(text), result);
449 CHECK_RETURN(status == napi_ok, "GetValue failed", JsUtil::Const::Null(env));
450 return result;
451 }
452
GetForward(napi_env env,napi_callback_info info)453 napi_value JsTextInputClientEngine::GetForward(napi_env env, napi_callback_info info)
454 {
455 auto ctxt = std::make_shared<GetForwardContext>();
456 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
457 PARAM_CHECK_RETURN(env, argc > 0, "should 1 or 2 parameters!", TYPE_NONE, napi_generic_failure);
458 auto status = JsUtils::GetValue(env, argv[0], ctxt->length);
459 if (status == napi_ok) {
460 ctxt->info = { std::chrono::system_clock::now(), EditorEvent::GET_FORWARD };
461 editorQueue_.Push(ctxt->info);
462 }
463 return status;
464 };
465 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
466 napi_value data = GetResult(env, ctxt->text);
467 *result = data;
468 return napi_ok;
469 };
470 auto exec = [ctxt](AsyncCall::Context *ctx) {
471 editorQueue_.Wait(ctxt->info);
472 std::u16string temp;
473 int32_t code = InputMethodAbility::GetInstance()->GetTextBeforeCursor(ctxt->length, temp);
474 editorQueue_.Pop();
475 if (code == ErrorCode::NO_ERROR) {
476 ctxt->status = napi_ok;
477 ctxt->SetState(ctxt->status);
478 ctxt->text = Str16ToStr8(temp);
479 } else {
480 ctxt->SetErrorCode(code);
481 }
482 };
483 ctxt->SetAction(std::move(input), std::move(output));
484 // 2 means JsAPI:getForward has 2 params at most.
485 AsyncCall asyncCall(env, info, ctxt, 2);
486 return asyncCall.Call(env, exec, "getForward");
487 }
488
GetBackwardSync(napi_env env,napi_callback_info info)489 napi_value JsTextInputClientEngine::GetBackwardSync(napi_env env, napi_callback_info info)
490 {
491 EditorEventInfo eventInfo = { std::chrono::system_clock::now(), EditorEvent::GET_BACKWARD };
492 editorQueue_.Push(eventInfo);
493 editorQueue_.Wait(eventInfo);
494 size_t argc = 1;
495 napi_value argv[1] = { nullptr };
496 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
497 int32_t length = 0;
498 // 1 means least param num.
499 if (argc < 1 || JsUtil::GetType(env, argv[0]) != napi_number || !JsUtil::GetValue(env, argv[0], length)
500 || length < 0) {
501 editorQueue_.Pop();
502 JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK, "please check the params", TYPE_NONE);
503 return JsUtil::Const::Null(env);
504 }
505 IMSA_HILOGD("Get backward, length: %{public}d", length);
506 std::u16string text;
507 int32_t ret = InputMethodAbility::GetInstance()->GetTextAfterCursor(length, text);
508 editorQueue_.Pop();
509 if (ret != ErrorCode::NO_ERROR) {
510 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to get backward", TYPE_NONE);
511 return JsUtil::Const::Null(env);
512 }
513 napi_value result = nullptr;
514 auto status = JsUtils::GetValue(env, Str16ToStr8(text), result);
515 CHECK_RETURN(status == napi_ok, "GetValue failed", JsUtil::Const::Null(env));
516 return result;
517 }
518
GetBackward(napi_env env,napi_callback_info info)519 napi_value JsTextInputClientEngine::GetBackward(napi_env env, napi_callback_info info)
520 {
521 auto ctxt = std::make_shared<GetBackwardContext>();
522 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
523 PARAM_CHECK_RETURN(env, argc > 0, "should 1 or 2 parameters!", TYPE_NONE, napi_generic_failure);
524 auto status = JsUtils::GetValue(env, argv[0], ctxt->length);
525 if (status == napi_ok) {
526 ctxt->info = { std::chrono::system_clock::now(), EditorEvent::GET_BACKWARD };
527 editorQueue_.Push(ctxt->info);
528 }
529 return status;
530 };
531 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
532 napi_value data = GetResult(env, ctxt->text);
533 *result = data;
534 return napi_ok;
535 };
536 auto exec = [ctxt](AsyncCall::Context *ctx) {
537 editorQueue_.Wait(ctxt->info);
538 std::u16string temp;
539 int32_t code = InputMethodAbility::GetInstance()->GetTextAfterCursor(ctxt->length, temp);
540 editorQueue_.Pop();
541 if (code == ErrorCode::NO_ERROR) {
542 ctxt->status = napi_ok;
543 ctxt->SetState(ctxt->status);
544 ctxt->text = Str16ToStr8(temp);
545 } else {
546 ctxt->SetErrorCode(code);
547 }
548 };
549 ctxt->SetAction(std::move(input), std::move(output));
550 // 2 means JsAPI:getBackward has 2 params at most.
551 AsyncCall asyncCall(env, info, ctxt, 2);
552 return asyncCall.Call(env, exec, "getBackward");
553 }
554
GetEditorAttributeSync(napi_env env,napi_callback_info info)555 napi_value JsTextInputClientEngine::GetEditorAttributeSync(napi_env env, napi_callback_info info)
556 {
557 int32_t enterKeyType = 0;
558 int32_t ret = InputMethodAbility::GetInstance()->GetEnterKeyType(enterKeyType);
559 if (ret != ErrorCode::NO_ERROR) {
560 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to getEnterKeyType", TYPE_NONE);
561 }
562 IMSA_HILOGD("getEditorAttribute, enterKeyType: %{public}d", enterKeyType);
563
564 int32_t inputPattern = 0;
565 ret = InputMethodAbility::GetInstance()->GetInputPattern(inputPattern);
566 if (ret != ErrorCode::NO_ERROR) {
567 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to getInputPattern", TYPE_NONE);
568 }
569 IMSA_HILOGD("getEditorAttribute, patternCode: %{public}d", inputPattern);
570
571 const InputAttribute attribute = { .inputPattern = enterKeyType, .enterKeyType = inputPattern };
572 return JsUtils::GetValue(env, attribute);
573 }
574
GetEditorAttribute(napi_env env,napi_callback_info info)575 napi_value JsTextInputClientEngine::GetEditorAttribute(napi_env env, napi_callback_info info)
576 {
577 auto ctxt = std::make_shared<GetEditorAttributeContext>();
578 auto input = [ctxt](
579 napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { return napi_ok; };
580 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
581 napi_value data = GetResultEditorAttribute(env, ctxt);
582 *result = data;
583 return napi_ok;
584 };
585 auto exec = [ctxt](AsyncCall::Context *ctx) {
586 int32_t typeCode = InputMethodAbility::GetInstance()->GetEnterKeyType(ctxt->enterKeyType);
587 int32_t patternCode = InputMethodAbility::GetInstance()->GetInputPattern(ctxt->inputPattern);
588 if (typeCode == ErrorCode::NO_ERROR && patternCode == ErrorCode::NO_ERROR) {
589 ctxt->status = napi_ok;
590 ctxt->SetState(ctxt->status);
591 } else {
592 typeCode == ErrorCode::NO_ERROR ? ctxt->SetErrorCode(patternCode) : ctxt->SetErrorCode(typeCode);
593 }
594 };
595 ctxt->SetAction(std::move(input), std::move(output));
596 // 1 means JsAPI:getEditorAttribute has 1 params at most.
597 AsyncCall asyncCall(env, info, ctxt, 1);
598 return asyncCall.Call(env, exec, "getEditorAttribute");
599 }
600
SelectByRange(napi_env env,napi_callback_info info)601 napi_value JsTextInputClientEngine::SelectByRange(napi_env env, napi_callback_info info)
602 {
603 IMSA_HILOGD("run in");
604 auto ctxt = std::make_shared<SelectContext>();
605 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
606 PARAM_CHECK_RETURN(env, argc > 0, "should 1 or 2 parameters!", TYPE_NONE, napi_generic_failure);
607 napi_valuetype valueType = napi_undefined;
608 napi_typeof(env, argv[0], &valueType);
609 PARAM_CHECK_RETURN(env, valueType == napi_object, "range", TYPE_OBJECT, napi_generic_failure);
610 auto status = GetSelectRange(env, argv[0], ctxt);
611 if (status == napi_ok) {
612 ctxt->info = { std::chrono::system_clock::now(), EditorEvent::SELECT_BY_RANGE };
613 editorQueue_.Push(ctxt->info);
614 }
615 return status;
616 };
617 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status { return napi_ok; };
618 auto exec = [ctxt](AsyncCall::Context *ctx) {
619 editorQueue_.Wait(ctxt->info);
620 int32_t code = InputMethodAbility::GetInstance()->SelectByRange(ctxt->start, ctxt->end);
621 editorQueue_.Pop();
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 asyncCall.Call(env, exec, "selectByRange");
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 EditorEventInfo eventInfo = { std::chrono::system_clock::now(), EditorEvent::SELECT_BY_RANGE};
639 editorQueue_.Push(eventInfo);
640 editorQueue_.Wait(eventInfo);
641 size_t argc = 1;
642 napi_value argv[1] = { nullptr };
643 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
644 if (argc < 1 || JsUtil::GetType(env, argv[0]) != napi_object) {
645 editorQueue_.Pop();
646 JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK, "please check the params", TYPE_NONE);
647 return JsUtil::Const::Null(env);
648 }
649 auto ctxt = std::make_shared<SelectContext>();
650 auto status = GetSelectRange(env, argv[0], ctxt);
651 if (status != napi_ok) {
652 editorQueue_.Pop();
653 JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK, "failed to get start or end.", TYPE_NONE);
654 return JsUtil::Const::Null(env);
655 }
656 IMSA_HILOGD("selectByRangeSync, start: %{public}d, end: %{public}d", ctxt->start, ctxt->end);
657 int32_t ret = InputMethodAbility::GetInstance()->SelectByRange(ctxt->start, ctxt->end);
658 editorQueue_.Pop();
659 if (ret != ErrorCode::NO_ERROR) {
660 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to select by range.", TYPE_NONE);
661 }
662 return JsUtil::Const::Null(env);
663 }
664
SelectByMovementSync(napi_env env,napi_callback_info info)665 napi_value JsTextInputClientEngine::SelectByMovementSync(napi_env env, napi_callback_info info)
666 {
667 IMSA_HILOGD("SelectByMovementSync");
668 EditorEventInfo eventInfo = { std::chrono::system_clock::now(), EditorEvent::SELECT_BY_MOVEMENT};
669 editorQueue_.Push(eventInfo);
670 editorQueue_.Wait(eventInfo);
671 size_t argc = 1;
672 napi_value argv[1] = { nullptr };
673 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
674 if (argc < 1 || JsUtil::GetType(env, argv[0]) != napi_object) {
675 editorQueue_.Pop();
676 JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK, "please check the params", TYPE_NONE);
677 return JsUtil::Const::Null(env);
678 }
679 auto ctxt = std::make_shared<SelectContext>();
680 auto status = GetSelectMovement(env, argv[0], ctxt);
681 if (status != napi_ok) {
682 editorQueue_.Pop();
683 JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK, "failed to get direction.", TYPE_NONE);
684 return JsUtil::Const::Null(env);
685 }
686 IMSA_HILOGD("selectByMovement , direction: %{public}d", ctxt->direction);
687 int32_t ret = InputMethodAbility::GetInstance()->SelectByMovement(ctxt->direction);
688 editorQueue_.Pop();
689 if (ret != ErrorCode::NO_ERROR) {
690 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to select by movement.", TYPE_NONE);
691 }
692 return JsUtil::Const::Null(env);
693 }
694
SelectByMovement(napi_env env,napi_callback_info info)695 napi_value JsTextInputClientEngine::SelectByMovement(napi_env env, napi_callback_info info)
696 {
697 IMSA_HILOGD("run in");
698 auto ctxt = std::make_shared<SelectContext>();
699 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
700 PARAM_CHECK_RETURN(env, argc > 0, "should 1 or 2 parameters!", TYPE_NONE, napi_generic_failure);
701 napi_valuetype valueType = napi_undefined;
702 napi_typeof(env, argv[0], &valueType);
703 PARAM_CHECK_RETURN(env, valueType == napi_object, "movement", TYPE_OBJECT, napi_generic_failure);
704 auto status = GetSelectMovement(env, argv[0], ctxt);
705 if (status == napi_ok) {
706 ctxt->info = { std::chrono::system_clock::now(), EditorEvent::SELECT_BY_MOVEMENT };
707 editorQueue_.Push(ctxt->info);
708 }
709 return status;
710 };
711 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status { return napi_ok; };
712 auto exec = [ctxt](AsyncCall::Context *ctx) {
713 editorQueue_.Wait(ctxt->info);
714 int32_t code = InputMethodAbility::GetInstance()->SelectByMovement(ctxt->direction);
715 editorQueue_.Pop();
716 if (code == ErrorCode::NO_ERROR) {
717 ctxt->status = napi_ok;
718 ctxt->SetState(ctxt->status);
719 } else {
720 ctxt->SetErrorCode(code);
721 }
722 };
723 ctxt->SetAction(std::move(input), std::move(output));
724 // 2 means JsAPI:selectByMovement has 2 params at most.
725 AsyncCall asyncCall(env, info, ctxt, 2);
726 return asyncCall.Call(env, exec, "selectByMovement");
727 }
728
SendExtendAction(napi_env env,napi_callback_info info)729 napi_value JsTextInputClientEngine::SendExtendAction(napi_env env, napi_callback_info info)
730 {
731 auto ctxt = std::make_shared<SendExtendActionContext>();
732 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
733 PARAM_CHECK_RETURN(env, argc > 0, "should 1 or 2 parameters!", TYPE_NONE, napi_generic_failure);
734 auto status = JsUtils::GetValue(env, argv[0], ctxt->action);
735 if (status == napi_ok) {
736 ctxt->info = { std::chrono::system_clock::now(), EditorEvent::SEND_EXTEND_ACTION };
737 editorQueue_.Push(ctxt->info);
738 }
739 return status;
740 };
741 auto exec = [ctxt](AsyncCall::Context *ctx) {
742 editorQueue_.Wait(ctxt->info);
743 int32_t code = InputMethodAbility::GetInstance()->SendExtendAction(ctxt->action);
744 editorQueue_.Pop();
745 if (code == ErrorCode::NO_ERROR) {
746 ctxt->SetState(napi_ok);
747 return;
748 }
749 ctxt->SetErrorCode(code);
750 };
751 ctxt->SetAction(std::move(input));
752 // 2 means JsAPI:sendExtendAction has 2 params at most.
753 AsyncCall asyncCall(env, info, ctxt, 2);
754 return asyncCall.Call(env, exec, "sendExtendAction");
755 }
756
GetTextIndexAtCursor(napi_env env,napi_callback_info info)757 napi_value JsTextInputClientEngine::GetTextIndexAtCursor(napi_env env, napi_callback_info info)
758 {
759 IMSA_HILOGE("GetTextIndexAtCursor");
760 auto ctxt = std::make_shared<GetTextIndexAtCursorContext>();
761 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
762 ctxt->info = { std::chrono::system_clock::now(), EditorEvent::GET_TEXT_INDEX_AT_CURSOR };
763 editorQueue_.Push(ctxt->info);
764 return napi_ok;
765 };
766 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
767 return napi_create_int32(env, ctxt->index, result);
768 };
769 auto exec = [ctxt](AsyncCall::Context *ctx) {
770 editorQueue_.Wait(ctxt->info);
771 int32_t code = InputMethodAbility::GetInstance()->GetTextIndexAtCursor(ctxt->index);
772 editorQueue_.Pop();
773 if (code == ErrorCode::NO_ERROR) {
774 ctxt->status = napi_ok;
775 ctxt->SetState(ctxt->status);
776 } else {
777 ctxt->SetErrorCode(code);
778 }
779 };
780 ctxt->SetAction(std::move(input), std::move(output));
781 // 1 means JsAPI:getTextIndexAtCursor has 1 params at most.
782 AsyncCall asyncCall(env, info, ctxt, 1);
783 return asyncCall.Call(env, exec, "getTextIndexAtCursor");
784 }
785
GetTextIndexAtCursorSync(napi_env env,napi_callback_info info)786 napi_value JsTextInputClientEngine::GetTextIndexAtCursorSync(napi_env env, napi_callback_info info)
787 {
788 IMSA_HILOGE("GetTextIndexAtCursorSync");
789 EditorEventInfo eventInfo = { std::chrono::system_clock::now(), EditorEvent::GET_TEXT_INDEX_AT_CURSOR};
790 editorQueue_.Push(eventInfo);
791 editorQueue_.Wait(eventInfo);
792 int32_t index = 0;
793 int32_t ret = InputMethodAbility::GetInstance()->GetTextIndexAtCursor(index);
794 editorQueue_.Pop();
795 if (ret != ErrorCode::NO_ERROR) {
796 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to get text index at cursor.", TYPE_NONE);
797 }
798 return JsUtil::GetValue(env, index);
799 }
800 } // namespace MiscServices
801 } // namespace OHOS
802