1 /*
2  * Copyright (c) 2022 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_keyboard_delegate_setting.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_keyboard_controller_engine.h"
23 #include "js_text_input_client_engine.h"
24 #include "js_util.h"
25 #include "js_utils.h"
26 #include "key_event_napi.h"
27 #include "napi/native_api.h"
28 #include "napi/native_node_api.h"
29 
30 namespace OHOS {
31 namespace MiscServices {
32 constexpr size_t ARGC_ONE = 1;
33 constexpr size_t ARGC_TWO = 2;
34 const std::string JsKeyboardDelegateSetting::KDS_CLASS_NAME = "KeyboardDelegate";
35 thread_local napi_ref JsKeyboardDelegateSetting::KDSRef_ = nullptr;
36 
37 std::mutex JsKeyboardDelegateSetting::keyboardMutex_;
38 std::shared_ptr<JsKeyboardDelegateSetting> JsKeyboardDelegateSetting::keyboardDelegate_{ nullptr };
39 std::mutex JsKeyboardDelegateSetting::eventHandlerMutex_;
40 std::shared_ptr<AppExecFwk::EventHandler> JsKeyboardDelegateSetting::handler_{ nullptr };
41 
Init(napi_env env,napi_value exports)42 napi_value JsKeyboardDelegateSetting::Init(napi_env env, napi_value exports)
43 {
44     napi_property_descriptor descriptor[] = {
45         DECLARE_NAPI_PROPERTY("OPTION_ASCII", GetJsConstProperty(env, static_cast<uint32_t>(20))),
46         DECLARE_NAPI_PROPERTY("OPTION_NONE", GetJsConstProperty(env, static_cast<uint32_t>(0))),
47         DECLARE_NAPI_PROPERTY("OPTION_AUTO_CAP_CHARACTERS", GetJsConstProperty(env, static_cast<uint32_t>(2))),
48         DECLARE_NAPI_PROPERTY("OPTION_AUTO_CAP_SENTENCES", GetJsConstProperty(env, static_cast<uint32_t>(8))),
49         DECLARE_NAPI_PROPERTY("OPTION_AUTO_WORDS", GetJsConstProperty(env, static_cast<uint32_t>(4))),
50         DECLARE_NAPI_PROPERTY("OPTION_MULTI_LINE", GetJsConstProperty(env, static_cast<uint32_t>(1))),
51         DECLARE_NAPI_PROPERTY("OPTION_NO_FULLSCREEN", GetJsConstProperty(env, static_cast<uint32_t>(10))),
52         DECLARE_NAPI_PROPERTY("CURSOR_UP", GetJsConstProperty(env, static_cast<uint32_t>(1))),
53         DECLARE_NAPI_PROPERTY("CURSOR_DOWN", GetJsConstProperty(env, static_cast<uint32_t>(2))),
54         DECLARE_NAPI_PROPERTY("CURSOR_LEFT", GetJsConstProperty(env, static_cast<uint32_t>(3))),
55         DECLARE_NAPI_PROPERTY("CURSOR_RIGHT", GetJsConstProperty(env, static_cast<uint32_t>(4))),
56 
57         DECLARE_NAPI_PROPERTY("FLAG_SELECTING", GetJsConstProperty(env, static_cast<uint32_t>(2))),
58         DECLARE_NAPI_PROPERTY("FLAG_SINGLE_LINE", GetJsConstProperty(env, static_cast<uint32_t>(1))),
59 
60         DECLARE_NAPI_PROPERTY("DISPLAY_MODE_PART", GetJsConstProperty(env, static_cast<uint32_t>(0))),
61         DECLARE_NAPI_PROPERTY("DISPLAY_MODE_FULL", GetJsConstProperty(env, static_cast<uint32_t>(1))),
62         DECLARE_NAPI_PROPERTY("WINDOW_TYPE_INPUT_METHOD_FLOAT", GetJsConstProperty(env, static_cast<uint32_t>(2105))),
63 
64         DECLARE_NAPI_FUNCTION("createKeyboardDelegate", CreateKeyboardDelegate),
65         DECLARE_NAPI_FUNCTION("getKeyboardDelegate", GetKeyboardDelegate),
66     };
67     NAPI_CALL(
68         env, napi_define_properties(env, exports, sizeof(descriptor) / sizeof(napi_property_descriptor), descriptor));
69 
70     napi_property_descriptor properties[] = {
71         DECLARE_NAPI_FUNCTION("on", Subscribe),
72         DECLARE_NAPI_FUNCTION("off", UnSubscribe),
73     };
74     napi_value cons = nullptr;
75     NAPI_CALL(env, napi_define_class(env, KDS_CLASS_NAME.c_str(), KDS_CLASS_NAME.size(), JsConstructor, nullptr,
76                        sizeof(properties) / sizeof(napi_property_descriptor), properties, &cons));
77     NAPI_CALL(env, napi_create_reference(env, cons, 1, &KDSRef_));
78     NAPI_CALL(env, napi_set_named_property(env, exports, KDS_CLASS_NAME.c_str(), cons));
79     return exports;
80 };
81 
GetJsConstProperty(napi_env env,uint32_t num)82 napi_value JsKeyboardDelegateSetting::GetJsConstProperty(napi_env env, uint32_t num)
83 {
84     napi_value jsNumber = nullptr;
85     napi_create_int32(env, num, &jsNumber);
86     return jsNumber;
87 };
88 
GetKeyboardDelegateSetting()89 std::shared_ptr<JsKeyboardDelegateSetting> JsKeyboardDelegateSetting::GetKeyboardDelegateSetting()
90 {
91     if (keyboardDelegate_ == nullptr) {
92         std::lock_guard<std::mutex> lock(keyboardMutex_);
93         if (keyboardDelegate_ == nullptr) {
94             auto delegate = std::make_shared<JsKeyboardDelegateSetting>();
95             if (delegate == nullptr) {
96                 IMSA_HILOGE("keyboard delegate is nullptr!");
97                 return nullptr;
98             }
99             keyboardDelegate_ = delegate;
100         }
101     }
102     return keyboardDelegate_;
103 }
104 
InitKeyboardDelegate()105 bool JsKeyboardDelegateSetting::InitKeyboardDelegate()
106 {
107     if (!InputMethodAbility::GetInstance()->IsCurrentIme()) {
108         return false;
109     }
110     auto delegate = GetKeyboardDelegateSetting();
111     if (delegate == nullptr) {
112         return false;
113     }
114     InputMethodAbility::GetInstance()->SetKdListener(delegate);
115     {
116         std::lock_guard<std::mutex> lock(eventHandlerMutex_);
117         handler_ = AppExecFwk::EventHandler::Current();
118     }
119     return true;
120 }
121 
JsConstructor(napi_env env,napi_callback_info cbinfo)122 napi_value JsKeyboardDelegateSetting::JsConstructor(napi_env env, napi_callback_info cbinfo)
123 {
124     napi_value thisVar = nullptr;
125     NAPI_CALL(env, napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr));
126     auto delegate = GetKeyboardDelegateSetting();
127     if (delegate == nullptr || !InitKeyboardDelegate()) {
128         IMSA_HILOGE("failed to get delegate!");
129         napi_value result = nullptr;
130         napi_get_null(env, &result);
131         return result;
132     }
133     napi_status status = napi_wrap(
134         env, thisVar, delegate.get(), [](napi_env env, void *nativeObject, void *hint) {}, nullptr, nullptr);
135     if (status != napi_ok) {
136         IMSA_HILOGE("failed to wrap: %{public}d!", status);
137         return nullptr;
138     }
139     if (delegate->loop_ == nullptr) {
140         napi_get_uv_event_loop(env, &delegate->loop_);
141     }
142     return thisVar;
143 };
144 
CreateKeyboardDelegate(napi_env env,napi_callback_info info)145 napi_value JsKeyboardDelegateSetting::CreateKeyboardDelegate(napi_env env, napi_callback_info info)
146 {
147     return GetKDInstance(env, info);
148 }
149 
GetKeyboardDelegate(napi_env env,napi_callback_info info)150 napi_value JsKeyboardDelegateSetting::GetKeyboardDelegate(napi_env env, napi_callback_info info)
151 {
152     return GetKDInstance(env, info);
153 }
154 
GetKDInstance(napi_env env,napi_callback_info info)155 napi_value JsKeyboardDelegateSetting::GetKDInstance(napi_env env, napi_callback_info info)
156 {
157     napi_value instance = nullptr;
158     napi_value cons = nullptr;
159     if (napi_get_reference_value(env, KDSRef_, &cons) != napi_ok) {
160         IMSA_HILOGE("failed to get reference value!");
161         return nullptr;
162     }
163     if (napi_new_instance(env, cons, 0, nullptr, &instance) != napi_ok) {
164         IMSA_HILOGE("failed to new instance!");
165         return nullptr;
166     }
167     return instance;
168 }
169 
RegisterListener(napi_value callback,std::string type,std::shared_ptr<JSCallbackObject> callbackObj)170 void JsKeyboardDelegateSetting::RegisterListener(
171     napi_value callback, std::string type, std::shared_ptr<JSCallbackObject> callbackObj)
172 {
173     IMSA_HILOGD("RegisterListener %{public}s", type.c_str());
174     std::lock_guard<std::recursive_mutex> lock(mutex_);
175     if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) {
176         IMSA_HILOGD("methodName %{public}s is not registered!", type.c_str());
177     }
178     auto callbacks = jsCbMap_[type];
179     bool ret = std::any_of(callbacks.begin(), callbacks.end(), [&callback](std::shared_ptr<JSCallbackObject> cb) {
180         return JsUtils::Equals(cb->env_, callback, cb->callback_, cb->threadId_);
181     });
182     if (ret) {
183         IMSA_HILOGD("JsKeyboardDelegateSetting callback already registered!");
184         return;
185     }
186 
187     IMSA_HILOGI("add %{public}s callbackObj into jsCbMap_.", type.c_str());
188     jsCbMap_[type].push_back(std::move(callbackObj));
189 }
190 
UnRegisterListener(napi_value callback,std::string type)191 void JsKeyboardDelegateSetting::UnRegisterListener(napi_value callback, std::string type)
192 {
193     IMSA_HILOGI("unregister listener: %{public}s.", type.c_str());
194     std::lock_guard<std::recursive_mutex> lock(mutex_);
195     if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) {
196         IMSA_HILOGE("methodName %{public}s is not unregistered!", type.c_str());
197         return;
198     }
199 
200     if (callback == nullptr) {
201         jsCbMap_.erase(type);
202         IMSA_HILOGE("callback is nullptr");
203         return;
204     }
205 
206     for (auto item = jsCbMap_[type].begin(); item != jsCbMap_[type].end(); item++) {
207         if ((callback != nullptr)
208             && (JsUtils::Equals((*item)->env_, callback, (*item)->callback_, (*item)->threadId_))) {
209             jsCbMap_[type].erase(item);
210             break;
211         }
212     }
213     if (jsCbMap_[type].empty()) {
214         jsCbMap_.erase(type);
215     }
216 }
217 
Subscribe(napi_env env,napi_callback_info info)218 napi_value JsKeyboardDelegateSetting::Subscribe(napi_env env, napi_callback_info info)
219 {
220     size_t argc = ARGC_TWO;
221     napi_value argv[ARGC_TWO] = { nullptr };
222     napi_value thisVar = nullptr;
223     void *data = nullptr;
224     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
225     std::string type;
226     // 2 means least param num.
227     if (argc < 2 || !JsUtil::GetValue(env, argv[0], type) ||
228         !EventChecker::IsValidEventType(EventSubscribeModule::KEYBOARD_DELEGATE, type) ||
229         JsUtil::GetType(env, argv[1]) != napi_function) {
230         IMSA_HILOGE("subscribe failed, type: %{public}s!", type.c_str());
231         return nullptr;
232     }
233     IMSA_HILOGD("subscribe type: %{public}s.", type.c_str());
234     auto engine = reinterpret_cast<JsKeyboardDelegateSetting *>(JsUtils::GetNativeSelf(env, info));
235     if (engine == nullptr) {
236         return nullptr;
237     }
238     std::shared_ptr<JSCallbackObject> callback =
239         std::make_shared<JSCallbackObject>(env, argv[1], std::this_thread::get_id());
240     engine->RegisterListener(argv[ARGC_ONE], type, callback);
241 
242     napi_value result = nullptr;
243     napi_get_null(env, &result);
244     return result;
245 }
246 
UnSubscribe(napi_env env,napi_callback_info info)247 napi_value JsKeyboardDelegateSetting::UnSubscribe(napi_env env, napi_callback_info info)
248 {
249     size_t argc = ARGC_TWO;
250     napi_value argv[ARGC_TWO] = { nullptr };
251     napi_value thisVar = nullptr;
252     void *data = nullptr;
253     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
254     std::string type;
255     // 1 means least param num.
256     if (argc < 1 || !JsUtil::GetValue(env, argv[0], type) ||
257         !EventChecker::IsValidEventType(EventSubscribeModule::KEYBOARD_DELEGATE, type)) {
258         IMSA_HILOGE("unsubscribe failed, type: %{public}s!", type.c_str());
259         return nullptr;
260     }
261 
262     // if the second param is not napi_function/napi_null/napi_undefined, return
263     auto paramType = JsUtil::GetType(env, argv[1]);
264     if (paramType != napi_function && paramType != napi_null && paramType != napi_undefined) {
265         return nullptr;
266     }
267     // if the second param is napi_function, delete it, else delete all
268     argv[1] = paramType == napi_function ? argv[1] : nullptr;
269 
270     IMSA_HILOGD("unsubscribe type: %{public}s.", type.c_str());
271     auto delegate = reinterpret_cast<JsKeyboardDelegateSetting *>(JsUtils::GetNativeSelf(env, info));
272     if (delegate == nullptr) {
273         return nullptr;
274     }
275     delegate->UnRegisterListener(argv[ARGC_ONE], type);
276     napi_value result = nullptr;
277     napi_get_null(env, &result);
278     return result;
279 }
280 
GetResultOnKeyEvent(napi_env env,int32_t keyCode,int32_t keyStatus)281 napi_value JsKeyboardDelegateSetting::GetResultOnKeyEvent(napi_env env, int32_t keyCode, int32_t keyStatus)
282 {
283     napi_value KeyboardDelegate = nullptr;
284     napi_create_object(env, &KeyboardDelegate);
285 
286     napi_value jsKeyCode = nullptr;
287     napi_create_int32(env, keyCode, &jsKeyCode);
288     napi_set_named_property(env, KeyboardDelegate, "keyCode", jsKeyCode);
289 
290     napi_value jsKeyAction = nullptr;
291     napi_create_int32(env, keyStatus, &jsKeyAction);
292     napi_set_named_property(env, KeyboardDelegate, "keyAction", jsKeyAction);
293 
294     return KeyboardDelegate;
295 }
296 
OnDealKeyEvent(const std::shared_ptr<MMI::KeyEvent> & keyEvent,sptr<KeyEventConsumerProxy> & consumer)297 bool JsKeyboardDelegateSetting::OnDealKeyEvent(
298     const std::shared_ptr<MMI::KeyEvent> &keyEvent, sptr<KeyEventConsumerProxy> &consumer)
299 {
300     auto eventHandler = GetEventHandler();
301     if (eventHandler == nullptr) {
302         IMSA_HILOGE("eventHandler is nullptr!");
303         return false;
304     }
305     auto keyEventEntry =
306         GetEntry("keyEvent", [keyEvent](UvEntry &entry) { entry.pullKeyEventPara = keyEvent; });
307     KeyEventPara para{ keyEvent->GetKeyCode(), keyEvent->GetKeyAction(), false };
308     std::string type = (keyEvent->GetKeyAction() == ARGC_TWO ? "keyDown" : "keyUp");
309     auto keyCodeEntry = GetEntry(type, [&para](UvEntry &entry) {
310         entry.keyEventPara = { para.keyCode, para.keyStatus, para.isOnKeyEvent };
311     });
312     if (keyEventEntry == nullptr && keyCodeEntry == nullptr) {
313         IMSA_HILOGW("key event callback is not registered.");
314         return false;
315     }
316     IMSA_HILOGD("run in.");
317     auto task = [keyEventEntry, keyCodeEntry, consumer]() { DealKeyEvent(keyEventEntry, keyCodeEntry, consumer); };
318     eventHandler->PostTask(task, "OnDealKeyEvent");
319     return true;
320 }
321 
DealKeyEvent(const std::shared_ptr<UvEntry> & keyEventEntry,const std::shared_ptr<UvEntry> & keyCodeEntry,const sptr<KeyEventConsumerProxy> & consumer)322 void JsKeyboardDelegateSetting::DealKeyEvent(const std::shared_ptr<UvEntry> &keyEventEntry,
323     const std::shared_ptr<UvEntry> &keyCodeEntry, const sptr<KeyEventConsumerProxy> &consumer)
324 {
325     bool isKeyEventConsumed = false;
326     bool isKeyCodeConsumed = false;
327     if (keyEventEntry != nullptr) {
328         auto getKeyEventProperty = [keyEventEntry](napi_env env, napi_value *args, uint8_t argc) -> bool {
329             if (argc == 0) {
330                 return false;
331             }
332             napi_value keyEventObject{};
333             auto result = napi_create_object(env, &keyEventObject);
334             CHECK_RETURN((result == napi_ok) && (keyEventObject != nullptr), "create object", false);
335             result = MMI::KeyEventNapi::CreateKeyEvent(env, keyEventEntry->pullKeyEventPara, keyEventObject);
336             CHECK_RETURN((result == napi_ok) && (keyEventObject != nullptr), "create key event object", false);
337             // 0 means the first param of callback.
338             args[0] = keyEventObject;
339             return true;
340         };
341         // 1 means callback has one param.
342         JsCallbackHandler::Traverse(keyEventEntry->vecCopy, { 1, getKeyEventProperty }, isKeyEventConsumed);
343     }
344     if (keyCodeEntry != nullptr) {
345         auto getKeyEventProperty = [keyCodeEntry](napi_env env, napi_value *args, uint8_t argc) -> bool {
346             InputMethodSyncTrace tracer("Create parameter");
347             if (argc == 0) {
348                 return false;
349             }
350             napi_value jsObject =
351                 GetResultOnKeyEvent(env, keyCodeEntry->keyEventPara.keyCode, keyCodeEntry->keyEventPara.keyStatus);
352             if (jsObject == nullptr) {
353                 IMSA_HILOGE("jsObject is nullptr!");
354                 return false;
355             }
356             // 0 means the first param of callback.
357             args[0] = jsObject;
358             return true;
359         };
360         // 1 means callback has one param.
361         JsCallbackHandler::Traverse(keyCodeEntry->vecCopy, { 1, getKeyEventProperty }, isKeyCodeConsumed);
362     }
363     bool consumeResult = isKeyEventConsumed || isKeyCodeConsumed;
364     if (consumer != nullptr) {
365         consumer->OnKeyEventResult(consumeResult);
366         if (!consumeResult) {
367             IMSA_HILOGW("ime is not consumed, result: %{public}d.", consumeResult);
368         }
369     }
370 }
371 
OnKeyEvent(const std::shared_ptr<MMI::KeyEvent> & keyEvent,sptr<KeyEventConsumerProxy> & consumer)372 bool JsKeyboardDelegateSetting::OnKeyEvent(
373     const std::shared_ptr<MMI::KeyEvent> &keyEvent, sptr<KeyEventConsumerProxy> &consumer)
374 {
375     std::string type = "keyEvent";
376     auto entry = GetEntry(type, [keyEvent, &consumer](UvEntry &entry) {
377         entry.pullKeyEventPara = keyEvent;
378         entry.keyEvenetConsumer = consumer;
379     });
380     if (entry == nullptr) {
381         return false;
382     }
383     auto eventHandler = GetEventHandler();
384     if (eventHandler == nullptr) {
385         IMSA_HILOGE("eventHandler is nullptr!");
386         return false;
387     }
388 
389     IMSA_HILOGI("run in.");
390     StartAsync("OnFullKeyEvent", static_cast<int32_t>(TraceTaskId::ON_FULL_KEY_EVENT));
391     auto task = [entry]() {
392         auto getKeyEventProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
393             InputMethodSyncTrace tracer("Create parameter");
394             if (argc == 0) {
395                 return false;
396             }
397             napi_value keyEventObject{};
398             auto result = napi_create_object(env, &keyEventObject);
399             CHECK_RETURN((result == napi_ok) && (keyEventObject != nullptr), "create object", false);
400             result = MMI::KeyEventNapi::CreateKeyEvent(env, entry->pullKeyEventPara, keyEventObject);
401             CHECK_RETURN((result == napi_ok) && (keyEventObject != nullptr), "create key event object", false);
402             // 0 means the first param of callback.
403             args[0] = keyEventObject;
404             return true;
405         };
406         bool isConsumed = false;
407         // 1 means callback has one param.
408         JsCallbackHandler::Traverse(entry->vecCopy, { 1, getKeyEventProperty }, isConsumed);
409         auto consumer = entry->keyEvenetConsumer;
410         if (consumer != nullptr) {
411             IMSA_HILOGE("consumer result: %{public}d!", isConsumed);
412             consumer->OnKeyEventConsumeResult(isConsumed);
413         }
414         FinishAsync("OnFullKeyEvent", static_cast<int32_t>(TraceTaskId::ON_FULL_KEY_EVENT));
415     };
416     eventHandler->PostTask(task, type);
417     return true;
418 }
419 
OnKeyEvent(int32_t keyCode,int32_t keyStatus,sptr<KeyEventConsumerProxy> & consumer)420 bool JsKeyboardDelegateSetting::OnKeyEvent(int32_t keyCode, int32_t keyStatus, sptr<KeyEventConsumerProxy> &consumer)
421 {
422     KeyEventPara para{ keyCode, keyStatus, false };
423     std::string type = (keyStatus == ARGC_TWO ? "keyDown" : "keyUp");
424     auto entry = GetEntry(type, [&para, &consumer](UvEntry &entry) {
425         entry.keyEventPara = { para.keyCode, para.keyStatus, para.isOnKeyEvent };
426         entry.keyEvenetConsumer = consumer;
427     });
428     if (entry == nullptr) {
429         return false;
430     }
431     auto eventHandler = GetEventHandler();
432     if (eventHandler == nullptr) {
433         IMSA_HILOGE("eventHandler is nullptr!");
434         return false;
435     }
436 
437     IMSA_HILOGI("run in.");
438     StartAsync("OnKeyEvent", static_cast<int32_t>(TraceTaskId::ON_KEY_EVENT));
439     auto task = [entry]() {
440         InputMethodSyncTrace tracer("OnkeyEvent UV_QUEUE_WORK");
441         auto getKeyEventProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
442             InputMethodSyncTrace tracer("Create parameter");
443             if (argc == 0) {
444                 return false;
445             }
446             napi_value jsObject = GetResultOnKeyEvent(env, entry->keyEventPara.keyCode, entry->keyEventPara.keyStatus);
447             if (jsObject == nullptr) {
448                 IMSA_HILOGE("jsObject is nullptr!");
449                 return false;
450             }
451             // 0 means the first param of callback.
452             args[0] = jsObject;
453             return true;
454         };
455         bool isConsumed = false;
456         // 1 means callback has one param.
457         JsCallbackHandler::Traverse(entry->vecCopy, { 1, getKeyEventProperty }, isConsumed);
458         auto consumer = entry->keyEvenetConsumer;
459         if (consumer != nullptr) {
460             IMSA_HILOGE("consumer result: %{public}d", isConsumed);
461             consumer->OnKeyCodeConsumeResult(isConsumed);
462         }
463         FinishAsync("OnKeyEvent", static_cast<int32_t>(TraceTaskId::ON_KEY_EVENT));
464     };
465     eventHandler->PostTask(task, type);
466     return true;
467 }
468 
OnCursorUpdate(int32_t positionX,int32_t positionY,int32_t height)469 void JsKeyboardDelegateSetting::OnCursorUpdate(int32_t positionX, int32_t positionY, int32_t height)
470 {
471     CursorPara para{ positionX, positionY, height };
472     std::string type = "cursorContextChange";
473     auto entry = GetEntry(type, [&para](UvEntry &entry) {
474         entry.curPara.positionX = para.positionX;
475         entry.curPara.positionY = para.positionY;
476         entry.curPara.height = para.height;
477     });
478     if (entry == nullptr) {
479         return;
480     }
481     auto eventHandler = GetEventHandler();
482     if (eventHandler == nullptr) {
483         IMSA_HILOGE("eventHandler is nullptr!");
484         return;
485     }
486     IMSA_HILOGD("the cursor for x: %{public}d, y: %{public}d, height: %{public}d.", positionX, positionY,
487         height);
488     auto task = [entry]() {
489         auto paramGetter = [&entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
490             if (argc < 3) {
491                 return false;
492             }
493             // 0 means the first param of callback.
494             napi_create_int32(env, entry->curPara.positionX, &args[0]);
495             // 1 means the second param of callback.
496             napi_create_int32(env, entry->curPara.positionY, &args[1]);
497             // 2 means the third param of callback.
498             napi_create_int32(env, entry->curPara.height, &args[2]);
499             return true;
500         };
501         // 3 means callback has three params.
502         JsCallbackHandler::Traverse(entry->vecCopy, { 3, paramGetter });
503     };
504     handler_->PostTask(task, type);
505 }
506 
OnSelectionChange(int32_t oldBegin,int32_t oldEnd,int32_t newBegin,int32_t newEnd)507 void JsKeyboardDelegateSetting::OnSelectionChange(int32_t oldBegin, int32_t oldEnd, int32_t newBegin, int32_t newEnd)
508 {
509     SelectionPara para{ oldBegin, oldEnd, newBegin, newEnd };
510     std::string type = "selectionChange";
511     auto entry = GetEntry(type, [&para](UvEntry &entry) {
512         entry.selPara.oldBegin = para.oldBegin;
513         entry.selPara.oldEnd = para.oldEnd;
514         entry.selPara.newBegin = para.newBegin;
515         entry.selPara.newEnd = para.newEnd;
516     });
517     if (entry == nullptr) {
518         return;
519     }
520     auto eventHandler = GetEventHandler();
521     if (eventHandler == nullptr) {
522         IMSA_HILOGE("eventHandler is nullptr!");
523         return;
524     }
525     IMSA_HILOGD("the selection for oldBegin: %{public}d, oldEnd: %{public}d, newBegin: %{public}d, newEnd: "
526                 "%{public}d.", oldBegin, oldEnd, newBegin, newEnd);
527     auto task = [entry]() {
528         auto paramGetter = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
529             if (argc < 4) {
530                 return false;
531             }
532             // 0 means the first param of callback.
533             napi_create_int32(env, entry->selPara.oldBegin, &args[0]);
534             // 1 means the second param of callback.
535             napi_create_int32(env, entry->selPara.oldEnd, &args[1]);
536             // 2 means the third param of callback.
537             napi_create_int32(env, entry->selPara.newBegin, &args[2]);
538             // 3 means the fourth param of callback.
539             napi_create_int32(env, entry->selPara.newEnd, &args[3]);
540             return true;
541         };
542         // 4 means callback has four params.
543         JsCallbackHandler::Traverse(entry->vecCopy, { 4, paramGetter });
544     };
545     eventHandler->PostTask(task, type);
546 }
547 
OnTextChange(const std::string & text)548 void JsKeyboardDelegateSetting::OnTextChange(const std::string &text)
549 {
550     std::string type = "textChange";
551     auto entry = GetEntry(type, [&text](UvEntry &entry) { entry.text = text; });
552     if (entry == nullptr) {
553         IMSA_HILOGE("failed to get uv entry!");
554         return;
555     }
556     auto eventHandler = GetEventHandler();
557     if (eventHandler == nullptr) {
558         IMSA_HILOGE("eventHandler is nullptr!");
559         return;
560     }
561     IMSA_HILOGD("run in.");
562 
563     auto task = [entry]() {
564         auto getTextChangeProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
565             if (argc == 0) {
566                 return false;
567             }
568             // 0 means the first param of callback.
569             napi_create_string_utf8(env, entry->text.c_str(), NAPI_AUTO_LENGTH, &args[0]);
570             return true;
571         };
572         // 1 means callback has one param.
573         JsCallbackHandler::Traverse(entry->vecCopy, { 1, getTextChangeProperty });
574     };
575     eventHandler->PostTask(task, type);
576 }
577 
OnEditorAttributeChange(const InputAttribute & inputAttribute)578 void JsKeyboardDelegateSetting::OnEditorAttributeChange(const InputAttribute &inputAttribute)
579 {
580     std::string type = "editorAttributeChanged";
581     auto entry = GetEntry(type, [&inputAttribute](UvEntry &entry) { entry.inputAttribute = inputAttribute; });
582     if (entry == nullptr) {
583         return;
584     }
585     auto eventHandler = GetEventHandler();
586     if (eventHandler == nullptr) {
587         IMSA_HILOGE("eventHandler is nullptr!");
588         return;
589     }
590     IMSA_HILOGD("enterKeyType: %{public}d, inputPattern: %{public}d, previewSupport: %{public}d",
591         inputAttribute.enterKeyType, inputAttribute.inputPattern, inputAttribute.isTextPreviewSupported);
592     auto task = [entry]() {
593         auto paramGetter = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
594             if (argc == 0) {
595                 return false;
596             }
597             napi_value jsObject = JsInputAttribute::Write(env, entry->inputAttribute);
598             if (jsObject == JsUtil::Const::Null(env)) {
599                 IMSA_HILOGE("jsObject is nullptr!");
600                 return false;
601             }
602             // 0 means the first param of callback.
603             args[0] = jsObject;
604             return true;
605         };
606         // 1 means callback has one param.
607         JsCallbackHandler::Traverse(entry->vecCopy, { 1, paramGetter });
608     };
609     eventHandler->PostTask(task, type);
610 }
611 
GetUVwork(const std::string & type,EntrySetter entrySetter)612 uv_work_t *JsKeyboardDelegateSetting::GetUVwork(const std::string &type, EntrySetter entrySetter)
613 {
614     IMSA_HILOGD("start, type: %{public}s", type.c_str());
615     UvEntry *entry = nullptr;
616     {
617         std::lock_guard<std::recursive_mutex> lock(mutex_);
618 
619         if (jsCbMap_[type].empty()) {
620             IMSA_HILOGD("%{public}s cb-vector is empty.", type.c_str());
621             return nullptr;
622         }
623         entry = new (std::nothrow) UvEntry(jsCbMap_[type], type);
624         if (entry == nullptr) {
625             IMSA_HILOGE("entry is nullptr!");
626             return nullptr;
627         }
628         if (entrySetter != nullptr) {
629             entrySetter(*entry);
630         }
631     }
632     uv_work_t *work = new (std::nothrow) uv_work_t;
633     if (work == nullptr) {
634         IMSA_HILOGE("work is nullptr!");
635         delete entry;
636         return nullptr;
637     }
638     work->data = entry;
639     return work;
640 }
641 
GetEventHandler()642 std::shared_ptr<AppExecFwk::EventHandler> JsKeyboardDelegateSetting::GetEventHandler()
643 {
644     std::lock_guard<std::mutex> lock(eventHandlerMutex_);
645     return handler_;
646 }
647 
GetEntry(const std::string & type,EntrySetter entrySetter)648 std::shared_ptr<JsKeyboardDelegateSetting::UvEntry> JsKeyboardDelegateSetting::GetEntry(
649     const std::string &type, EntrySetter entrySetter)
650 {
651     IMSA_HILOGD("start, type: %{public}s", type.c_str());
652     std::shared_ptr<UvEntry> entry = nullptr;
653     {
654         std::lock_guard<std::recursive_mutex> lock(mutex_);
655         if (jsCbMap_[type].empty()) {
656             IMSA_HILOGD("%{public}s cb-vector is empty.", type.c_str());
657             return nullptr;
658         }
659         entry = std::make_shared<UvEntry>(jsCbMap_[type], type);
660     }
661     if (entrySetter != nullptr) {
662         entrySetter(*entry);
663     }
664     return entry;
665 }
666 } // namespace MiscServices
667 } // namespace OHOS