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 namespace OHOS {
27 namespace MMI {
28 namespace {
29 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, MMI_LOG_DOMAIN, "JSRegisterUtil" };
30 } // namespace
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;
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,KeyEventMonitorInfo * event)121 int32_t GetPreSubscribeId(Callbacks &callbacks, KeyEventMonitorInfo *event)
122 {
123 CHKPR(event, ERROR_NULL_POINTER);
124 auto it = callbacks.find(event->eventType);
125 if (it == callbacks.end() || it->second.empty()) {
126 MMI_HILOGE("The callbacks is empty");
127 return JS_CALLBACK_EVENT_FAILED;
128 }
129 CHKPR(it->second.front(), ERROR_NULL_POINTER);
130 return it->second.front()->subscribeId;
131 }
132
DelEventCallbackRef(const napi_env & env,std::list<KeyEventMonitorInfo * > & info,napi_value handler,int32_t & subscribeId)133 int32_t DelEventCallbackRef(const napi_env &env, std::list<KeyEventMonitorInfo *> &info,
134 napi_value handler, int32_t &subscribeId)
135 {
136 CALL_DEBUG_ENTER;
137 for (auto iter = info.begin(); iter != info.end();) {
138 if (*iter == nullptr) {
139 info.erase(iter++);
140 continue;
141 }
142 if (handler != nullptr) {
143 napi_value iterHandler = nullptr;
144 CHKRR(napi_get_reference_value(env, (*iter)->callback[0], &iterHandler),
145 GET_REFERENCE_VALUE, JS_CALLBACK_EVENT_FAILED);
146 bool isEquals = false;
147 CHKRR(napi_strict_equals(env, handler, iterHandler, &isEquals), STRICT_EQUALS, JS_CALLBACK_EVENT_FAILED);
148 if (isEquals) {
149 CHKRR(napi_delete_reference(env, (*iter)->callback[0]), DELETE_REFERENCE, JS_CALLBACK_EVENT_FAILED);
150 if ((*iter)->callback[0] != nullptr) {
151 MMI_HILOGW("current (*iter)->callback[0] is not nullptr");
152 (*iter)->callback[0] = nullptr;
153 }
154 KeyEventMonitorInfo *monitorInfo = *iter;
155 info.erase(iter++);
156 if (info.empty()) {
157 subscribeId = monitorInfo->subscribeId;
158 }
159 delete monitorInfo;
160 monitorInfo = nullptr;
161 MMI_HILOGD("Callback has deleted, size:%{public}zu", info.size());
162 return JS_CALLBACK_EVENT_SUCCESS;
163 }
164 ++iter;
165 continue;
166 }
167 CHKRR(napi_delete_reference(env, (*iter)->callback[0]), DELETE_REFERENCE, JS_CALLBACK_EVENT_FAILED);
168 if ((*iter)->callback[0] != nullptr) {
169 MMI_HILOGW("(*iter)->callback[0] is not nullptr");
170 (*iter)->callback[0] = nullptr;
171 }
172 KeyEventMonitorInfo *monitorInfo = *iter;
173 info.erase(iter++);
174 if (info.empty()) {
175 subscribeId = monitorInfo->subscribeId;
176 }
177 delete monitorInfo;
178 monitorInfo = nullptr;
179 MMI_HILOGD("Callback has deleted, size:%{public}zu", info.size());
180 }
181 MMI_HILOGD("Callback size:%{public}zu", info.size());
182 return JS_CALLBACK_EVENT_SUCCESS;
183 }
184
AddEventCallback(const napi_env & env,Callbacks & callbacks,KeyEventMonitorInfo * event)185 int32_t AddEventCallback(const napi_env &env, Callbacks &callbacks, KeyEventMonitorInfo *event)
186 {
187 CALL_DEBUG_ENTER;
188 CHKPR(event, ERROR_NULL_POINTER);
189 if (callbacks.find(event->eventType) == callbacks.end()) {
190 MMI_HILOGD("No callback in %{public}s", event->eventType.c_str());
191 callbacks[event->eventType] = {};
192 }
193 napi_value handler1 = nullptr;
194 napi_status status = napi_get_reference_value(env, event->callback[0], &handler1);
195 if (status != napi_ok) {
196 MMI_HILOGE("Handler1 get reference value failed");
197 return JS_CALLBACK_EVENT_FAILED;
198 }
199 auto it = callbacks.find(event->eventType);
200 for (const auto &iter : it->second) {
201 napi_value handler2 = nullptr;
202 status = napi_get_reference_value(env, (*iter).callback[0], &handler2);
203 if (status != napi_ok) {
204 MMI_HILOGE("Handler2 get reference value failed");
205 return JS_CALLBACK_EVENT_FAILED;
206 }
207 bool isEqual = false;
208 status = napi_strict_equals(env, handler1, handler2, &isEqual);
209 if (status != napi_ok) {
210 MMI_HILOGE("Compare two handler failed");
211 return JS_CALLBACK_EVENT_FAILED;
212 }
213 if (isEqual) {
214 MMI_HILOGE("Callback already exist");
215 return JS_CALLBACK_EVENT_FAILED;
216 }
217 }
218 it->second.push_back(event);
219 return JS_CALLBACK_EVENT_SUCCESS;
220 }
221
DelEventCallback(const napi_env & env,Callbacks & callbacks,KeyEventMonitorInfo * event,int32_t & subscribeId)222 int32_t DelEventCallback(const napi_env &env, Callbacks &callbacks, KeyEventMonitorInfo *event, int32_t &subscribeId)
223 {
224 CALL_DEBUG_ENTER;
225 CHKPR(event, ERROR_NULL_POINTER);
226 if (callbacks.count(event->eventType) <= 0) {
227 MMI_HILOGE("Callback doesn't exists");
228 return JS_CALLBACK_EVENT_FAILED;
229 }
230 auto &info = callbacks[event->eventType];
231 MMI_HILOGD("EventType:%{public}s, keyEventMonitorInfos:%{public}zu", event->eventType.c_str(), info.size());
232 napi_value eventHandler = nullptr;
233 if (event->callback[0] != nullptr) {
234 CHKRR(napi_get_reference_value(env, event->callback[0], &eventHandler), GET_REFERENCE_VALUE,
235 JS_CALLBACK_EVENT_FAILED);
236 }
237 return DelEventCallbackRef(env, info, eventHandler, subscribeId);
238 }
239
AsyncWorkFn(const napi_env & env,KeyEventMonitorInfo * event,napi_value & result)240 static void AsyncWorkFn(const napi_env &env, KeyEventMonitorInfo *event, napi_value &result)
241 {
242 CHKPV(event);
243 CHKPV(event->keyOption);
244 MMI_HILOGD("Status > 0 enter");
245 CHKRV(napi_create_object(env, &result), CREATE_OBJECT);
246 napi_value arr;
247 CHKRV(napi_create_array(env, &arr), CREATE_ARRAY);
248 std::set<int32_t> preKeys = event->keyOption->GetPreKeys();
249 int32_t i = 0;
250 napi_value value;
251 for (const auto &preKey : preKeys) {
252 CHKRV(napi_create_int32(env, preKey, &value), CREATE_INT32);
253 CHKRV(napi_set_element(env, arr, i, value), SET_ELEMENT);
254 ++i;
255 }
256 std::string preKeysStr = "preKeys";
257 NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, result, preKeysStr.c_str(), arr));
258 MMI::SetNamedProperty(env, result, "finalKey", event->keyOption->GetFinalKey());
259 MMI::SetNamedProperty(env, result, "isFinalKeyDown", event->keyOption->IsFinalKeyDown());
260 MMI::SetNamedProperty(env, result, "finalKeyDownDuration", event->keyOption->GetFinalKeyDownDuration());
261 }
262
263 struct KeyEventMonitorInfoWorker {
264 napi_env env { nullptr };
265 KeyEventMonitorInfo *reportEvent { nullptr };
266 };
267
UvQueueWorkAsyncCallback(uv_work_t * work,int32_t status)268 void UvQueueWorkAsyncCallback(uv_work_t *work, int32_t status)
269 {
270 CALL_DEBUG_ENTER;
271 CHKPV(work);
272 if (work->data == nullptr) {
273 MMI_HILOGE("Check data is nullptr");
274 delete work;
275 work = nullptr;
276 return;
277 }
278 (void)status;
279 KeyEventMonitorInfoWorker *dataWorker = static_cast<KeyEventMonitorInfoWorker *>(work->data);
280 delete work;
281 work = nullptr;
282 KeyEventMonitorInfo *event = dataWorker->reportEvent;
283 napi_env env = dataWorker->env;
284 delete dataWorker;
285 dataWorker = nullptr;
286 CHKPV(event);
287 event->delCallback = nullptr;
288 napi_handle_scope scope = nullptr;
289 napi_open_handle_scope(env, &scope);
290 if (scope == nullptr) {
291 MMI_HILOGE("Scope is nullptr");
292 return;
293 }
294 napi_value callback = nullptr;
295 MMI_HILOGD("deliver uv work from %{public}d", GetPid());
296 if (event->callback[0] == nullptr) {
297 MMI_HILOGE("event->callback[0] is nullptr");
298 napi_close_handle_scope(env, scope);
299 return;
300 }
301 CHKRV_SCOPE(env, napi_get_reference_value(env, event->callback[0], &callback), GET_REFERENCE_VALUE, scope);
302 napi_value result = nullptr;
303 AsyncWorkFn(env, event, result);
304 napi_value callResult = nullptr;
305 CHKRV_SCOPE(env, napi_call_function(env, nullptr, callback, 1, &result, &callResult), CALL_FUNCTION, scope);
306 napi_close_handle_scope(env, scope);
307 }
308
EmitAsyncCallbackWork(KeyEventMonitorInfo * reportEvent)309 void EmitAsyncCallbackWork(KeyEventMonitorInfo *reportEvent)
310 {
311 CALL_DEBUG_ENTER;
312 CHKPV(reportEvent);
313 uv_loop_s *loop = nullptr;
314 CHKRV(napi_get_uv_event_loop(reportEvent->env, &loop), GET_UV_EVENT_LOOP);
315 uv_work_t *work = new (std::nothrow) uv_work_t;
316 CHKPV(work);
317 KeyEventMonitorInfoWorker *dataWorker = new (std::nothrow) KeyEventMonitorInfoWorker();
318 if (dataWorker == nullptr) {
319 MMI_HILOGE("dataWorker is nullptr");
320 delete work;
321 return;
322 }
323 reportEvent->delCallback = [dataWorker]() {dataWorker->reportEvent = nullptr;};
324 dataWorker->env = reportEvent->env;
325 dataWorker->reportEvent = reportEvent;
326 work->data = static_cast<void *>(dataWorker);
327
328 int32_t ret = uv_queue_work_with_qos(loop, work, [](uv_work_t *work) {}, UvQueueWorkAsyncCallback,
329 uv_qos_user_initiated);
330 if (ret != 0) {
331 delete dataWorker;
332 delete work;
333 }
334 }
335 } // namespace MMI
336 } // namespace OHOS