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