• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023-2023 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  * Description: supply napi callback realization for interfaces.
15  * Author: zhangjingnan
16  * Create: 2023-4-11
17  */
18 
19 #include <memory>
20 #include "cast_engine_log.h"
21 #include "napi_callback.h"
22 #include "napi_castengine_utils.h"
23 
24 namespace OHOS {
25 namespace CastEngine {
26 namespace CastEngineClient {
27 DEFINE_CAST_ENGINE_LABEL("Cast-Napi-Callback");
28 
NapiCallback(napi_env env)29 NapiCallback::NapiCallback(napi_env env) : env_(env)
30 {
31     if (env != nullptr) {
32         NAPI_CALL_RETURN_VOID(env, napi_get_uv_event_loop(env, &loop_));
33     }
34 }
35 
~NapiCallback()36 NapiCallback::~NapiCallback()
37 {
38     CLOGD("no memory leak for queue-callback");
39     env_ = nullptr;
40 }
41 
GetEnv() const42 napi_env NapiCallback::GetEnv() const
43 {
44     return env_;
45 }
46 
AfterWorkCallback(std::shared_ptr<DataContext> context)47 void NapiCallback::AfterWorkCallback(std::shared_ptr<DataContext> context)
48 {
49     int argc = 0;
50     napi_handle_scope scope = nullptr;
51     napi_open_handle_scope(context->env, &scope);
52     napi_value argv[ARGC_MAX] = { nullptr };
53     if (context->getter) {
54         argc = ARGC_MAX;
55         context->getter(context->env, argc, argv);
56     }
57 
58     napi_value undefined = nullptr;
59     if (napi_get_undefined(context->env, &undefined) != napi_ok) {
60         CLOGE("napi_get_undefined failed");
61         napi_close_handle_scope(context->env, scope);
62         return;
63     }
64     if (!IsCallbackValid(context->env, context->method, context->event)) {
65         CLOGE("callback is invalid event:%{public}d", context->event);
66         napi_close_handle_scope(context->env, scope);
67         return;
68     }
69     napi_value callback = nullptr;
70     if (napi_get_reference_value(context->env, context->method, &callback) != napi_ok) {
71         CLOGE("napi_get_reference_value failed");
72         ReleaseRef(context->env, context->method);
73         napi_close_handle_scope(context->env, scope);
74         return;
75     }
76     napi_value callResult = nullptr;
77     if (napi_call_function(context->env, undefined, callback, argc, argv, &callResult) != napi_ok) {
78         CLOGE("napi_call_function failed");
79     }
80     ReleaseRef(context->env, context->method);
81     napi_close_handle_scope(context->env, scope);
82 }
83 
Call(napi_ref method,NapiArgsGetter & getter,int32_t event)84 void NapiCallback::Call(napi_ref method, NapiArgsGetter &getter, int32_t event)
85 {
86     CLOGD("Start to have JS call");
87     if (loop_ == nullptr) {
88         CLOGE("loop_ is nullptr");
89         return;
90     }
91     if (method == nullptr) {
92         CLOGE("method is nullptr");
93         return;
94     }
95 
96     auto *work = new (std::nothrow) uv_work_t;
97     if (work == nullptr) {
98         CLOGE("no memory for uv_work_t");
99         return;
100     }
101     auto callback = shared_from_this();
102     work->data = new (std::nothrow) DataContext { env_, method, std::move(getter), callback, event };
103     if (work->data == nullptr) {
104         CLOGE("no memory for DataContext");
105         delete work;
106         return;
107     }
108     int res = uv_queue_work(loop_, work, [](uv_work_t *work) {}, [](uv_work_t *work, int status) {
109         std::shared_ptr<DataContext> context(static_cast<DataContext *>(work->data), [work](DataContext *ptr) {
110             delete ptr;
111             delete work;
112         });
113         if (!context->callback) {
114             CLOGE("callback is nullptr");
115             return;
116         }
117         context->callback->AfterWorkCallback(context);
118     });
119     if (res != 0) {
120         CLOGE("uv queue work failed");
121         delete static_cast<DataContext *>(work->data);
122         delete work;
123         return;
124     }
125 }
126 
AddCallback(napi_env env,int32_t event,napi_value callback)127 napi_status NapiCallback::AddCallback(napi_env env, int32_t event, napi_value callback)
128 {
129     if ((event >= EVENT_TYPE_MAX) || (event < 0)) {
130         CLOGE("event %{public}d is invalid", event);
131         return napi_generic_failure;
132     }
133     CLOGI("Add callback %{public}d", event);
134     std::lock_guard<std::mutex> lockGuard(lock_);
135     constexpr int initialRefCount = 1;
136     napi_ref ref = nullptr;
137     if (GetRefByCallback(env, callbacks_[event], callback, ref) != napi_ok) {
138         CLOGE("get callback reference failed");
139         return napi_generic_failure;
140     }
141     if (ref != nullptr) {
142         CLOGD("callback has been registered");
143         return napi_ok;
144     }
145     napi_status status = napi_create_reference(env, callback, initialRefCount, &ref);
146     if (status != napi_ok) {
147         CLOGE("napi_create_reference failed");
148         return status;
149     }
150     callbacks_[event].push_back(ref);
151     return napi_ok;
152 }
153 
RemoveCallback(napi_env env,int32_t event,napi_value callback)154 napi_status NapiCallback::RemoveCallback(napi_env env, int32_t event, napi_value callback)
155 {
156     if ((event >= EVENT_TYPE_MAX) || (event < 0)) {
157         CLOGE("event %{public}d is invalid", event);
158         return napi_generic_failure;
159     }
160     std::lock_guard<std::mutex> lockGuard(lock_);
161     if (callback == nullptr) {
162         for (auto &callbackRef : callbacks_[event]) {
163             ReleaseRef(env, callbackRef);
164         }
165         callbacks_[event].clear();
166         return napi_ok;
167     }
168     napi_ref ref = nullptr;
169     if (GetRefByCallback(env, callbacks_[event], callback, ref) != napi_ok) {
170         CLOGE("get callback reference failed");
171         return napi_generic_failure;
172     }
173     if (ref == nullptr) {
174         CLOGD("callback has been remove");
175         return napi_ok;
176     }
177     callbacks_[event].remove(ref);
178 
179     return ReleaseRef(env, ref);
180 }
181 
HandleEvent(int32_t event,NapiArgsGetter & getter)182 void NapiCallback::HandleEvent(int32_t event, NapiArgsGetter &getter)
183 {
184     if ((event >= EVENT_TYPE_MAX) || (event < 0)) {
185         CLOGE("event %{public}d is invalid", event);
186         return;
187     }
188     std::lock_guard<std::mutex> lockGuard(lock_);
189     if (callbacks_[event].empty()) {
190         CLOGE("not register callback event=%{public}d", event);
191         return;
192     }
193     for (auto ref = callbacks_[event].begin(); ref != callbacks_[event].end(); ++ref) {
194         Call(*ref, getter, event);
195     }
196 }
197 
IsCallbackValid(napi_env env,napi_ref ref,int32_t event)198 bool NapiCallback::IsCallbackValid(napi_env env, napi_ref ref, int32_t event)
199 {
200     if ((event >= EVENT_TYPE_MAX) || (event < 0)) {
201         CLOGE("event %{public}d is invalid", event);
202         return false;
203     }
204     std::lock_guard<std::mutex> lockGuard(lock_);
205     for (auto &callbackRef : callbacks_[event]) {
206         if (callbackRef != ref) {
207             continue;
208         }
209         uint32_t refCount = 0xff;
210         napi_status ret = napi_reference_ref(env, ref, &refCount);
211         if (ret != napi_ok || refCount <= 1) {
212             CLOGE("callback is invalid ret: %{public}d refCount: %{public}d", ret, refCount);
213             return false;
214         }
215         return true;
216     }
217     CLOGE("callback has been remove");
218     return false;
219 }
220 
IsCallbackListEmpty()221 bool NapiCallback::IsCallbackListEmpty()
222 {
223     std::lock_guard<std::mutex> lockGuard(lock_);
224     for (auto &callback : callbacks_) {
225         if (!callback.empty()) {
226             return false;
227         }
228     }
229     return true;
230 }
231 
ReleaseRef(napi_env env,napi_ref ref)232 napi_status NapiCallback::ReleaseRef(napi_env env, napi_ref ref)
233 {
234     uint32_t refCount = 0xff;
235     napi_status ret = napi_ok;
236     napi_reference_unref(env, ref, &refCount);
237     if (refCount == 0) {
238         CLOGI("refCount is 0, delete callback reference");
239         ret = napi_delete_reference(env, ref);
240     }
241     if (ret != napi_ok) {
242         CLOGE("delete callback reference failed");
243     }
244     return ret;
245 }
246 } // namespace CastEngineClient
247 } // namespace CastEngine
248 } // namespace OHOS