diff options
author | Laszlo Agocs <laszlo.agocs@qt.io> | 2021-08-12 14:33:04 +0200 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2021-08-16 18:17:08 +0000 |
commit | a9fe872b7a43d5083832b1fa62f36ed0f99c7c8b (patch) | |
tree | fc8ea00fdd82dd2f329ddd5c8b518e8916c52244 /tests | |
parent | aeaef5a2b181c9830d1d994857435d2b6b21060b (diff) |
Check for exposed instead of just visible in grabWindow
This issue is reproducable both in Qt 5 and 6. When calling grabWindow()
"early enough", such as from an Component.onCompleted handler, the
window is not ready for rendering (as it has not yet been exposed, there
is no scenegraph initialized, etc.). Checking the visible property is
not safe here, because it may well be true right from the start during
the window object's life, while telling nothing about the actual
low-level state. This becomes fatal in particular when one relies on
setting Window.visible to true from QML, instead of calling show() from
C++, because then the isVisible() check is clearly wrong (already true,
while the window is nowhere near to be ready for rendering) and leads to
hitting the wrong branch in the main condition of grabWindow().
It is probably safe to assume that the isVisible() check in grabWindow()
was never actually correct, and perhaps was an oversight. What it wants
to test for is being exposed (i.e. ready to render, with the scenegraph
and related machinery up and running), but it rather just tests the
visible property of the QWindow. It's just that in the majority of uses
that happens not to cause any problems in practice, either because
grabWindow() is only called once the window is up and running, or
because toggling visible is deferred to show() from C++ which masks the
underlying issue in the logic.
Switch over to isRenderable() which is a helper function based on the
exposed state.
Add an autotest case as well.
Fixes: QTBUG-95393
Change-Id: Ia0193229f6b3980ff6bd51d85db1408017c43b38
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
(cherry picked from commit b6ac70b9219ad9a2036b61686f2890b830b425c2)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/auto/quick/qquickwindow/data/earlyGrab.qml | 25 | ||||
-rw-r--r-- | tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 35 |
2 files changed, 60 insertions, 0 deletions
diff --git a/tests/auto/quick/qquickwindow/data/earlyGrab.qml b/tests/auto/quick/qquickwindow/data/earlyGrab.qml new file mode 100644 index 0000000000..74427c9492 --- /dev/null +++ b/tests/auto/quick/qquickwindow/data/earlyGrab.qml @@ -0,0 +1,25 @@ +import QtQuick +import Test + +Window { + id: window + width: 400 + height: 400 + color: "red" + + // Important for the test to set visible early on, not wait until + // show() from C++. What is really verified here is that the + // content grabbing takes the real window state into account + // (visible vs. exposed). + visible: true + + Grabber { + id: grabber + objectName: "grabber" + } + + Item { + anchors.fill: parent + Component.onCompleted: grabber.grab(window) + } +} diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index edfe487c5e..8d1fd7c0ba 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -446,6 +446,7 @@ private slots: void grab_data(); void grab(); + void earlyGrab(); void multipleWindows(); void animationsWhileHidden(); @@ -1564,6 +1565,40 @@ void tst_qquickwindow::grab() } } +class Grabber : public QObject +{ + Q_OBJECT +public: + Q_INVOKABLE void grab(QObject *obj) { + QQuickWindow *window = qobject_cast<QQuickWindow *>(obj); + images.append(window->grabWindow()); + } + QVector<QImage> images; +}; + +void tst_qquickwindow::earlyGrab() +{ + if ((QGuiApplication::platformName() == QLatin1String("offscreen")) + || (QGuiApplication::platformName() == QLatin1String("minimal"))) + QSKIP("Skipping due to grabWindow not functional on offscreen/minimal platforms"); + + qmlRegisterType<Grabber>("Test", 1, 0, "Grabber"); + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("earlyGrab.qml")); + QScopedPointer<QQuickWindow> window(qobject_cast<QQuickWindow *>(component.create())); + QVERIFY(!window.isNull()); + window->setTitle(QTest::currentTestFunction()); + + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + Grabber *grabber = qobject_cast<Grabber *>(window->findChild<QObject *>("grabber")); + QVERIFY(grabber); + QCOMPARE(grabber->images.count(), 1); + QVERIFY(!grabber->images[0].isNull()); + QCOMPARE(grabber->images[0].convertToFormat(QImage::Format_RGBX8888).pixel(10, 20), QColor(Qt::red).rgb()); +} + void tst_qquickwindow::multipleWindows() { QList<QQuickWindow *> windows; |