Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qohosplugincore.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <qohosplugincore.h>
5
7#include <QtCore/private/qohoslogger_p.h>
8#include <QtCore/private/qohosmemoizingjsthreadfetcher_p.h>
9#include <algorithm>
10#include <cstdint>
11#include <deque>
12#include <exception>
13#include <future>
14#include <map>
15#include <mutex>
16#include <napi.h>
17#include <napi/native_api.h>
18#include <optional>
19#include <qohosutils.h>
20#include <render/qohosjswindowregistry.h>
21#include <tuple>
22#include <typeindex>
23#include <vector>
24
25QT_BEGIN_NAMESPACE
26
27namespace QtOhos {
28
29namespace {
30
31constexpr const char *forceLoadJsModulesEnvVariableName = "IO__QT__OHOS__FORCE_LOAD_JS_MODULES";
32
33constexpr const char *napiLoadableModules[] = {
34 "@ohos.abilityAccessCtrl",
35 "@ohos.accessibility",
36 "@ohos.app.ability.AbilityConstant",
37 "@ohos.app.ability.ConfigurationConstant",
38 "@ohos.app.ability.childProcessManager",
39 "@ohos.app.ability.contextConstant",
40 "@ohos.app.ability.wantConstant",
41 "@ohos.arkui.uiExtension",
42 "@ohos.bluetooth.access",
43 "@ohos.bluetooth.connection",
44 "@ohos.bluetooth.socket",
45 "@ohos.bundle.bundleManager",
46 "@ohos.data.uniformTypeDescriptor",
47 "@ohos.deviceInfo",
48 "@ohos.display",
49 "@ohos.file.fileuri",
50 "@ohos.file.picker",
51 "@ohos.font",
52 "@ohos.geoLocationManager",
53 "@ohos.graphics.text",
54 "@ohos.i18n",
55 "@ohos.inputMethod",
56 "@ohos.intl",
57 "@ohos.multimedia.image",
58 "@ohos.multimedia.media",
59 "@ohos.multimodalInput.inputDevice",
60 "@ohos.multimodalInput.pointer",
61 "@ohos.net.connection",
62 "@ohos.notificationManager",
63 "@ohos.pasteboard",
64 "@ohos.resourceManager",
65 "@ohos.settings",
66 "@ohos.web.webview",
67 "@ohos.wifiManager",
68 "@ohos.window",
69};
70
71class DummyQAbilityPeer : public QAbilityPeer
72{
73public:
74 DummyQAbilityPeer() = default;
75
76 std::string instanceId() override;
77 QNapi::Object uiContext() override;
78 QNapi::Object qAbility() override;
79 QNapi::Object launchWant() override;
80 QObjectThreadSafeRef qWindowRef() override;
81 std::optional<QNapi::Promise> qWindowDestroyPromise() override;
82 void forceResolveQWindowDestroyPromiseIfPresent(Napi::Env env) override;
83 std::shared_ptr<std::atomic_bool> destroyAllowedFlag() override;
84
85 void setQWindow(Napi::Env env, QObjectThreadSafeRef) override;
86
87 void *tryCastWithTypeIdObject(const void *matchTypeIdObject) override;
88};
89
90std::string DummyQAbilityPeer::instanceId()
91{
92 return {};
93}
94
95QNapi::Object DummyQAbilityPeer::uiContext()
96{
97 return QNapi::Object();
98}
99
100QNapi::Object DummyQAbilityPeer::qAbility()
101{
102 return QNapi::Object();
103}
104
105QNapi::Object DummyQAbilityPeer::launchWant()
106{
107 return QNapi::Object();
108}
109
110QObjectThreadSafeRef DummyQAbilityPeer::qWindowRef()
111{
112 return {};
113}
114
115std::optional<QNapi::Promise> DummyQAbilityPeer::qWindowDestroyPromise()
116{
117 return std::nullopt;
118}
119
120void DummyQAbilityPeer::forceResolveQWindowDestroyPromiseIfPresent(Napi::Env)
121{
122}
123
124std::shared_ptr<std::atomic_bool> DummyQAbilityPeer::destroyAllowedFlag()
125{
126 return std::make_shared<std::atomic_bool>(false);
127}
128
129void DummyQAbilityPeer::setQWindow(Napi::Env, QObjectThreadSafeRef)
130{
131}
132
133void *DummyQAbilityPeer::tryCastWithTypeIdObject(const void *)
134{
135 return nullptr;
136}
137
138template<typename Task>
139class PreQueuingTasksExecutor
140{
141public:
142 void setUnderlyingExecutor(
143 std::function<void(Task)> executor,
144 std::function<void(Task)> optSyncFlushExecutor = nullptr);
145 void invokeTask(Task task);
146
147private:
148 std::recursive_mutex m_executorMutex;
149 std::function<void(Task)> m_optUnderlyingExecutor;
150 std::deque<Task> m_pendingTasks;
151};
152
153template<typename Task>
154void PreQueuingTasksExecutor<Task>::setUnderlyingExecutor(
155 std::function<void(Task)> executor, std::function<void(Task)> optSyncFlushExecutor)
156{
157 std::lock_guard<std::recursive_mutex> executorLock(m_executorMutex);
158 auto &flushExecutor = optSyncFlushExecutor ? optSyncFlushExecutor : executor;
159 while (!m_pendingTasks.empty()) {
160 auto task = std::move(m_pendingTasks.front());
161 m_pendingTasks.pop_front();
162 flushExecutor(std::move(task));
163 }
164 m_optUnderlyingExecutor = std::move(executor);
165}
166
167template<typename Task>
168void PreQueuingTasksExecutor<Task>::invokeTask(Task task)
169{
170 std::lock_guard<std::recursive_mutex> executorLock(m_executorMutex);
171 if (m_optUnderlyingExecutor)
172 m_optUnderlyingExecutor(std::move(task));
173 else
174 m_pendingTasks.push_back(std::move(task));
175}
176
177using PreQueuingJsTasksExecutor = PreQueuingTasksExecutor<std::function<void(JsState &)>>;
178
179std::function<void(std::function<void(JsState &)>)> makeJsThreadTasksExecutor(JsState *jsState)
180{
181 struct Context {
182 std::mutex pendingTasksMutex;
183 bool pendingTasksRunnerActive = false;
184 std::vector<std::function<void(JsState &)>> pendingTasks;
185 Napi::ThreadSafeFunction executorThreadSafeFunc;
186 };
187 auto ctx = std::make_shared<Context>();
188
189 ctx->executorThreadSafeFunc = Napi::ThreadSafeFunction::New(
190 jsState->env(),
191 QNapi::Function::New(
192 jsState->env(),
193 [ctx = ctx.get()](const CallbackInfo &cbInfo) {
194 while (true) {
195 std::vector<std::function<void(JsState &)>> pendingTasks;
196 {
197 std::lock_guard<std::mutex> pendingTasksLock(ctx->pendingTasksMutex);
198 pendingTasks = std::exchange(ctx->pendingTasks, {});
199 if (pendingTasks.empty()) {
200 ctx->pendingTasksRunnerActive = false;
201 break;
202 }
203 }
204 for (auto &task : pendingTasks)
205 task(cbInfo.jsState());
206 }
207 },
208 "QCoreOhosTasksExecutorFunc"),
209 "QCoreOhosTasksExecutor", 0, 1);
210
211 return [ctx](std::function<void(JsState &)> task) {
212 std::lock_guard<std::mutex> pendingTasksLock(ctx->pendingTasksMutex);
213 ctx->pendingTasks.push_back(std::move(task));
214 if (!ctx->pendingTasksRunnerActive) {
215 ctx->executorThreadSafeFunc.NonBlockingCall();
216 ctx->pendingTasksRunnerActive = true;
217 }
218 };
219}
220
221QNapi::Symbol getJsWindowsTrackerIsClosingPropSymbol(JsState &jsState)
222{
223 struct SymbolTag
224 {
225 };
226
227 return jsState.getJsSymbolForType<SymbolTag>();
228}
229
230std::optional<QNapi::Object> tryLoadJsModuleViaNapi(napi_env env, const std::string &moduleName)
231{
232 qOhosPrintfDebug("%s: loading napi module '%s' via napi_load_module()", Q_FUNC_INFO, moduleName.c_str());
233
234 napi_value result = nullptr;
235 auto status = napi_load_module(env, moduleName.c_str(), &result);
236 if (status != napi_ok) {
237 Napi::Env napiEnv(env);
238 if (napiEnv.IsExceptionPending())
239 napiEnv.GetAndClearPendingException();
240 qOhosPrintfWarning(
241 "%s: napi_load_module failed for module '%s' (status=%d)",
242 Q_FUNC_INFO, moduleName.c_str(), static_cast<int>(status));
243 return std::nullopt;
244 }
245
246 Napi::Value resultValue(env, result);
247 if (!resultValue.IsObject()) {
248 qOhosPrintfWarning(
249 "%s: napi_load_module returned %s instead of an object for module '%s'",
250 Q_FUNC_INFO, QNapi::getValueTypeString(resultValue).c_str(), moduleName.c_str());
251 return std::nullopt;
252 }
253
254 return QNapi::Object(env, result);
255}
256
257QNapi::Object loadJsModuleViaNapiOrFail(napi_env env, const std::string &moduleName)
258{
259 auto optModule = tryLoadJsModuleViaNapi(env, moduleName);
260 if (!optModule)
261 qOhosReportFatalErrorAndAbort("%s: required module '%s' not loaded", Q_FUNC_INFO, moduleName.c_str());
262
263 return optModule.value();
264}
265
266QNapi::Object loadJsModuleViaEtsFactoryOrFail(
267 const QNapi::Function &etsModuleFactory, const std::string &moduleName)
268{
269 qOhosPrintfDebug("%s: loading napi module '%s' via ets factory", Q_FUNC_INFO, moduleName.c_str());
270
271 auto result = etsModuleFactory.Call({});
272 if (!result.IsObject()) {
273 qOhosReportFatalErrorAndAbort(
274 "%s: ets factory for module '%s' did not return an object",
275 Q_FUNC_INFO, moduleName.c_str());
276 }
277
278 return QNapi::checkedCast<QNapi::Object>(result);
279}
280
281template<typename T>
282std::function<T(JsState &)> makeMemoizingJsValueSupplier(std::function<T(JsState &)> baseJsSupplier)
283{
284 auto memoizedValue = moveToSharedPtr(QNapi::Reference<T>());
285 return [baseJsSupplier = std::move(baseJsSupplier), memoizedValue](JsState &jsState) mutable {
286 if (memoizedValue->IsEmpty()) {
287 *memoizedValue = QNapi::Reference<T>::makePersistentFrom(baseJsSupplier(jsState));
288 baseJsSupplier = nullptr;
289 }
290 return memoizedValue->Value();
291 };
292}
293
294class JsStateImpl : public JsState
295{
296public:
297 JsStateImpl() = default;
298
299 void initInJsThread(
300 napi_env env, std::map<std::string, QNapi::Reference<QNapi::Function>> &&etsModulesFactories,
301 std::shared_ptr<AppFunctions> appFunctions, QtRunMode qtRunMode);
302
303 bool isJsThread();
304
305 void addQAbilityPeerInJsThread(std::shared_ptr<QAbilityPeer> qAbilityPeer);
306 void removeMatchingQAbilityPeerInJsThread(QNapi::Object qAbility);
307
308 void dispatchNewWantInJsThread(QNapi::Object qAbility, QNapi::Object want, QNapi::Object launchParam);
309
310 void invokeTask(std::function<void(JsState &)> &&task);
311
312 napi_env env() override;
313 QNapi::Object getModule(const std::string &moduleName);
314
315 QNapi::Object appLaunchWant() override;
316 std::optional<QNapi::Object> optAppLaunchParam() override;
317 std::optional<QNapi::Object> tryGetModule(const std::string &moduleName) override;
318
319 QNapi::Object defaultWindowStageOrEmpty() override;
320 QNapi::Object defaultUiContextOrEmpty() override;
321
322 std::shared_ptr<QAbilityPeer> defaultQAbilityPeer() override;
323 std::shared_ptr<QAbilityPeer> tryGetQAbilityPeerByInstanceId(const std::string &instanceId) override;
324 std::shared_ptr<QAbilityPeer> tryGetQAbilityPeerByInstance(QNapi::Object qAbility) override;
325 std::optional<QNapi::Object> tryGetJsWindowByQWindow(QObjectThreadSafeRef qwindow) override;
326 std::optional<QNapi::Object> tryGetQAbilityByQWindow(QObjectThreadSafeRef qwindow) override;
327 std::optional<QNapi::Object> defaultQAbility() override;
328
329 void visitEachQAbilityPeer(const std::function<void(std::shared_ptr<QAbilityPeer>)> &visitor) override;
330
331 void startNewQAbilityInstance(
332 QNapi::Object baseQAbility, QObjectThreadSafeRef qwindow,
333 QNapi::Object optStartOptions,
334 std::function<void(JsState &, std::shared_ptr<QAbilityPeer>)> startupNotifyFunc) override;
335
336 void startAppProcess(
337 const std::string &processId, QNapi::Object requestWant,
338 QNapi::Object optStartOptions, std::function<void(QOhosJsState &)> continueFunc) override;
339
340 std::shared_ptr<void> registerNewWantConsumer(
341 QOhosConsumer<QOhosJsState &, QNapi::Object, QNapi::Object, QNapi::Object> wantConsumer) override;
342
343 void setOnContinueRequestsHandler(
344 QNapi::Object qAbility,
345 std::function<void(QOhosJsState &, QNapi::Object, QOhosConsumer<QOhosJsState &, QNapi::Number>)> requestsHandler) override;
346
347 void setDestroyFromSystemAllowed(QNapi::Object qAbility, bool destroyAllowed) override;
348
349 void startNoUiChildProcess(const std::string &libraryName, const std::vector<std::string> &args) override;
350
351 QtRunMode qtRunMode() override;
352
353private:
354 template<typename PeerMatchFunc>
355 std::shared_ptr<QAbilityPeer> tryFindMatchingQAbilityPeer(PeerMatchFunc &&matchFunc);
356
357 void *getAttachedObjectWithLazyCreate(
358 const std::type_info &objectTypeInfo, QOhosSupplier<std::shared_ptr<void>> objectFactory) override;
359
360 std::tuple<QNapi::Object, std::string> extractModuleFromEvalExpr(const std::string &expr) override;
361 QNapi::Number mapOhosEnumToJs(int enumValue, const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)()) override;
362 std::optional<int> tryMapOhosEnumFromJs(QNapi::Number enumJsValue, const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)()) override;
363 int mapOhosEnumFromJs(QNapi::Number enumJsValue, const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)()) override;
364 QNapi::Symbol getJsSymbolForType(const std::type_info &typeInfo) override;
365
366 const std::vector<std::pair<int, double>> &getOhosEnumEnumerators(
367 const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)());
368 std::vector<std::pair<int, double>> resolveOhosEnumEnumerators(const OhosEnumInfo &enumInfo);
369
370 void forceLoadAllJsModulesIfRequested();
371
372 pthread_t m_jsThread = {};
373 napi_env m_env = nullptr;
374 QNapi::Reference<QNapi::Object> m_appLaunchWant;
375 QNapi::Reference<QNapi::Object> m_optAppLaunchParam;
376 std::shared_ptr<QAbilityPeer> m_defaultQAbilityPeer;
377 std::map<std::string, std::shared_ptr<QAbilityPeer>> m_qAbilityPeers;
378 std::map<std::string, std::function<QNapi::Object(JsState &)>> m_jsModulesFactories;
379 std::map<std::string, QNapi::Reference<QNapi::Object>> m_onDemandJsModules;
380 std::shared_ptr<AppFunctions> m_appFunctions;
381 PreQueuingJsTasksExecutor m_tasksExecutor;
382 std::map<std::uint64_t, std::shared_ptr<QOhosConsumer<QOhosJsState &, QNapi::Object, QNapi::Object, QNapi::Object>>> m_newWantConsumers;
383 std::uint64_t m_nextNewWantConsumerId = 1;
384 std::map<std::type_index, std::vector<std::pair<int, double>>> m_ohosEnumsEnumerators;
385 std::map<std::type_index, QNapi::Reference<QNapi::Symbol>> m_jsSymbolsRefs;
386 QtRunMode m_qtRunMode;
387 std::map<std::type_index, std::shared_ptr<void>> m_attachedObjects;
388};
389
390bool JsStateImpl::isJsThread()
391{
392 return pthread_self() == m_jsThread;
393}
394
395void JsStateImpl::initInJsThread(
396 napi_env env, std::map<std::string, QNapi::Reference<QNapi::Function>> &&etsModulesFactories,
397 std::shared_ptr<AppFunctions> appFunctions, QtRunMode qtRunMode)
398{
399 m_jsThread = pthread_self();
400 m_env = env;
401 m_defaultQAbilityPeer = std::make_shared<DummyQAbilityPeer>();
402 m_appFunctions = appFunctions;
403 m_qtRunMode = qtRunMode;
404
405 for (const char *moduleName : napiLoadableModules) {
406 m_jsModulesFactories.emplace(
407 moduleName,
408 makeMemoizingJsValueSupplier<QNapi::Object>(
409 [moduleName](JsState &jsState) {
410 return loadJsModuleViaNapiOrFail(jsState.env(), moduleName);
411 }));
412 }
413 for (auto &etsModulesFactoryEntry : etsModulesFactories) {
414 const std::string &moduleName = etsModulesFactoryEntry.first;
415 auto etsModuleFactory = moveToSharedPtr(std::move(etsModulesFactoryEntry.second));
416 m_jsModulesFactories.emplace(
417 moduleName,
418 makeMemoizingJsValueSupplier<QNapi::Object>(
419 [moduleName, etsModuleFactory](JsState &) {
420 return loadJsModuleViaEtsFactoryOrFail(etsModuleFactory->Value(), moduleName);
421 }));
422 }
423 m_jsModulesFactories.emplace(
424 "Global",
425 [](JsState &jsState) -> QNapi::Object {
426 return Napi::Env(jsState.env()).Global();
427 });
428
429 m_tasksExecutor.setUnderlyingExecutor(makeJsThreadTasksExecutor(this));
430}
431
432void JsStateImpl::addQAbilityPeerInJsThread(std::shared_ptr<QAbilityPeer> qAbilityPeer)
433{
434 forceLoadAllJsModulesIfRequested();
435
436 if (m_appLaunchWant.IsEmpty())
437 m_appLaunchWant = Napi::Persistent(qAbilityPeer->launchWant());
438
439 auto optQUiAbilityPeer = QUiAbilityPeer::tryCastFromQAbilityPeerOrNull(qAbilityPeer);
440 if (m_optAppLaunchParam.IsEmpty() && optQUiAbilityPeer) {
441 m_optAppLaunchParam = QNapi::Reference<>::makePersistentFrom(optQUiAbilityPeer->launchParam());
442 qOhosPrintfInfo(
443 "JsStateImpl: appLaunchParam set from Ability with instanceId='%s'",
444 qAbilityPeer->instanceId().c_str());
445 }
446
447 if (m_qAbilityPeers.empty())
448 m_defaultQAbilityPeer = qAbilityPeer;
449
450 m_qAbilityPeers.emplace(qAbilityPeer->instanceId(), qAbilityPeer);
451
452 qOhosPrintfDebug(
453 "JsStateImpl: added QAbilityPeer, instanceId: %s, qwindow: %s",
454 qAbilityPeer->instanceId().c_str(), qAbilityPeer->qWindowRef().refName().c_str());
455}
456
457void JsStateImpl::removeMatchingQAbilityPeerInJsThread(QNapi::Object qAbility)
458{
459 Napi::HandleScope funcScope{qAbility.Env()};
460
461 auto foundPeerIter = std::find_if(
462 m_qAbilityPeers.begin(), m_qAbilityPeers.end(),
463 [&](const auto &entry) {
464 return entry.second->qAbility() == qAbility;
465 });
466
467 if (foundPeerIter != m_qAbilityPeers.end()) {
468 auto removedPeer = foundPeerIter->second;
469 m_qAbilityPeers.erase(foundPeerIter);
470 if (m_defaultQAbilityPeer == removedPeer) {
471 m_defaultQAbilityPeer =
472 !m_qAbilityPeers.empty()
473 ? m_qAbilityPeers.begin()->second
474 : std::make_shared<DummyQAbilityPeer>();
475 }
476 qOhosPrintfDebug(
477 "JsStateImpl: removed QAbilityPeer, instanceId: %s, qwindow: %s",
478 removedPeer->instanceId().c_str(), removedPeer->qWindowRef().refName().c_str());
479 }
480}
481
482void JsStateImpl::dispatchNewWantInJsThread(
483 QNapi::Object qAbility, QNapi::Object want, QNapi::Object launchParam)
484{
485 std::vector<std::uint64_t> consumersIds;
486 for (const auto &consumerEntry : m_newWantConsumers)
487 consumersIds.push_back(consumerEntry.first);
488
489 for (auto consumerId : consumersIds) {
490 auto consumerIter = m_newWantConsumers.find(consumerId);
491 if (consumerIter != m_newWantConsumers.end()) {
492 auto consumer = consumerIter->second;
493 (*consumer)(*this, qAbility, want, launchParam);
494 }
495 }
496}
497
498void JsStateImpl::invokeTask(std::function<void(JsState &)> &&task)
499{
500 m_tasksExecutor.invokeTask(std::move(task));
501}
502
503napi_env JsStateImpl::env()
504{
505 return m_env;
506}
507
508void JsStateImpl::forceLoadAllJsModulesIfRequested()
509{
510 if (qEnvironmentVariableIntValue(forceLoadJsModulesEnvVariableName) == 0)
511 return;
512
513 qOhosPrintfDebug("%s: forcibly loading all JS modules", Q_FUNC_INFO);
514
515 for (const auto &moduleFactoryEntry : m_jsModulesFactories)
516 getModule(moduleFactoryEntry.first);
517}
518
519QNapi::Object JsStateImpl::getModule(const std::string &moduleName)
520{
521 using namespace std::string_literals;
522
523 auto moduleFactoryIter = m_jsModulesFactories.find(moduleName);
524 if (moduleFactoryIter == m_jsModulesFactories.end())
525 throw QNapi::makeLoggedException(m_env, "JS module not found: "s + moduleName);
526
527 return moduleFactoryIter->second(*this);
528}
529
530std::optional<QNapi::Object> JsStateImpl::tryGetModule(const std::string &moduleName)
531{
532 auto moduleFactoryIter = m_jsModulesFactories.find(moduleName);
533 if (moduleFactoryIter != m_jsModulesFactories.end())
534 return moduleFactoryIter->second(*this);
535
536 auto onDemandModuleIter = m_onDemandJsModules.find(moduleName);
537 if (onDemandModuleIter != m_onDemandJsModules.end())
538 return onDemandModuleIter->second.Value();
539
540 auto optModule = tryLoadJsModuleViaNapi(m_env, moduleName);
541 if (!optModule)
542 return std::nullopt;
543
544 m_onDemandJsModules.emplace(moduleName, QNapi::Reference<>::makePersistentFrom(optModule.value()));
545 return optModule;
546}
547
548QNapi::Object JsStateImpl::appLaunchWant()
549{
550 return m_appLaunchWant.Value();
551}
552
553std::optional<QNapi::Object> JsStateImpl::optAppLaunchParam()
554{
555 return !m_optAppLaunchParam.IsEmpty()
556 ? std::optional(m_optAppLaunchParam.Value())
557 : std::nullopt;
558}
559
560QNapi::Object JsStateImpl::defaultWindowStageOrEmpty()
561{
562 auto qUiAbilityPeer = QUiAbilityPeer::tryCastFromQAbilityPeerOrNull(m_defaultQAbilityPeer);
563 if (!qUiAbilityPeer)
564 return QNapi::Object();
565
566 return qUiAbilityPeer->windowStage();
567}
568
569QNapi::Object JsStateImpl::defaultUiContextOrEmpty()
570{
571 return m_defaultQAbilityPeer->uiContext();
572}
573
574std::shared_ptr<QAbilityPeer> JsStateImpl::defaultQAbilityPeer()
575{
576 return m_defaultQAbilityPeer;
577}
578
579std::shared_ptr<QAbilityPeer> JsStateImpl::tryGetQAbilityPeerByInstanceId(const std::string &instanceId)
580{
581 auto foundIter = m_qAbilityPeers.find(instanceId);
582 return foundIter != m_qAbilityPeers.end() ? foundIter->second : nullptr;
583}
584
585std::shared_ptr<QAbilityPeer> JsStateImpl::tryGetQAbilityPeerByInstance(QNapi::Object qAbility)
586{
587 Napi::HandleScope findPeerScope(qAbility.Env());
588 return tryFindMatchingQAbilityPeer(
589 [&](const auto &peer) {
590 return peer->qAbility() == qAbility;
591 });
592}
593
594std::optional<QNapi::Object> JsStateImpl::tryGetJsWindowByQWindow(QObjectThreadSafeRef qwindow)
595{
596 auto optJsWindowRef = QOhosJsWindowRegistry::instance(*this).tryFindJsWindowByQWindowRef(qwindow);
597 return optJsWindowRef ? std::optional(optJsWindowRef->jsObject()) : std::nullopt;
598}
599
600std::optional<QNapi::Object> JsStateImpl::tryGetQAbilityByQWindow(QObjectThreadSafeRef qwindow)
601{
602 auto qAbilityPeer = tryFindMatchingQAbilityPeer(
603 [&](const auto &peer) {
604 return peer->qWindowRef() == qwindow;
605 });
606 if (!qAbilityPeer) {
607 auto optJsWindowRef = QOhosJsWindowRegistry::instance(*this).tryFindJsWindowByQWindowRef(qwindow);
608 if (optJsWindowRef)
609 qAbilityPeer = tryGetQAbilityPeerByInstanceId(
610 optJsWindowRef->owningQAbilityInstanceId());
611 }
612 return qAbilityPeer ? std::optional(qAbilityPeer->qAbility()) : std::nullopt;
613}
614
615std::optional<QNapi::Object> JsStateImpl::defaultQAbility()
616{
617 auto qAbility = defaultQAbilityPeer()->qAbility();
618 return !qAbility.IsEmpty() ? std::optional(qAbility) : std::nullopt;
619}
620
621void JsStateImpl::setOnContinueRequestsHandler(
622 QNapi::Object qAbility,
623 std::function<void(QOhosJsState &, QNapi::Object, QOhosConsumer<QOhosJsState &, QNapi::Number>)> requestsHandler)
624{
626 tryGetQAbilityPeerByInstance(qAbility));
627 if (!uiAbilityPeer) {
628 qOhosPrintfError(
629 "%s: no QUiAbilityPeer for the given qAbility, handler not set", Q_FUNC_INFO);
630 return;
631 }
632
633 uiAbilityPeer->setOnContinueRequestsHandler(
634 [requestsHandler = std::move(requestsHandler)](
635 JsState &jsState, QNapi::Object wantParams,
636 QOhosConsumer<JsState &, QOhosAbilityOnContinueResult> resultConsumer) {
637 requestsHandler(
638 jsState, wantParams,
639 [resultConsumer = std::move(resultConsumer)](QOhosJsState &resultJsState, QNapi::Number result) {
640 auto optResultEnum = resultJsState.tryMapOhosEnumFromJs<QOhosAbilityOnContinueResult>(result);
641 if (!optResultEnum) {
642 qOhosPrintfWarning(
643 "%s: got invalid OnContinueResult value (%f) from requests handler, rejecting the request",
644 Q_FUNC_INFO, result.DoubleValue());
645 }
646 resultConsumer(
647 JsState::castFrom(resultJsState),
648 optResultEnum.value_or(QOhosAbilityOnContinueResult::REJECT));
649 });
650 });
651}
652
653void JsStateImpl::setDestroyFromSystemAllowed(QNapi::Object qAbility, bool destroyAllowed)
654{
655 auto abilityPeer = tryGetQAbilityPeerByInstance(qAbility);
656 if (!abilityPeer) {
657 qOhosPrintfWarning(
658 "%s: no QAbilityPeer for the given qAbility, flag not set to %s",
659 Q_FUNC_INFO, mapBoolToTrueFalseStr(destroyAllowed));
660 return;
661 }
662 abilityPeer->destroyAllowedFlag()->store(destroyAllowed);
663}
664
665void JsStateImpl::visitEachQAbilityPeer(const std::function<void(std::shared_ptr<QAbilityPeer>)> &visitor)
666{
667 auto qAbilityPeersSnapshot = m_qAbilityPeers;
668 for (const auto &qAbilityPeerEntry : qAbilityPeersSnapshot)
669 visitor(qAbilityPeerEntry.second);
670}
671
672void JsStateImpl::startNewQAbilityInstance(
673 QNapi::Object baseQAbility, QObjectThreadSafeRef qwindow,
674 QNapi::Object optStartOptions,
675 std::function<void(JsState &, std::shared_ptr<QAbilityPeer>)> startupNotifyFunc)
676{
677 m_appFunctions->startQAbilityInstance(
678 baseQAbility, qwindow, optStartOptions, std::move(startupNotifyFunc));
679}
680
681void JsStateImpl::startAppProcess(
682 const std::string &processId, QNapi::Object requestWant,
683 QNapi::Object optStartOptions, std::function<void(QOhosJsState &)> continueFunc)
684{
685 auto optBaseQAbility = defaultQAbility();
686 if (optBaseQAbility) {
687 m_appFunctions->startAppProcess(
688 optBaseQAbility.value(), processId, requestWant, optStartOptions, std::move(continueFunc));
689 } else {
690 invokeTask(std::move(continueFunc));
691 }
692}
693
694std::shared_ptr<void> JsStateImpl::registerNewWantConsumer(
695 QOhosConsumer<QOhosJsState &, QNapi::Object, QNapi::Object, QNapi::Object> wantConsumer)
696{
697 const auto consumerId = m_nextNewWantConsumerId++;
698 m_newWantConsumers.emplace(consumerId, moveToSharedPtr(std::move(wantConsumer)));
699 return makeDestroyNotifier(
700 [this, consumerId]() {
701 m_newWantConsumers.erase(consumerId);
702 });
703}
704
705void JsStateImpl::startNoUiChildProcess(const std::string &libraryName, const std::vector<std::string> &args)
706{
707 m_appFunctions->startNoUiChildProcess(*this, libraryName, args);
708}
709
710QtRunMode JsStateImpl::qtRunMode()
711{
712 return m_qtRunMode;
713}
714
715template<typename PeerMatchFunc>
716std::shared_ptr<QAbilityPeer> JsStateImpl::tryFindMatchingQAbilityPeer(PeerMatchFunc &&matchFunc)
717{
718 auto foundIter = std::find_if(
719 m_qAbilityPeers.begin(), m_qAbilityPeers.end(),
720 [&](const auto &entry) {
721 return matchFunc(entry.second);
722 });
723 return foundIter != m_qAbilityPeers.end() ? foundIter->second : nullptr;
724}
725
726void *JsStateImpl::getAttachedObjectWithLazyCreate(
727 const std::type_info &objectTypeInfo, QOhosSupplier<std::shared_ptr<void>> objectFactory)
728{
729 auto objectIter = m_attachedObjects.find(objectTypeInfo);
730 if (objectIter == m_attachedObjects.end())
731 std::tie(objectIter, std::ignore) = m_attachedObjects.emplace(objectTypeInfo, objectFactory());
732
733 return objectIter->second.get();
734}
735
736std::tuple<QNapi::Object, std::string> JsStateImpl::extractModuleFromEvalExpr(const std::string &expr)
737{
738 using namespace std::string_literals;
739
740 std::size_t longestModulePrefixSize = 0;
741 auto considerAsLongestPrefix = [&](const std::string &name) {
742 if (name.size() > longestModulePrefixSize
743 && expr.compare(0, name.size(), name) == 0
744 && (expr.size() == name.size() || expr[name.size()] == '.')) {
745 longestModulePrefixSize = name.size();
746 }
747 };
748 for (const auto &moduleFactoryEntry : m_jsModulesFactories)
749 considerAsLongestPrefix(moduleFactoryEntry.first);
750
751 if (longestModulePrefixSize == 0) {
752 throw QNapi::makeLoggedException(
753 env(), "global expression doesn't start with known module path: '"s + expr + "'");
754 }
755
756 return std::make_tuple(
757 getModule(expr.substr(0, longestModulePrefixSize)),
758 longestModulePrefixSize < expr.size()
759 ? expr.substr(longestModulePrefixSize + 1)
760 : "");
761}
762
763QNapi::Number JsStateImpl::mapOhosEnumToJs(
764 int enumValue, const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)())
765{
766 using namespace std::string_literals;
767
768 const auto &enumeratorsValues = getOhosEnumEnumerators(enumTypeInfo, ohosEnumInfoFactory);
769
770 auto valueIter = std::find_if(
771 enumeratorsValues.begin(), enumeratorsValues.end(),
772 [&](const auto &valuePair) {
773 return valuePair.first == enumValue;
774 });
775
776 if (valueIter == enumeratorsValues.end()) {
777 throw QNapi::makeLoggedException(
778 env(),
779 "Illegal C++ value of enumerator for enum '"s + enumTypeInfo.name() + "': "
780 + std::to_string(enumValue));
781 }
782
783 return QNapi::Number::New(env(), valueIter->second);
784}
785
786std::optional<int> JsStateImpl::tryMapOhosEnumFromJs(
787 QNapi::Number enumJsValue, const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)())
788{
789 double enumDoubleJsValue = enumJsValue.DoubleValue();
790
791 const auto &enumeratorsValues = getOhosEnumEnumerators(enumTypeInfo, ohosEnumInfoFactory);
792
793 auto valueIter = std::find_if(
794 enumeratorsValues.begin(), enumeratorsValues.end(),
795 [&](const auto &valuePair) {
796 return valuePair.second == enumDoubleJsValue;
797 });
798
799 return valueIter != enumeratorsValues.end()
800 ? std::optional(valueIter->first)
801 : std::nullopt;
802}
803
804int JsStateImpl::mapOhosEnumFromJs(
805 QNapi::Number enumJsValue, const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)())
806{
807 using namespace std::string_literals;
808
809 auto optEnumValue = tryMapOhosEnumFromJs(enumJsValue, enumTypeInfo, ohosEnumInfoFactory);
810 if (!optEnumValue.has_value()) {
811 throw QNapi::makeLoggedException(
812 enumJsValue.Env(),
813 "Illegal Napi value of enumerator for enum '"s + enumTypeInfo.name() + "': "
814 + std::to_string(enumJsValue.DoubleValue()));
815 }
816 return optEnumValue.value();
817}
818
819QNapi::Symbol JsStateImpl::getJsSymbolForType(const std::type_info &typeInfo)
820{
821 using namespace std::string_literals;
822
823 auto jsSymbolRefIter = m_jsSymbolsRefs.find(typeInfo);
824 if (jsSymbolRefIter == m_jsSymbolsRefs.end()) {
825 std::tie(jsSymbolRefIter, std::ignore) = m_jsSymbolsRefs.emplace(
826 typeInfo,
827 QNapi::Reference<>::makePersistentFrom(
828 QNapi::Symbol::New(env(), "_io_qt_"s + typeInfo.name())));
829 }
830 return jsSymbolRefIter->second.Value();
831}
832
833const std::vector<std::pair<int, double>> &JsStateImpl::getOhosEnumEnumerators(
834 const std::type_info &enumTypeInfo, OhosEnumInfo (*ohosEnumInfoFactory)())
835{
836 auto enumeratorsIter = m_ohosEnumsEnumerators.find(enumTypeInfo);
837 if (enumeratorsIter == m_ohosEnumsEnumerators.end()) {
838 std::tie(enumeratorsIter, std::ignore) = m_ohosEnumsEnumerators.emplace(
839 enumTypeInfo, resolveOhosEnumEnumerators(ohosEnumInfoFactory()));
840 }
841
842 return enumeratorsIter->second;
843}
844
845std::vector<std::pair<int, double>> JsStateImpl::resolveOhosEnumEnumerators(const OhosEnumInfo &enumInfo)
846{
847 using namespace std::string_literals;
848
849 QNapi::Object enumObj;
850 try {
851 enumObj = eval<QNapi::Object>(enumInfo.fullTypeName);
852 } catch (const Napi::Error &e) {
853 throw QNapi::makeLoggedException(
854 env(), "JS enum '"s + enumInfo.fullTypeName + "' not found: "s + e.what());
855 }
856
857 std::vector<std::pair<int, double>> enumeratorsValues;
858
859 for (const auto &enumNamePair : enumInfo.enumeratorsNames) {
860 const auto *enumeratorName = enumNamePair.second;
861
862 QNapi::Value jsEnumeratorValue = QNapi::getPropOrUndefined(enumObj, enumeratorName);
863
864 if (!jsEnumeratorValue.IsNumber()) {
865 throw QNapi::makeLoggedException(
866 env(), "JS enum '"s + enumInfo.fullTypeName + "' doesn't contain: "s + enumeratorName);
867 }
868
869 enumeratorsValues.emplace_back(
870 enumNamePair.first, QNapi::checkedCast<QNapi::Number>(jsEnumeratorValue).DoubleValue());
871 }
872
873 return enumeratorsValues;
874}
875
876JsStateImpl &getJsStateImpl()
877{
878 static JsStateImpl jsStateImpl;
879 return jsStateImpl;
880}
881
882QOhosMtBlockingCallsGateway<JsState> &getQtJsBlockingCallsGateway()
883{
884 static auto qtJsBlockingCallsGateway = QOhosMtBlockingCallsGateway<JsState>::makeInstance(
885 invokeInQtThread,
886 [](std::function<void(JsState &)> task) {
887 getJsStateImpl().invokeTask(std::move(task));
888 });
889 return *qtJsBlockingCallsGateway;
890}
891
892class JsThreadOpsImpl : public ::QOhosJsThreadOps
893{
894public:
895 QOhosJsState &jsState() override;
896
897 void invoke(std::function<void(QOhosJsState &)> &&task) override;
898 void invokeAndWaitForContinue(
899 std::function<void(QOhosJsState &, QOhosTaskPromise<>)> &&task,
900 std::string callerContextName) override;
901 void runAndWait(
902 const std::function<void(QOhosJsState &)> &task,
903 std::string callerContextName) override;
904};
905
906QOhosJsState &JsThreadOpsImpl::jsState()
907{
908 return getJsStateImpl();
909}
910
911void JsThreadOpsImpl::invoke(std::function<void(QOhosJsState &)> &&task)
912{
913 getJsStateImpl().invokeTask(
914 [sharedTask = takeFunctionToSharedPtr(task)](JsState &jsState) {
915 const auto takenTask = std::exchange(*sharedTask, nullptr);
916 takenTask(jsState);
917 });
918}
919
920void JsThreadOpsImpl::invokeAndWaitForContinue(
921 std::function<void(QOhosJsState &, QOhosTaskPromise<>)> &&task,
922 std::string callerContextName)
923{
924 const auto funcInfo = Q_FUNC_INFO;
925
926 struct JsTaskCompletionState
927 {
928 std::mutex mutex;
929 bool outerTaskPromiseSettled = false;
930 std::optional<std::string> jsThreadExceptionMsg;
931 };
932 auto completionState = std::make_shared<JsTaskCompletionState>();
933
934 auto catchingTaskWrapper =
935 [task = std::move(task), completionState, callerContextName, funcInfo](
936 JsState &jsState, QOhosTaskPromise<> outerTaskPromise) {
937 auto sharedOuterTaskPromise =
938 std::make_shared<QOhosTaskPromise<>>(std::move(outerTaskPromise));
939
940 auto settleOuterTaskPromise =
941 [completionState, sharedOuterTaskPromise](std::optional<std::string> exceptionMsg) {
942 {
943 std::lock_guard<std::mutex> lock(completionState->mutex);
944 if (completionState->outerTaskPromiseSettled)
945 return;
946 completionState->outerTaskPromiseSettled = true;
947 completionState->jsThreadExceptionMsg = std::move(exceptionMsg);
948 }
949 (*sharedOuterTaskPromise)();
950 };
951
952 auto innerTaskPromise = QOhosTaskPromise<>(
953 [settleOuterTaskPromise]() {
954 settleOuterTaskPromise({});
955 },
956 [funcInfo, callerContextName]() {
957 if (std::uncaught_exceptions() == 0) {
958 qOhosReportFatalErrorAndAbort(
959 "%s: promise destroyed without notifying the caller: %s",
960 funcInfo, callerContextName.c_str());
961 }
962 },
963 callerContextName);
964
965 try {
966 task(jsState, std::move(innerTaskPromise));
967 } catch (const std::exception &e) {
968 settleOuterTaskPromise(std::optional<std::string>(e.what()));
969 } catch (...) {
970 settleOuterTaskPromise(std::optional<std::string>("<unknown exception>"));
971 }
972 };
973
974 if (getQtState().isQtThread()) {
975 getQtJsBlockingCallsGateway().runInSlaveThreadAndWaitForContinue(
976 std::move(catchingTaskWrapper), callerContextName);
977 } else {
978 auto taskReadyPromise = std::make_shared<std::promise<void>>();
979 auto taskReadyFuture = taskReadyPromise->get_future();
980
981 auto sharedTaskPromise = std::make_shared<QOhosTaskPromise<>>(
982 [taskReadyPromise]() {
983 taskReadyPromise->set_value();
984 },
985 [funcInfo, callerContextName]() {
986 qOhosReportFatalErrorAndAbort(
987 "%s: promise destroyed without notifying the caller: %s",
988 funcInfo, callerContextName.c_str());
989 },
990 callerContextName);
991
992 getJsStateImpl().invokeTask(
993 [catchingTaskWrapper = std::move(catchingTaskWrapper), sharedTaskPromise](JsState &jsState) {
994 catchingTaskWrapper(jsState, std::move(*sharedTaskPromise));
995 });
996 taskReadyFuture.wait();
997 }
998
999 if (completionState->jsThreadExceptionMsg) {
1000 qOhosReportFatalErrorAndAbort(
1001 "%s: exception from task invoked in JS thread (caller: \"%s\"): %s",
1002 Q_FUNC_INFO, callerContextName.c_str(),
1003 completionState->jsThreadExceptionMsg.value().c_str());
1004 }
1005}
1006
1007void JsThreadOpsImpl::runAndWait(
1008 const std::function<void(QOhosJsState &)> &task,
1009 std::string callerContextName)
1010{
1011 if (getJsStateImpl().isJsThread()) {
1012 task(getJsStateImpl());
1013 } else {
1014 invokeAndWaitForContinue(
1015 [&](auto &jsState, auto taskPromise) {
1016 task(jsState);
1017 taskPromise();
1018 },
1019 std::move(callerContextName));
1020 }
1021}
1022
1023}
1024
1025AppFunctions::~AppFunctions() = default;
1026
1027QAbilityPeer::~QAbilityPeer() = default;
1028
1029QAbilityPeer::QAbilityPeer() = default;
1030
1031QUiAbilityPeer::QUiAbilityPeer() = default;
1032
1033QUiAbilityPeer::~QUiAbilityPeer() = default;
1034
1035const nullptr_t QUiAbilityPeer::typeIdObject = {};
1036
1037void *QUiAbilityPeer::tryCastWithTypeIdObject(const void *matchTypeIdObject)
1038{
1039 return matchTypeIdObject == &typeIdObject ? this : nullptr;
1040}
1041
1043 std::shared_ptr<QAbilityPeer> qAbilityPeer)
1044{
1045 if (!qAbilityPeer)
1046 return nullptr;
1047
1048 auto qUiAbilityPeer = reinterpret_cast<QUiAbilityPeer *>(
1049 qAbilityPeer->tryCastWithTypeIdObject(&typeIdObject));
1050
1051 return qUiAbilityPeer != nullptr
1052 ? qUiAbilityPeer->shared_from_this()
1053 : nullptr;
1054}
1055
1056JsState::~JsState() = default;
1057
1058JsState::JsState() = default;
1059
1060JsState &JsState::castFrom(QOhosJsState &jsState)
1061{
1062 if (&jsState != &getJsStateImpl())
1063 qOhosReportFatalErrorAndAbort("%s: jsState is not the plugin's JsState", Q_FUNC_INFO);
1064
1065 return static_cast<JsState &>(jsState);
1066}
1067
1069{
1070 return getJsStateImpl();
1071}
1072
1073void JsWindowsTracker::tagWindowAsClosing(QNapi::Object jsWindow, const char *logContext)
1074{
1075 qOhosPrintfDebug("Tagging JS Window as closing (from: %s)", logContext);
1076 Napi::HandleScope funcScope{jsWindow.Env()};
1077 jsWindow.Set(
1078 getJsWindowsTrackerIsClosingPropSymbol(getJsStateImpl()),
1079 QNapi::Boolean::New(jsWindow.Env(), true));
1080}
1081
1082bool JsWindowsTracker::isWindowClosing(QNapi::Object jsWindow)
1083{
1084 Napi::HandleScope funcScope{jsWindow.Env()};
1085 auto isClosingPropSymbol = getJsWindowsTrackerIsClosingPropSymbol(getJsStateImpl());
1086 auto isClosingValue = jsWindow.Has(isClosingPropSymbol)
1087 ? jsWindow.Get(isClosingPropSymbol)
1088 : QNapi::Value();
1089 return isClosingValue.IsBoolean() && QNapi::checkedCast<QNapi::Boolean>(isClosingValue).Value();
1090}
1091
1095{
1096 static JsThreadOpsImpl jsThreadOpsImpl;
1097 QOhosJsThreadOps::registerInstance(&jsThreadOpsImpl);
1098
1099 getJsStateImpl().initInJsThread(env, std::move(jsModulesFactories), appFunctions, qtRunMode);
1100
1101 qOhosEnableMemoizingJsThreadFetchers(getJsStateImpl());
1102}
1103
1105{
1106 return getJsStateImpl().isJsThread();
1107}
1108
1109void addJsQAbilityPeer(std::shared_ptr<QAbilityPeer> qAbilityPeer)
1110{
1111 getJsStateImpl().addQAbilityPeerInJsThread(qAbilityPeer);
1112}
1113
1114void removeMatchingJsQAbilityPeer(QNapi::Object qAbility)
1115{
1116 getJsStateImpl().removeMatchingQAbilityPeerInJsThread(qAbility);
1117}
1118
1119void dispatchNewWant(QNapi::Object qAbility, QNapi::Object want, QNapi::Object launchParam)
1120{
1121 getJsStateImpl().dispatchNewWantInJsThread(qAbility, want, launchParam);
1122}
1123
1125 std::function<void(std::function<void()>)> &&task,
1126 std::chrono::nanoseconds timeout)
1127{
1128 if (getJsStateImpl().isJsThread()) {
1129 auto result = getQtJsBlockingCallsGateway().tryInvokeInMasterThreadAndTryWaitForContinue(std::move(task), timeout);
1130 return result == QOhosMtBlockingCallsGateway<JsState>::MasterThreadTaskResult::Finished;
1131 } else {
1132 auto taskReadyPromise = std::make_shared<std::promise<void>>();
1133 auto taskReadyFuture = taskReadyPromise->get_future();
1134
1135 auto continueFunc = [taskReadyPromise]() {
1136 taskReadyPromise->set_value();
1137 };
1138
1139 invokeInQtThread(
1140 [task = std::move(task), continueFunc = std::move(continueFunc)]() mutable {
1141 task(std::move(continueFunc));
1142 });
1143 auto waitResult = taskReadyFuture.wait_for(timeout);
1144
1145 return waitResult == std::future_status::ready;
1146 }
1147}
1148
1149}
1150
1151QT_END_NAMESPACE
JsState & jsState() const
~JsState() override
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)
void removeMatchingJsQAbilityPeer(QNapi::Object qAbility)
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)
bool isJsThread()
QtOhosQpa::enums::ohos::app::ability::AbilityConstant::OnContinueResult QOhosAbilityOnContinueResult
void addJsQAbilityPeer(std::shared_ptr< QAbilityPeer > qAbilityPeer)
void dispatchNewWant(QNapi::Object qAbility, QNapi::Object want, QNapi::Object launchParam)