1 /*
2  * Copyright (c) 2021-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_register_util.h"
17 
18 #include <cinttypes>
19 #include <uv.h>
20 
21 #include "error_multimodal.h"
22 #include "napi_constants.h"
23 #include "util_napi.h"
24 #include "util_napi_error.h"
25 
26 #undef MMI_LOG_TAG
27 #define MMI_LOG_TAG "JSRegisterUtil"
28 
29 namespace OHOS {
30 namespace MMI {
31 
SetNamedProperty(const napi_env & env,napi_value & object,const std::string & name,int32_t value)32 void SetNamedProperty(const napi_env &env, napi_value &object, const std::string &name, int32_t value)
33 {
34     MMI_HILOGD("%{public}s=%{public}d", name.c_str(), value);
35     napi_value napiValue;
36     CHKRV(napi_create_int32(env, value, &napiValue), CREATE_INT32);
37     NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, object, name.c_str(), napiValue));
38 }
39 
SetNamedProperty(const napi_env & env,napi_value & object,const std::string & name,std::string value)40 void SetNamedProperty(const napi_env &env, napi_value &object, const std::string &name, std::string value)
41 {
42     MMI_HILOGD("%{public}s=%{public}s", name.c_str(), value.c_str());
43     napi_value napiValue;
44     CHKRV(napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &napiValue), CREATE_STRING_UTF8);
45     NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, object, name.c_str(), napiValue));
46 }
47 
GetNamedPropertyBool(const napi_env & env,const napi_value & object,const std::string & name,bool & ret)48 bool GetNamedPropertyBool(const napi_env &env, const napi_value &object, const std::string &name, bool &ret)
49 {
50     napi_value napiValue = {};
51     napi_get_named_property(env, object, name.c_str(), &napiValue);
52     napi_valuetype tmpType = napi_undefined;
53 
54     CHKRF(napi_typeof(env, napiValue, &tmpType), TYPEOF);
55     if (tmpType != napi_boolean) {
56         MMI_HILOGE("The value is not bool");
57         THROWERR_API9(env, COMMON_PARAMETER_ERROR, name.c_str(), "bool");
58         return false;
59     }
60     CHKRF(napi_get_value_bool(env, napiValue, &ret), GET_VALUE_BOOL);
61     MMI_HILOGD("%{public}s=%{public}d", name.c_str(), ret);
62     return true;
63 }
64 
GetNamedPropertyInt32(const napi_env & env,const napi_value & object,const std::string & name)65 std::optional<int32_t> GetNamedPropertyInt32(const napi_env &env, const napi_value &object, const std::string &name)
66 {
67     napi_value napiValue = {};
68     napi_get_named_property(env, object, name.c_str(), &napiValue);
69     napi_valuetype tmpType = napi_undefined;
70     if (napi_typeof(env, napiValue, &tmpType) != napi_ok) {
71         MMI_HILOGE("Call napi_typeof failed");
72         return std::nullopt;
73     }
74     if (tmpType != napi_number) {
75         MMI_HILOGE("The value is not number");
76         THROWERR_API9(env, COMMON_PARAMETER_ERROR, name.c_str(), "number");
77         return std::nullopt;
78     }
79     int32_t ret = 0;
80     if (napi_get_value_int32(env, napiValue, &ret) != napi_ok) {
81         MMI_HILOGE("Call napi_get_value_int32 failed");
82         return std::nullopt;
83     }
84     MMI_HILOGD("%{public}s=%{public}d", name.c_str(), ret);
85     return std::make_optional(ret);
86 }
87 
GetPreKeys(const napi_env & env,const napi_value & value,std::set<int32_t> & params)88 napi_value GetPreKeys(const napi_env &env, const napi_value &value, std::set<int32_t> &params)
89 {
90     CALL_DEBUG_ENTER;
91     uint32_t arrayLength = 0;
92     CHKRP(napi_get_array_length(env, value, &arrayLength), GET_ARRAY_LENGTH);
93     for (uint32_t i = 0; i < arrayLength; i++) {
94         napi_value napiElement;
95         CHKRP(napi_get_element(env, value, i, &napiElement), GET_ELEMENT);
96         napi_valuetype valuetype;
97         CHKRP(napi_typeof(env, napiElement, &valuetype), TYPEOF);
98         if (valuetype != napi_number) {
99             MMI_HILOGE("PreKeys Wrong argument type, Number expected");
100             THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "element of preKeys must be number");
101             return nullptr;
102         }
103         int32_t value = 0;
104         CHKRP(napi_get_value_int32(env, napiElement, &value), GET_VALUE_INT32);
105         if (value < 0) {
106             MMI_HILOGE("preKey:%{public}d is less 0, can not process", value);
107             THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "element of preKeys must be greater than or equal to 0");
108             return nullptr;
109         }
110         MMI_HILOGD("Get int array number:%{public}d", value);
111         if (!params.insert(value).second) {
112             MMI_HILOGE("Params insert value failed");
113             return nullptr;
114         }
115     }
116     napi_value ret;
117     CHKRP(napi_create_int32(env, RET_OK, &ret), CREATE_INT32);
118     return ret;
119 }
120 
GetPreSubscribeId(Callbacks & callbacks,sptr<KeyEventMonitorInfo> event)121 int32_t GetPreSubscribeId(Callbacks &callbacks, sptr<KeyEventMonitorInfo> event)
122 {
123     CHKPR(event, ERROR_NULL_POINTER);
124     std::lock_guard guard(sCallBacksMutex);
125     auto it = callbacks.find(event->eventType);
126     if (it == callbacks.end() || it->second.empty()) {
127         MMI_HILOGE("The callbacks is empty");
128         return JS_CALLBACK_EVENT_FAILED;
129     }
130     CHKPR(it->second.front(), ERROR_NULL_POINTER);
131     return it->second.front()->subscribeId;
132 }
133 
DelEventCallbackRef(const napi_env & env,std::list<sptr<KeyEventMonitorInfo>> & info,napi_value handler,int32_t & subscribeId)134 int32_t DelEventCallbackRef(const napi_env &env, std::list<sptr<KeyEventMonitorInfo>> &info,
135     napi_value handler, int32_t &subscribeId)
136 {
137     CALL_DEBUG_ENTER;
138     for (auto iter = info.begin(); iter != info.end();) {
139         if (*iter == nullptr) {
140             info.erase(iter++);
141             continue;
142         }
143         if (handler != nullptr) {
144             napi_value iterHandler = nullptr;
145             CHKRR(napi_get_reference_value(env, (*iter)->callback, &iterHandler),
146                   GET_REFERENCE_VALUE, JS_CALLBACK_EVENT_FAILED);
147             bool isEquals = false;
148             CHKRR(napi_strict_equals(env, handler, iterHandler, &isEquals), STRICT_EQUALS, JS_CALLBACK_EVENT_FAILED);
149             if (isEquals) {
150                 sptr<KeyEventMonitorInfo> monitorInfo = *iter;
151                 info.erase(iter++);
152                 if (info.empty()) {
153                     subscribeId = monitorInfo->subscribeId;
154                 }
155                 MMI_HILOGD("Callback has deleted, size:%{public}zu", info.size());
156                 return JS_CALLBACK_EVENT_SUCCESS;
157             }
158             ++iter;
159             continue;
160         }
161         sptr<KeyEventMonitorInfo> monitorInfo = *iter;
162         info.erase(iter++);
163         if (info.empty()) {
164             subscribeId = monitorInfo->subscribeId;
165         }
166         MMI_HILOGD("Callback has deleted, size:%{public}zu", info.size());
167     }
168     MMI_HILOGD("Callback size:%{public}zu", info.size());
169     return JS_CALLBACK_EVENT_SUCCESS;
170 }
171 
AddEventCallback(const napi_env & env,Callbacks & callbacks,sptr<KeyEventMonitorInfo> event)172 int32_t AddEventCallback(const napi_env &env, Callbacks &callbacks, sptr<KeyEventMonitorInfo> event)
173 {
174     CALL_DEBUG_ENTER;
175     std::lock_guard guard(sCallBacksMutex);
176     CHKPR(event, ERROR_NULL_POINTER);
177     if (callbacks.find(event->eventType) == callbacks.end()) {
178         MMI_HILOGD("No callback in %{public}s", event->eventType.c_str());
179         callbacks[event->eventType] = {};
180     }
181     napi_value handler1 = nullptr;
182     napi_status status = napi_get_reference_value(env, event->callback, &handler1);
183     if (status != napi_ok) {
184         MMI_HILOGE("Handler1 get reference value failed");
185         return JS_CALLBACK_EVENT_FAILED;
186     }
187     auto it = callbacks.find(event->eventType);
188     for (const auto &iter: it->second) {
189         napi_value handler2 = nullptr;
190         status = napi_get_reference_value(env, iter->callback, &handler2);
191         if (status != napi_ok) {
192             MMI_HILOGE("Handler2 get reference value failed");
193             return JS_CALLBACK_EVENT_FAILED;
194         }
195         bool isEqual = false;
196         status = napi_strict_equals(env, handler1, handler2, &isEqual);
197         if (status != napi_ok) {
198             MMI_HILOGE("Compare two handler failed");
199             return JS_CALLBACK_EVENT_FAILED;
200         }
201         if (isEqual) {
202             MMI_HILOGE("Callback already exist");
203             return JS_CALLBACK_EVENT_FAILED;
204         }
205     }
206     it->second.push_back(event);
207     return JS_CALLBACK_EVENT_SUCCESS;
208 }
209 
DelEventCallback(const napi_env & env,Callbacks & callbacks,sptr<KeyEventMonitorInfo> event,int32_t & subscribeId)210 int32_t DelEventCallback(const napi_env &env, Callbacks &callbacks, sptr<KeyEventMonitorInfo> event,
211     int32_t &subscribeId)
212 {
213     CALL_DEBUG_ENTER;
214     std::lock_guard guard(sCallBacksMutex);
215     CHKPR(event, ERROR_NULL_POINTER);
216     if (callbacks.count(event->eventType) <= 0) {
217         MMI_HILOGE("Callback doesn't exists");
218         return JS_CALLBACK_EVENT_FAILED;
219     }
220     auto &info = callbacks[event->eventType];
221     MMI_HILOGD("EventType:%{public}s, keyEventMonitorInfos:%{public}zu", event->eventType.c_str(), info.size());
222     napi_value eventHandler = nullptr;
223     if (event->callback != nullptr) {
224         CHKRR(napi_get_reference_value(env, event->callback, &eventHandler), GET_REFERENCE_VALUE,
225               JS_CALLBACK_EVENT_FAILED);
226     }
227     return DelEventCallbackRef(env, info, eventHandler, subscribeId);
228 }
229 
AsyncWorkFn(const napi_env & env,std::shared_ptr<KeyOption> keyOption,napi_value & result)230 static void AsyncWorkFn(const napi_env &env, std::shared_ptr<KeyOption> keyOption, napi_value &result)
231 {
232     CHKPV(keyOption);
233     MMI_HILOGD("Status > 0 enter");
234     CHKRV(napi_create_object(env, &result), CREATE_OBJECT);
235     napi_value arr;
236     CHKRV(napi_create_array(env, &arr), CREATE_ARRAY);
237     std::set <int32_t> preKeys = keyOption->GetPreKeys();
238     int32_t i = 0;
239     napi_value value;
240     for (const auto &preKey: preKeys) {
241         CHKRV(napi_create_int32(env, preKey, &value), CREATE_INT32);
242         CHKRV(napi_set_element(env, arr, i, value), SET_ELEMENT);
243         ++i;
244     }
245     std::string preKeysStr = "preKeys";
246     NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, result, preKeysStr.c_str(), arr));
247     MMI::SetNamedProperty(env, result, "finalKey", keyOption->GetFinalKey());
248     MMI::SetNamedProperty(env, result, "isFinalKeyDown", keyOption->IsFinalKeyDown());
249     MMI::SetNamedProperty(env, result, "finalKeyDownDuration", keyOption->GetFinalKeyDownDuration());
250 }
251 
UvQueueWorkAsyncCallback(uv_work_t * work,int32_t status)252 void UvQueueWorkAsyncCallback(uv_work_t *work, int32_t status)
253 {
254     CALL_DEBUG_ENTER;
255     CHKPV(work);
256     if (work->data == nullptr) {
257         JsUtil::DeletePtr<uv_work_t *>(work);
258         MMI_HILOGE("Check data is nullptr");
259         return;
260     }
261     (void)status;
262     sptr<KeyEventMonitorInfo> dataWorker(static_cast<KeyEventMonitorInfo *>(work->data));
263     JsUtil::DeletePtr<uv_work_t *>(work);
264     dataWorker->DecStrongRef(nullptr);
265     CHKPV(dataWorker->env);
266     napi_handle_scope scope = nullptr;
267     napi_open_handle_scope(dataWorker->env, &scope);
268     if (scope == nullptr) {
269         MMI_HILOGE("Scope is nullptr");
270         return;
271     }
272     napi_value callback = nullptr;
273     MMI_HILOGD("deliver uv work from %{public}d", GetPid());
274     if (dataWorker->callback == nullptr) {
275         MMI_HILOGE("dataWorker->callback is nullptr");
276         napi_close_handle_scope(dataWorker->env, scope);
277         return;
278     }
279     if ((napi_get_reference_value(dataWorker->env, dataWorker->callback, &callback)) != napi_ok) {
280         MMI_HILOGE("%{public}s failed", std::string(GET_REFERENCE_VALUE).c_str());
281         napi_close_handle_scope(dataWorker->env, scope);
282         return;
283     }
284     napi_value result = nullptr;
285     AsyncWorkFn(dataWorker->env, dataWorker->keyOption, result);
286     napi_value callResult = nullptr;
287     if ((napi_call_function(dataWorker->env, nullptr, callback, 1, &result, &callResult)) != napi_ok) {
288         MMI_HILOGE("%{public}s failed", std::string(CALL_FUNCTION).c_str());
289         napi_close_handle_scope(dataWorker->env, scope);
290         return;
291     }
292     napi_close_handle_scope(dataWorker->env, scope);
293 }
294 
EmitAsyncCallbackWork(sptr<KeyEventMonitorInfo> reportEvent)295 void EmitAsyncCallbackWork(sptr<KeyEventMonitorInfo> reportEvent)
296 {
297     CALL_DEBUG_ENTER;
298     CHKPV(reportEvent);
299     uv_loop_s *loop = nullptr;
300     CHKRV(napi_get_uv_event_loop(reportEvent->env, &loop), GET_UV_EVENT_LOOP);
301     uv_work_t *work = new (std::nothrow) uv_work_t;
302     if (work == nullptr) {
303         MMI_HILOGE("work is nullptr");
304         return;
305     }
306     reportEvent->IncStrongRef(nullptr);
307     work->data = reportEvent.GetRefPtr();
308     int32_t ret = uv_queue_work_with_qos(
309         loop, work,
310         [](uv_work_t *work) {
311             MMI_HILOGD("uv_queue_work callback function is called");
312         },
313         UvQueueWorkAsyncCallback, uv_qos_user_initiated);
314     if (ret != 0) {
315         JsUtil::DeletePtr<uv_work_t *>(work);
316     }
317 }
318 } // namespace MMI
319 } // namespace OHOS