1 /*
2 * Copyright (c) 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_backend_timer_module.h"
17
18 #include "base/log/log.h"
19
20 namespace OHOS::Ace {
21 namespace {
22 template<class T>
ConvertNativeValueTo(NativeValue * value)23 inline T* ConvertNativeValueTo(NativeValue* value)
24 {
25 return (value != nullptr) ? static_cast<T*>(value->GetInterface(T::INTERFACE_ID)) : nullptr;
26 }
27
BindNativeFunction(NativeEngine & engine,NativeObject & object,const char * name,const char * moduleName,NativeCallback func)28 void BindNativeFunction(NativeEngine& engine, NativeObject& object, const char* name,
29 const char* moduleName, NativeCallback func)
30 {
31 std::string fullName(moduleName);
32 fullName += ".";
33 fullName += name;
34 object.SetProperty(name, engine.CreateFunction(fullName.c_str(), fullName.length(), func, nullptr));
35 }
36
SetCallbackTimer(NativeEngine & engine,NativeCallbackInfo & info,bool isInterval)37 NativeValue* SetCallbackTimer(NativeEngine& engine, NativeCallbackInfo& info, bool isInterval)
38 {
39 // Parameter check, must have at least 2 params
40 if (info.argc < 2 || info.argv[0]->TypeOf() != NATIVE_FUNCTION || info.argv[1]->TypeOf() != NATIVE_NUMBER) {
41 LOGE("Set callback timer failed with invalid parameter.");
42 return engine.CreateUndefined();
43 }
44
45 // Parse parameter
46 std::shared_ptr<NativeReference> func(engine.CreateReference(info.argv[0], 1));
47 int64_t delayTime = *ConvertNativeValueTo<NativeNumber>(info.argv[1]);
48 std::vector<std::shared_ptr<NativeReference>> params;
49 for (size_t index = 2; index < info.argc; ++index) {
50 params.emplace_back(std::shared_ptr<NativeReference>(engine.CreateReference(info.argv[index], 1)));
51 }
52
53 // Get callbackId
54 uint32_t callbackId = JsBackendTimerModule::GetInstance()->AddCallBack(func, params, &engine);
55
56 // Post task
57 JsBackendTimerModule::GetInstance()->PostTimerCallback(callbackId, delayTime, isInterval, true, &engine);
58
59 return engine.CreateNumber(callbackId);
60 }
61
ClearCallbackTimer(NativeEngine & engine,NativeCallbackInfo & info)62 NativeValue* ClearCallbackTimer(NativeEngine& engine, NativeCallbackInfo& info)
63 {
64 // parameter check, must have at least 1 param
65 if (info.argc < 1 || info.argv[0]->TypeOf() != NATIVE_NUMBER) {
66 LOGE("Clear callback timer failed with invalid parameter.");
67 return engine.CreateUndefined();
68 }
69
70 uint32_t callbackId = *ConvertNativeValueTo<NativeNumber>(info.argv[0]);
71 JsBackendTimerModule::GetInstance()->RemoveTimerCallback(callbackId);
72 return engine.CreateUndefined();
73 }
74
SetTimeout(NativeEngine * engine,NativeCallbackInfo * info)75 NativeValue* SetTimeout(NativeEngine* engine, NativeCallbackInfo* info)
76 {
77 if (engine == nullptr || info == nullptr) {
78 LOGE("Set timeout failed with engine or callback info is nullptr.");
79 return nullptr;
80 }
81
82 return SetCallbackTimer(*engine, *info, false);
83 }
84
SetInterval(NativeEngine * engine,NativeCallbackInfo * info)85 NativeValue* SetInterval(NativeEngine* engine, NativeCallbackInfo* info)
86 {
87 if (engine == nullptr || info == nullptr) {
88 LOGE("Set interval failed with engine or callback info is nullptr.");
89 return nullptr;
90 }
91
92 return SetCallbackTimer(*engine, *info, true);
93 }
94
ClearTimeoutOrInterval(NativeEngine * engine,NativeCallbackInfo * info)95 NativeValue* ClearTimeoutOrInterval(NativeEngine* engine, NativeCallbackInfo* info)
96 {
97 if (engine == nullptr || info == nullptr) {
98 LOGE("Clear timer failed with engine or callback info is nullptr.");
99 return nullptr;
100 }
101
102 return ClearCallbackTimer(*engine, *info);
103 }
104 } // namespace
105
TimerCallback(uint32_t callbackId,int64_t delayTime,bool isInterval)106 void JsBackendTimerModule::TimerCallback(uint32_t callbackId, int64_t delayTime, bool isInterval)
107 {
108 std::shared_ptr<NativeReference> func;
109 std::vector<std::shared_ptr<NativeReference>> params;
110 NativeEngine* engine = nullptr;
111 if (!GetCallBackById(callbackId, func, params, &engine)) {
112 return;
113 }
114
115 if (!engine) {
116 LOGE("engine is nullptr.");
117 return;
118 }
119
120 std::vector<NativeValue*> argc;
121 argc.reserve(params.size());
122 for (auto arg : params) {
123 argc.emplace_back(arg->Get());
124 }
125
126 engine->CallFunction(engine->CreateUndefined(), func->Get(), argc.data(), argc.size());
127
128 if (isInterval) {
129 PostTimerCallback(callbackId, delayTime, isInterval, false, engine);
130 } else {
131 RemoveTimerCallback(callbackId);
132 }
133 }
134
PostTimerCallback(uint32_t callbackId,int64_t delayTime,bool isInterval,bool isFirst,NativeEngine * engine)135 void JsBackendTimerModule::PostTimerCallback(uint32_t callbackId, int64_t delayTime, bool isInterval, bool isFirst,
136 NativeEngine* engine)
137 {
138 std::lock_guard<std::mutex> lock(mutex_);
139 if (!isFirst) {
140 auto taskNode = timeoutTaskMap_.find(callbackId);
141 if (taskNode == timeoutTaskMap_.end()) {
142 LOGE("Post timer callback failed, callbackId %{public}u not found.", callbackId);
143 return;
144 }
145 }
146
147 // CancelableCallback class can only be executed once.
148 CancelableCallback<void()> cancelableTimer;
149 cancelableTimer.Reset([callbackId, delayTime, isInterval] {
150 JsBackendTimerModule::GetInstance()->TimerCallback(callbackId, delayTime, isInterval);
151 });
152 auto result = timeoutTaskMap_.try_emplace(callbackId, cancelableTimer);
153 if (!result.second) {
154 result.first->second = cancelableTimer;
155 }
156
157 RefPtr<BackendDelegate> delegate = GetDelegateWithoutLock(engine);
158 if (delegate) {
159 delegate->PostDelayedJsTask(cancelableTimer, delayTime);
160 }
161 }
162
RemoveTimerCallback(uint32_t callbackId)163 void JsBackendTimerModule::RemoveTimerCallback(uint32_t callbackId)
164 {
165 std::lock_guard<std::mutex> lock(mutex_);
166 if (callbackNodeMap_.find(callbackId) != callbackNodeMap_.end()) {
167 callbackNodeMap_.erase(callbackId);
168 }
169
170 auto timeoutNode = timeoutTaskMap_.find(callbackId);
171 if (timeoutNode != timeoutTaskMap_.end()) {
172 timeoutNode->second.Cancel();
173 timeoutTaskMap_.erase(callbackId);
174 }
175 }
176
AddCallBack(const std::shared_ptr<NativeReference> & func,const std::vector<std::shared_ptr<NativeReference>> & params,NativeEngine * engine)177 uint32_t JsBackendTimerModule::AddCallBack(const std::shared_ptr<NativeReference>& func,
178 const std::vector<std::shared_ptr<NativeReference>>& params, NativeEngine* engine)
179 {
180 std::lock_guard<std::mutex> lock(mutex_);
181 ++callbackId_;
182 callbackNodeMap_[callbackId_].func = func;
183 callbackNodeMap_[callbackId_].params = params;
184 callbackNodeMap_[callbackId_].engine = engine;
185 return callbackId_;
186 }
187
GetCallBackById(uint32_t callbackId,std::shared_ptr<NativeReference> & func,std::vector<std::shared_ptr<NativeReference>> & params,NativeEngine ** engine)188 bool JsBackendTimerModule::GetCallBackById(uint32_t callbackId, std::shared_ptr<NativeReference>& func,
189 std::vector<std::shared_ptr<NativeReference>>& params, NativeEngine** engine)
190 {
191 std::lock_guard<std::mutex> lock(mutex_);
192 auto taskNode = callbackNodeMap_.find(callbackId);
193 if (taskNode == callbackNodeMap_.end()) {
194 LOGE("Get callback failed, callbackId %{public}u not found.", callbackId);
195 return false;
196 }
197
198 func = taskNode->second.func;
199 params = taskNode->second.params;
200 *engine = taskNode->second.engine;
201 return true;
202 }
203
GetInstance()204 JsBackendTimerModule* JsBackendTimerModule::GetInstance()
205 {
206 static JsBackendTimerModule instance;
207 return &instance;
208 }
209
GetDelegateWithoutLock(NativeEngine * engine)210 RefPtr<BackendDelegate> JsBackendTimerModule::GetDelegateWithoutLock(NativeEngine* engine)
211 {
212 auto delegateNode = delegateMap_.find(engine);
213 if (delegateNode == delegateMap_.end()) {
214 LOGE("Get delegate failed.");
215 return nullptr;
216 }
217
218 return delegateNode->second;
219 }
220
AddDelegate(NativeEngine * engine,const RefPtr<BackendDelegate> & delegate)221 void JsBackendTimerModule::AddDelegate(NativeEngine* engine, const RefPtr<BackendDelegate>& delegate)
222 {
223 std::lock_guard<std::mutex> lock(mutex_);
224 delegateMap_[engine] = delegate;
225 }
226
InitTimerModule(NativeEngine * engine,const RefPtr<BackendDelegate> & delegate)227 void JsBackendTimerModule::InitTimerModule(NativeEngine* engine, const RefPtr<BackendDelegate>& delegate)
228 {
229 if (engine == nullptr || delegate == nullptr) {
230 LOGE("InitTimerModule failed with engine or delegate is nullptr.");
231 return;
232 }
233
234 AddDelegate(engine, delegate);
235
236 NativeObject* globalObject = ConvertNativeValueTo<NativeObject>(engine->GetGlobal());
237 if (!globalObject) {
238 LOGE("Failed to get global object.");
239 return;
240 }
241
242 const char *moduleName = "JsBackendTimer";
243 BindNativeFunction(*engine, *globalObject, "setTimeout", moduleName, SetTimeout);
244 BindNativeFunction(*engine, *globalObject, "setInterval", moduleName, SetInterval);
245 BindNativeFunction(*engine, *globalObject, "clearTimeout", moduleName, ClearTimeoutOrInterval);
246 BindNativeFunction(*engine, *globalObject, "clearInterval", moduleName, ClearTimeoutOrInterval);
247 }
248 } // namespace OHOS::Ace