4#include <qohosplugincore.h>
7#include <QtCore/private/qohoslogger_p.h>
8#include <QtCore/private/qohosmemoizingjsthreadfetcher_p.h>
17#include <napi/native_api.h>
19#include <qohosqabilityinstancesmanager.h>
20#include <qohosutils.h>
21#include <render/qohosjswindowregistry.h>
32constexpr const char *napiLoadableModules[] = {
33 "@ohos.abilityAccessCtrl",
34 "@ohos.accessibility",
35 "@ohos.app.ability.AbilityConstant",
36 "@ohos.app.ability.ConfigurationConstant",
37 "@ohos.app.ability.childProcessManager",
38 "@ohos.app.ability.contextConstant",
39 "@ohos.app.ability.wantConstant",
40 "@ohos.arkui.uiExtension",
41 "@ohos.bluetooth.access",
42 "@ohos.bluetooth.connection",
43 "@ohos.bluetooth.socket",
44 "@ohos.bundle.bundleManager",
45 "@ohos.commonEventManager",
46 "@ohos.data.uniformTypeDescriptor",
52 "@ohos.geoLocationManager",
53 "@ohos.graphics.text",
57 "@ohos.multimedia.image",
58 "@ohos.multimedia.media",
59 "@ohos.multimodalInput.inputDevice",
60 "@ohos.multimodalInput.pointer",
61 "@ohos.net.connection",
62 "@ohos.notificationManager",
64 "@ohos.resourceManager",
71template<
typename Task>
72class PreQueuingTasksExecutor
75 void setUnderlyingExecutor(
76 std::function<
void(Task)> executor,
77 std::function<
void(Task)> optSyncFlushExecutor =
nullptr);
78 void invokeTask(Task task);
81 std::recursive_mutex m_executorMutex;
82 std::function<
void(Task)> m_optUnderlyingExecutor;
83 std::deque<Task> m_pendingTasks;
86template<
typename Task>
87void PreQueuingTasksExecutor<Task>::setUnderlyingExecutor(
88 std::function<
void(Task)> executor,
std::function<
void(Task)> optSyncFlushExecutor)
90 std::lock_guard<
std::recursive_mutex> executorLock(m_executorMutex);
91 auto &flushExecutor = optSyncFlushExecutor ? optSyncFlushExecutor : executor;
92 while (!m_pendingTasks.empty()) {
93 auto task =
std::move(m_pendingTasks.front());
94 m_pendingTasks.pop_front();
95 flushExecutor(
std::move(task));
97 m_optUnderlyingExecutor =
std::move(executor);
100template<
typename Task>
101void PreQueuingTasksExecutor<Task>::invokeTask(Task task)
103 std::lock_guard<
std::recursive_mutex> executorLock(m_executorMutex);
104 if (m_optUnderlyingExecutor)
105 m_optUnderlyingExecutor(
std::move(task));
107 m_pendingTasks.push_back(std::move(task));
110using PreQueuingJsTasksExecutor = PreQueuingTasksExecutor<
std::function<
void(
JsState &)>>;
115 std::mutex pendingTasksMutex;
116 bool pendingTasksRunnerActive =
false;
117 std::vector<std::function<
void(JsState &)>> pendingTasks;
118 Napi::ThreadSafeFunction executorThreadSafeFunc;
120 auto ctx =
std::make_shared<Context>();
122 ctx->executorThreadSafeFunc = Napi::ThreadSafeFunction::New(
124 QNapi::Function::New(
126 [ctx = ctx.get()](
const CallbackInfo &cbInfo) {
128 std::vector<std::function<
void(JsState &)>> pendingTasks;
130 std::lock_guard<std::mutex> pendingTasksLock(ctx->pendingTasksMutex);
131 pendingTasks = std::exchange(ctx->pendingTasks, {});
132 if (pendingTasks.empty()) {
133 ctx->pendingTasksRunnerActive =
false;
137 for (
auto &task : pendingTasks)
138 task(cbInfo.jsState());
141 "QCoreOhosTasksExecutorFunc"),
142 "QCoreOhosTasksExecutor", 0, 1);
144 return [ctx](
std::function<
void(
JsState &)> task) {
145 std::lock_guard<
std::mutex> pendingTasksLock(ctx->pendingTasksMutex);
146 ctx->pendingTasks.push_back(
std::move(task));
147 if (!ctx->pendingTasksRunnerActive) {
148 ctx->executorThreadSafeFunc.NonBlockingCall();
149 ctx->pendingTasksRunnerActive =
true;
154QNapi::Symbol getJsWindowsTrackerIsClosingPropSymbol(
JsState &jsState)
160 return jsState.getJsSymbolForType<SymbolTag>();
163std::optional<QNapi::Object> tryLoadJsModuleViaNapi(napi_env env,
const std::string &moduleName)
165 qOhosPrintfDebug(
"%s: loading napi module '%s' via napi_load_module()", Q_FUNC_INFO, moduleName.c_str());
167 napi_value result =
nullptr;
168 auto status = napi_load_module(env, moduleName.c_str(), &result);
169 if (status != napi_ok) {
170 Napi::Env napiEnv(env);
171 if (napiEnv.IsExceptionPending())
172 napiEnv.GetAndClearPendingException();
174 "%s: napi_load_module failed for module '%s' (status=%d)",
175 Q_FUNC_INFO, moduleName.c_str(),
static_cast<
int>(status));
179 Napi::Value resultValue(env, result);
180 if (!resultValue.IsObject()) {
182 "%s: napi_load_module returned %s instead of an object for module '%s'",
183 Q_FUNC_INFO, QNapi::getValueTypeString(resultValue).c_str(), moduleName.c_str());
187 return QNapi::Object(env, result);
190QNapi::Object loadJsModuleViaNapiOrFail(napi_env env,
const std::string &moduleName)
192 auto optModule = tryLoadJsModuleViaNapi(env, moduleName);
194 qOhosReportFatalErrorAndAbort(
"%s: required module '%s' not loaded", Q_FUNC_INFO, moduleName.c_str());
196 return optModule.value();
199QNapi::Object loadJsModuleViaEtsFactoryOrFail(
200 const QNapi::Function &etsModuleFactory,
const std::string &moduleName)
202 qOhosPrintfDebug(
"%s: loading napi module '%s' via ets factory", Q_FUNC_INFO, moduleName.c_str());
204 auto result = etsModuleFactory.Call({});
205 if (!result.IsObject()) {
206 qOhosReportFatalErrorAndAbort(
207 "%s: ets factory for module '%s' did not return an object",
208 Q_FUNC_INFO, moduleName.c_str());
211 return QNapi::checkedCast<QNapi::Object>(result);
215std::function<T(
JsState &)> makeMemoizingJsValueSupplier(
std::function<T(
JsState &)> baseJsSupplier)
217 auto memoizedValue = moveToSharedPtr(QNapi::Reference<T>());
218 return [baseJsSupplier =
std::move(baseJsSupplier), memoizedValue](
JsState &jsState)
mutable {
219 if (memoizedValue->IsEmpty()) {
220 *memoizedValue = QNapi::Reference<T>::makePersistentFrom(baseJsSupplier(jsState));
221 baseJsSupplier =
nullptr;
223 return memoizedValue->Value();
227class JsStateImpl :
public JsState
230 JsStateImpl() =
default;
233 napi_env env, std::map<std::string, QNapi::Reference<QNapi::Function>> &&etsModulesFactories,
234 std::shared_ptr<AppFunctions> appFunctions, QtRunMode qtRunMode);
238 void dispatchNewWantInJsThread(QNapi::Object qAbility, QNapi::Object want, QNapi::Object launchParam);
240 void invokeTask(
std::function<
void(
JsState &)> &&task);
242 napi_env env()
override;
243 QNapi::Object getModule(
const std::string &moduleName);
244 std::vector<std::string> predefinedJsModuleNames()
const;
246 QNapi::Object appLaunchWant()
override;
247 std::optional<QNapi::Object> optAppLaunchParam()
override;
248 std::optional<QNapi::Object> tryGetModule(
const std::string &moduleName)
override;
250 QNapi::Object defaultWindowStageOrEmpty()
override;
251 QNapi::Object defaultUiContextOrEmpty()
override;
253 std::optional<QNapi::Object> tryGetJsWindowByQWindow(QObjectThreadSafeRef qwindow)
override;
254 std::optional<QNapi::Object> tryGetQAbilityByQWindow(QObjectThreadSafeRef qwindow)
override;
255 std::optional<QNapi::Object> defaultQAbility()
override;
257 void startAppProcess(
258 const std::string &processId, QNapi::Object requestWant,
259 QNapi::Object optStartOptions, std::function<
void(QOhosJsState &)> continueFunc)
override;
261 std::shared_ptr<
void> registerNewWantConsumer(
262 QOhosConsumer<QOhosJsState &, QNapi::Object, QNapi::Object, QNapi::Object> wantConsumer)
override;
264 void setOnContinueRequestsHandler(
265 QNapi::Object qAbility,
266 std::function<
void(QOhosJsState &, QNapi::Object, QOhosConsumer<QOhosJsState &, QNapi::Number>)> requestsHandler)
override;
268 void setDestroyFromSystemAllowed(QNapi::Object qAbility,
bool destroyAllowed)
override;
270 void startNoUiChildProcess(
const std::string &libraryName,
const std::vector<std::string> &args)
override;
277 void *getAttachedObjectWithLazyCreate(
278 const std::type_info &objectTypeInfo, QOhosSupplier<std::shared_ptr<
void>> objectFactory)
override;
280 std::tuple<QNapi::Object, std::string> extractModuleFromEvalExpr(
const std::string &expr)
override;
281 QNapi::Number mapOhosEnumToJs(
int enumValue,
const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)())
override;
282 std::optional<
int> tryMapOhosEnumFromJs(QNapi::Number enumJsValue,
const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)())
override;
283 int mapOhosEnumFromJs(QNapi::Number enumJsValue,
const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)())
override;
284 QNapi::Symbol getJsSymbolForType(
const std::type_info &typeInfo)
override;
286 const std::vector<std::pair<
int,
double>> &getOhosEnumEnumerators(
287 const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)());
288 std::vector<std::pair<
int,
double>> resolveOhosEnumEnumerators(
const OhosEnumInfo &enumInfo);
290 pthread_t m_jsThread = {};
291 napi_env m_env =
nullptr;
292 std::map<std::string, std::function<QNapi::Object(JsState &)>> m_jsModulesFactories;
293 std::map<std::string, QNapi::Reference<QNapi::Object>> m_onDemandJsModules;
295 PreQueuingJsTasksExecutor m_tasksExecutor;
296 std::map<std::uint64_t, std::shared_ptr<QOhosConsumer<QOhosJsState &, QNapi::Object, QNapi::Object, QNapi::Object>>> m_newWantConsumers;
297 std::uint64_t m_nextNewWantConsumerId = 1;
298 std::map<std::type_index, std::vector<std::pair<
int,
double>>> m_ohosEnumsEnumerators;
299 std::map<std::type_index, QNapi::Reference<QNapi::Symbol>> m_jsSymbolsRefs;
301 std::map<std::type_index, std::shared_ptr<
void>> m_attachedObjects;
304bool JsStateImpl::isJsThread()
306 return pthread_self() == m_jsThread;
309void JsStateImpl::initInJsThread(
310 napi_env env, std::map<std::string, QNapi::Reference<QNapi::Function>> &&etsModulesFactories,
311 std::shared_ptr<AppFunctions> appFunctions, QtRunMode qtRunMode)
313 m_jsThread = pthread_self();
315 m_appFunctions = appFunctions;
316 m_qtRunMode = qtRunMode;
318 for (
const char *moduleName : napiLoadableModules) {
319 m_jsModulesFactories.emplace(
321 makeMemoizingJsValueSupplier<QNapi::Object>(
322 [moduleName](JsState &jsState) {
323 return loadJsModuleViaNapiOrFail(jsState.env(), moduleName);
326 for (
auto &etsModulesFactoryEntry : etsModulesFactories) {
327 const std::string &moduleName = etsModulesFactoryEntry.first;
328 auto etsModuleFactory = moveToSharedPtr(std::move(etsModulesFactoryEntry.second));
329 m_jsModulesFactories.emplace(
331 makeMemoizingJsValueSupplier<QNapi::Object>(
332 [moduleName, etsModuleFactory](JsState &) {
333 return loadJsModuleViaEtsFactoryOrFail(etsModuleFactory->Value(), moduleName);
336 m_jsModulesFactories.emplace(
338 [](JsState &jsState) -> QNapi::Object {
339 return Napi::Env(jsState.env()).Global();
342 m_tasksExecutor.setUnderlyingExecutor(makeJsThreadTasksExecutor(
this));
345void JsStateImpl::dispatchNewWantInJsThread(
346 QNapi::Object qAbility, QNapi::Object want, QNapi::Object launchParam)
348 std::vector<std::uint64_t> consumersIds;
349 for (
const auto &consumerEntry : m_newWantConsumers)
350 consumersIds.push_back(consumerEntry.first);
352 for (
auto consumerId : consumersIds) {
353 auto consumerIter = m_newWantConsumers.find(consumerId);
354 if (consumerIter != m_newWantConsumers.end()) {
355 auto consumer = consumerIter->second;
356 (*consumer)(*
this, qAbility, want, launchParam);
361void JsStateImpl::invokeTask(
std::function<
void(
JsState &)> &&task)
363 m_tasksExecutor.invokeTask(std::move(task));
366napi_env JsStateImpl::env()
371std::vector<std::string> JsStateImpl::predefinedJsModuleNames()
const
373 std::vector<std::string> moduleNames;
374 for (
const auto &moduleFactoryEntry : m_jsModulesFactories)
375 moduleNames.push_back(moduleFactoryEntry.first);
379QNapi::Object JsStateImpl::getModule(
const std::string &moduleName)
381 using namespace std::string_literals;
383 auto moduleFactoryIter = m_jsModulesFactories.find(moduleName);
384 if (moduleFactoryIter == m_jsModulesFactories.end())
385 throw QNapi::makeLoggedException(m_env,
"JS module not found: "s + moduleName);
387 return moduleFactoryIter->second(*
this);
390std::optional<QNapi::Object> JsStateImpl::tryGetModule(
const std::string &moduleName)
392 auto moduleFactoryIter = m_jsModulesFactories.find(moduleName);
393 if (moduleFactoryIter != m_jsModulesFactories.end())
394 return moduleFactoryIter->second(*
this);
396 auto onDemandModuleIter = m_onDemandJsModules.find(moduleName);
397 if (onDemandModuleIter != m_onDemandJsModules.end())
398 return onDemandModuleIter->second.Value();
400 auto optModule = tryLoadJsModuleViaNapi(m_env, moduleName);
404 m_onDemandJsModules.emplace(moduleName, QNapi::Reference<>::makePersistentFrom(optModule.value()));
408QNapi::Object JsStateImpl::appLaunchWant()
410 return qAbilityInstancesManager().appLaunchWant();
413std::optional<QNapi::Object> JsStateImpl::optAppLaunchParam()
415 return qAbilityInstancesManager().optAppLaunchParam();
418QNapi::Object JsStateImpl::defaultWindowStageOrEmpty()
422 return QNapi::Object();
424 return qUiAbilityPeer->windowStage();
427QNapi::Object JsStateImpl::defaultUiContextOrEmpty()
429 return qAbilityInstancesManager().defaultQAbilityPeer()->uiContext();
432std::optional<QNapi::Object> JsStateImpl::tryGetJsWindowByQWindow(QObjectThreadSafeRef qwindow)
434 auto optJsWindowRef = QOhosJsWindowRegistry::instance(*
this).tryFindJsWindowByQWindowRef(qwindow);
435 return optJsWindowRef ?
std::optional(optJsWindowRef->jsObject()) :
std::nullopt;
438std::optional<QNapi::Object> JsStateImpl::tryGetQAbilityByQWindow(QObjectThreadSafeRef qwindow)
440 auto qAbilityPeer = qAbilityInstancesManager().tryGetQAbilityPeerByQWindow(qwindow);
442 auto optJsWindowRef = QOhosJsWindowRegistry::instance(*
this).tryFindJsWindowByQWindowRef(qwindow);
445 optJsWindowRef->owningQAbilityInstanceId()
);
447 return qAbilityPeer ?
std::optional(qAbilityPeer->qAbility()) :
std::nullopt;
450std::optional<QNapi::Object> JsStateImpl::defaultQAbility()
452 auto qAbility = qAbilityInstancesManager().defaultQAbilityPeer()->qAbility();
453 return !qAbility.IsEmpty() ?
std::optional(qAbility) :
std::nullopt;
456void JsStateImpl::setOnContinueRequestsHandler(
457 QNapi::Object qAbility,
458 std::function<
void(QOhosJsState &, QNapi::Object, QOhosConsumer<QOhosJsState &, QNapi::Number>)> requestsHandler)
461 qAbilityInstancesManager().tryGetQAbilityPeerByInstance(qAbility)
);
462 if (!uiAbilityPeer) {
464 "%s: no QUiAbilityPeer for the given qAbility, handler not set", Q_FUNC_INFO);
468 uiAbilityPeer->setOnContinueRequestsHandler(
469 [requestsHandler = std::move(requestsHandler)](
470 JsState &jsState, QNapi::Object wantParams,
471 QOhosConsumer<JsState &, QOhosAbilityOnContinueResult> resultConsumer) {
474 [resultConsumer = std::move(resultConsumer)](QOhosJsState &resultJsState, QNapi::Number result) {
476 if (!optResultEnum) {
478 "%s: got invalid OnContinueResult value (%f) from requests handler, rejecting the request",
479 Q_FUNC_INFO, result.DoubleValue());
482 JsState::castFrom(resultJsState),
488void JsStateImpl::setDestroyFromSystemAllowed(QNapi::Object qAbility,
bool destroyAllowed)
490 auto abilityPeer = qAbilityInstancesManager().tryGetQAbilityPeerByInstance(qAbility);
493 "%s: no QAbilityPeer for the given qAbility, flag not set to %s",
497 abilityPeer->destroyAllowedFlag()->store(destroyAllowed);
500void JsStateImpl::startAppProcess(
501 const std::string &processId, QNapi::Object requestWant,
502 QNapi::Object optStartOptions, std::function<
void(QOhosJsState &)> continueFunc)
504 auto optBaseQAbility = defaultQAbility();
505 if (optBaseQAbility) {
506 m_appFunctions->startAppProcess(
507 optBaseQAbility.value(), processId, requestWant, optStartOptions, std::move(continueFunc));
509 invokeTask(
std::move(continueFunc));
513std::shared_ptr<
void> JsStateImpl::registerNewWantConsumer(
514 QOhosConsumer<QOhosJsState &, QNapi::Object, QNapi::Object, QNapi::Object> wantConsumer)
516 const auto consumerId = m_nextNewWantConsumerId++;
517 m_newWantConsumers.emplace(consumerId, moveToSharedPtr(std::move(wantConsumer)));
518 return makeDestroyNotifier(
519 [
this, consumerId]() {
520 m_newWantConsumers.erase(consumerId);
524void JsStateImpl::startNoUiChildProcess(
const std::string &libraryName,
const std::vector<std::string> &args)
526 m_appFunctions->startNoUiChildProcess(*
this, libraryName, args);
536 return QAbilityInstancesManager::instance(*
this);
539void *JsStateImpl::getAttachedObjectWithLazyCreate(
540 const std::type_info &objectTypeInfo, QOhosSupplier<std::shared_ptr<
void>> objectFactory)
542 auto objectIter = m_attachedObjects.find(objectTypeInfo);
543 if (objectIter == m_attachedObjects.end())
544 std::tie(objectIter,
std::ignore) = m_attachedObjects.emplace(objectTypeInfo, objectFactory());
546 return objectIter->second.get();
549std::tuple<QNapi::Object, std::string> JsStateImpl::extractModuleFromEvalExpr(
const std::string &expr)
551 using namespace std::string_literals;
553 std::size_t longestModulePrefixSize = 0;
554 auto considerAsLongestPrefix = [&](
const std::string &name) {
555 if (name.size() > longestModulePrefixSize
556 && expr.compare(0, name.size(), name) == 0
557 && (expr.size() == name.size() || expr[name.size()] ==
'.')) {
558 longestModulePrefixSize = name.size();
561 for (
const auto &moduleFactoryEntry : m_jsModulesFactories)
562 considerAsLongestPrefix(moduleFactoryEntry.first);
564 if (longestModulePrefixSize == 0) {
565 throw QNapi::makeLoggedException(
566 env(),
"global expression doesn't start with known module path: '"s + expr +
"'");
569 return std::make_tuple(
570 getModule(expr.substr(0, longestModulePrefixSize)),
571 longestModulePrefixSize < expr.size()
572 ? expr.substr(longestModulePrefixSize + 1)
576QNapi::Number JsStateImpl::mapOhosEnumToJs(
577 int enumValue,
const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)())
579 using namespace std::string_literals;
581 const auto &enumeratorsValues = getOhosEnumEnumerators(enumTypeInfo, ohosEnumInfoFactory);
583 auto valueIter =
std::find_if(
584 enumeratorsValues.begin(), enumeratorsValues.end(),
585 [&](
const auto &valuePair) {
586 return valuePair.first == enumValue;
589 if (valueIter == enumeratorsValues.end()) {
590 throw QNapi::makeLoggedException(
592 "Illegal C++ value of enumerator for enum '"s + enumTypeInfo.name() +
"': "
593 + std::to_string(enumValue));
596 return QNapi::Number::New(env(), valueIter->second);
599std::optional<
int> JsStateImpl::tryMapOhosEnumFromJs(
600 QNapi::Number enumJsValue,
const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)())
602 double enumDoubleJsValue = enumJsValue.DoubleValue();
604 const auto &enumeratorsValues = getOhosEnumEnumerators(enumTypeInfo, ohosEnumInfoFactory);
606 auto valueIter =
std::find_if(
607 enumeratorsValues.begin(), enumeratorsValues.end(),
608 [&](
const auto &valuePair) {
609 return valuePair.second == enumDoubleJsValue;
612 return valueIter != enumeratorsValues.end()
613 ?
std::optional(valueIter->first)
617int JsStateImpl::mapOhosEnumFromJs(
618 QNapi::Number enumJsValue,
const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)())
620 using namespace std::string_literals;
622 auto optEnumValue = tryMapOhosEnumFromJs(enumJsValue, enumTypeInfo, ohosEnumInfoFactory);
623 if (!optEnumValue.has_value()) {
624 throw QNapi::makeLoggedException(
626 "Illegal Napi value of enumerator for enum '"s + enumTypeInfo.name() +
"': "
627 + std::to_string(enumJsValue.DoubleValue()));
629 return optEnumValue.value();
632QNapi::Symbol JsStateImpl::getJsSymbolForType(
const std::type_info &typeInfo)
634 using namespace std::string_literals;
636 auto jsSymbolRefIter = m_jsSymbolsRefs.find(typeInfo);
637 if (jsSymbolRefIter == m_jsSymbolsRefs.end()) {
638 std::tie(jsSymbolRefIter,
std::ignore) = m_jsSymbolsRefs.emplace(
640 QNapi::Reference<>::makePersistentFrom(
641 QNapi::Symbol::New(env(),
"_io_qt_"s + typeInfo.name())));
643 return jsSymbolRefIter->second.Value();
646const std::vector<std::pair<
int,
double>> &JsStateImpl::getOhosEnumEnumerators(
647 const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)())
649 auto enumeratorsIter = m_ohosEnumsEnumerators.find(enumTypeInfo);
650 if (enumeratorsIter == m_ohosEnumsEnumerators.end()) {
651 std::tie(enumeratorsIter,
std::ignore) = m_ohosEnumsEnumerators.emplace(
652 enumTypeInfo, resolveOhosEnumEnumerators(ohosEnumInfoFactory()));
655 return enumeratorsIter->second;
658std::vector<std::pair<
int,
double>> JsStateImpl::resolveOhosEnumEnumerators(
const OhosEnumInfo &enumInfo)
660 using namespace std::string_literals;
662 QNapi::Object enumObj;
664 enumObj = eval<QNapi::Object>(enumInfo.fullTypeName);
665 }
catch (
const Napi::Error &e) {
666 throw QNapi::makeLoggedException(
667 env(),
"JS enum '"s + enumInfo.fullTypeName +
"' not found: "s + e.what());
670 std::vector<std::pair<
int,
double>> enumeratorsValues;
672 for (
const auto &enumNamePair : enumInfo.enumeratorsNames) {
673 const auto *enumeratorName = enumNamePair.second;
675 QNapi::Value jsEnumeratorValue = QNapi::getPropOrUndefined(enumObj, enumeratorName);
677 if (!jsEnumeratorValue.IsNumber()) {
678 throw QNapi::makeLoggedException(
679 env(),
"JS enum '"s + enumInfo.fullTypeName +
"' doesn't contain: "s + enumeratorName);
682 enumeratorsValues.emplace_back(
683 enumNamePair.first, QNapi::checkedCast<QNapi::Number>(jsEnumeratorValue).DoubleValue());
686 return enumeratorsValues;
689JsStateImpl &getJsStateImpl()
691 static JsStateImpl jsStateImpl;
697 static auto qtJsBlockingCallsGateway = QOhosMtBlockingCallsGateway<JsState>::makeInstance(
699 [](std::function<
void(JsState &)> task) {
700 getJsStateImpl().invokeTask(std::move(task));
702 return *qtJsBlockingCallsGateway;
705class JsThreadOpsImpl :
public ::QOhosJsThreadOps
708 QOhosJsState &jsState()
override;
710 void invoke(std::function<
void(QOhosJsState &)> &&task)
override;
711 void invokeAndWaitForContinue(
712 std::function<
void(QOhosJsState &, QOhosTaskPromise<>)> &&task,
713 std::string callerContextName)
override;
715 const std::function<
void(QOhosJsState &)> &task,
716 std::string callerContextName)
override;
719QOhosJsState &JsThreadOpsImpl::jsState()
721 return getJsStateImpl();
724void JsThreadOpsImpl::invoke(std::function<
void(QOhosJsState &)> &&task)
726 getJsStateImpl().invokeTask(
727 [sharedTask = takeFunctionToSharedPtr(task)](
JsState &jsState) {
728 const auto takenTask = std::exchange(*sharedTask,
nullptr);
733void JsThreadOpsImpl::invokeAndWaitForContinue(
734 std::function<
void(QOhosJsState &, QOhosTaskPromise<>)> &&task,
735 std::string callerContextName)
737 const auto funcInfo = Q_FUNC_INFO;
739 struct JsTaskCompletionState
742 bool outerTaskPromiseSettled =
false;
743 std::optional<
std::string> jsThreadExceptionMsg;
745 auto completionState =
std::make_shared<JsTaskCompletionState>();
747 auto catchingTaskWrapper =
748 [task = std::move(task), completionState, callerContextName, funcInfo](
749 JsState &jsState, QOhosTaskPromise<> outerTaskPromise) {
750 auto sharedOuterTaskPromise =
751 std::make_shared<QOhosTaskPromise<>>(std::move(outerTaskPromise));
753 auto settleOuterTaskPromise =
754 [completionState, sharedOuterTaskPromise](
std::optional<
std::string> exceptionMsg) {
756 std::lock_guard<
std::mutex> lock(completionState->mutex);
757 if (completionState->outerTaskPromiseSettled)
759 completionState->outerTaskPromiseSettled =
true;
760 completionState->jsThreadExceptionMsg =
std::move(exceptionMsg);
762 (*sharedOuterTaskPromise)();
765 auto innerTaskPromise = QOhosTaskPromise<>(
766 [settleOuterTaskPromise]() {
767 settleOuterTaskPromise({});
769 [funcInfo, callerContextName]() {
770 if (
std::uncaught_exceptions() == 0) {
771 qOhosReportFatalErrorAndAbort(
772 "%s: promise destroyed without notifying the caller: %s",
773 funcInfo, callerContextName.c_str());
779 task(jsState,
std::move(innerTaskPromise));
780 }
catch (
const std::exception &e) {
781 settleOuterTaskPromise(std::optional<std::string>(e.what()));
783 settleOuterTaskPromise(std::optional<std::string>(
"<unknown exception>"));
787 if (getQtState().isQtThread()) {
788 getQtJsBlockingCallsGateway().runInSlaveThreadAndWaitForContinue(
789 std::move(catchingTaskWrapper), callerContextName);
791 auto taskReadyPromise =
std::make_shared<
std::promise<
void>>();
792 auto taskReadyFuture = taskReadyPromise->get_future();
794 auto sharedTaskPromise = std::make_shared<QOhosTaskPromise<>>(
795 [taskReadyPromise]() {
796 taskReadyPromise->set_value();
798 [funcInfo, callerContextName]() {
799 qOhosReportFatalErrorAndAbort(
800 "%s: promise destroyed without notifying the caller: %s",
801 funcInfo, callerContextName.c_str());
805 getJsStateImpl().invokeTask(
806 [catchingTaskWrapper = std::move(catchingTaskWrapper), sharedTaskPromise](
JsState &jsState) {
807 catchingTaskWrapper(jsState,
std::move(*sharedTaskPromise));
809 taskReadyFuture.wait();
812 if (completionState->jsThreadExceptionMsg) {
813 qOhosReportFatalErrorAndAbort(
814 "%s: exception from task invoked in JS thread (caller: \"%s\"): %s",
815 Q_FUNC_INFO, callerContextName.c_str(),
816 completionState->jsThreadExceptionMsg.value().c_str());
820void JsThreadOpsImpl::runAndWait(
821 const std::function<
void(QOhosJsState &)> &task,
822 std::string callerContextName)
824 if (getJsStateImpl().isJsThread()) {
825 task(getJsStateImpl());
827 invokeAndWaitForContinue(
828 [&](
auto &jsState,
auto taskPromise) {
832 std::move(callerContextName));
852 return matchTypeIdObject == &typeIdObject ?
this :
nullptr;
862 qAbilityPeer->tryCastWithTypeIdObject(&typeIdObject));
864 return qUiAbilityPeer !=
nullptr
865 ? qUiAbilityPeer->shared_from_this()
875 if (&jsState != &getJsStateImpl())
876 qOhosReportFatalErrorAndAbort(
"%s: jsState is not the plugin's JsState", Q_FUNC_INFO);
878 return static_cast<
JsState &>(jsState);
883 return getJsStateImpl();
888 qOhosPrintfDebug(
"Tagging JS Window as closing (from: %s)", logContext);
889 Napi::HandleScope funcScope{jsWindow.Env()};
891 getJsWindowsTrackerIsClosingPropSymbol(getJsStateImpl()),
892 QNapi::Boolean::New(jsWindow.Env(),
true));
897 Napi::HandleScope funcScope{jsWindow.Env()};
898 auto isClosingPropSymbol = getJsWindowsTrackerIsClosingPropSymbol(getJsStateImpl());
899 auto isClosingValue = jsWindow.Has(isClosingPropSymbol)
900 ? jsWindow.Get(isClosingPropSymbol)
902 return isClosingValue.IsBoolean() && QNapi::checkedCast<QNapi::Boolean>(isClosingValue).Value();
907 return getJsStateImpl().predefinedJsModuleNames();
914 static JsThreadOpsImpl jsThreadOpsImpl;
915 QOhosJsThreadOps::registerInstance(&jsThreadOpsImpl);
917 getJsStateImpl().initInJsThread(env,
std::move(jsModulesFactories), appFunctions, qtRunMode);
919 qOhosEnableMemoizingJsThreadFetchers(getJsStateImpl());
924 return getJsStateImpl().isJsThread();
927void dispatchNewWant(QNapi::Object qAbility, QNapi::Object want, QNapi::Object launchParam)
929 getJsStateImpl().dispatchNewWantInJsThread(qAbility, want, launchParam);
933 std::function<
void(
std::function<
void()>)> &&task,
934 std::chrono::nanoseconds timeout)
936 if (getJsStateImpl().isJsThread()) {
937 auto result = getQtJsBlockingCallsGateway().tryInvokeInMasterThreadAndTryWaitForContinue(
std::move(task), timeout);
938 return result == QOhosMtBlockingCallsGateway<JsState>::MasterThreadTaskResult::Finished;
940 auto taskReadyPromise =
std::make_shared<
std::promise<
void>>();
941 auto taskReadyFuture = taskReadyPromise->get_future();
943 auto continueFunc = [taskReadyPromise]() {
944 taskReadyPromise->set_value();
948 [task = std::move(task), continueFunc = std::move(continueFunc)]()
mutable {
949 task(
std::move(continueFunc));
951 auto waitResult = taskReadyFuture.wait_for(timeout);
953 return waitResult ==
std::future_status::ready;
JsState & jsState() const
void * tryCastWithTypeIdObject(const void *matchTypeIdObject) final
static std::shared_ptr< QUiAbilityPeer > tryCastFromQAbilityPeerOrNull(std::shared_ptr< QAbilityPeer > qAbilityPeer)
~QUiAbilityPeer() override
std::string const char * mapBoolToTrueFalseStr(bool value)
std::vector< std::string > predefinedJsModuleNames()
void initJsThreadState(napi_env env, std::map< std::string, QNapi::Reference< QNapi::Function > > &&jsModulesFactories, std::shared_ptr< AppFunctions > appFunctions, QtRunMode qtRunMode)
Q_REQUIRED_RESULT bool tryInvokeInQtThreadAndTryWaitForContinue(std::function< void(std::function< void()>)> &&task, std::chrono::nanoseconds timeout)
QtOhosQpa::enums::ohos::app::ability::AbilityConstant::OnContinueResult QOhosAbilityOnContinueResult
void dispatchNewWant(QNapi::Object qAbility, QNapi::Object want, QNapi::Object launchParam)
virtual std::shared_ptr< QAbilityPeer > defaultQAbilityPeer()=0
virtual std::shared_ptr< QAbilityPeer > tryGetQAbilityPeerByInstanceId(const std::string &instanceId)=0