/* * * Tencent is pleased to support the open source community by making * Hippy available. * * Copyright (C) 2019 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #include "core/modules/timer-module.h" #include #include "core/engine-impl.h" #include "core/modules/js-value-helper.h" #include "core/modules/module-register.h" #include "core/napi/callback-info.h" #include "core/task/javascript-task-runner.h" #include "core/task/javascript-task.h" REGISTER_MODULE(TimerModule, SetTimeout) REGISTER_MODULE(TimerModule, ClearTimeout) REGISTER_MODULE(TimerModule, SetInterval) REGISTER_MODULE(TimerModule, ClearInterval) namespace napi = ::hippy::napi; static const std::string kAsyncTaskEndKey = "ASYNC_TASK_END"; using RegisterFunction = hippy::base::RegisterFunction; using RegisterMap = hippy::base::RegisterMap; TimerModule::TimerModule(hippy::napi::napi_context context) { std::weak_ptr engine = EngineImpl::instance()->GetEngineWithContext(context); if (engine.lock()) { task_runner_ = engine.lock()->jsRunner(); } } TimerModule::~TimerModule() { for (auto& item : task_map_) { std::shared_ptr task = item.second->task_.lock(); task_runner_->cancelTask(task); } } void TimerModule::SetTimeout(const napi::CallbackInfo& info) { info.GetReturnValue()->Set(Start(info, false)); } void TimerModule::ClearTimeout(const napi::CallbackInfo& info) { ClearInterval(info); } void TimerModule::SetInterval(const napi::CallbackInfo& info) { info.GetReturnValue()->Set(Start(info, true)); } void TimerModule::ClearInterval(const napi::CallbackInfo& info) { std::shared_ptr env = info.GetEnv(); napi::napi_context context = env->getContext(); HIPPY_CHECK(context); int32_t argument1 = 0; if (!napi::napi_get_value_number(context, info[0], &argument1)) { info.GetExceptionValue()->Set(context, "The first argument must be int32."); return; } TaskId task_id = argument1; Cancel(task_id); info.GetReturnValue()->Set(napi_create_number(context, task_id)); } napi::napi_value TimerModule::Start(const napi::CallbackInfo& info, bool repeat) { std::shared_ptr env = info.GetEnv(); napi::napi_context context = env->getContext(); HIPPY_CHECK(context); napi::napi_value function = info[0]; if (!napi::napi_is_function(context, function)) { info.GetExceptionValue()->Set(context, "The first argument must be function."); return nullptr; } double number = 0; napi::napi_get_value_number(context, info[1], &number); hippy::base::TaskRunner::DelayedTimeInMs interval = static_cast( std::max(.0, number)); std::shared_ptr task = std::make_shared(); std::weak_ptr weak_task = task; std::weak_ptr weak_env = env; napi::napi_value_weak weak_function = function; task->callback = [this, context, weak_function, weak_task, repeat, interval, runner = task_runner_, weak_env] { napi::napi_value function = weak_function.lock(); if (function) { hippy::InvokeJsFunction(context, function); } std::shared_ptr env = weak_env.lock(); if (!env) { return; } std::unique_ptr& map = env->GetRegisterMap(); if (map) { RegisterMap::const_iterator it = map->find(kAsyncTaskEndKey); if (it != map->end()) { RegisterFunction f = it->second; if (f) { f(nullptr); } } } std::shared_ptr delayed_task = weak_task.lock(); if (repeat) { if (delayed_task) { runner->postDelayedTask(delayed_task, interval); } } else { RemoveTask(delayed_task); } }; task_runner_->postDelayedTask(task, interval); std::shared_ptr entry = std::make_shared(context, task, function); std::pair> item{task->id_, std::move(entry)}; task_map_.insert(item); return napi_create_number(context, task->id_); } void TimerModule::RemoveTask(std::shared_ptr task) { if (!task) { return; } task_map_.erase(task->id_); } void TimerModule::Cancel(TaskId task_id) { auto item = task_map_.find(task_id); if (item != task_map_.end()) { std::shared_ptr task = item->second->task_.lock(); task_runner_->cancelTask(task); task_map_.erase(item->first); } }