• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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