1 /*
2 * Copyright (c) 2021-2025 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 #include "listener_base.h"
16
17 #include <memory>
18 #include <uv.h>
19
20 #include "camera_log.h"
21 #include "js_native_api.h"
22 #include "js_native_api_types.h"
23
24 namespace OHOS {
25 namespace CameraStandard {
ListenerBase(napi_env env)26 ListenerBase::ListenerBase(napi_env env) : env_(env)
27 {
28 MEDIA_DEBUG_LOG("ListenerBase is called.");
29 auto ret = napi_add_env_cleanup_hook(env_, ListenerBase::CleanUp, this);
30 if (ret != napi_status::napi_ok) {
31 MEDIA_ERR_LOG("add env hook error: %{public}d", ret);
32 }
33 }
34
~ListenerBase()35 ListenerBase::~ListenerBase()
36 {
37 MEDIA_DEBUG_LOG("~ListenerBase is called.");
38 auto ret = napi_remove_env_cleanup_hook(env_, ListenerBase::CleanUp, this);
39 if (ret != napi_status::napi_ok) {
40 MEDIA_ERR_LOG("remove env hook error: %{public}d", ret);
41 }
42 }
43
ExecuteCallbackData(napi_env env,napi_value errCode,napi_value returnData)44 ListenerBase::ExecuteCallbackData::ExecuteCallbackData(napi_env env, napi_value errCode, napi_value returnData)
45 : env_(env), errCode_(errCode), returnData_(returnData) {};
46
SaveCallbackReference(const std::string eventName,napi_value callback,bool isOnce)47 void ListenerBase::SaveCallbackReference(const std::string eventName, napi_value callback, bool isOnce)
48 {
49 CHECK_RETURN_ELOG(callback == nullptr,
50 "SaveCallbackReference:%s js callback is nullptr, save nothing", eventName.c_str());
51 napi_valuetype valueType = napi_undefined;
52 napi_typeof(env_, callback, &valueType);
53 CHECK_RETURN_ELOG(valueType != napi_function,
54 "SaveCallbackReference:%s js callback valueType is not function", eventName.c_str());
55 auto& callbackList = GetCallbackList(eventName);
56 std::lock_guard<std::mutex> lock(callbackList.listMutex);
57 for (auto it = callbackList.refList.begin(); it != callbackList.refList.end(); ++it) {
58 bool isSameCallback = CameraNapiUtils::IsSameNapiValue(env_, callback, it->GetCallbackFunction());
59 CHECK_RETURN_ELOG(isSameCallback, "SaveCallbackReference: has same callback, nothing to do");
60 }
61 callbackList.refList.emplace_back(AutoRef(env_, callback, isOnce));
62 MEDIA_DEBUG_LOG("Save callback reference success, %s callback list size [%{public}zu]", eventName.c_str(),
63 callbackList.refList.size());
64 }
65
RemoveCallbackRef(const std::string eventName,napi_value callback)66 void ListenerBase::RemoveCallbackRef(const std::string eventName, napi_value callback)
67 {
68 if (callback == nullptr) {
69 MEDIA_INFO_LOG("RemoveCallbackReference: js callback is nullptr, remove all callback reference");
70 RemoveAllCallbacks(eventName);
71 return;
72 }
73 auto& callbackList = GetCallbackList(eventName);
74 std::lock_guard<std::mutex> lock(callbackList.listMutex);
75 for (auto it = callbackList.refList.begin(); it != callbackList.refList.end(); ++it) {
76 bool isSameCallback = CameraNapiUtils::IsSameNapiValue(env_, callback, it->GetCallbackFunction());
77 if (isSameCallback) {
78 MEDIA_INFO_LOG("RemoveCallbackReference: find %s callback, delete it", eventName.c_str());
79 callbackList.refList.erase(it);
80 return;
81 }
82 }
83 MEDIA_INFO_LOG("RemoveCallbackReference: %s callback not find", eventName.c_str());
84 }
85
ExecuteCallback(const std::string eventName,const ExecuteCallbackNapiPara & callbackPara) const86 void ListenerBase::ExecuteCallback(const std::string eventName, const ExecuteCallbackNapiPara& callbackPara) const
87 {
88 MEDIA_DEBUG_LOG("ListenerBase::ExecuteCallback is called");
89 auto& callbackList = GetCallbackList(eventName);
90 std::lock_guard<std::mutex> lock(callbackList.listMutex);
91 for (auto it = callbackList.refList.begin(); it != callbackList.refList.end();) {
92 napi_call_function(env_, callbackPara.recv, it->GetCallbackFunction(), callbackPara.argc, callbackPara.argv,
93 callbackPara.result);
94 if (it->isOnce_) {
95 MEDIA_DEBUG_LOG("ListenerBase::ExecuteCallback, once to del %s", eventName.c_str());
96 it = callbackList.refList.erase(it);
97 } else {
98 it++;
99 }
100 }
101 MEDIA_DEBUG_LOG("ListenerBase::ExecuteCallback, %s callback list size [%{public}zu]", eventName.c_str(),
102 callbackList.refList.size());
103 }
104
ExecuteCallbackScopeSafe(const std::string eventName,const std::function<ExecuteCallbackData ()> fun) const105 void ListenerBase::ExecuteCallbackScopeSafe(
106 const std::string eventName, const std::function<ExecuteCallbackData()> fun) const
107 {
108 napi_handle_scope scope_ = nullptr;
109 if (!env_) {
110 MEDIA_ERR_LOG("ListenerBase::ExecuteCallbackScopeSafe env is nullptr");
111 return;
112 }
113 napi_open_handle_scope(env_, &scope_);
114
115 MEDIA_DEBUG_LOG("ListenerBase::ExecuteCallbackScopeSafe %{public}s is called", eventName.c_str());
116 auto& callbackList = GetCallbackList(eventName);
117 std::lock_guard<std::mutex> lock(callbackList.listMutex);
118 for (auto it = callbackList.refList.begin(); it != callbackList.refList.end();) {
119 // Do not move this call out of loop.
120 ExecuteCallbackData callbackData = fun();
121 if (callbackData.env_ == nullptr) {
122 MEDIA_ERR_LOG("ExecuteCallbackScopeSafe %{public}s env is null ", eventName.c_str());
123 continue;
124 }
125
126 napi_value result[ARGS_TWO] = { nullptr, nullptr };
127 napi_value retVal;
128 result[0] = callbackData.errCode_;
129 result[1] = callbackData.returnData_;
130
131 napi_call_function(callbackData.env_, nullptr, it->GetCallbackFunction(), ARGS_TWO, result, &retVal);
132 if (it->isOnce_) {
133 it = callbackList.refList.erase(it);
134 } else {
135 it++;
136 }
137 }
138 MEDIA_DEBUG_LOG("ListenerBase::ExecuteCallbackScopeSafe, %s callback list size [%{public}zu]", eventName.c_str(),
139 callbackList.refList.size());
140
141 napi_close_handle_scope(env_, scope_);
142 }
143
RemoveAllCallbacks(const std::string eventName)144 void ListenerBase::RemoveAllCallbacks(const std::string eventName)
145 {
146 auto& callbackList = GetCallbackList(eventName);
147 std::lock_guard<std::mutex> lock(callbackList.listMutex);
148 callbackList.refList.clear();
149 MEDIA_INFO_LOG("RemoveAllCallbacks: remove all js callbacks success");
150 }
151
IsEmpty(const std::string eventName) const152 bool ListenerBase::IsEmpty(const std::string eventName) const
153 {
154 auto& callbackList = GetCallbackList(eventName);
155 std::lock_guard<std::mutex> lock(callbackList.listMutex);
156 return callbackList.refList.empty();
157 }
158
CleanUp(void * data)159 void ListenerBase::CleanUp(void* data)
160 {
161 MEDIA_INFO_LOG("ListenerBase::CleanUp enter");
162 ListenerBase* listener = reinterpret_cast<ListenerBase*>(data);
163 if (!listener) {
164 return;
165 }
166 listener->CleanUpImpl();
167 }
168
CleanUpImpl()169 void ListenerBase::CleanUpImpl()
170 {
171 MEDIA_INFO_LOG("ListenerBase::CleanUpImpl enter");
172 ClearNamedCallbackMap();
173 env_ = nullptr;
174 }
175
176 } // namespace CameraStandard
177 } // namespace OHOS
178