1 /*
2  * Copyright (c) 2021-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_register_util.h"
17 
18 #include <cinttypes>
19 #include <uv.h>
20 
21 #include "error_multimodal.h"
22 #include "input_manager.h"
23 #include "napi_constants.h"
24 #include "util_napi.h"
25 #include "util_napi_error.h"
26 
27 #undef MMI_LOG_TAG
28 #define MMI_LOG_TAG "JSRegisterUtil"
29 
30 namespace OHOS {
31 namespace MMI {
32 
TypeOf(napi_env env, napi_value value, napi_valuetype type)33 bool TypeOf(napi_env env, napi_value value, napi_valuetype type)
34 {
35     napi_valuetype valueType = napi_undefined;
36     CHKRF(napi_typeof(env, value, &valueType), TYPEOF);
37     if (valueType != type) {
38         return false;
39     }
40     return true;
41 }
42 
SetNamedProperty(const napi_env &env, napi_value &object, const std::string &name, int32_t value)43 void SetNamedProperty(const napi_env &env, napi_value &object, const std::string &name, int32_t value)
44 {
45     MMI_HILOGD("%{public}s=%{public}d", name.c_str(), value);
46     napi_value napiValue;
47     CHKRV(napi_create_int32(env, value, &napiValue), CREATE_INT32);
48     NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, object, name.c_str(), napiValue));
49 }
50 
SetNamedProperty(const napi_env &env, napi_value &object, const std::string &name, std::string value)51 void SetNamedProperty(const napi_env &env, napi_value &object, const std::string &name, std::string value)
52 {
53     MMI_HILOGD("%{public}s=%{public}s", name.c_str(), value.c_str());
54     napi_value napiValue;
55     CHKRV(napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &napiValue), CREATE_STRING_UTF8);
56     NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, object, name.c_str(), napiValue));
57 }
58 
GetNamedPropertyBool(const napi_env &env, const napi_value &object, const std::string &name, bool &ret)59 bool GetNamedPropertyBool(const napi_env &env, const napi_value &object, const std::string &name, bool &ret)
60 {
61     napi_value napiValue = {};
62     bool exist = false;
63     napi_status status = napi_has_named_property(env, object, name.c_str(), &exist);
64     if (status != napi_ok || !exist) {
65         MMI_HILOGD("Can not find %{public}s property", name.c_str());
66         return false;
67     }
68     napi_get_named_property(env, object, name.c_str(), &napiValue);
69     napi_valuetype tmpType = napi_undefined;
70 
71     CHKRF(napi_typeof(env, napiValue, &tmpType), TYPEOF);
72     if (tmpType != napi_boolean) {
73         MMI_HILOGE("The value is not bool");
74         THROWERR_API9(env, COMMON_PARAMETER_ERROR, name.c_str(), "bool");
75         return false;
76     }
77     CHKRF(napi_get_value_bool(env, napiValue, &ret), GET_VALUE_BOOL);
78     MMI_HILOGD("%{public}s=%{public}d", name.c_str(), ret);
79     return true;
80 }
81 
GetNamedPropertyInt32(const napi_env &env, const napi_value &object, const std::string &name)82 std::optional<int32_t> GetNamedPropertyInt32(const napi_env &env, const napi_value &object, const std::string &name)
83 {
84     napi_value napiValue = {};
85     bool exist = false;
86     napi_status status = napi_has_named_property(env, object, name.c_str(), &exist);
87     if (status != napi_ok || !exist) {
88         MMI_HILOGD("Can not find %{public}s property", name.c_str());
89         return std::nullopt;
90     }
91     napi_get_named_property(env, object, name.c_str(), &napiValue);
92     napi_valuetype tmpType = napi_undefined;
93     if (napi_typeof(env, napiValue, &tmpType) != napi_ok) {
94         MMI_HILOGE("Call napi_typeof failed");
95         return std::nullopt;
96     }
97     if (tmpType != napi_number) {
98         MMI_HILOGE("The value is not number");
99         THROWERR_API9(env, COMMON_PARAMETER_ERROR, name.c_str(), "number");
100         return std::nullopt;
101     }
102     int32_t ret = 0;
103     if (napi_get_value_int32(env, napiValue, &ret) != napi_ok) {
104         MMI_HILOGE("Call napi_get_value_int32 failed");
105         return std::nullopt;
106     }
107     MMI_HILOGD("%{public}s=%{public}d", name.c_str(), ret);
108     return std::make_optional(ret);
109 }
110 
GetPreKeys(const napi_env &env, const napi_value &value, std::set<int32_t> &params)111 napi_value GetPreKeys(const napi_env &env, const napi_value &value, std::set<int32_t> &params)
112 {
113     CALL_DEBUG_ENTER;
114     uint32_t arrayLength = 0;
115     CHKRP(napi_get_array_length(env, value, &arrayLength), GET_ARRAY_LENGTH);
116     for (uint32_t i = 0; i < arrayLength; i++) {
117         napi_value napiElement;
118         CHKRP(napi_get_element(env, value, i, &napiElement), GET_ELEMENT);
119         napi_valuetype valuetype;
120         CHKRP(napi_typeof(env, napiElement, &valuetype), TYPEOF);
121         if (valuetype != napi_number) {
122             MMI_HILOGE("PreKeys Wrong argument type, Number expected");
123             THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "element of preKeys must be number");
124             return nullptr;
125         }
126         int32_t value = 0;
127         CHKRP(napi_get_value_int32(env, napiElement, &value), GET_VALUE_INT32);
128         if (value < 0) {
129             MMI_HILOGE("preKey:%{public}d is less 0, can not process", value);
130             THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "element of preKeys must be greater than or equal to 0");
131             return nullptr;
132         }
133         MMI_HILOGD("Get int array number:%{public}d", value);
134         if (!params.insert(value).second) {
135             MMI_HILOGE("Params insert value failed");
136             return nullptr;
137         }
138     }
139     napi_value ret;
140     CHKRP(napi_create_int32(env, RET_OK, &ret), CREATE_INT32);
141     return ret;
142 }
143 
GetPreSubscribeId(Callbacks &callbacks, sptr<KeyEventMonitorInfo> event)144 int32_t GetPreSubscribeId(Callbacks &callbacks, sptr<KeyEventMonitorInfo> event)
145 {
146     CHKPR(event, ERROR_NULL_POINTER);
147     std::lock_guard guard(sCallBacksMutex);
148     auto it = callbacks.find(event->eventType);
149     if (it == callbacks.end() || it->second.empty()) {
150         MMI_HILOGE("The callbacks is empty");
151         return JS_CALLBACK_EVENT_FAILED;
152     }
153     CHKPR(it->second.front(), ERROR_NULL_POINTER);
154     return it->second.front()->subscribeId;
155 }
156 
DelEventCallbackRef(const napi_env &env, std::list<sptr<KeyEventMonitorInfo>> &info, napi_value handler, int32_t &subscribeId)157 int32_t DelEventCallbackRef(const napi_env &env, std::list<sptr<KeyEventMonitorInfo>> &info,
158     napi_value handler, int32_t &subscribeId)
159 {
160     CALL_DEBUG_ENTER;
161     for (auto iter = info.begin(); iter != info.end();) {
162         if (*iter == nullptr) {
163             info.erase(iter++);
164             continue;
165         }
166         if (handler != nullptr) {
167             napi_value iterHandler = nullptr;
168             CHKRR(napi_get_reference_value(env, (*iter)->callback, &iterHandler),
169                   GET_REFERENCE_VALUE, JS_CALLBACK_EVENT_FAILED);
170             bool isEquals = false;
171             CHKRR(napi_strict_equals(env, handler, iterHandler, &isEquals), STRICT_EQUALS, JS_CALLBACK_EVENT_FAILED);
172             if (isEquals) {
173                 sptr<KeyEventMonitorInfo> monitorInfo = *iter;
174                 info.erase(iter++);
175                 if (info.empty()) {
176                     subscribeId = monitorInfo->subscribeId;
177                 }
178                 MMI_HILOGD("Callback has deleted, size:%{public}zu", info.size());
179                 return JS_CALLBACK_EVENT_SUCCESS;
180             }
181             ++iter;
182             continue;
183         }
184         sptr<KeyEventMonitorInfo> monitorInfo = *iter;
185         info.erase(iter++);
186         if (info.empty()) {
187             subscribeId = monitorInfo->subscribeId;
188         }
189         MMI_HILOGD("Callback has deleted, size:%{public}zu", info.size());
190     }
191     MMI_HILOGD("Callback size:%{public}zu", info.size());
192     return JS_CALLBACK_EVENT_SUCCESS;
193 }
194 
AddEventCallback(const napi_env &env, Callbacks &callbacks, sptr<KeyEventMonitorInfo> event)195 int32_t AddEventCallback(const napi_env &env, Callbacks &callbacks, sptr<KeyEventMonitorInfo> event)
196 {
197     CALL_DEBUG_ENTER;
198     std::lock_guard guard(sCallBacksMutex);
199     CHKPR(event, ERROR_NULL_POINTER);
200     if (callbacks.find(event->eventType) == callbacks.end()) {
201         MMI_HILOGD("No callback in %{public}s", event->eventType.c_str());
202         callbacks[event->eventType] = {};
203     }
204     napi_value handler1 = nullptr;
205     napi_status status = napi_get_reference_value(env, event->callback, &handler1);
206     if (status != napi_ok) {
207         MMI_HILOGE("Handler1 get reference value failed");
208         return JS_CALLBACK_EVENT_FAILED;
209     }
210     auto it = callbacks.find(event->eventType);
211     for (const auto &iter: it->second) {
212         napi_value handler2 = nullptr;
213         status = napi_get_reference_value(env, iter->callback, &handler2);
214         if (status != napi_ok) {
215             MMI_HILOGE("Handler2 get reference value failed");
216             return JS_CALLBACK_EVENT_FAILED;
217         }
218         bool isEqual = false;
219         status = napi_strict_equals(env, handler1, handler2, &isEqual);
220         if (status != napi_ok) {
221             MMI_HILOGE("Compare two handler failed");
222             return JS_CALLBACK_EVENT_FAILED;
223         }
224         if (isEqual) {
225             MMI_HILOGE("Callback already exist");
226             return JS_CALLBACK_EVENT_FAILED;
227         }
228     }
229     it->second.push_back(event);
230     return JS_CALLBACK_EVENT_SUCCESS;
231 }
232 
DelEventCallback(const napi_env &env, Callbacks &callbacks, sptr<KeyEventMonitorInfo> event, int32_t &subscribeId)233 int32_t DelEventCallback(const napi_env &env, Callbacks &callbacks, sptr<KeyEventMonitorInfo> event,
234     int32_t &subscribeId)
235 {
236     CALL_DEBUG_ENTER;
237     std::lock_guard guard(sCallBacksMutex);
238     CHKPR(event, ERROR_NULL_POINTER);
239     if (callbacks.count(event->eventType) <= 0) {
240         MMI_HILOGE("Callback doesn't exists");
241         return JS_CALLBACK_EVENT_FAILED;
242     }
243     auto &info = callbacks[event->eventType];
244     MMI_HILOGD("EventType:%{private}s, keyEventMonitorInfos:%{public}zu", event->eventType.c_str(), info.size());
245     napi_value eventHandler = nullptr;
246     if (event->callback != nullptr) {
247         CHKRR(napi_get_reference_value(env, event->callback, &eventHandler), GET_REFERENCE_VALUE,
248               JS_CALLBACK_EVENT_FAILED);
249     }
250     return DelEventCallbackRef(env, info, eventHandler, subscribeId);
251 }
252 
AsyncWorkFn(const napi_env &env, std::shared_ptr<KeyOption> keyOption, napi_value &result, std::string keyType)253 static void AsyncWorkFn(const napi_env &env, std::shared_ptr<KeyOption> keyOption, napi_value &result,
254     std::string keyType)
255 {
256     CHKPV(keyOption);
257     MMI_HILOGD("Status > 0 enter");
258     CHKRV(napi_create_object(env, &result), CREATE_OBJECT);
259     napi_value arr;
260     CHKRV(napi_create_array(env, &arr), CREATE_ARRAY);
261     std::set <int32_t> preKeys = keyOption->GetPreKeys();
262     int32_t i = 0;
263     napi_value value;
264     for (const auto &preKey: preKeys) {
265         CHKRV(napi_create_int32(env, preKey, &value), CREATE_INT32);
266         CHKRV(napi_set_element(env, arr, i, value), SET_ELEMENT);
267         ++i;
268     }
269     std::string preKeysStr = "preKeys";
270     NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, result, preKeysStr.c_str(), arr));
271     MMI::SetNamedProperty(env, result, "finalKey", keyOption->GetFinalKey());
272     if (keyType == SUBSCRIBE_TYPE) {
273         MMI::SetNamedProperty(env, result, "isFinalKeyDown", keyOption->IsFinalKeyDown());
274         MMI::SetNamedProperty(env, result, "finalKeyDownDuration", keyOption->GetFinalKeyDownDuration());
275     }
276     MMI::SetNamedProperty(env, result, "isRepeat", static_cast<int32_t>(keyOption->IsRepeat()));
277 }
278 
UvQueueWorkAsyncCallback(uv_work_t *work, int32_t status)279 void UvQueueWorkAsyncCallback(uv_work_t *work, int32_t status)
280 {
281     CALL_DEBUG_ENTER;
282     CHKPV(work);
283     if (work->data == nullptr) {
284         DeletePtr<uv_work_t *>(work);
285         MMI_HILOGE("Check data is nullptr");
286         return;
287     }
288     (void)status;
289     sptr<KeyEventMonitorInfo> dataWorker(static_cast<KeyEventMonitorInfo *>(work->data));
290     DeletePtr<uv_work_t *>(work);
291     dataWorker->DecStrongRef(nullptr);
292     CHKPV(dataWorker->env);
293     napi_handle_scope scope = nullptr;
294     napi_open_handle_scope(dataWorker->env, &scope);
295     if (scope == nullptr) {
296         MMI_HILOGE("Scope is nullptr");
297         return;
298     }
299     napi_value callback = nullptr;
300     MMI_HILOGD("deliver uv work from %{public}d", GetPid());
301     if (dataWorker->callback == nullptr) {
302         MMI_HILOGE("dataWorker->callback is nullptr");
303         napi_close_handle_scope(dataWorker->env, scope);
304         return;
305     }
306     if ((napi_get_reference_value(dataWorker->env, dataWorker->callback, &callback)) != napi_ok) {
307         MMI_HILOGE("%{public}s failed", std::string(GET_REFERENCE_VALUE).c_str());
308         napi_close_handle_scope(dataWorker->env, scope);
309         return;
310     }
311     napi_value result = nullptr;
312     AsyncWorkFn(dataWorker->env, dataWorker->keyOption, result, dataWorker->name);
313     napi_value callResult = nullptr;
314     if ((napi_call_function(dataWorker->env, nullptr, callback, 1, &result, &callResult)) != napi_ok) {
315         MMI_HILOGE("%{public}s failed", std::string(CALL_FUNCTION).c_str());
316         napi_close_handle_scope(dataWorker->env, scope);
317         return;
318     }
319     napi_close_handle_scope(dataWorker->env, scope);
320 }
321 
EmitAsyncCallbackWork(sptr<KeyEventMonitorInfo> reportEvent)322 void EmitAsyncCallbackWork(sptr<KeyEventMonitorInfo> reportEvent)
323 {
324     CALL_DEBUG_ENTER;
325     CHKPV(reportEvent);
326     uv_loop_s *loop = nullptr;
327     CHKRV(napi_get_uv_event_loop(reportEvent->env, &loop), GET_UV_EVENT_LOOP);
328     uv_work_t *work = new (std::nothrow) uv_work_t;
329     if (work == nullptr) {
330         MMI_HILOGE("work is nullptr");
331         return;
332     }
333     reportEvent->IncStrongRef(nullptr);
334     work->data = reportEvent.GetRefPtr();
335     int32_t ret = uv_queue_work_with_qos(
336         loop, work,
337         [](uv_work_t *work) {
338             MMI_HILOGD("uv_queue_work callback function is called");
339         },
340         UvQueueWorkAsyncCallback, uv_qos_user_initiated);
341     if (ret != 0) {
342         DeletePtr<uv_work_t *>(work);
343     }
344 }
345 
ConvertHotkeyToNapiValue(napi_env env, std::unique_ptr<KeyOption> &keyOption)346 napi_value ConvertHotkeyToNapiValue(napi_env env, std::unique_ptr<KeyOption> &keyOption)
347 {
348     napi_value obj = nullptr;
349     CHKRP(napi_create_object(env, &obj), CREATE_OBJECT);
350     napi_value preKeysArray = nullptr;
351     CHKRP(napi_create_array(env, &preKeysArray), CREATE_ARRAY);
352     int32_t index = 0;
353     std::set<int32_t> preKeys = keyOption->GetPreKeys();
354     for (auto key : preKeys) {
355         napi_value keyVal = nullptr;
356         CHKRP(napi_create_int32(env, key, &keyVal), CREATE_INT32);
357         CHKRP(napi_set_element(env, preKeysArray, index++, keyVal), SET_ELEMENT);
358     }
359     CHKRP(napi_set_named_property(env, obj, "preKeys", preKeysArray), SET_NAMED_PROPERTY);
360     napi_value finalKeyVal = nullptr;
361     CHKRP(napi_create_int32(env, keyOption->GetFinalKey(), &finalKeyVal), CREATE_INT32);
362     CHKRP(napi_set_named_property(env, obj, "finalKey", finalKeyVal), SET_NAMED_PROPERTY);
363     return obj;
364 }
365 
ConvertHotkeysToNapiArray(sptr<CallbackInfo> cb)366 napi_value ConvertHotkeysToNapiArray(sptr<CallbackInfo> cb)
367 {
368     napi_value keyOptionArray = nullptr;
369     CHKRP(napi_create_array(cb->env, &keyOptionArray), CREATE_ARRAY);
370     if (cb->errCode != RET_OK) {
371         MMI_HILOGE("Get Hotkeys failed, errCode:%{public}d", cb->errCode);
372         return keyOptionArray;
373     }
374     for (size_t i = 0; i < cb->keyOptions.size(); ++i) {
375         napi_value obj = ConvertHotkeyToNapiValue(cb->env, cb->keyOptions[i]);
376         if (obj == nullptr) {
377             MMI_HILOGE("ConvertHotkeyToNapiValue fail");
378             return keyOptionArray;
379         }
380         CHKRP(napi_set_element(cb->env, keyOptionArray, i, obj), SET_ELEMENT);
381     }
382     return keyOptionArray;
383 }
384 
GreateBusinessError(napi_env env, int32_t errCode, std::string errMessage)385 napi_value GreateBusinessError(napi_env env, int32_t errCode, std::string errMessage)
386 {
387     CALL_DEBUG_ENTER;
388     napi_value result = nullptr;
389     napi_value resultCode = nullptr;
390     napi_value resultMessage = nullptr;
391     CHKRP(napi_create_int32(env, errCode, &resultCode), CREATE_INT32);
392     CHKRP(napi_create_string_utf8(env, errMessage.data(), NAPI_AUTO_LENGTH, &resultMessage), CREATE_STRING_UTF8);
393     CHKRP(napi_create_error(env, nullptr, resultMessage, &result), CREATE_ERROR);
394     CHKRP(napi_set_named_property(env, result, ERR_CODE.c_str(), resultCode), SET_NAMED_PROPERTY);
395     return result;
396 }
397 
CallHotkeyPromiseWork(uv_work_t *work, int32_t status)398 void CallHotkeyPromiseWork(uv_work_t *work, int32_t status)
399 {
400     CALL_DEBUG_ENTER;
401     CHKPV(work);
402     if (work->data == nullptr) {
403         DeletePtr<uv_work_t *>(work);
404         MMI_HILOGE("Check data is nullptr");
405         return;
406     }
407     sptr<CallbackInfo> cb(static_cast<CallbackInfo *>(work->data));
408     DeletePtr<uv_work_t *>(work);
409     cb->DecStrongRef(nullptr);
410     CHKPV(cb->env);
411     napi_handle_scope scope = nullptr;
412     napi_open_handle_scope(cb->env, &scope);
413     CHKPV(scope);
414     napi_value callResult = ConvertHotkeysToNapiArray(cb);
415     if (callResult == nullptr) {
416         MMI_HILOGE("Check callResult is nullptr");
417         napi_close_handle_scope(cb->env, scope);
418         return;
419     }
420     CHKRV_SCOPE(cb->env, napi_resolve_deferred(cb->env, cb->deferred, callResult), RESOLVE_DEFERRED, scope);
421     napi_close_handle_scope(cb->env, scope);
422 }
423 
EmitSystemHotkey(sptr<CallbackInfo> cb)424 void EmitSystemHotkey(sptr<CallbackInfo> cb)
425 {
426     CALL_DEBUG_ENTER;
427     CHKPV(cb);
428     CHKPV(cb->env);
429     uv_loop_s *loop = nullptr;
430     CHKRV(napi_get_uv_event_loop(cb->env, &loop), GET_UV_EVENT_LOOP);
431     uv_work_t *work = new (std::nothrow) uv_work_t;
432     CHKPV(work);
433     cb->IncStrongRef(nullptr);
434     work->data = cb.GetRefPtr();
435     int32_t ret = 0;
436     ret = uv_queue_work_with_qos(
437         loop, work,
438         [](uv_work_t *work) {
439             MMI_HILOGD("uv_queue_work CallHotkeyPromiseWork callback function is called");
440         }, CallHotkeyPromiseWork, uv_qos_user_initiated);
441     if (ret != 0) {
442         MMI_HILOGE("uv_queue_work_with_qos failed");
443         cb->DecStrongRef(nullptr);
444         DeletePtr<uv_work_t *>(work);
445     }
446 }
447 
GetSystemHotkey(napi_env env, napi_value handle)448 napi_value GetSystemHotkey(napi_env env, napi_value handle)
449 {
450     CALL_DEBUG_ENTER;
451     sptr<CallbackInfo> cb = new (std::nothrow) CallbackInfo();
452     CHKPP(cb);
453     cb->env = env;
454     napi_value promise = nullptr;
455     CHKRP(napi_create_promise(env, &cb->deferred, &promise), CREATE_PROMISE);
456     std::vector<std::unique_ptr<KeyOption>> keyOptions;
457     int32_t count = 0;
458     cb->errCode = InputManager::GetInstance()->GetAllSystemHotkeys(keyOptions, count);
459     cb->keyOptions = std::move(keyOptions);
460     EmitSystemHotkey(cb);
461     return promise;
462 }
463 } // namespace MMI
464 } // namespace OHOS