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