Revision control

Copy as Markdown

// Copyright (C) 2026 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qohosjstools_p.h"
#include <QtCore/private/qohoscommon_p.h>
#include <QtCore/private/qohoslogger_p.h>
#include <exception>
#include <iterator>
#include <vector>
QT_BEGIN_NAMESPACE
std::shared_ptr<void> registerQOhosOnOffMethodsBasedEventHandler(
QNapi::Object eventSourceObject, const std::string &eventTypeName,
QNapi::CallbackFuncWrapper eventHandler, QOhosOnOffMethodsBasedEventHandlerOptions options)
{
struct Context
{
std::function<QNapi::Value(const QNapi::CallbackInfo &)> eventHandler;
std::function<bool(QNapi::Object)> eventSourceAliveCheckFunc;
QNapi::Reference<QNapi::Value> optExtraOnArg;
QNapi::Reference<QNapi::Value> optExtraOffArg;
};
auto env = eventSourceObject.Env();
auto sharedContext = QtOhos::moveToSharedPtr(
Context{
.eventHandler = std::move(eventHandler.callbackFunc()),
.eventSourceAliveCheckFunc = options.optEventSourceAliveCheckFunc
? std::move(options.optEventSourceAliveCheckFunc)
: [](QNapi::Object) {
return true;
},
.optExtraOnArg = options.extraOnArg.has_value()
? QNapi::Reference<>::makePersistentFrom(
options.extraOnArg.value().mapToValue(env))
: QNapi::Reference<>::makeEmpty(),
.optExtraOffArg = options.extraOffArg.has_value()
? QNapi::Reference<>::makePersistentFrom(
options.extraOffArg.value().mapToValue(env))
: QNapi::Reference<>::makeEmpty(),
});
auto jsEventHandlerRef = QtOhos::moveToSharedPtr(
QNapi::Reference<>::makePersistentFrom(
QNapi::Function::New(
eventSourceObject.Env(),
[eventTypeName, weakContext = QtOhos::makeWeakPtr(sharedContext)](const QNapi::CallbackInfo &cbInfo) {
auto sharedContext = weakContext.lock();
if (sharedContext) {
return sharedContext->eventHandler(cbInfo);
} else {
qOhosPrintfWarning(
"%s: got unexpected '%s' event callback call for detached handler",
Q_FUNC_INFO, eventTypeName.c_str());
return cbInfo.Env().Undefined();
}
})));
std::vector<QNapi::ValueWrapper> onCallArgs;
onCallArgs.push_back(eventTypeName);
if (!sharedContext->optExtraOnArg.IsEmpty())
onCallArgs.push_back(sharedContext->optExtraOnArg.Value());
onCallArgs.push_back(jsEventHandlerRef->Value());
bool onCallSuccessful;
try {
eventSourceObject.eval("on(*)", onCallArgs);
onCallSuccessful = true;
} catch (const Napi::Error &error) {
onCallSuccessful = false;
if (options.optOnCallExceptionHandler) {
options.optOnCallExceptionHandler(error);
} else {
throw;
}
}
if (!onCallSuccessful)
return nullptr;
auto eventSourceWeakRef = QtOhos::moveToSharedPtr(Napi::Weak(eventSourceObject));
return QtOhos::makeDestroyNotifier(
[eventSourceWeakRef, eventTypeName, sharedContext, jsEventHandlerRef]() {
auto eventSourceValue = eventSourceWeakRef->Value();
if (eventSourceValue.IsObject()) {
auto eventSourceObject = QNapi::checkedCast<QNapi::Object>(eventSourceValue);
if (sharedContext->eventSourceAliveCheckFunc(eventSourceObject)) {
try {
std::vector<QNapi::ValueWrapper> offCallArgs;
offCallArgs.push_back(eventTypeName);
if (!sharedContext->optExtraOffArg.IsEmpty())
offCallArgs.push_back(sharedContext->optExtraOffArg.Value());
offCallArgs.push_back(jsEventHandlerRef->Value());
eventSourceObject.eval("off(*)", offCallArgs);
} catch (const Napi::Error &e) {
qOhosPrintfError(
"%s: got exception from off(%s, ...) call (ignoring): %s",
Q_FUNC_INFO, eventTypeName.c_str(), e.what());
}
} else {
qOhosPrintfDebug(
"%s: not calling off(%s, ...), event source 'considered' not alive",
Q_FUNC_INFO, eventTypeName.c_str());
}
} else {
qOhosPrintfDebug(
"%s: not calling off(%s, ...), event source not alive",
Q_FUNC_INFO, eventTypeName.c_str());
}
});
}
std::shared_ptr<void> registerOhosAppContextEnvironmentCallback(
QOhosJsState &jsState,
std::vector<std::pair<std::string, QNapi::CallbackFuncWrapper>> environmentCallbackMethods)
{
auto optQAbility = jsState.defaultQAbility();
if (!optQAbility.has_value())
return {};
auto environmentCallback = QNapi::makeObject(
jsState.env(),
std::vector<std::pair<std::string, QNapi::ValueWrapper>>(
std::make_move_iterator(environmentCallbackMethods.begin()),
std::make_move_iterator(environmentCallbackMethods.end())));
auto appContextRefPtr = QtOhos::moveToSharedPtr(
QNapi::Reference<>::makePersistentFrom(
optQAbility->eval<QNapi::Object>("context.getApplicationContext()")));
double environmentCallbackId = appContextRefPtr->eval<QNapi::Number>(
"on(*)",
{"environment", environmentCallback});
return std::shared_ptr<void>(
nullptr,
[environmentCallbackId, appContextRefPtr](auto) {
QOhosJsThreadGateway::runAndWait(
[&](QOhosJsState &) {
auto appContextRef = std::move(*appContextRefPtr);
appContextRef.eval(
"off(*)",
{"environment", environmentCallbackId});
},
Q_FUNC_INFO);
});
}
void runQOhosJsCodeLoggingExceptions(napi_env env, const char *callerContextName, const std::function<void()> &code)
{
Napi::Env napiEnv(env);
Napi::HandleScope codeScope(napiEnv);
try {
code();
} catch (const Napi::Error &error) {
qOhosPrintfWarning("%s: caught a JS error: %s", callerContextName, error.what());
} catch (const std::exception &e) {
qOhosPrintfWarning("%s: caught an exception: %s", callerContextName, e.what());
} catch (...) {
qOhosPrintfWarning("%s: caught an unknown exception", callerContextName);
}
if (napiEnv.IsExceptionPending()) {
auto pendingException = napiEnv.GetAndClearPendingException();
qOhosPrintfWarning("%s: cleared a pending JS exception: %s", callerContextName, pendingException.what());
}
}
QT_END_NAMESPACE