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 "event_checker.h"
19 #include "input_method_ability.h"
20 #include "inputmethod_trace.h"
21 #include "js_callback_handler.h"
22 #include "js_util.h"
23 #include "js_utils.h"
24 #include "napi/native_api.h"
25 #include "napi/native_node_api.h"
26 #include "string_ex.h"
27 #include "string_utils.h"
28 #include "wm_common.h"
29 #include "res_config.h"
30 #include "resource_manager.h"
31 #include "variant_util.h"
32
33 namespace OHOS {
34 namespace MiscServices {
35 #define ASYNC_POST(env, ctx) asyncCall.Post((env), (ctx), taskQueue_, __FUNCTION__)
36 using namespace std::chrono;
37 thread_local napi_ref JsTextInputClientEngine::TICRef_ = nullptr;
38 const std::string JsTextInputClientEngine::TIC_CLASS_NAME = "TextInputClient";
39 constexpr int32_t DEVICE_TYPE_2IN1 = 7;
40 constexpr int32_t MAX_WAIT_TIME = 5000;
41 constexpr int32_t MAX_WAIT_TIME_PRIVATE_COMMAND = 2000;
42 constexpr int32_t MAX_WAIT_TIME_MESSAGE_HANDLER = 2000;
43 constexpr size_t ARGC_TWO = 2;
44 constexpr size_t ARGC_ONE = 1;
45 std::shared_ptr<AsyncCall::TaskQueue> JsTextInputClientEngine::taskQueue_ = std::make_shared<AsyncCall::TaskQueue>();
46 BlockQueue<MessageHandlerInfo> JsTextInputClientEngine::messageHandlerQueue_{ MAX_WAIT_TIME_MESSAGE_HANDLER };
47 std::mutex JsTextInputClientEngine::engineMutex_;
48 std::shared_ptr<JsTextInputClientEngine> JsTextInputClientEngine::textInputClientEngine_{ nullptr };
49 std::mutex JsTextInputClientEngine::eventHandlerMutex_;
50 std::shared_ptr<AppExecFwk::EventHandler> JsTextInputClientEngine::handler_{ nullptr };
51 uint32_t JsTextInputClientEngine::traceId_{ 0 };
52 int32_t JsTextInputClientEngine::deviceTypeCache_{ -1 };
Init(napi_env env,napi_value info)53 napi_value JsTextInputClientEngine::Init(napi_env env, napi_value info)
54 {
55 IMSA_HILOGD("JsTextInputClientEngine init");
56 napi_property_descriptor properties[] = { DECLARE_NAPI_FUNCTION("sendKeyFunction", SendKeyFunction),
57 DECLARE_NAPI_FUNCTION("deleteForward", DeleteForward), DECLARE_NAPI_FUNCTION("deleteBackward", DeleteBackward),
58 DECLARE_NAPI_FUNCTION("insertText", InsertText), DECLARE_NAPI_FUNCTION("getForward", GetForward),
59 DECLARE_NAPI_FUNCTION("getBackward", GetBackward),
60 DECLARE_NAPI_FUNCTION("getEditorAttribute", GetEditorAttribute),
61 DECLARE_NAPI_FUNCTION("getTextIndexAtCursor", GetTextIndexAtCursor),
62 DECLARE_NAPI_FUNCTION("moveCursor", MoveCursor), DECLARE_NAPI_FUNCTION("selectByRange", SelectByRange),
63 DECLARE_NAPI_FUNCTION("selectByMovement", SelectByMovement),
64 DECLARE_NAPI_FUNCTION("sendExtendAction", SendExtendAction),
65 DECLARE_NAPI_FUNCTION("insertTextSync", InsertTextSync),
66 DECLARE_NAPI_FUNCTION("moveCursorSync", MoveCursorSync),
67 DECLARE_NAPI_FUNCTION("getEditorAttributeSync", GetEditorAttributeSync),
68 DECLARE_NAPI_FUNCTION("selectByRangeSync", SelectByRangeSync),
69 DECLARE_NAPI_FUNCTION("selectByMovementSync", SelectByMovementSync),
70 DECLARE_NAPI_FUNCTION("getTextIndexAtCursorSync", GetTextIndexAtCursorSync),
71 DECLARE_NAPI_FUNCTION("deleteForwardSync", DeleteForwardSync),
72 DECLARE_NAPI_FUNCTION("deleteBackwardSync", DeleteBackwardSync),
73 DECLARE_NAPI_FUNCTION("getForwardSync", GetForwardSync),
74 DECLARE_NAPI_FUNCTION("getBackwardSync", GetBackwardSync),
75 DECLARE_NAPI_FUNCTION("sendPrivateCommand", SendPrivateCommand),
76 DECLARE_NAPI_FUNCTION("getCallingWindowInfo", GetCallingWindowInfo),
77 DECLARE_NAPI_FUNCTION("setPreviewText", SetPreviewText),
78 DECLARE_NAPI_FUNCTION("setPreviewTextSync", SetPreviewTextSync),
79 DECLARE_NAPI_FUNCTION("finishTextPreview", FinishTextPreview),
80 DECLARE_NAPI_FUNCTION("finishTextPreviewSync", FinishTextPreviewSync),
81 DECLARE_NAPI_FUNCTION("sendMessage", SendMessage),
82 DECLARE_NAPI_FUNCTION("recvMessage", RecvMessage),
83 DECLARE_NAPI_FUNCTION("getAttachOptions", GetAttachOptions),
84 DECLARE_NAPI_FUNCTION("on", Subscribe),
85 DECLARE_NAPI_FUNCTION("off", UnSubscribe) };
86 napi_value cons = nullptr;
87 NAPI_CALL(env, napi_define_class(env, TIC_CLASS_NAME.c_str(), TIC_CLASS_NAME.size(), JsConstructor, nullptr,
88 sizeof(properties) / sizeof(napi_property_descriptor), properties, &cons));
89 NAPI_CALL(env, napi_create_reference(env, cons, 1, &TICRef_));
90 NAPI_CALL(env, napi_set_named_property(env, info, TIC_CLASS_NAME.c_str(), cons));
91
92 return info;
93 }
94
IsTargetDeviceType(int32_t resDeviceType)95 bool JsTextInputClientEngine::IsTargetDeviceType(int32_t resDeviceType)
96 {
97 if (deviceTypeCache_ != -1) {
98 if (deviceTypeCache_ == resDeviceType) {
99 return true;
100 }
101 return false;
102 }
103 std::shared_ptr<AbilityRuntime::ApplicationContext> context =
104 AbilityRuntime::ApplicationContext::GetApplicationContext();
105 if (context == nullptr) {
106 return false;
107 }
108 auto resourceManager = context->GetResourceManager();
109 auto resConfig = std::unique_ptr<Global::Resource::ResConfig>(Global::Resource::CreateResConfig());
110 if (resourceManager == nullptr || resConfig == nullptr) {
111 return false;
112 }
113 resourceManager->GetResConfig(*resConfig);
114
115 Global::Resource::DeviceType deviceType = resConfig->GetDeviceType();
116 deviceTypeCache_ = static_cast<int32_t>(deviceType);
117 if (static_cast<int32_t>(deviceType) == resDeviceType) {
118 return true;
119 }
120 return false;
121 }
122
MoveCursor(napi_env env,napi_callback_info info)123 napi_value JsTextInputClientEngine::MoveCursor(napi_env env, napi_callback_info info)
124 {
125 auto ctxt = std::make_shared<MoveCursorContext>();
126 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
127 PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
128 PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "direction type must be number!",
129 TYPE_NONE, napi_generic_failure);
130 auto status = JsUtils::GetValue(env, argv[0], ctxt->num);
131 // 1 means least param num.
132 PARAM_CHECK_RETURN(env, ctxt->num >= 0, "direction should be not less than 0!", TYPE_NONE,
133 napi_generic_failure);
134 return status;
135 };
136 auto exec = [ctxt](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
137 auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
138 if (code == ErrorCode::NO_ERROR) {
139 ctxt->status = napi_ok;
140 ctxt->SetState(ctxt->status);
141 } else {
142 ctxt->SetErrorCode(code);
143 }
144 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
145 };
146 int32_t code = InputMethodAbility::GetInstance().MoveCursor(ctxt->num, rspCallBack);
147 if (code != ErrorCode::NO_ERROR) {
148 ctxt->SetErrorCode(code);
149 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
150 }
151 };
152 ctxt->SetAction(std::move(input));
153 // 2 means JsAPI:moveCursor has 2 params at most.
154 EditAsyncCall asyncCall(env, info, ctxt, 2);
155 return asyncCall.Call(env, exec, __FUNCTION__);
156 }
157
MoveCursorSync(napi_env env,napi_callback_info info)158 napi_value JsTextInputClientEngine::MoveCursorSync(napi_env env, napi_callback_info info)
159 {
160 size_t argc = 1;
161 napi_value argv[1] = { nullptr };
162 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
163 int32_t direction = 0;
164 // 1 means least param num.
165 PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
166 PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "direction must be number!", TYPE_NUMBER,
167 HandleParamCheckFailure(env));
168 PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], direction), "direction covert failed!", TYPE_NONE,
169 HandleParamCheckFailure(env));
170 PARAM_CHECK_RETURN(env, direction >= 0, "direction should be no less than 0!", TYPE_NONE,
171 HandleParamCheckFailure(env));
172 IMSA_HILOGD("moveCursor , direction: %{public}d", direction);
173 int32_t ret = InputMethodAbility::GetInstance().MoveCursor(direction);
174 if (ret != ErrorCode::NO_ERROR) {
175 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to move cursor!", TYPE_NONE);
176 }
177 return JsUtil::Const::Null(env);
178 }
179
JsConstructor(napi_env env,napi_callback_info info)180 napi_value JsTextInputClientEngine::JsConstructor(napi_env env, napi_callback_info info)
181 {
182 napi_value thisVar = nullptr;
183 NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr));
184
185 auto clientObject = GetTextInputClientEngine();
186 if (clientObject == nullptr || !InitTextInputClientEngine()) {
187 IMSA_HILOGE("clientObject is nullptr!");
188 napi_value result = nullptr;
189 napi_get_null(env, &result);
190 return result;
191 }
192 napi_status status =
193 napi_wrap(env, thisVar, clientObject.get(), [](napi_env env, void *data, void *hint) { }, nullptr, nullptr);
194 if (status != napi_ok) {
195 IMSA_HILOGE("failed to wrap: %{public}d!", status);
196 return nullptr;
197 }
198 return thisVar;
199 }
200
GetTextInputClientEngine()201 std::shared_ptr<JsTextInputClientEngine> JsTextInputClientEngine::GetTextInputClientEngine()
202 {
203 if (textInputClientEngine_ == nullptr) {
204 std::lock_guard<std::mutex> lock(engineMutex_);
205 if (textInputClientEngine_ == nullptr) {
206 textInputClientEngine_ = std::make_shared<JsTextInputClientEngine>();
207 }
208 }
209 return textInputClientEngine_;
210 }
211
InitTextInputClientEngine()212 bool JsTextInputClientEngine::InitTextInputClientEngine()
213 {
214 if (!InputMethodAbility::GetInstance().IsCurrentIme()) {
215 return false;
216 }
217 auto engine = GetTextInputClientEngine();
218 if (engine == nullptr) {
219 return false;
220 }
221 InputMethodAbility::GetInstance().SetTextInputClientListener(engine);
222 {
223 std::lock_guard<std::mutex> lock(eventHandlerMutex_);
224 handler_ = AppExecFwk::EventHandler::Current();
225 }
226 return true;
227 }
228
GetTextInputClientInstance(napi_env env)229 napi_value JsTextInputClientEngine::GetTextInputClientInstance(napi_env env)
230 {
231 napi_value instance = nullptr;
232 napi_value cons = nullptr;
233 if (napi_get_reference_value(env, TICRef_, &cons) != napi_ok) {
234 IMSA_HILOGE("failed to get reference value!");
235 return nullptr;
236 }
237 if (napi_new_instance(env, cons, 0, nullptr, &instance) != napi_ok) {
238 IMSA_HILOGE("failed to new instance!");
239 return nullptr;
240 }
241 return instance;
242 }
243
GetResult(napi_env env,std::string & text)244 napi_value JsTextInputClientEngine::GetResult(napi_env env, std::string &text)
245 {
246 napi_value jsText = nullptr;
247 napi_create_string_utf8(env, text.c_str(), NAPI_AUTO_LENGTH, &jsText);
248 return jsText;
249 }
250
GetSelectRange(napi_env env,napi_value argv,std::shared_ptr<SelectContext> ctxt)251 napi_status JsTextInputClientEngine::GetSelectRange(napi_env env, napi_value argv, std::shared_ptr<SelectContext> ctxt)
252 {
253 napi_status status = napi_generic_failure;
254 napi_value napiValue = nullptr;
255 status = napi_get_named_property(env, argv, "start", &napiValue);
256 PARAM_CHECK_RETURN(env, status == napi_ok, "start of range cannot empty and must be number.", TYPE_NONE, status);
257 status = JsUtils::GetValue(env, napiValue, ctxt->start);
258 CHECK_RETURN(status == napi_ok, "failed to get start value!", status);
259
260 status = napi_get_named_property(env, argv, "end", &napiValue);
261 PARAM_CHECK_RETURN(env, status == napi_ok, "end of range cannot empty and must be number.", TYPE_NONE, status);
262 status = JsUtils::GetValue(env, napiValue, ctxt->end);
263 if (status != napi_ok) {
264 IMSA_HILOGE("failed to get end value!");
265 }
266 return status;
267 }
268
GetSelectMovement(napi_env env,napi_value argv,std::shared_ptr<SelectContext> ctxt)269 napi_status JsTextInputClientEngine::GetSelectMovement(napi_env env, napi_value argv,
270 std::shared_ptr<SelectContext> ctxt)
271 {
272 napi_status status = napi_generic_failure;
273 napi_value napiValue = nullptr;
274 status = napi_get_named_property(env, argv, "direction", &napiValue);
275 PARAM_CHECK_RETURN(env, status == napi_ok, "direction must be exist!", TYPE_NONE, status);
276 status = JsUtils::GetValue(env, napiValue, ctxt->direction);
277 if (status != napi_ok) {
278 IMSA_HILOGE("failed to get direction value!");
279 }
280 PARAM_CHECK_RETURN(env, status == napi_ok, "direction type must be Direction!", TYPE_NONE, status);
281 return status;
282 }
283
SendKeyFunction(napi_env env,napi_callback_info info)284 napi_value JsTextInputClientEngine::SendKeyFunction(napi_env env, napi_callback_info info)
285 {
286 auto ctxt = std::make_shared<SendKeyFunctionContext>();
287 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
288 PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
289 napi_status ret = JsUtils::GetValue(env, argv[0], ctxt->action);
290 PARAM_CHECK_RETURN(env, ret == napi_ok, "action type must be number!", TYPE_NONE, napi_generic_failure);
291 return napi_ok;
292 };
293 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
294 napi_status status = napi_get_boolean(env, ctxt->isSendKeyFunction, result);
295 return status;
296 };
297 auto exec = [ctxt](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
298 auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
299 if (code == ErrorCode::NO_ERROR) {
300 ctxt->status = napi_ok;
301 ctxt->SetState(ctxt->status);
302 ctxt->isSendKeyFunction = true;
303 } else {
304 ctxt->SetErrorCode(code);
305 }
306 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
307 };
308 int32_t code = InputMethodAbility::GetInstance().SendFunctionKey(ctxt->action, rspCallBack);
309 if (code != ErrorCode::NO_ERROR) {
310 ctxt->SetErrorCode(code);
311 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
312 }
313 };
314 ctxt->SetAction(std::move(input), std::move(output));
315 // 2 means JsAPI:sendKeyFunction has 2 params at most.
316 EditAsyncCall asyncCall(env, info, ctxt, 2);
317 return asyncCall.Call(env, exec, __FUNCTION__);
318 }
319
SendPrivateCommand(napi_env env,napi_callback_info info)320 napi_value JsTextInputClientEngine::SendPrivateCommand(napi_env env, napi_callback_info info)
321 {
322 auto ctxt = std::make_shared<SendPrivateCommandContext>();
323 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
324 PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
325 napi_status status = JsUtils::GetValue(env, argv[0], ctxt->privateCommand);
326 CHECK_RETURN(status == napi_ok,
327 "commandData covert failed, type must be Record<string, CommandDataType>", status);
328 PARAM_CHECK_RETURN(env, TextConfig::IsPrivateCommandValid(ctxt->privateCommand),
329 "commandData size limit 32KB, count limit 5.", TYPE_NONE, napi_generic_failure);
330 ctxt->info = { std::chrono::system_clock::now(), ctxt->privateCommand };
331 return status;
332 };
333 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status { return napi_ok; };
334 auto exec = [ctxt](AsyncCall::Context *ctx) {
335 int32_t code = InputMethodAbility::GetInstance().SendPrivateCommand(ctxt->privateCommand);
336 if (code == ErrorCode::NO_ERROR) {
337 ctxt->status = napi_ok;
338 ctxt->SetState(ctxt->status);
339 } else {
340 ctxt->SetErrorCode(code);
341 }
342 };
343 ctxt->SetAction(std::move(input), std::move(output));
344 // 1 means JsAPI:SendPrivateCommand has 1 param at most.
345 AsyncCall asyncCall(env, info, ctxt, 1);
346 return ASYNC_POST(env, exec);
347 }
348
DeleteForwardSync(napi_env env,napi_callback_info info)349 napi_value JsTextInputClientEngine::DeleteForwardSync(napi_env env, napi_callback_info info)
350 {
351 InputMethodSyncTrace tracer("JS_DeleteForwardSync", GenerateTraceId());
352 size_t argc = 1;
353 napi_value argv[1] = { nullptr };
354 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
355 int32_t length = 0;
356 // 1 means least param num.
357 PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
358 PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length must be number!", TYPE_NUMBER,
359 HandleParamCheckFailure(env));
360 PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], length), "length covert failed", TYPE_NONE,
361 HandleParamCheckFailure(env));
362 PARAM_CHECK_RETURN(env, length >= 0, "length should not less than 0!", TYPE_NONE, HandleParamCheckFailure(env));
363 IMSA_HILOGD("delete forward, length: %{public}d.", length);
364 int32_t ret = InputMethodAbility::GetInstance().DeleteForward(length);
365 if (ret != ErrorCode::NO_ERROR) {
366 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to delete forward", TYPE_NONE);
367 }
368 return JsUtil::Const::Null(env);
369 }
370
DeleteForward(napi_env env,napi_callback_info info)371 napi_value JsTextInputClientEngine::DeleteForward(napi_env env, napi_callback_info info)
372 {
373 auto traceId = GenerateTraceId();
374 InputMethodSyncTrace tracer("JS_DeleteForward_Start", traceId);
375 auto ctxt = std::make_shared<DeleteForwardContext>();
376 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
377 PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
378 PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length type must be number!",
379 TYPE_NONE, napi_generic_failure);
380 auto status = JsUtils::GetValue(env, argv[0], ctxt->length);
381 PARAM_CHECK_RETURN(env, ctxt->length >= 0, "length should no less than 0!", TYPE_NONE, napi_generic_failure);
382 return status;
383 };
384 auto output = [ctxt, traceId](napi_env env, napi_value *result) -> napi_status {
385 InputMethodSyncTrace tracer("JS_DeleteForward_Complete", traceId);
386 napi_status status = napi_get_boolean(env, ctxt->isDeleteForward, result);
387 return status;
388 };
389 auto exec = [ctxt, traceId](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
390 InputMethodSyncTrace tracer("JS_DeleteForward_Exec", traceId);
391 auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
392 if (code == ErrorCode::NO_ERROR) {
393 ctxt->status = napi_ok;
394 ctxt->SetState(ctxt->status);
395 ctxt->isDeleteForward = true;
396 } else {
397 ctxt->SetErrorCode(code);
398 }
399 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
400 };
401 int32_t code = InputMethodAbility::GetInstance().DeleteForward(ctxt->length, rspCallBack);
402 if (code != ErrorCode::NO_ERROR) {
403 ctxt->SetErrorCode(code);
404 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
405 }
406 };
407 ctxt->SetAction(std::move(input), std::move(output));
408 // 2 means JsAPI:deleteForward has 2 params at most.
409 EditAsyncCall asyncCall(env, info, ctxt, 2);
410 return asyncCall.Call(env, exec, __FUNCTION__);
411 }
412
DeleteBackwardSync(napi_env env,napi_callback_info info)413 napi_value JsTextInputClientEngine::DeleteBackwardSync(napi_env env, napi_callback_info info)
414 {
415 size_t argc = 1;
416 napi_value argv[1] = { nullptr };
417 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
418 int32_t length = 0;
419 // 1 means least param num.
420 PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
421 PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length must be number!", TYPE_NUMBER,
422 HandleParamCheckFailure(env));
423 PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], length), "length covert failed!", TYPE_NONE,
424 HandleParamCheckFailure(env));
425 PARAM_CHECK_RETURN(env, length >= 0, "length should no less than 0!", TYPE_NONE, HandleParamCheckFailure(env));
426 IMSA_HILOGD("delete backward, length: %{public}d.", length);
427 int32_t ret = InputMethodAbility::GetInstance().DeleteBackward(length);
428 if (ret != ErrorCode::NO_ERROR) {
429 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to delete backward", TYPE_NONE);
430 }
431 return JsUtil::Const::Null(env);
432 }
433
DeleteBackward(napi_env env,napi_callback_info info)434 napi_value JsTextInputClientEngine::DeleteBackward(napi_env env, napi_callback_info info)
435 {
436 auto ctxt = std::make_shared<DeleteBackwardContext>();
437 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
438 PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
439 PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "param length type must be number!",
440 TYPE_NONE, napi_generic_failure);
441 auto status = JsUtils::GetValue(env, argv[0], ctxt->length);
442 return status;
443 };
444 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
445 napi_status status = napi_get_boolean(env, ctxt->isDeleteBackward, result);
446 return status;
447 };
448 auto exec = [ctxt](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
449 auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
450 if (code == ErrorCode::NO_ERROR) {
451 ctxt->status = napi_ok;
452 ctxt->SetState(ctxt->status);
453 ctxt->isDeleteBackward = true;
454 } else {
455 ctxt->SetErrorCode(code);
456 }
457 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
458 };
459 int32_t code = InputMethodAbility::GetInstance().DeleteBackward(ctxt->length, rspCallBack);
460 if (code != ErrorCode::NO_ERROR) {
461 ctxt->SetErrorCode(code);
462 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
463 }
464 };
465 ctxt->SetAction(std::move(input), std::move(output));
466 // 2 means JsAPI:deleteBackward has 2 params at most.
467 EditAsyncCall asyncCall(env, info, ctxt, 2);
468 return asyncCall.Call(env, exec, __FUNCTION__);
469 }
470
InsertText(napi_env env,napi_callback_info info)471 napi_value JsTextInputClientEngine::InsertText(napi_env env, napi_callback_info info)
472 {
473 auto traceId = GenerateTraceId();
474 InputMethodSyncTrace tracer("JS_InsertText_Start", traceId);
475 auto ctxt = std::make_shared<InsertTextContext>();
476 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
477 PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
478 PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_string, "text type must be string",
479 TYPE_NONE, napi_generic_failure);
480 auto status = JsUtils::GetValue(env, argv[0], ctxt->text);
481 return status;
482 };
483 auto output = [ctxt, traceId](napi_env env, napi_value *result) -> napi_status {
484 InputMethodSyncTrace tracer("JS_InsertText_Complete", traceId);
485 napi_status status = napi_get_boolean(env, ctxt->isInsertText, result);
486 return status;
487 };
488 auto exec = [ctxt, traceId](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
489 InputMethodSyncTrace tracer("JS_InsertText_Exec", traceId);
490 auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
491 if (code == ErrorCode::NO_ERROR) {
492 ctxt->status = napi_ok;
493 ctxt->SetState(ctxt->status);
494 ctxt->isInsertText = true;
495 } else {
496 ctxt->SetErrorCode(code);
497 }
498 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
499 };
500
501 int32_t code = InputMethodAbility::GetInstance().InsertText(ctxt->text, rspCallBack);
502 if (code != ErrorCode::NO_ERROR) {
503 ctxt->SetErrorCode(code);
504 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
505 }
506 };
507 ctxt->SetAction(std::move(input), std::move(output));
508 // 2 means JsAPI:insertText has 2 params at most.
509 EditAsyncCall asyncCall(env, info, ctxt, 2);
510 return asyncCall.Call(env, exec, __FUNCTION__);
511 }
512
InsertTextSync(napi_env env,napi_callback_info info)513 napi_value JsTextInputClientEngine::InsertTextSync(napi_env env, napi_callback_info info)
514 {
515 InputMethodSyncTrace tracer("JS_InsertTextSync", GenerateTraceId());
516 size_t argc = 1;
517 napi_value argv[1] = { nullptr };
518 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
519 std::string text;
520 // 1 means least param num.
521 PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
522 PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_string, "text must be string!", TYPE_STRING,
523 HandleParamCheckFailure(env));
524 PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], text), "text covert failed!", TYPE_NONE,
525 HandleParamCheckFailure(env));
526 IMSA_HILOGD("insert text, text: %{public}s.", text.c_str());
527 int32_t ret = InputMethodAbility::GetInstance().InsertText(text);
528 if (ret != ErrorCode::NO_ERROR) {
529 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to insert text!", TYPE_NONE);
530 }
531 return JsUtil::Const::Null(env);
532 }
533
GetForwardSync(napi_env env,napi_callback_info info)534 napi_value JsTextInputClientEngine::GetForwardSync(napi_env env, napi_callback_info info)
535 {
536 InputMethodSyncTrace tracer("JS_GetForwardSync", GenerateTraceId());
537 size_t argc = 1;
538 napi_value argv[1] = { nullptr };
539 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
540 int32_t length = 0;
541 // 1 means least param num.
542 PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
543 PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length must be string!", TYPE_NUMBER,
544 HandleParamCheckFailure(env));
545 PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], length), "length covert failed!", TYPE_NONE,
546 HandleParamCheckFailure(env));
547 PARAM_CHECK_RETURN(env, length >= 0, "length should no less than 0!", TYPE_NONE, HandleParamCheckFailure(env));
548 IMSA_HILOGD("get forward, length: %{public}d.", length);
549 std::u16string text;
550 int32_t ret = InputMethodAbility::GetInstance().GetTextBeforeCursor(length, text);
551 if (ret != ErrorCode::NO_ERROR) {
552 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to get forward!", TYPE_NONE);
553 return JsUtil::Const::Null(env);
554 }
555 napi_value result = nullptr;
556 auto status = JsUtils::GetValue(env, Str16ToStr8(text), result);
557 CHECK_RETURN(status == napi_ok, "GetValue failed", JsUtil::Const::Null(env));
558 return result;
559 }
560
GetForward(napi_env env,napi_callback_info info)561 napi_value JsTextInputClientEngine::GetForward(napi_env env, napi_callback_info info)
562 {
563 auto traceId = GenerateTraceId();
564 InputMethodSyncTrace tracer("JS_GetForward_Start", traceId);
565 auto ctxt = std::make_shared<GetForwardContext>();
566 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
567 PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
568 PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length type must be number!",
569 TYPE_NONE, napi_generic_failure);
570 auto status = JsUtils::GetValue(env, argv[0], ctxt->length);
571 return status;
572 };
573 auto output = [ctxt, traceId](napi_env env, napi_value *result) -> napi_status {
574 InputMethodSyncTrace tracer("JS_GetForward_Complete", traceId);
575 napi_value data = GetResult(env, ctxt->text);
576 *result = data;
577 return napi_ok;
578 };
579 auto exec = [ctxt, traceId](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
580 InputMethodSyncTrace tracer("JS_GetForward_Exec", traceId);
581 auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
582 if (code == ErrorCode::NO_ERROR) {
583 VariantUtil::GetValue(data, ctxt->text);
584 ctxt->status = napi_ok;
585 ctxt->SetState(ctxt->status);
586 } else {
587 ctxt->SetErrorCode(code);
588 }
589 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
590 };
591 std::u16string temp;
592 int32_t code = InputMethodAbility::GetInstance().GetTextBeforeCursor(ctxt->length, temp, rspCallBack);
593 if (code != ErrorCode::NO_ERROR) {
594 ctxt->SetErrorCode(code);
595 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
596 }
597 };
598 ctxt->SetAction(std::move(input), std::move(output));
599 // 2 means JsAPI:getForward has 2 params at most.
600 EditAsyncCall asyncCall(env, info, ctxt, 2);
601 return asyncCall.Call(env, exec, __FUNCTION__);
602 }
603
GetBackwardSync(napi_env env,napi_callback_info info)604 napi_value JsTextInputClientEngine::GetBackwardSync(napi_env env, napi_callback_info info)
605 {
606 size_t argc = 1;
607 napi_value argv[1] = { nullptr };
608 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
609 int32_t length = 0;
610 // 1 means least param num.
611 PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
612 PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length must be string!", TYPE_NUMBER,
613 HandleParamCheckFailure(env));
614 PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], length), "length covert failed!", TYPE_NONE,
615 HandleParamCheckFailure(env));
616 PARAM_CHECK_RETURN(env, length >= 0, "length should not less than 0!", TYPE_NONE, HandleParamCheckFailure(env));
617 IMSA_HILOGD("get backward, length: %{public}d.", length);
618 std::u16string text;
619 int32_t ret = InputMethodAbility::GetInstance().GetTextAfterCursor(length, text);
620 if (ret != ErrorCode::NO_ERROR) {
621 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to get backward!", TYPE_NONE);
622 return JsUtil::Const::Null(env);
623 }
624 napi_value result = nullptr;
625 auto status = JsUtils::GetValue(env, Str16ToStr8(text), result);
626 CHECK_RETURN(status == napi_ok, "GetValue failed", JsUtil::Const::Null(env));
627 return result;
628 }
629
GetBackward(napi_env env,napi_callback_info info)630 napi_value JsTextInputClientEngine::GetBackward(napi_env env, napi_callback_info info)
631 {
632 auto ctxt = std::make_shared<GetBackwardContext>();
633 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
634 PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
635 PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_number, "length type must be number!",
636 TYPE_NONE, napi_generic_failure);
637 auto status = JsUtils::GetValue(env, argv[0], ctxt->length);
638 return status;
639 };
640 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
641 napi_value data = GetResult(env, ctxt->text);
642 *result = data;
643 return napi_ok;
644 };
645 auto exec = [ctxt](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
646 auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
647 if (code == ErrorCode::NO_ERROR) {
648 VariantUtil::GetValue(data, ctxt->text);
649 ctxt->status = napi_ok;
650 ctxt->SetState(ctxt->status);
651 } else {
652 ctxt->SetErrorCode(code);
653 }
654 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
655 };
656 std::u16string temp;
657 int32_t code = InputMethodAbility::GetInstance().GetTextAfterCursor(ctxt->length, temp, rspCallBack);
658 if (code != ErrorCode::NO_ERROR) {
659 ctxt->SetErrorCode(code);
660 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
661 }
662 };
663 ctxt->SetAction(std::move(input), std::move(output));
664 // 2 means JsAPI:getBackward has 2 params at most.
665 EditAsyncCall asyncCall(env, info, ctxt, 2);
666 return asyncCall.Call(env, exec, __FUNCTION__);
667 }
668
GetEditorAttributeSync(napi_env env,napi_callback_info info)669 napi_value JsTextInputClientEngine::GetEditorAttributeSync(napi_env env, napi_callback_info info)
670 {
671 TextTotalConfig config;
672 int32_t ret = InputMethodAbility::GetInstance().GetTextConfig(config);
673 if (ret != ErrorCode::NO_ERROR) {
674 IMSA_HILOGE("failed to get text config: %{public}d!", ret);
675 JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_IMCLIENT, "failed to get text config!", TYPE_NONE);
676 }
677 IMSA_HILOGD("inputPattern: %{public}d, enterKeyType: %{public}d, isTextPreviewSupported: %{public}d.",
678 config.inputAttribute.inputPattern, config.inputAttribute.enterKeyType,
679 config.inputAttribute.isTextPreviewSupported);
680 return JsInputAttribute::Write(env, config.inputAttribute);
681 }
682
GetEditorAttribute(napi_env env,napi_callback_info info)683 napi_value JsTextInputClientEngine::GetEditorAttribute(napi_env env, napi_callback_info info)
684 {
685 auto ctxt = std::make_shared<GetEditorAttributeContext>();
686 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
687 *result = JsInputAttribute::Write(env, ctxt->inputAttribute);
688 return napi_ok;
689 };
690 auto exec = [ctxt](AsyncCall::Context *ctx) {
691 TextTotalConfig config;
692 int32_t ret = InputMethodAbility::GetInstance().GetTextConfig(config);
693 ctxt->inputAttribute = config.inputAttribute;
694 if (ret == ErrorCode::NO_ERROR) {
695 ctxt->SetState(napi_ok);
696 IMSA_HILOGD("inputPattern: %{public}d, enterKeyType: %{public}d, isTextPreviewSupported: %{public}d",
697 config.inputAttribute.inputPattern, config.inputAttribute.enterKeyType,
698 config.inputAttribute.isTextPreviewSupported);
699 } else {
700 IMSA_HILOGE("failed to get text config: %{public}d!", ret);
701 ctxt->SetErrorCode(IMFErrorCode::EXCEPTION_IMCLIENT);
702 ctxt->SetErrorMessage("failed to get text config!");
703 }
704 };
705 ctxt->SetAction(nullptr, std::move(output));
706 // 1 means JsAPI:getEditorAttribute has 1 param at most.
707 AsyncCall asyncCall(env, info, ctxt, 1);
708 return ASYNC_POST(env, exec);
709 }
710
SelectByRange(napi_env env,napi_callback_info info)711 napi_value JsTextInputClientEngine::SelectByRange(napi_env env, napi_callback_info info)
712 {
713 IMSA_HILOGD("run in");
714 auto ctxt = std::make_shared<SelectContext>();
715 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
716 PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
717 napi_valuetype valueType = napi_undefined;
718 napi_typeof(env, argv[0], &valueType);
719 PARAM_CHECK_RETURN(env, valueType == napi_object, "range type must be Range!", TYPE_NONE,
720 napi_generic_failure);
721 auto status = GetSelectRange(env, argv[0], ctxt);
722 return status;
723 };
724 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status { return napi_ok; };
725 auto exec = [ctxt](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
726 auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
727 if (code == ErrorCode::NO_ERROR) {
728 ctxt->status = napi_ok;
729 ctxt->SetState(ctxt->status);
730 } else {
731 ctxt->SetErrorCode(code);
732 }
733 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
734 };
735 int32_t code = InputMethodAbility::GetInstance().SelectByRange(ctxt->start, ctxt->end, rspCallBack);
736 if (code != ErrorCode::NO_ERROR) {
737 ctxt->SetErrorCode(code);
738 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
739 }
740 };
741 ctxt->SetAction(std::move(input), std::move(output));
742 // 2 means JsAPI:selectByRange has 2 params at most.
743 EditAsyncCall asyncCall(env, info, ctxt, 2);
744 return asyncCall.Call(env, exec, __FUNCTION__);
745 }
746
SelectByRangeSync(napi_env env,napi_callback_info info)747 napi_value JsTextInputClientEngine::SelectByRangeSync(napi_env env, napi_callback_info info)
748 {
749 IMSA_HILOGD("SelectByRangeSync");
750 size_t argc = 1;
751 napi_value argv[1] = { nullptr };
752 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
753 PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
754 PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_object, "range type must be Range!", TYPE_NONE,
755 HandleParamCheckFailure(env));
756 auto ctxt = std::make_shared<SelectContext>();
757 auto status = GetSelectRange(env, argv[0], ctxt);
758 if (status != napi_ok) {
759 JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK,
760 "failed to get start or end, should have start and end number!", TYPE_NONE);
761 return JsUtil::Const::Null(env);
762 }
763 IMSA_HILOGD("start: %{public}d, end: %{public}d.", ctxt->start, ctxt->end);
764 int32_t ret = InputMethodAbility::GetInstance().SelectByRange(ctxt->start, ctxt->end);
765 if (ret != ErrorCode::NO_ERROR) {
766 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to select by range!", TYPE_NONE);
767 }
768 return JsUtil::Const::Null(env);
769 }
770
SelectByMovementSync(napi_env env,napi_callback_info info)771 napi_value JsTextInputClientEngine::SelectByMovementSync(napi_env env, napi_callback_info info)
772 {
773 IMSA_HILOGD("run in");
774 size_t argc = 1;
775 napi_value argv[1] = { nullptr };
776 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
777 PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, HandleParamCheckFailure(env));
778 PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_object, "movement type must be Movement!",
779 TYPE_NONE, HandleParamCheckFailure(env));
780 auto ctxt = std::make_shared<SelectContext>();
781 auto status = GetSelectMovement(env, argv[0], ctxt);
782 if (status != napi_ok) {
783 JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK, "direction covert failed!", TYPE_NONE);
784 return JsUtil::Const::Null(env);
785 }
786 IMSA_HILOGD("direction: %{public}d.", ctxt->direction);
787 int32_t ret = InputMethodAbility::GetInstance().SelectByMovement(ctxt->direction);
788 if (ret != ErrorCode::NO_ERROR) {
789 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to select by movement!", TYPE_NONE);
790 }
791 return JsUtil::Const::Null(env);
792 }
793
SelectByMovement(napi_env env,napi_callback_info info)794 napi_value JsTextInputClientEngine::SelectByMovement(napi_env env, napi_callback_info info)
795 {
796 IMSA_HILOGD("run in");
797 auto ctxt = std::make_shared<SelectContext>();
798 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
799 PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
800 napi_valuetype valueType = napi_undefined;
801 napi_typeof(env, argv[0], &valueType);
802 PARAM_CHECK_RETURN(env, valueType == napi_object, "movement type must be Movement!", TYPE_NONE,
803 napi_generic_failure);
804 auto status = GetSelectMovement(env, argv[0], ctxt);
805 return status;
806 };
807 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status { return napi_ok; };
808 auto exec = [ctxt](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
809 auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
810 if (code == ErrorCode::NO_ERROR) {
811 ctxt->status = napi_ok;
812 ctxt->SetState(ctxt->status);
813 } else {
814 ctxt->SetErrorCode(code);
815 }
816 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
817 };
818 int32_t code = InputMethodAbility::GetInstance().SelectByMovement(ctxt->direction, rspCallBack);
819 if (code != ErrorCode::NO_ERROR) {
820 ctxt->SetErrorCode(code);
821 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
822 }
823 };
824 ctxt->SetAction(std::move(input), std::move(output));
825 // 2 means JsAPI:selectByMovement has 2 params at most.
826 EditAsyncCall asyncCall(env, info, ctxt, 2);
827 return asyncCall.Call(env, exec, __FUNCTION__);
828 }
829
SendExtendAction(napi_env env,napi_callback_info info)830 napi_value JsTextInputClientEngine::SendExtendAction(napi_env env, napi_callback_info info)
831 {
832 auto ctxt = std::make_shared<SendExtendActionContext>();
833 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
834 PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
835 auto status = JsUtils::GetValue(env, argv[0], ctxt->action);
836 PARAM_CHECK_RETURN(env, status == napi_ok, "action must be number and should in ExtendAction",
837 TYPE_NONE, napi_generic_failure);
838 return status;
839 };
840 auto exec = [ctxt](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
841 auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
842 if (code == ErrorCode::NO_ERROR) {
843 ctxt->SetState(napi_ok);
844 } else {
845 ctxt->SetErrorCode(code);
846 }
847 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
848 };
849 int32_t code = InputMethodAbility::GetInstance().SendExtendAction(ctxt->action, rspCallBack);
850 if (code != ErrorCode::NO_ERROR) {
851 ctxt->SetErrorCode(code);
852 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
853 }
854 };
855 ctxt->SetAction(std::move(input));
856 // 2 means JsAPI:sendExtendAction has 2 params at most.
857 EditAsyncCall asyncCall(env, info, ctxt, 2);
858 return asyncCall.Call(env, exec, __FUNCTION__);
859 }
860
GetTextIndexAtCursor(napi_env env,napi_callback_info info)861 napi_value JsTextInputClientEngine::GetTextIndexAtCursor(napi_env env, napi_callback_info info)
862 {
863 IMSA_HILOGD("GetTextIndexAtCursor");
864 auto ctxt = std::make_shared<GetTextIndexAtCursorContext>();
865 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
866 return napi_ok;
867 };
868 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
869 return napi_create_int32(env, ctxt->index, result);
870 };
871 auto exec = [ctxt](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
872 auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
873 if (code == ErrorCode::NO_ERROR) {
874 VariantUtil::GetValue(data, ctxt->index);
875 ctxt->status = napi_ok;
876 ctxt->SetState(ctxt->status);
877 } else {
878 ctxt->SetErrorCode(code);
879 }
880 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
881 };
882 int32_t code = InputMethodAbility::GetInstance().GetTextIndexAtCursor(ctxt->index, rspCallBack);
883 if (code != ErrorCode::NO_ERROR) {
884 ctxt->SetErrorCode(code);
885 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
886 }
887 };
888 ctxt->SetAction(std::move(input), std::move(output));
889 // 1 means JsAPI:getTextIndexAtCursor has 1 param at most.
890 EditAsyncCall asyncCall(env, info, ctxt, 1);
891 return asyncCall.Call(env, exec, __FUNCTION__);
892 }
893
SetPreviewText(napi_env env,napi_callback_info info)894 napi_value JsTextInputClientEngine::SetPreviewText(napi_env env, napi_callback_info info)
895 {
896 auto traceId = GenerateTraceId();
897 InputMethodSyncTrace tracer("JS_SetPreviewText_Start", traceId);
898 IMSA_HILOGD("JsTextInputClientEngine in");
899 auto ctxt = std::make_shared<SetPreviewTextContext>();
900 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
901 if (GetPreviewTextParam(env, argc, argv, ctxt->text, ctxt->range) != napi_ok) {
902 return napi_generic_failure;
903 }
904 return napi_ok;
905 };
906 auto output = [ctxt, traceId](napi_env env, napi_value *result) -> napi_status {
907 InputMethodSyncTrace tracer("JS_SetPreviewText_Complete", traceId);
908 return napi_ok;
909 };
910 auto exec = [ctxt, traceId](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
911 InputMethodSyncTrace tracer("JS_SetPreviewText_Exec", traceId);
912 auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
913 if (code == ErrorCode::NO_ERROR) {
914 IMSA_HILOGD("exec setPreviewText success");
915 ctxt->SetState(napi_ok);
916 } else if (code == ErrorCode::ERROR_INVALID_RANGE) {
917 ctxt->SetErrorCode(code);
918 ctxt->SetErrorMessage("range should be included in preview text range, otherwise should be included in "
919 "total text range!");
920 } else {
921 ctxt->SetErrorCode(code);
922 }
923 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
924 };
925 int32_t code = InputMethodAbility::GetInstance().SetPreviewText(ctxt->text, ctxt->range, rspCallBack);
926 if (code != ErrorCode::NO_ERROR) {
927 ctxt->SetErrorCode(code);
928 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
929 }
930 };
931 ctxt->SetAction(std::move(input), std::move(output));
932 // 2 means JsAPI:setPreviewText needs 2 params at most
933 EditAsyncCall asyncCall(env, info, ctxt, 2);
934 return asyncCall.Call(env, exec, __FUNCTION__);
935 }
936
SetPreviewTextSync(napi_env env,napi_callback_info info)937 napi_value JsTextInputClientEngine::SetPreviewTextSync(napi_env env, napi_callback_info info)
938 {
939 InputMethodSyncTrace tracer("JS_SetPreviewTextSync", GenerateTraceId());
940 IMSA_HILOGD("start.");
941 // 2 means JsAPI:setPreviewText needs 2 params at most
942 size_t argc = 2;
943 napi_value argv[2] = { nullptr };
944 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
945 std::string text;
946 Range range;
947 if (GetPreviewTextParam(env, argc, argv, text, range) != napi_ok) {
948 return JsUtil::Const::Null(env);
949 }
950 int32_t ret = InputMethodAbility::GetInstance().SetPreviewText(text, range);
951 if (ret == ErrorCode::ERROR_INVALID_RANGE) {
952 JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK,
953 "range should be included in preview text range, otherwise should be included in total text range",
954 TYPE_NONE);
955 } else if (ret != ErrorCode::NO_ERROR) {
956 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to set preview text!", TYPE_NONE);
957 }
958 return JsUtil::Const::Null(env);
959 }
960
FinishTextPreview(napi_env env,napi_callback_info info)961 napi_value JsTextInputClientEngine::FinishTextPreview(napi_env env, napi_callback_info info)
962 {
963 auto traceId = GenerateTraceId();
964 InputMethodSyncTrace tracer("JS_FinishTextPreview_Start", traceId);
965 IMSA_HILOGD("start.");
966 auto ctxt = std::make_shared<FinishTextPreviewContext>();
967 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
968 return napi_ok;
969 };
970 auto output = [ctxt, traceId](napi_env env, napi_value *result) -> napi_status {
971 InputMethodSyncTrace tracer("JS_FinishTextPreview_Complete", traceId);
972 return napi_ok;
973 };
974 auto exec = [ctxt, traceId](AsyncCall::Context *ctx, AsyncCall::Context::CallBackAction completeFunc) {
975 InputMethodSyncTrace tracer("JS_FinishTextPreview_Exec", traceId);
976 auto rspCallBack = [ctxt, completeFunc](int32_t code, const ResponseData &data) -> void {
977 if (code == ErrorCode::NO_ERROR) {
978 IMSA_HILOGI("exec finishTextPreview success.");
979 ctxt->SetState(napi_ok);
980 } else {
981 ctxt->SetErrorCode(code);
982 }
983 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
984 };
985 int32_t code = InputMethodAbility::GetInstance().FinishTextPreview(rspCallBack);
986 if (code != ErrorCode::NO_ERROR) {
987 ctxt->SetErrorCode(code);
988 completeFunc != nullptr ? completeFunc() : IMSA_HILOGE("completeFunc is nullptr");
989 }
990 };
991 ctxt->SetAction(std::move(input), std::move(output));
992 // 0 means JsAPI:finishTextPreview needs no param
993 EditAsyncCall asyncCall(env, info, ctxt, 0);
994 return asyncCall.Call(env, exec, __FUNCTION__);
995 }
996
FinishTextPreviewSync(napi_env env,napi_callback_info info)997 napi_value JsTextInputClientEngine::FinishTextPreviewSync(napi_env env, napi_callback_info info)
998 {
999 InputMethodSyncTrace tracer("JS_FinishTextPreviewSync", GenerateTraceId());
1000 IMSA_HILOGD("JsTextInputClientEngine in");
1001 int32_t ret = InputMethodAbility::GetInstance().FinishTextPreview();
1002 if (ret != ErrorCode::NO_ERROR) {
1003 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to finish text preview!", TYPE_NONE);
1004 }
1005 return JsUtil::Const::Null(env);
1006 }
1007
GetTextIndexAtCursorSync(napi_env env,napi_callback_info info)1008 napi_value JsTextInputClientEngine::GetTextIndexAtCursorSync(napi_env env, napi_callback_info info)
1009 {
1010 IMSA_HILOGD("start.");
1011 int32_t index = 0;
1012 int32_t ret = InputMethodAbility::GetInstance().GetTextIndexAtCursor(index);
1013 if (ret != ErrorCode::NO_ERROR) {
1014 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to get text index at cursor!", TYPE_NONE);
1015 }
1016 return JsUtil::GetValue(env, index);
1017 }
1018
GetCallingWindowInfo(napi_env env,napi_callback_info info)1019 napi_value JsTextInputClientEngine::GetCallingWindowInfo(napi_env env, napi_callback_info info)
1020 {
1021 IMSA_HILOGD("start.");
1022 auto ctxt = std::make_shared<GetCallingWindowInfoContext>();
1023 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
1024 *result = JsCallingWindowInfo::Write(env, ctxt->windowInfo);
1025 return napi_ok;
1026 };
1027 auto exec = [ctxt](AsyncCall::Context *ctx) {
1028 int32_t ret = InputMethodAbility::GetInstance().GetCallingWindowInfo(ctxt->windowInfo);
1029 if (ret == ErrorCode::NO_ERROR) {
1030 IMSA_HILOGI("exec GetCallingWindowInfo success.");
1031 ctxt->SetState(napi_ok);
1032 return;
1033 }
1034 ctxt->SetErrorCode(ret);
1035 };
1036 ctxt->SetAction(nullptr, std::move(output));
1037 // 0 means JsAPI:getCallingWindowInfo needs no parameter.
1038 AsyncCall asyncCall(env, info, ctxt, 0);
1039 return ASYNC_POST(env, exec);
1040 }
1041
GetPreviewTextParam(napi_env env,size_t argc,napi_value * argv,std::string & text,Range & range)1042 napi_status JsTextInputClientEngine::GetPreviewTextParam(napi_env env, size_t argc, napi_value *argv,
1043 std::string &text, Range &range)
1044 {
1045 // 2 means JsAPI:setPreviewText needs 2 params at least.
1046 PARAM_CHECK_RETURN(env, argc >= 2, "at least two parameters is required!", TYPE_NONE, napi_generic_failure);
1047 PARAM_CHECK_RETURN(env, JsUtil::GetValue(env, argv[0], text), "text covert failed, must be string!",
1048 TYPE_NONE, napi_generic_failure);
1049 PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[1]) == napi_object, "range type must be Range!", TYPE_NONE,
1050 napi_generic_failure);
1051 PARAM_CHECK_RETURN(env, JsRange::Read(env, argv[1], range),
1052 "range covert failed, the range should have numbers start and end", TYPE_NONE, napi_generic_failure);
1053 return napi_ok;
1054 }
1055
RegisterListener(napi_value callback,std::string type,std::shared_ptr<JSCallbackObject> callbackObj)1056 void JsTextInputClientEngine::RegisterListener(napi_value callback, std::string type,
1057 std::shared_ptr<JSCallbackObject> callbackObj)
1058 {
1059 IMSA_HILOGD("register listener: %{public}s.", type.c_str());
1060 std::lock_guard<std::recursive_mutex> lock(mutex_);
1061 if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) {
1062 IMSA_HILOGD("methodName: %{public}s not registered!", type.c_str());
1063 }
1064 auto callbacks = jsCbMap_[type];
1065 bool ret = std::any_of(callbacks.begin(), callbacks.end(), [&callback](std::shared_ptr<JSCallbackObject> cb) {
1066 if (cb == nullptr) {
1067 return false;
1068 }
1069 return JsUtils::Equals(cb->env_, callback, cb->callback_, cb->threadId_);
1070 });
1071 if (ret) {
1072 IMSA_HILOGD("JsTextInputClientEngine callback already registered!");
1073 return;
1074 }
1075
1076 IMSA_HILOGI("add %{public}s callbackObj into jsCbMap_.", type.c_str());
1077 jsCbMap_[type].push_back(std::move(callbackObj));
1078 }
1079
UnRegisterListener(napi_value callback,std::string type)1080 void JsTextInputClientEngine::UnRegisterListener(napi_value callback, std::string type)
1081 {
1082 IMSA_HILOGI("unregister listener: %{public}s.", type.c_str());
1083 std::lock_guard<std::recursive_mutex> lock(mutex_);
1084 if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) {
1085 IMSA_HILOGE("methodName: %{public}s already unregistered!", type.c_str());
1086 return;
1087 }
1088
1089 if (callback == nullptr) {
1090 jsCbMap_.erase(type);
1091 IMSA_HILOGE("callback is nullptr.");
1092 return;
1093 }
1094
1095 for (auto item = jsCbMap_[type].begin(); item != jsCbMap_[type].end(); item++) {
1096 if (JsUtils::Equals((*item)->env_, callback, (*item)->callback_, (*item)->threadId_)) {
1097 jsCbMap_[type].erase(item);
1098 break;
1099 }
1100 }
1101
1102 if (jsCbMap_[type].empty()) {
1103 jsCbMap_.erase(type);
1104 }
1105 }
1106
Write(napi_env env,const Rosen::Rect & nativeObject)1107 napi_value JsRect::Write(napi_env env, const Rosen::Rect &nativeObject)
1108 {
1109 napi_value jsObject = nullptr;
1110 napi_create_object(env, &jsObject);
1111 bool ret = JsUtil::Object::WriteProperty(env, jsObject, "left", nativeObject.posX_);
1112 ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "top", nativeObject.posY_);
1113 ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "width", nativeObject.width_);
1114 ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "height", nativeObject.height_);
1115 return ret ? jsObject : JsUtil::Const::Null(env);
1116 }
1117
Read(napi_env env,napi_value jsObject,Rosen::Rect & nativeObject)1118 bool JsRect::Read(napi_env env, napi_value jsObject, Rosen::Rect &nativeObject)
1119 {
1120 auto ret = JsUtil::Object::ReadProperty(env, jsObject, "left", nativeObject.posX_);
1121 ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "top", nativeObject.posY_);
1122 ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "width", nativeObject.width_);
1123 ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "height", nativeObject.height_);
1124 return ret;
1125 }
1126
Write(napi_env env,const CallingWindowInfo & nativeObject)1127 napi_value JsCallingWindowInfo::Write(napi_env env, const CallingWindowInfo &nativeObject)
1128 {
1129 napi_value jsObject = nullptr;
1130 napi_create_object(env, &jsObject);
1131 bool ret = JsUtil::Object::WriteProperty(env, jsObject, "rect", JsRect::Write(env, nativeObject.rect));
1132 ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "status", static_cast<uint32_t>(nativeObject.status));
1133 return ret ? jsObject : JsUtil::Const::Null(env);
1134 }
1135
Read(napi_env env,napi_value object,CallingWindowInfo & nativeObject)1136 bool JsCallingWindowInfo::Read(napi_env env, napi_value object, CallingWindowInfo &nativeObject)
1137 {
1138 napi_value rectObject = nullptr;
1139 napi_get_named_property(env, object, "rect", &rectObject);
1140 auto ret = JsRect::Read(env, rectObject, nativeObject.rect);
1141 uint32_t status = 0;
1142 ret = ret && JsUtil::Object::ReadProperty(env, object, "status", status);
1143 nativeObject.status = static_cast<Rosen::WindowStatus>(status);
1144 return ret;
1145 }
1146
HandleParamCheckFailure(napi_env env)1147 napi_value JsTextInputClientEngine::HandleParamCheckFailure(napi_env env)
1148 {
1149 return JsUtil::Const::Null(env);
1150 }
1151
Write(napi_env env,const Range & nativeObject)1152 napi_value JsRange::Write(napi_env env, const Range &nativeObject)
1153 {
1154 napi_value jsObject = nullptr;
1155 napi_create_object(env, &jsObject);
1156 bool ret = JsUtil::Object::WriteProperty(env, jsObject, "start", nativeObject.start);
1157 ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "end", nativeObject.end);
1158 return ret ? jsObject : JsUtil::Const::Null(env);
1159 }
1160
Read(napi_env env,napi_value jsObject,Range & nativeObject)1161 bool JsRange::Read(napi_env env, napi_value jsObject, Range &nativeObject)
1162 {
1163 auto ret = JsUtil::Object::ReadProperty(env, jsObject, "start", nativeObject.start);
1164 ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "end", nativeObject.end);
1165 return ret;
1166 }
1167
Write(napi_env env,const InputAttribute & nativeObject)1168 napi_value JsInputAttribute::Write(napi_env env, const InputAttribute &nativeObject)
1169 {
1170 napi_value jsObject = nullptr;
1171 napi_create_object(env, &jsObject);
1172 auto ret = JsUtil::Object::WriteProperty(env, jsObject, "inputPattern", nativeObject.inputPattern);
1173 ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "enterKeyType", nativeObject.enterKeyType);
1174 ret = ret &&
1175 JsUtil::Object::WriteProperty(env, jsObject, "isTextPreviewSupported", nativeObject.isTextPreviewSupported);
1176 // not care write bundleName fail
1177 JsUtil::Object::WriteProperty(env, jsObject, "bundleName", nativeObject.bundleName);
1178 ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "immersiveMode", nativeObject.immersiveMode);
1179 ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "windowId", nativeObject.windowId);
1180 ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "displayId",
1181 static_cast<uint32_t>(nativeObject.callingDisplayId));
1182 ret = ret && JsUtil::Object::WritePropertyU16String(env, jsObject, "placeholder", nativeObject.placeholder);
1183 ret = ret && JsUtil::Object::WritePropertyU16String(env, jsObject, "abilityName", nativeObject.abilityName);
1184 int32_t capitalizeMode = static_cast<int32_t>(nativeObject.capitalizeMode);
1185 ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "capitalizeMode", capitalizeMode);
1186 ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "gradientMode", nativeObject.gradientMode);
1187 if (InputMethodAbility::GetInstance().IsSystemApp()) {
1188 ret = ret && JsUtil::Object::WriteProperty(env, jsObject, "fluidLightMode", nativeObject.fluidLightMode);
1189 }
1190 return ret ? jsObject : JsUtil::Const::Null(env);
1191 }
1192
Read(napi_env env,napi_value jsObject,InputAttribute & nativeObject)1193 bool JsInputAttribute::Read(napi_env env, napi_value jsObject, InputAttribute &nativeObject)
1194 {
1195 auto ret = JsUtil::Object::ReadProperty(env, jsObject, "inputPattern", nativeObject.inputPattern);
1196 ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "enterKeyType", nativeObject.enterKeyType);
1197 ret = ret &&
1198 JsUtil::Object::ReadProperty(env, jsObject, "isTextPreviewSupported", nativeObject.isTextPreviewSupported);
1199 // not care read bundleName fail
1200 JsUtil::Object::ReadProperty(env, jsObject, "bundleName", nativeObject.bundleName);
1201 JsUtil::Object::ReadPropertyU16String(env, jsObject, "placeholder", nativeObject.placeholder);
1202 IMSA_HILOGD("placeholder:%{public}s", StringUtils::ToHex(nativeObject.placeholder).c_str());
1203 JsUtil::Object::ReadPropertyU16String(env, jsObject, "abilityName", nativeObject.abilityName);
1204 IMSA_HILOGD("abilityName:%{public}s", StringUtils::ToHex(nativeObject.abilityName).c_str());
1205 int32_t capitalizeMode;
1206 if (!JsUtil::Object::ReadProperty(env, jsObject, "capitalizeMode", capitalizeMode)) {
1207 nativeObject.capitalizeMode = CapitalizeMode::NONE;
1208 } else {
1209 nativeObject.capitalizeMode = static_cast<CapitalizeMode>(capitalizeMode);
1210 }
1211 ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "immersiveMode", nativeObject.immersiveMode);
1212 ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "gradientMode", nativeObject.gradientMode);
1213 if (InputMethodAbility::GetInstance().IsSystemApp()) {
1214 ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "fluidLightMode", nativeObject.fluidLightMode);
1215 }
1216 return ret;
1217 }
1218
Write(napi_env env,const AttachOptions & attachOptions)1219 napi_value JsAttachOptions::Write(napi_env env, const AttachOptions &attachOptions)
1220 {
1221 napi_value jsObject = nullptr;
1222 napi_create_object(env, &jsObject);
1223 bool ret = JsUtil::Object::WriteProperty(
1224 env, jsObject, "requestKeyboardReason", static_cast<uint32_t>(attachOptions.requestKeyboardReason));
1225 ret = ret && JsUtil::Object::WriteProperty(
1226 env, jsObject, "isSimpleKeyboardEnabled", attachOptions.isSimpleKeyboardEnabled);
1227 return ret ? jsObject : JsUtil::Const::Null(env);
1228 }
1229
Read(napi_env env,napi_value jsObject,AttachOptions & attachOptions)1230 bool JsAttachOptions::Read(napi_env env, napi_value jsObject, AttachOptions &attachOptions)
1231 {
1232 uint32_t requestKeyboardReason = static_cast<uint32_t>(RequestKeyboardReason::NONE);
1233 auto ret = JsUtil::Object::ReadProperty(env, jsObject, "requestKeyboardReason", requestKeyboardReason);
1234 attachOptions.requestKeyboardReason = static_cast<RequestKeyboardReason>(requestKeyboardReason);
1235 ret = ret && JsUtil::Object::ReadProperty(env, jsObject, "isSimpleKeyboardEnabled",
1236 attachOptions.isSimpleKeyboardEnabled);
1237 return ret;
1238 }
1239
SendMessage(napi_env env,napi_callback_info info)1240 napi_value JsTextInputClientEngine::SendMessage(napi_env env, napi_callback_info info)
1241 {
1242 auto ctxt = std::make_shared<SendMessageContext>();
1243 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
1244 PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_generic_failure);
1245 PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_string, "msgId",
1246 TYPE_STRING, napi_generic_failure);
1247 CHECK_RETURN(JsUtils::GetValue(env, argv[0], ctxt->arrayBuffer.msgId) == napi_ok,
1248 "msgId covert failed!", napi_generic_failure);
1249 ctxt->arrayBuffer.jsArgc = argc;
1250 // 1 means first param msgId.
1251 if (argc > 1) {
1252 bool isArryBuffer = false;
1253 // 1 means second param msgParam index.
1254 CHECK_RETURN(napi_is_arraybuffer(env, argv[1], &isArryBuffer) == napi_ok,
1255 "napi_is_arraybuffer failed!", napi_generic_failure);
1256 PARAM_CHECK_RETURN(env, isArryBuffer, "msgParam", TYPE_ARRAY_BUFFER, napi_generic_failure);
1257 CHECK_RETURN(JsUtils::GetValue(env, argv[1], ctxt->arrayBuffer.msgParam) == napi_ok,
1258 "msgParam covert failed!", napi_generic_failure);
1259 }
1260 PARAM_CHECK_RETURN(env, ArrayBuffer::IsSizeValid(ctxt->arrayBuffer),
1261 "msgId limit 256B and msgParam limit 128KB.", TYPE_NONE, napi_generic_failure);
1262 ctxt->info = { std::chrono::system_clock::now(), ctxt->arrayBuffer };
1263 messageHandlerQueue_.Push(ctxt->info);
1264 return napi_ok;
1265 };
1266 auto exec = [ctxt](AsyncCall::Context *ctx) {
1267 messageHandlerQueue_.Wait(ctxt->info);
1268 int32_t code = InputMethodAbility::GetInstance().SendMessage(ctxt->arrayBuffer);
1269 messageHandlerQueue_.Pop();
1270 if (code == ErrorCode::NO_ERROR) {
1271 ctxt->status = napi_ok;
1272 ctxt->SetState(ctxt->status);
1273 } else {
1274 ctxt->SetErrorCode(code);
1275 }
1276 };
1277 ctxt->SetAction(std::move(input), nullptr);
1278 // 2 means JsAPI:sendMessage has 2 params at most.
1279 AsyncCall asyncCall(env, info, ctxt, 2);
1280 return asyncCall.Call(env, exec, "imaSendMessage");
1281 }
1282
RecvMessage(napi_env env,napi_callback_info info)1283 napi_value JsTextInputClientEngine::RecvMessage(napi_env env, napi_callback_info info)
1284 {
1285 size_t argc = ARGC_TWO;
1286 napi_value argv[ARGC_TWO] = {nullptr};
1287 napi_value thisVar = nullptr;
1288 void *data = nullptr;
1289 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
1290 if (argc < 0) {
1291 IMSA_HILOGE("RecvMessage failed! argc abnormal.");
1292 return nullptr;
1293 }
1294 void *native = nullptr;
1295 auto status = napi_unwrap(env, thisVar, &native);
1296 CHECK_RETURN((status == napi_ok && native != nullptr), "napi_unwrap failed!", nullptr);
1297 auto inputClient = reinterpret_cast<JsTextInputClientEngine *>(native);
1298 if (inputClient == nullptr) {
1299 IMSA_HILOGI("Unwrap js object self is nullptr.");
1300 return nullptr;
1301 }
1302 if (argc == 0) {
1303 IMSA_HILOGI("RecvMessage off.");
1304 InputMethodAbility::GetInstance().RegisterMsgHandler();
1305 return nullptr;
1306 }
1307 IMSA_HILOGI("RecvMessage on.");
1308 PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_object, "msgHnadler (MessageHandler)",
1309 TYPE_OBJECT, nullptr);
1310
1311 napi_value onMessage = nullptr;
1312 CHECK_RETURN(napi_get_named_property(env, argv[0], "onMessage", &onMessage) == napi_ok,
1313 "Get onMessage property failed!", nullptr);
1314 CHECK_RETURN(JsUtil::GetType(env, onMessage) == napi_function, "onMessage is not napi_function!", nullptr);
1315 napi_value onTerminated = nullptr;
1316 CHECK_RETURN(napi_get_named_property(env, argv[0], "onTerminated", &onTerminated) == napi_ok,
1317 "Get onMessage property failed!", nullptr);
1318 CHECK_RETURN(JsUtil::GetType(env, onTerminated) == napi_function, "onTerminated is not napi_function!", nullptr);
1319
1320 std::shared_ptr<MsgHandlerCallbackInterface> callback =
1321 std::make_shared<JsTextInputClientEngine::JsMessageHandler>(env, onTerminated, onMessage);
1322 InputMethodAbility::GetInstance().RegisterMsgHandler(callback);
1323 napi_value result = nullptr;
1324 napi_get_null(env, &result);
1325 return result;
1326 }
1327
OnAttachOptionsChanged(const AttachOptions & attachOptions)1328 void JsTextInputClientEngine::OnAttachOptionsChanged(const AttachOptions &attachOptions)
1329 {
1330 IMSA_HILOGD("OnAttachOptionsChanged requestKeyboardReason:%{public}d.", attachOptions.requestKeyboardReason);
1331 std::string type = "attachOptionsDidChange";
1332 auto entry = GetEntry(type, [&attachOptions](UvEntry &entry) {
1333 entry.attachOptions.requestKeyboardReason = attachOptions.requestKeyboardReason;
1334 entry.attachOptions.isSimpleKeyboardEnabled = attachOptions.isSimpleKeyboardEnabled;
1335 });
1336 if (entry == nullptr) {
1337 IMSA_HILOGE("failed to get uv entry!");
1338 return;
1339 }
1340 auto eventHandler = GetEventHandler();
1341 if (eventHandler == nullptr) {
1342 IMSA_HILOGE("eventHandler is nullptr!");
1343 return;
1344 }
1345 auto task = [entry]() {
1346 auto gitAttachOptionsParams = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
1347 if (argc == 0) {
1348 return false;
1349 }
1350 napi_value attachOptions = JsAttachOptions::Write(env, entry->attachOptions);
1351 // 0 means the first param of callback.
1352 args[0] = attachOptions;
1353 return true;
1354 };
1355 JsCallbackHandler::Traverse({ entry->vecCopy }, { 1, gitAttachOptionsParams });
1356 };
1357 eventHandler->PostTask(task, type, 0, AppExecFwk::EventQueue::Priority::VIP);
1358 }
1359
GetAttachOptions(napi_env env,napi_callback_info info)1360 napi_value JsTextInputClientEngine::GetAttachOptions(napi_env env, napi_callback_info info)
1361 {
1362 auto attachOptions = InputMethodAbility::GetInstance().GetAttachOptions();
1363 IMSA_HILOGD("GetAttachOptions:%{public}d/%{public}d.", attachOptions.requestKeyboardReason,
1364 attachOptions.isSimpleKeyboardEnabled);
1365 return JsAttachOptions::Write(env, attachOptions);
1366 }
1367
Subscribe(napi_env env,napi_callback_info info)1368 napi_value JsTextInputClientEngine::Subscribe(napi_env env, napi_callback_info info)
1369 {
1370 size_t argc = ARGC_TWO;
1371 napi_value argv[ARGC_TWO] = { nullptr };
1372 napi_value thisVar = nullptr;
1373 void *data = nullptr;
1374 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
1375 std::string type;
1376 // 2 means least param num.
1377 if (argc < ARGC_TWO || !JsUtil::GetValue(env, argv[0], type) ||
1378 !EventChecker::IsValidEventType(EventSubscribeModule::TEXT_INPUT_CLIENT, type) ||
1379 JsUtil::GetType(env, argv[1]) != napi_function) {
1380 IMSA_HILOGE("subscribe failed, type: %{public}s.", type.c_str());
1381 return nullptr;
1382 }
1383 IMSA_HILOGD("subscribe type:%{public}s.", type.c_str());
1384 auto engine = reinterpret_cast<JsTextInputClientEngine *>(JsUtils::GetNativeSelf(env, info));
1385 if (engine == nullptr) {
1386 return nullptr;
1387 }
1388 std::shared_ptr<JSCallbackObject> callback =
1389 std::make_shared<JSCallbackObject>(env, argv[ARGC_ONE], std::this_thread::get_id(),
1390 AppExecFwk::EventHandler::Current());
1391 engine->RegisterListener(argv[ARGC_ONE], type, callback);
1392
1393 napi_value result = nullptr;
1394 napi_get_null(env, &result);
1395 return result;
1396 }
1397
UnSubscribe(napi_env env,napi_callback_info info)1398 napi_value JsTextInputClientEngine::UnSubscribe(napi_env env, napi_callback_info info)
1399 {
1400 size_t argc = ARGC_TWO;
1401 napi_value argv[ARGC_TWO] = { nullptr };
1402 napi_value thisVar = nullptr;
1403 void *data = nullptr;
1404 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
1405 std::string type;
1406 // 1 means least param num.
1407 if (argc < ARGC_ONE || !JsUtil::GetValue(env, argv[0], type) ||
1408 !EventChecker::IsValidEventType(EventSubscribeModule::TEXT_INPUT_CLIENT, type)) {
1409 IMSA_HILOGE("unsubscribe failed, type: %{public}s!", type.c_str());
1410 return nullptr;
1411 }
1412 // if the second param is not napi_function/napi_null/napi_undefined, return
1413 auto paramType = JsUtil::GetType(env, argv[1]);
1414 if (paramType != napi_function && paramType != napi_null && paramType != napi_undefined) {
1415 return nullptr;
1416 }
1417 // if the second param is napi_function, delete it, else delete all
1418 argv[1] = paramType == napi_function ? argv[1] : nullptr;
1419
1420 IMSA_HILOGD("unsubscribe type: %{public}s.", type.c_str());
1421 auto setting = reinterpret_cast<JsTextInputClientEngine *>(JsUtils::GetNativeSelf(env, info));
1422 if (setting == nullptr) {
1423 return nullptr;
1424 }
1425 setting->UnRegisterListener(argv[ARGC_ONE], type);
1426 napi_value result = nullptr;
1427 napi_get_null(env, &result);
1428 return result;
1429 }
1430
GetEventHandler()1431 std::shared_ptr<AppExecFwk::EventHandler> JsTextInputClientEngine::GetEventHandler()
1432 {
1433 std::lock_guard<std::mutex> lock(eventHandlerMutex_);
1434 return handler_;
1435 }
1436
GetEntry(const std::string & type,EntrySetter entrySetter)1437 std::shared_ptr<JsTextInputClientEngine::UvEntry> JsTextInputClientEngine::GetEntry(
1438 const std::string &type, EntrySetter entrySetter)
1439 {
1440 IMSA_HILOGD("type: %{public}s.", type.c_str());
1441 std::shared_ptr<UvEntry> entry = nullptr;
1442 {
1443 std::lock_guard<std::recursive_mutex> lock(mutex_);
1444 if (jsCbMap_[type].empty()) {
1445 IMSA_HILOGD("%{public}s cb-vector is empty", type.c_str());
1446 return nullptr;
1447 }
1448 entry = std::make_shared<UvEntry>(jsCbMap_[type], type);
1449 }
1450 if (entrySetter != nullptr) {
1451 entrySetter(*entry);
1452 }
1453 return entry;
1454 }
1455
OnTerminated()1456 int32_t JsTextInputClientEngine::JsMessageHandler::OnTerminated()
1457 {
1458 std::lock_guard<decltype(callbackObjectMutex_)> lock(callbackObjectMutex_);
1459 if (jsMessageHandler_ == nullptr) {
1460 IMSA_HILOGI("jsCallbackObject is nullptr, can not call OnTerminated!.");
1461 return ErrorCode::ERROR_NULL_POINTER;
1462 }
1463 auto eventHandler = jsMessageHandler_->GetEventHandler();
1464 if (eventHandler == nullptr) {
1465 IMSA_HILOGI("EventHandler is nullptr!.");
1466 return ErrorCode::ERROR_NULL_POINTER;
1467 }
1468 auto task = [jsCallback = std::move(jsMessageHandler_)]() {
1469 napi_value callback = nullptr;
1470 napi_value global = nullptr;
1471 if (jsCallback == nullptr) {
1472 IMSA_HILOGI("jsCallback is nullptr!.");
1473 return;
1474 }
1475 napi_get_reference_value(jsCallback->env_, jsCallback->onTerminatedCallback_, &callback);
1476 if (callback != nullptr) {
1477 napi_get_global(jsCallback->env_, &global);
1478 napi_value output = nullptr;
1479 // 0 means the callback has no param.
1480 auto status = napi_call_function(jsCallback->env_, global, callback, 0, nullptr, &output);
1481 if (status != napi_ok) {
1482 IMSA_HILOGI("Call js function failed!.");
1483 output = nullptr;
1484 }
1485 }
1486 };
1487 eventHandler->PostTask(task, "IMA_MsgHandler_OnTerminated", 0, AppExecFwk::EventQueue::Priority::VIP);
1488 return ErrorCode::NO_ERROR;
1489 }
1490
OnMessage(const ArrayBuffer & arrayBuffer)1491 int32_t JsTextInputClientEngine::JsMessageHandler::OnMessage(const ArrayBuffer &arrayBuffer)
1492 {
1493 std::lock_guard<decltype(callbackObjectMutex_)> lock(callbackObjectMutex_);
1494 if (jsMessageHandler_ == nullptr) {
1495 IMSA_HILOGE("MessageHandler was not regist!.");
1496 return ErrorCode::ERROR_MSG_HANDLER_NOT_REGIST;
1497 }
1498 auto eventHandler = jsMessageHandler_->GetEventHandler();
1499 if (eventHandler == nullptr) {
1500 IMSA_HILOGI("EventHandler is nullptr!.");
1501 return ErrorCode::ERROR_NULL_POINTER;
1502 }
1503 auto task = [jsCallbackObject = jsMessageHandler_, arrayBuffer]() {
1504 napi_value callback = nullptr;
1505 napi_value global = nullptr;
1506 if (jsCallbackObject == nullptr) {
1507 IMSA_HILOGI("jsCallbackObject is nullptr!.");
1508 return;
1509 }
1510 napi_get_reference_value(jsCallbackObject->env_, jsCallbackObject->onMessageCallback_, &callback);
1511 if (callback != nullptr) {
1512 napi_get_global(jsCallbackObject->env_, &global);
1513 napi_value output = nullptr;
1514 napi_value argv[ARGC_TWO] = { nullptr };
1515 // 2 means just use the first two parameters
1516 if (JsUtils::GetMessageHandlerCallbackParam(argv, jsCallbackObject, arrayBuffer, 2) != napi_ok) {
1517 IMSA_HILOGE("Get message handler callback param failed!.");
1518 return;
1519 }
1520 // The maximum valid parameters count of callback is 2.
1521 auto callbackArgc = arrayBuffer.jsArgc > ARGC_ONE ? ARGC_TWO : ARGC_ONE;
1522 auto status = napi_call_function(jsCallbackObject->env_, global, callback, callbackArgc, argv, &output);
1523 if (status != napi_ok) {
1524 IMSA_HILOGI("Call js function failed!.");
1525 output = nullptr;
1526 }
1527 }
1528 };
1529 eventHandler->PostTask(task, "IMC_MsgHandler_OnMessage", 0, AppExecFwk::EventQueue::Priority::VIP);
1530 return ErrorCode::NO_ERROR;
1531 }
1532 } // namespace MiscServices
1533 } // namespace OHOS