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> ¶ms)
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