• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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