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(env,
68 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(napi_value callback, std::string type,
171 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(const std::shared_ptr<MMI::KeyEvent> &keyEvent,
298 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 = GetEntry("keyEvent", [keyEvent](UvEntry &entry) { entry.pullKeyEventPara = keyEvent; });
306 KeyEventPara para{ keyEvent->GetKeyCode(), keyEvent->GetKeyAction(), false };
307 std::string type = (keyEvent->GetKeyAction() == ARGC_TWO ? "keyDown" : "keyUp");
308 auto keyCodeEntry = GetEntry(type, [¶](UvEntry &entry) {
309 entry.keyEventPara = { para.keyCode, para.keyStatus, para.isOnKeyEvent };
310 });
311 if (keyEventEntry == nullptr && keyCodeEntry == nullptr) {
312 IMSA_HILOGW("key event callback is not registered.");
313 return false;
314 }
315 IMSA_HILOGD("run in.");
316 auto task = [keyEventEntry, keyCodeEntry, consumer]() { DealKeyEvent(keyEventEntry, keyCodeEntry, consumer); };
317 eventHandler->PostTask(task, "OnDealKeyEvent");
318 return true;
319 }
320
DealKeyEvent(const std::shared_ptr<UvEntry> &keyEventEntry, const std::shared_ptr<UvEntry> &keyCodeEntry, const sptr<KeyEventConsumerProxy> &consumer)321 void JsKeyboardDelegateSetting::DealKeyEvent(const std::shared_ptr<UvEntry> &keyEventEntry,
322 const std::shared_ptr<UvEntry> &keyCodeEntry, const sptr<KeyEventConsumerProxy> &consumer)
323 {
324 bool isKeyEventConsumed = false;
325 bool isKeyCodeConsumed = false;
326 if (keyEventEntry != nullptr) {
327 auto getKeyEventProperty = [keyEventEntry](napi_env env, napi_value *args, uint8_t argc) -> bool {
328 if (argc == 0) {
329 return false;
330 }
331 napi_value keyEventObject{};
332 auto result = napi_create_object(env, &keyEventObject);
333 CHECK_RETURN((result == napi_ok) && (keyEventObject != nullptr), "create object", false);
334 result = MMI::KeyEventNapi::CreateKeyEvent(env, keyEventEntry->pullKeyEventPara, keyEventObject);
335 CHECK_RETURN((result == napi_ok) && (keyEventObject != nullptr), "create key event object", false);
336 // 0 means the first param of callback.
337 args[0] = keyEventObject;
338 return true;
339 };
340 // 1 means callback has one param.
341 JsCallbackHandler::Traverse(keyEventEntry->vecCopy, { 1, getKeyEventProperty }, isKeyEventConsumed);
342 }
343 if (keyCodeEntry != nullptr) {
344 auto getKeyEventProperty = [keyCodeEntry](napi_env env, napi_value *args, uint8_t argc) -> bool {
345 InputMethodSyncTrace tracer("Create parameter");
346 if (argc == 0) {
347 return false;
348 }
349 napi_value jsObject =
350 GetResultOnKeyEvent(env, keyCodeEntry->keyEventPara.keyCode, keyCodeEntry->keyEventPara.keyStatus);
351 if (jsObject == nullptr) {
352 IMSA_HILOGE("jsObject is nullptr!");
353 return false;
354 }
355 // 0 means the first param of callback.
356 args[0] = jsObject;
357 return true;
358 };
359 // 1 means callback has one param.
360 JsCallbackHandler::Traverse(keyCodeEntry->vecCopy, { 1, getKeyEventProperty }, isKeyCodeConsumed);
361 }
362 bool consumeResult = isKeyEventConsumed || isKeyCodeConsumed;
363 if (consumer != nullptr) {
364 consumer->OnKeyEventResult(consumeResult);
365 if (!consumeResult) {
366 IMSA_HILOGW("ime is not consumed, result: %{public}d.", consumeResult);
367 }
368 }
369 }
370
OnKeyEvent(const std::shared_ptr<MMI::KeyEvent> &keyEvent, sptr<KeyEventConsumerProxy> &consumer)371 bool JsKeyboardDelegateSetting::OnKeyEvent(const std::shared_ptr<MMI::KeyEvent> &keyEvent,
372 sptr<KeyEventConsumerProxy> &consumer)
373 {
374 std::string type = "keyEvent";
375 auto entry = GetEntry(type, [keyEvent, &consumer](UvEntry &entry) {
376 entry.pullKeyEventPara = keyEvent;
377 entry.keyEvenetConsumer = consumer;
378 });
379 if (entry == nullptr) {
380 return false;
381 }
382 auto eventHandler = GetEventHandler();
383 if (eventHandler == nullptr) {
384 IMSA_HILOGE("eventHandler is nullptr!");
385 return false;
386 }
387
388 IMSA_HILOGI("run in.");
389 StartAsync("OnFullKeyEvent", static_cast<int32_t>(TraceTaskId::ON_FULL_KEY_EVENT));
390 auto task = [entry]() {
391 auto getKeyEventProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
392 InputMethodSyncTrace tracer("Create parameter");
393 if (argc == 0) {
394 return false;
395 }
396 napi_value keyEventObject{};
397 auto result = napi_create_object(env, &keyEventObject);
398 CHECK_RETURN((result == napi_ok) && (keyEventObject != nullptr), "create object", false);
399 result = MMI::KeyEventNapi::CreateKeyEvent(env, entry->pullKeyEventPara, keyEventObject);
400 CHECK_RETURN((result == napi_ok) && (keyEventObject != nullptr), "create key event object", false);
401 // 0 means the first param of callback.
402 args[0] = keyEventObject;
403 return true;
404 };
405 bool isConsumed = false;
406 // 1 means callback has one param.
407 JsCallbackHandler::Traverse(entry->vecCopy, { 1, getKeyEventProperty }, isConsumed);
408 auto consumer = entry->keyEvenetConsumer;
409 if (consumer != nullptr) {
410 IMSA_HILOGE("consumer result: %{public}d!", isConsumed);
411 consumer->OnKeyEventConsumeResult(isConsumed);
412 }
413 FinishAsync("OnFullKeyEvent", static_cast<int32_t>(TraceTaskId::ON_FULL_KEY_EVENT));
414 };
415 eventHandler->PostTask(task, type);
416 return true;
417 }
418
OnKeyEvent(int32_t keyCode, int32_t keyStatus, sptr<KeyEventConsumerProxy> &consumer)419 bool JsKeyboardDelegateSetting::OnKeyEvent(int32_t keyCode, int32_t keyStatus, sptr<KeyEventConsumerProxy> &consumer)
420 {
421 KeyEventPara para{ keyCode, keyStatus, false };
422 std::string type = (keyStatus == ARGC_TWO ? "keyDown" : "keyUp");
423 auto entry = GetEntry(type, [¶, &consumer](UvEntry &entry) {
424 entry.keyEventPara = { para.keyCode, para.keyStatus, para.isOnKeyEvent };
425 entry.keyEvenetConsumer = consumer;
426 });
427 if (entry == nullptr) {
428 return false;
429 }
430 auto eventHandler = GetEventHandler();
431 if (eventHandler == nullptr) {
432 IMSA_HILOGE("eventHandler is nullptr!");
433 return false;
434 }
435
436 IMSA_HILOGI("run in.");
437 StartAsync("OnKeyEvent", static_cast<int32_t>(TraceTaskId::ON_KEY_EVENT));
438 auto task = [entry]() {
439 InputMethodSyncTrace tracer("OnkeyEvent UV_QUEUE_WORK");
440 auto getKeyEventProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
441 InputMethodSyncTrace tracer("Create parameter");
442 if (argc == 0) {
443 return false;
444 }
445 napi_value jsObject = GetResultOnKeyEvent(env, entry->keyEventPara.keyCode, entry->keyEventPara.keyStatus);
446 if (jsObject == nullptr) {
447 IMSA_HILOGE("jsObject is nullptr!");
448 return false;
449 }
450 // 0 means the first param of callback.
451 args[0] = jsObject;
452 return true;
453 };
454 bool isConsumed = false;
455 // 1 means callback has one param.
456 JsCallbackHandler::Traverse(entry->vecCopy, { 1, getKeyEventProperty }, isConsumed);
457 auto consumer = entry->keyEvenetConsumer;
458 if (consumer != nullptr) {
459 IMSA_HILOGE("consumer result: %{public}d!", isConsumed);
460 consumer->OnKeyCodeConsumeResult(isConsumed);
461 }
462 FinishAsync("OnKeyEvent", static_cast<int32_t>(TraceTaskId::ON_KEY_EVENT));
463 };
464 eventHandler->PostTask(task, type);
465 return true;
466 }
467
OnCursorUpdate(int32_t positionX, int32_t positionY, int32_t height)468 void JsKeyboardDelegateSetting::OnCursorUpdate(int32_t positionX, int32_t positionY, int32_t height)
469 {
470 CursorPara para{ positionX, positionY, height };
471 std::string type = "cursorContextChange";
472 auto entry = GetEntry(type, [¶](UvEntry &entry) {
473 entry.curPara.positionX = para.positionX;
474 entry.curPara.positionY = para.positionY;
475 entry.curPara.height = para.height;
476 });
477 if (entry == nullptr) {
478 return;
479 }
480 auto eventHandler = GetEventHandler();
481 if (eventHandler == nullptr) {
482 IMSA_HILOGE("eventHandler is nullptr!");
483 return;
484 }
485 IMSA_HILOGD("the cursor for x: %{public}d, y: %{public}d, height: %{public}d.", positionX, positionY,
486 height);
487 auto task = [entry]() {
488 auto paramGetter = [&entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
489 if (argc < 3) {
490 return false;
491 }
492 // 0 means the first param of callback.
493 napi_create_int32(env, entry->curPara.positionX, &args[0]);
494 // 1 means the second param of callback.
495 napi_create_int32(env, entry->curPara.positionY, &args[1]);
496 // 2 means the third param of callback.
497 napi_create_int32(env, entry->curPara.height, &args[2]);
498 return true;
499 };
500 // 3 means callback has three params.
501 JsCallbackHandler::Traverse(entry->vecCopy, { 3, paramGetter });
502 };
503 handler_->PostTask(task, type);
504 }
505
OnSelectionChange(int32_t oldBegin, int32_t oldEnd, int32_t newBegin, int32_t newEnd)506 void JsKeyboardDelegateSetting::OnSelectionChange(int32_t oldBegin, int32_t oldEnd, int32_t newBegin, int32_t newEnd)
507 {
508 SelectionPara para{ oldBegin, oldEnd, newBegin, newEnd };
509 std::string type = "selectionChange";
510 auto entry = GetEntry(type, [¶](UvEntry &entry) {
511 entry.selPara.oldBegin = para.oldBegin;
512 entry.selPara.oldEnd = para.oldEnd;
513 entry.selPara.newBegin = para.newBegin;
514 entry.selPara.newEnd = para.newEnd;
515 });
516 if (entry == nullptr) {
517 return;
518 }
519 auto eventHandler = GetEventHandler();
520 if (eventHandler == nullptr) {
521 IMSA_HILOGE("eventHandler is nullptr!");
522 return;
523 }
524 IMSA_HILOGD("the selection for oldBegin: %{public}d, oldEnd: %{public}d, newBegin: %{public}d, newEnd: "
525 "%{public}d.", 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 eventHandler->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 auto entry = GetEntry(type, [&text](UvEntry &entry) { entry.text = text; });
551 if (entry == nullptr) {
552 IMSA_HILOGE("failed to get uv entry!");
553 return;
554 }
555 auto eventHandler = GetEventHandler();
556 if (eventHandler == nullptr) {
557 IMSA_HILOGE("eventHandler is nullptr!");
558 return;
559 }
560 IMSA_HILOGD("run in.");
561
562 auto task = [entry]() {
563 auto getTextChangeProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
564 if (argc == 0) {
565 return false;
566 }
567 // 0 means the first param of callback.
568 napi_create_string_utf8(env, entry->text.c_str(), NAPI_AUTO_LENGTH, &args[0]);
569 return true;
570 };
571 // 1 means callback has one param.
572 JsCallbackHandler::Traverse(entry->vecCopy, { 1, getTextChangeProperty });
573 };
574 eventHandler->PostTask(task, type);
575 }
576
OnEditorAttributeChange(const InputAttribute &inputAttribute)577 void JsKeyboardDelegateSetting::OnEditorAttributeChange(const InputAttribute &inputAttribute)
578 {
579 std::string type = "editorAttributeChanged";
580 auto entry = GetEntry(type, [&inputAttribute](UvEntry &entry) { entry.inputAttribute = inputAttribute; });
581 if (entry == nullptr) {
582 return;
583 }
584 auto eventHandler = GetEventHandler();
585 if (eventHandler == nullptr) {
586 IMSA_HILOGE("eventHandler is nullptr!");
587 return;
588 }
589 IMSA_HILOGD("enterKeyType: %{public}d, inputPattern: %{public}d, previewSupport: %{public}d",
590 inputAttribute.enterKeyType, inputAttribute.inputPattern, inputAttribute.isTextPreviewSupported);
591 auto task = [entry]() {
592 auto paramGetter = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
593 if (argc == 0) {
594 return false;
595 }
596 napi_value jsObject = JsInputAttribute::Write(env, entry->inputAttribute);
597 if (jsObject == JsUtil::Const::Null(env)) {
598 IMSA_HILOGE("jsObject is nullptr!");
599 return false;
600 }
601 // 0 means the first param of callback.
602 args[0] = jsObject;
603 return true;
604 };
605 // 1 means callback has one param.
606 JsCallbackHandler::Traverse(entry->vecCopy, { 1, paramGetter });
607 };
608 eventHandler->PostTask(task, type);
609 }
610
GetUVwork(const std::string &type, EntrySetter entrySetter)611 uv_work_t *JsKeyboardDelegateSetting::GetUVwork(const std::string &type, EntrySetter entrySetter)
612 {
613 IMSA_HILOGD("start, type: %{public}s", type.c_str());
614 UvEntry *entry = nullptr;
615 {
616 std::lock_guard<std::recursive_mutex> lock(mutex_);
617
618 if (jsCbMap_[type].empty()) {
619 IMSA_HILOGD("%{public}s cb-vector is empty.", type.c_str());
620 return nullptr;
621 }
622 entry = new (std::nothrow) UvEntry(jsCbMap_[type], type);
623 if (entry == nullptr) {
624 IMSA_HILOGE("entry is nullptr!");
625 return nullptr;
626 }
627 if (entrySetter != nullptr) {
628 entrySetter(*entry);
629 }
630 }
631 uv_work_t *work = new (std::nothrow) uv_work_t;
632 if (work == nullptr) {
633 IMSA_HILOGE("work is nullptr!");
634 delete entry;
635 return nullptr;
636 }
637 work->data = entry;
638 return work;
639 }
640
GetEventHandler()641 std::shared_ptr<AppExecFwk::EventHandler> JsKeyboardDelegateSetting::GetEventHandler()
642 {
643 std::lock_guard<std::mutex> lock(eventHandlerMutex_);
644 return handler_;
645 }
646
GetEntry(const std::string &type, EntrySetter entrySetter)647 std::shared_ptr<JsKeyboardDelegateSetting::UvEntry> JsKeyboardDelegateSetting::GetEntry(const std::string &type,
648 EntrySetter entrySetter)
649 {
650 IMSA_HILOGD("start, type: %{public}s", type.c_str());
651 std::shared_ptr<UvEntry> entry = nullptr;
652 {
653 std::lock_guard<std::recursive_mutex> lock(mutex_);
654 if (jsCbMap_[type].empty()) {
655 IMSA_HILOGD("%{public}s cb-vector is empty.", type.c_str());
656 return nullptr;
657 }
658 entry = std::make_shared<UvEntry>(jsCbMap_[type], type);
659 }
660 if (entrySetter != nullptr) {
661 entrySetter(*entry);
662 }
663 return entry;
664 }
665 } // namespace MiscServices
666 } // namespace OHOS