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
qohoswindowproxydatafactory.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 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 <render/qohoswindowproxydatafactory.h>
5
6#include <QtCore/qscopeguard.h>
7#include <cstdint>
8#include <qarkui/qxcomponentregistry.h>
9#include <qohosqabilityinstancesmanager.h>
10#include <qohosutils.h>
11#include <render/qohoswindowproxy.h>
12#include <render/qxcomponent.h>
13
14QT_BEGIN_NAMESPACE
15
16namespace {
17
19{
20 static std::uint64_t uniqueIdSuffixCounter = 0;
21 auto result = QStringLiteral("QtWindow_%1_%2")
22 .arg(internalWindowId.toString())
23 .arg(QString::number(uniqueIdSuffixCounter));
24 ++uniqueIdSuffixCounter;
25 return result.toStdString();
26}
27
34
43
55
66
68 QtOhos::JsState &jsState, const std::string &qAbilityInstanceId)
69{
70 auto qAbilityPeer = QtOhos::QAbilityInstancesManager::instance(jsState).tryGetQAbilityPeerByInstanceId(qAbilityInstanceId);
71 if (!qAbilityPeer) {
72 qOhosReportFatalErrorAndAbort(
73 "Failed to find QAbilityPeer for qAbilityInstanceId: %s", qAbilityInstanceId.c_str());
74 }
75 return qAbilityPeer;
76}
77
79{
81 auto nativeNodeXComponentOpt = registry.tryTakeNodeByXComponentId(xComponentId);
82 if (!nativeNodeXComponentOpt.has_value()) {
83 qOhosReportFatalErrorAndAbort(
84 "Failed to fetch native node xcomponent with id: %s",
85 xComponentId.stringId().c_str());
86 }
87 return nativeNodeXComponentOpt.value();
88}
89
91{
92 return jsState.eval<QNapi::Object>("LocalStorage.makeNewLocalStorage()");
93}
94
95QNapi::Object makeRectObject(Napi::Env env, const QRect &rect)
96{
97 return QNapi::makeObject(
98 env,
99 {
100 {"left", rect.x()},
101 {"top", rect.top()},
102 {"width", rect.width()},
103 {"height", rect.height()},
104 });
105}
106
108 QNapi::Object windowStageOrWindowObject,
109 const std::string &windowName, const SubWindowOptions &subWindowOptions)
110{
111 auto subWindowOptionsObject =
112 QNapi::makeObject(
113 windowStageOrWindowObject.Env(),
114 {
115 {"title", subWindowOptions.windowTitle},
116 {"decorEnabled", subWindowOptions.decorEnabled},
117 {"isModal", subWindowOptions.isModal},
118 {
119 "windowRect", makeRectObject(windowStageOrWindowObject.Env(), subWindowOptions.windowRect)
120 }
121 });
122 if (subWindowOptions.maximizeSupported)
123 subWindowOptionsObject.set("maximizeSupported", true);
124
125 return windowStageOrWindowObject.evalToPromiseOrRejectOnThrow(
126 "createSubWindowWithOptions(*)",
127 {windowName, subWindowOptionsObject});
128}
129
130// Optionless sub window creation, used on phones where the SessionManager-only
131// createSubWindowWithOptions() is unavailable. WindowStage.createSubWindow() is @crossplatform.
133 QNapi::Object windowStageOrWindowObject, const std::string &windowName)
134{
135 return windowStageOrWindowObject.evalToPromiseOrRejectOnThrow(
136 "createSubWindow(*)", {windowName});
137}
138
139// createSubWindow() exists only on the WindowStage, not on a Window.
141 const std::shared_ptr<QtOhos::QAbilityPeer> &qAbilityPeer, const std::string &windowName)
142{
143 auto optQUiAbilityPeer = QtOhos::QUiAbilityPeer::tryCastFromQAbilityPeerOrNull(qAbilityPeer);
144 if (!optQUiAbilityPeer) {
145 qOhosReportFatalErrorAndAbort(
146 "%s sub window creation requires an ability with a windowStage. Aborting...",
147 Q_FUNC_INFO);
148 }
149 return createSubWindow(optQUiAbilityPeer->windowStage(), windowName);
150}
151
152// Query SystemCapability.Window.SessionManager (needed by createSubWindowWithOptions())
153// directly rather than inferring it from the device type.
155{
156 try {
157 return jsState.eval<QNapi::Boolean>(
158 "Global.canIUse(*)",
159 {std::string("SystemCapability.Window.SessionManager")}).Value();
160 } catch (const Napi::Error &error) {
161 qOhosPrintfWarning(
162 "canIUse('SystemCapability.Window.SessionManager') query failed (%s); assuming the "
163 "capability is unavailable and using createSubWindow()",
164 error.what());
165 return false;
166 }
167}
168
169std::function<void(const QtOhos::CallbackInfo &)>
171{
172 auto sharedContext = QtOhos::moveToSharedPtr(std::move(subWindowOnAppearCbCtx));
173 return [sharedContext](const QtOhos::CallbackInfo &cbInfo) mutable {
174 if (!sharedContext)
175 return;
176
177 auto xComponent = takeNodeXComponentFromRegistryOrFail(sharedContext->xComponentId);
178
179 sharedContext->resultConsumer(
180 cbInfo.jsState(),
181 QOhosWindowProxyData {
182 .qAbilityPeer = sharedContext->qAbilityPeer,
183 .jsWindow = std::move(sharedContext->windowObject),
184 .windowProxyType = sharedContext->windowProxyType,
185 .subWindowMaximizeSupported = sharedContext->subWindowMaximizeSupported,
186 .nodeXComponent = std::make_shared<QXComponentNode>(xComponent),
187 .jsKeepAliveData = QtOhos::moveToSharedPtr(std::move(sharedContext->localStorageObj)),
188 .owningQWindowRef = sharedContext->owningQWindowRef,
189 });
190
191 sharedContext.reset();
192 };
193}
194
195QNapi::Promise loadWindowContents(QNapi::Object window, QNapi::Object localStorage, const std::string &contentPagePath)
196{
197 return window.evalToPromiseOrRejectOnThrow("loadContent(*)", {contentPagePath, localStorage});
198}
199
201 QtOhos::JsState &jsState, LocalStorageForWindowCreateInfo &&createInfo)
202{
203 auto *env = jsState.env();
204
205 auto localStorage = makeLocalStorage(jsState);
206 auto subWindowNativeNodeCreateInfo = QNapi::makeObject(
207 env,
208 {
209 {"xComponentId", createInfo.xComponentId.toNapiValue(env)},
210 {
211 "onDisAppear",
212 [xComponentId = createInfo.xComponentId.stringId()]() {
213 qOhosPrintfDebug(
214 "WindowNativeNodeCreateInfo.onDisAppear() called from JS for xComponentId='%s'",
215 xComponentId.c_str());
216 }
217 },
218 {
219 "onAppear",
220 makeSubWindowOnAppearCallbackHandler(SubWindowOnAppearContext {
221 .xComponentId = createInfo.xComponentId,
222 .localStorageObj = Napi::Persistent(localStorage),
223 .windowObject = Napi::Persistent(createInfo.windowObject),
224 .resultConsumer = std::move(createInfo.resultConsumer),
225 .qAbilityPeer = std::move(createInfo.qAbilityPeer),
226 .windowProxyType = createInfo.windowProxyType,
227 .subWindowMaximizeSupported = createInfo.subWindowMaximizeSupported,
228 .owningQWindowRef = createInfo.owningQWindowRef,
229 }),
230 }
231 });
232
233 localStorage.eval("setOrCreate(*)", {"createInfo", subWindowNativeNodeCreateInfo});
234 return localStorage;
235}
236
238 QtOhos::JsState &, const QNapi::Object &windowObject,
240{
241 struct LoadWindowContentsArgs
242 {
243 QNapi::Reference<QNapi::Object> window;
244 QNapi::Reference<QNapi::Object> localStorage;
245 std::string contentPagePath;
246 };
247
248 if (!context.disableWindowFocusableBeforeLoadContentHack)
249 return loadWindowContents(windowObject, context.localStorage, context.contentPagePath);
250
251 auto loadWindowContentsArgs = LoadWindowContentsArgs {
252 .window = Napi::Persistent(windowObject),
253 .localStorage = Napi::Persistent(context.localStorage),
254 .contentPagePath = context.contentPagePath,
255 };
256
257 return windowObject.evalToPromiseOrRejectOnThrow("setWindowFocusable(*)", {false})
258 .onThen([loadWindowContentsArgs = std::move(loadWindowContentsArgs)]() {
259 return loadWindowContents(
260 loadWindowContentsArgs.window.Value(),
261 loadWindowContentsArgs.localStorage.Value(),
262 loadWindowContentsArgs.contentPagePath);
263 });
264}
265
266}
267
269 QtOhos::JsState &jsState,
270 const QOhosWindowProxyMainWindowCreateInfo &createInfo,
271 QOhosConsumer<QtOhos::JsState &, QOhosWindowProxyData> resultConsumer)
272{
273 auto optBaseQAbility = jsState.defaultQAbility();
274 if (!optBaseQAbility)
275 qOhosReportFatalErrorAndAbort("%s: can't create window without the default Ability instance", Q_FUNC_INFO);
276
277 auto abilityStartupOptions =
278 QNapi::makeObject(
279 jsState.env(),
280 {
281 {"windowLeft", createInfo.frameGeometry.x()},
282 {"windowTop", createInfo.frameGeometry.y()},
283 {"windowWidth", createInfo.frameGeometry.width()},
284 {"windowHeight", createInfo.frameGeometry.height()},
285 });
286
287 if (createInfo.fullscreen) {
288 auto fullscreenWindowMode = jsState.eval<QNapi::Number>(
289 "@ohos.app.ability.AbilityConstant.WindowMode.WINDOW_MODE_FULLSCREEN");
290 abilityStartupOptions.set("windowMode", fullscreenWindowMode);
291 }
292
293 QtOhos::QAbilityInstancesManager::instance(jsState).startNewInstance(
294 optBaseQAbility.value(), createInfo.qWindowRef,
295 abilityStartupOptions,
296 [qWindowRef = createInfo.qWindowRef,
297 windowId = createInfo.windowId,
298 resultConsumer = std::move(resultConsumer)](QtOhos::JsState &jsState, std::shared_ptr<QtOhos::QAbilityPeer> qAbilityPeer) {
299 auto createInfo = QOhosWindowProxyExistingMainWindowCreateInfo {
300 .qWindowRef = qWindowRef,
301 .qAbilityInstanceId = qAbilityPeer->instanceId(),
302 .windowId = windowId,
303 };
304
305 makeWindowProxyDataForExistingMainWindowInJsThread(
306 jsState, createInfo, std::move(resultConsumer));
307 });
308}
309
311 QtOhos::JsState &jsState,
313 QOhosConsumer<QtOhos::JsState &, QOhosWindowProxyData> resultConsumer)
314{
316 getQAbilityPeerByInstanceIdOrFail(jsState, createInfo.qAbilityInstanceId));
317 if (!optQUiAbilityPeer) {
318 qOhosReportFatalErrorAndAbort(
319 "%s Attempting to make window proxy for main window for ability without windowStage. This is most likely a programming error. Aborting...",
320 Q_FUNC_INFO);
321 }
322 optQUiAbilityPeer->setQWindow(jsState.env(), createInfo.qWindowRef);
323
324 auto window = optQUiAbilityPeer->window();
325 auto nativeNodeXComponentId = QXComponentId::createForNativeNodeMainWindow(createInfo.qAbilityInstanceId);
326 auto nodeXComponent = takeNodeXComponentFromRegistryOrFail(nativeNodeXComponentId);
327
328 resultConsumer(
329 jsState,
330 QOhosWindowProxyData {
331 .qAbilityPeer = optQUiAbilityPeer,
332 .jsWindow = Napi::Persistent(window),
333 .windowProxyType = WindowProxyType::MainWindow,
334 .nodeXComponent = std::make_shared<QXComponentNode>(nodeXComponent),
335 .jsKeepAliveData = nullptr,
336 .owningQWindowRef = createInfo.qWindowRef,
337 });
338}
339
341 QtOhos::JsState &jsState,
342 const QOhosWindowProxySubWindowCreateInfo &createInfo,
343 QOhosConsumer<QtOhos::JsState &, QOhosWindowProxyData> resultConsumer)
344{
345 auto qAbilityPeer = getQAbilityPeerByInstanceIdOrFail(jsState, createInfo.qAbilityInstanceId);
346 auto optQUiAbilityPeer = QtOhos::QUiAbilityPeer::tryCastFromQAbilityPeerOrNull(qAbilityPeer);
347 if (!optQUiAbilityPeer) {
348 qOhosReportFatalErrorAndAbort(
349 "%s Attempting to make window proxy for sub window for ability without windowStage. This is most likely a programming error. Aborting...",
350 Q_FUNC_INFO);
351 }
352 makeWindowProxyDataForSubWindowInJsThread(
353 jsState, optQUiAbilityPeer->windowStage(), createInfo, std::move(resultConsumer));
354}
355
357 QtOhos::JsState &jsState,
358 QNapi::Object windowStageOrWindowObject,
359 const QOhosWindowProxySubWindowCreateInfo &createInfo,
360 QOhosConsumer<QtOhos::JsState &, QOhosWindowProxyData> resultConsumer)
361{
362 auto xComponentId = QXComponentId::createForNativeNodeSubWindow(createInfo.windowId);
363 struct Context
364 {
365 bool disableWindowFocusableBeforeLoadContentHack;
366 QXComponentId xComponentId;
367 std::shared_ptr<QtOhos::QAbilityPeer> qAbilityPeer;
368 QtOhos::QObjectThreadSafeRef owningQWindowRef;
369 bool createdWithMaximizeSupport;
370 };
371
372 auto qAbilityPeer = getQAbilityPeerByInstanceIdOrFail(jsState, createInfo.qAbilityInstanceId);
373
374 const auto windowName = makeOhosUniqueSystemWindowName(createInfo.windowId);
375
376 auto context = QtOhos::moveToSharedPtr(Context {
377 .disableWindowFocusableBeforeLoadContentHack = createInfo.disableWindowFocusableBeforeLoadContentHack,
378 .xComponentId = xComponentId,
379 .qAbilityPeer = qAbilityPeer,
380 .owningQWindowRef = createInfo.window,
381 .createdWithMaximizeSupport = createInfo.maximizeSupported,
382 });
383
384 // Prefer createSubWindowWithOptions() when the capability is present, but fall back to the
385 // @crossplatform createSubWindow() when it is absent or fails (its dropped options are
386 // applied separately after creation anyway, except maximizeSupported) rather than aborting
387 // the application.
388 auto subWindowCreationPromise = [&]() -> QNapi::Promise {
389 if (!isWindowSessionManagerAvailable(jsState)) {
390 context->createdWithMaximizeSupport = false;
391 return createSubWindowViaWindowStage(qAbilityPeer, windowName);
392 }
393
394 return createSubWindowWithOptions(
395 windowStageOrWindowObject,
396 windowName,
397 SubWindowOptions {
398 .windowTitle = createInfo.windowTitle,
399 .decorEnabled = createInfo.decorEnabled,
400 .isModal = createInfo.modal,
401 .maximizeSupported = createInfo.maximizeSupported,
402 .windowRect = createInfo.windowRect,
403 })
404 .onCatch([context, qAbilityPeer, windowName](const QtOhos::CallbackInfo &) -> QNapi::Promise {
405 // Unsupported in some window contexts even when the capability is advertised.
406 qOhosPrintfWarning(
407 "createSubWindowWithOptions() is unavailable in this context; "
408 "falling back to createSubWindow()");
409 context->createdWithMaximizeSupport = false;
410 return createSubWindowViaWindowStage(qAbilityPeer, windowName);
411 });
412 }();
413
414 subWindowCreationPromise
415 .onThen([resultConsumer = std::move(resultConsumer), context](const QtOhos::CallbackInfo &cbInfo) mutable {
416 auto windowObject = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
417
418 auto localStorage = makeLocalStorageForWindow(
419 cbInfo.jsState(),
420 LocalStorageForWindowCreateInfo {
421 .xComponentId = context->xComponentId,
422 .windowObject = windowObject,
423 .resultConsumer = std::move(resultConsumer),
424 .qAbilityPeer = context->qAbilityPeer,
425 .windowProxyType = WindowProxyType::SubWindow,
426 .subWindowMaximizeSupported = context->createdWithMaximizeSupport,
427 .owningQWindowRef = context->owningQWindowRef,
428 });
429
430 return onWindowCreatedLoadWindowContents(
431 cbInfo.jsState(), windowObject,
432 OnWindowCreatedLoadWindowContentsContext {
433 .disableWindowFocusableBeforeLoadContentHack = context->disableWindowFocusableBeforeLoadContentHack,
434 .contentPagePath = "pages/SubWindowNativeNode",
435 .localStorage = localStorage,
436 });
437 })
438 .onCatch([windowId = createInfo.windowId](const QtOhos::CallbackInfo &cbInfo) {
439 QtOhos::logJsCallbackError(cbInfo, "sub window creation failed");
440 qOhosReportFatalErrorAndAbort(
441 "Failed to create subwindow for windowId='%s'",
442 windowId.toStdString().c_str());
443 });
444}
445
447 QtOhos::JsState &jsState, const QOhosWindowProxyFloatWindowCreateInfo &createInfo,
448 QOhosConsumer<QtOhos::JsState &, QOhosWindowProxyData> resultConsumer)
449{
450 auto xComponentId = QXComponentId::createForNativeNodeFloatWindow(createInfo.internalWindowId);
451 auto qAbilityPeer = QtOhos::QAbilityInstancesManager::instance(jsState).defaultQAbilityPeer();
452
453 if (qAbilityPeer->qAbility().IsEmpty())
454 qOhosReportFatalErrorAndAbort("%s: can't create window without the default Ability instance", Q_FUNC_INFO);
455
456 auto configurationObject = QNapi::makeObject(
457 jsState.env(),
458 {
459 // NOTE - The parameter name is misleading, what it refers to actually is (system) window id
460 {"name", makeOhosUniqueSystemWindowName(createInfo.internalWindowId)},
461 {"windowType", jsState.eval<QNapi::Number>("@ohos.window.WindowType.TYPE_FLOAT")},
462 {"ctx", qAbilityPeer->qAbility().get<QNapi::Object>("context")},
463 });
464
465 struct Context
466 {
467 QXComponentId xComponentId;
468 std::shared_ptr<QtOhos::QAbilityPeer> qAbilityPeer;
469 QtOhos::QObjectThreadSafeRef owningQWindowRef;
470 };
471
472 auto context = Context {
473 .xComponentId = xComponentId,
474 .qAbilityPeer = qAbilityPeer,
475 .owningQWindowRef = createInfo.qWindowRef,
476 };
477
478 jsState
479 .evalToPromiseOrRejectOnThrow("@ohos.window.createWindow(*)", {configurationObject})
480 .onThen([resultConsumer = std::move(resultConsumer), context = std::move(context)](const QtOhos::CallbackInfo &cbInfo) mutable {
481 auto windowObject = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
482
483 auto localStorage = makeLocalStorageForWindow(
484 cbInfo.jsState(),
485 LocalStorageForWindowCreateInfo {
486 .xComponentId = context.xComponentId,
487 .windowObject = windowObject,
488 .resultConsumer = std::move(resultConsumer),
489 .qAbilityPeer = context.qAbilityPeer,
490 .windowProxyType = WindowProxyType::FloatWindow,
491 .owningQWindowRef = context.owningQWindowRef,
492 });
493
494 return onWindowCreatedLoadWindowContents(
495 cbInfo.jsState(), windowObject,
496 OnWindowCreatedLoadWindowContentsContext {
497 .disableWindowFocusableBeforeLoadContentHack = false,
498 .contentPagePath = "pages/FloatWindowNativeNode",
499 .localStorage = localStorage,
500 });
501 })
502 .onCatch([internalWindowId = createInfo.internalWindowId](const QtOhos::CallbackInfo &cbInfo) {
503 QtOhos::logJsCallbackError(cbInfo, "Failed to create TYPE_FLOAT window");
504 qOhosReportFatalErrorAndAbort(
505 "Failed to create TYPE_FLOAT window for windowId='%s'",
506 internalWindowId.toStdString().c_str());
507 });
508}
509
510
511QT_END_NAMESPACE
std::optional< QXComponentNode > tryTakeNodeByXComponentId(const QXComponentId &id)
static QXComponentRegistry & instance()
static QXComponentId createForNativeNodeMainWindow(const std::string &qAbilityInstanceId)
std::string stringId() const
JsState & jsState() const
static std::shared_ptr< QUiAbilityPeer > tryCastFromQAbilityPeerOrNull(std::shared_ptr< QAbilityPeer > qAbilityPeer)
std::shared_ptr< QtOhos::QAbilityPeer > getQAbilityPeerByInstanceIdOrFail(QtOhos::JsState &jsState, const std::string &qAbilityInstanceId)
QNapi::Object makeRectObject(Napi::Env env, const QRect &rect)
bool isWindowSessionManagerAvailable(QtOhos::JsState &jsState)
QNapi::Promise createSubWindowWithOptions(QNapi::Object windowStageOrWindowObject, const std::string &windowName, const SubWindowOptions &subWindowOptions)
QXComponentNode takeNodeXComponentFromRegistryOrFail(const QXComponentId &xComponentId)
std::function< void(const QtOhos::CallbackInfo &)> makeSubWindowOnAppearCallbackHandler(SubWindowOnAppearContext subWindowOnAppearCbCtx)
QNapi::Object makeLocalStorage(QtOhos::JsState &jsState)
QNapi::Promise createSubWindowViaWindowStage(const std::shared_ptr< QtOhos::QAbilityPeer > &qAbilityPeer, const std::string &windowName)
QNapi::Promise onWindowCreatedLoadWindowContents(QtOhos::JsState &, const QNapi::Object &windowObject, OnWindowCreatedLoadWindowContentsContext context)
QNapi::Promise loadWindowContents(QNapi::Object window, QNapi::Object localStorage, const std::string &contentPagePath)
std::string makeOhosUniqueSystemWindowName(QtOhos::InternalWindowId internalWindowId)
QNapi::Promise createSubWindow(QNapi::Object windowStageOrWindowObject, const std::string &windowName)
QNapi::Object makeLocalStorageForWindow(QtOhos::JsState &jsState, LocalStorageForWindowCreateInfo &&createInfo)
void makeWindowProxyDataForFloatWindowInJsThread(QtOhos::JsState &jsState, const QOhosWindowProxyFloatWindowCreateInfo &createInfo, QOhosConsumer< QtOhos::JsState &, QOhosWindowProxyData > resultConsumer)
void makeWindowProxyDataForSubWindowInJsThread(QtOhos::JsState &jsState, QNapi::Object windowStageOrWindowObject, const QOhosWindowProxySubWindowCreateInfo &createInfo, QOhosConsumer< QtOhos::JsState &, QOhosWindowProxyData > resultConsumer)
void makeWindowProxyDataForSubWindowInJsThread(QtOhos::JsState &jsState, const QOhosWindowProxySubWindowCreateInfo &createInfo, QOhosConsumer< QtOhos::JsState &, QOhosWindowProxyData > resultConsumer)
void makeWindowProxyDataForExistingMainWindowInJsThread(QtOhos::JsState &jsState, const QOhosWindowProxyExistingMainWindowCreateInfo &createInfo, QOhosConsumer< QtOhos::JsState &, QOhosWindowProxyData > resultConsumer)
void makeWindowProxyDataForMainWindowInJsThread(QtOhos::JsState &jsState, const QOhosWindowProxyMainWindowCreateInfo &createInfo, QOhosConsumer< QtOhos::JsState &, QOhosWindowProxyData > resultConsumer)
QXComponent< QXComponentType::Node > QXComponentNode
Definition qxcomponent.h:44
QOhosConsumer< QtOhos::JsState &, QOhosWindowProxyData > resultConsumer
QOhosConsumer< QtOhos::JsState &, QOhosWindowProxyData > resultConsumer
std::shared_ptr< QtOhos::QAbilityPeer > qAbilityPeer