diff options
author | Volker Hilsheimer <volker.hilsheimer@qt.io> | 2023-04-08 18:53:14 +0200 |
---|---|---|
committer | Volker Hilsheimer <volker.hilsheimer@qt.io> | 2023-04-18 01:23:15 +0200 |
commit | 645aaa25b17ad3437e63877576ef7dac7966a934 (patch) | |
tree | c94db9472bd35f575a1bffae6c1794eaf8383b5d /src | |
parent | 75a45dfbbf499641bd68ae4d719ffb83d9072882 (diff) | |
download | qtdeclarative-645aaa25b17ad3437e63877576ef7dac7966a934.tar.gz |
QQuickWidget: don't crash in accessibility when reparenting
QAccessibleQuickWidget delegates all calls to the QAccessibleQuickWindow,
which it had as a member that was initialized at construction time to
the offscreenWindow backing the QQuickWidget. Both are QAccessibleObject
subclasses, and QAccessibleObject stores the object it wraps as a
QPointer. The QAccessibleQuickWindow's object becomes null when that
offscreen window gets destroyed (for instance, when reparenting).
We might get called by the accessibility framework in that situation, as
we are clicking a button and the hierarchy changes.
To prevent crashes, we need to test for nullptr in QAccessibleQuickWindow.
However, that alone would leave us with a useless QAccessibleQuickWindow,
and in turn with a useless QAccessibleQuickWidget instance.
The QAccessibleQuickWindow is not directly exposed to the
accessibility framework, and all calls to it are dispatched through its
QAccessibleQuickWidget owner. We can't repair the QAccessibleQuickWindow
but we can replace it entirely if we manage it as a heap-allocated
object. Use a std::unique_ptr for that, which we can reset with a new
instance created from a new offscreen window in order to repair things.
We can now either test in all functions whether the window's window is
still alive. Or we can handle the destroyed() signal of the offscreen
window. The latter solution is a bit more involved, but generally more
scalable as we don't have to remember to check, and possibly repair, in
each QAccessibleQuickWidget function.
Pick-to: 6.5
Fixes: QTBUG-108226
Change-Id: Ib19c07d3679c0af28cb5aab4c80691cc57c4e514
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/quick/accessible/qaccessiblequickview.cpp | 10 | ||||
-rw-r--r-- | src/quickwidgets/qaccessiblequickwidget.cpp | 26 | ||||
-rw-r--r-- | src/quickwidgets/qaccessiblequickwidget_p.h | 4 |
3 files changed, 31 insertions, 9 deletions
diff --git a/src/quick/accessible/qaccessiblequickview.cpp b/src/quick/accessible/qaccessiblequickview.cpp index 5cd93b9613..08f5889070 100644 --- a/src/quick/accessible/qaccessiblequickview.cpp +++ b/src/quick/accessible/qaccessiblequickview.cpp @@ -21,7 +21,7 @@ QAccessibleQuickWindow::QAccessibleQuickWindow(QQuickWindow *object) QList<QQuickItem *> QAccessibleQuickWindow::rootItems() const { - if (QQuickItem *ci = window()->contentItem()) + if (QQuickItem *ci = window() ? window()->contentItem() : nullptr) return accessibleUnignoredChildren(ci); return QList<QQuickItem *>(); } @@ -47,7 +47,7 @@ QAccessibleInterface *QAccessibleQuickWindow::child(int index) const QAccessibleInterface *QAccessibleQuickWindow::focusChild() const { - QObject *focusObject = window()->focusObject(); + QObject *focusObject = window() ? window()->focusObject() : nullptr; if (focusObject) { QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(focusObject); if (!iface || iface == this || !iface->focusChild()) @@ -67,18 +67,22 @@ QAccessible::State QAccessibleQuickWindow::state() const QAccessible::State st; if (window() == QGuiApplication::focusWindow()) st.active = true; - if (!window()->isVisible()) + if (!window() || !window()->isVisible()) st.invisible = true; return st; } QRect QAccessibleQuickWindow::rect() const { + if (!window()) + return {}; return QRect(window()->x(), window()->y(), window()->width(), window()->height()); } QString QAccessibleQuickWindow::text(QAccessible::Text text) const { + if (!window()) + return {}; #ifdef Q_ACCESSIBLE_QUICK_ITEM_ENABLE_DEBUG_DESCRIPTION if (text == QAccessible::DebugDescription) { return QString::fromLatin1(object()->metaObject()->className()) ; diff --git a/src/quickwidgets/qaccessiblequickwidget.cpp b/src/quickwidgets/qaccessiblequickwidget.cpp index f67d3eecab..103afda054 100644 --- a/src/quickwidgets/qaccessiblequickwidget.cpp +++ b/src/quickwidgets/qaccessiblequickwidget.cpp @@ -11,32 +11,48 @@ QT_BEGIN_NAMESPACE QAccessibleQuickWidget::QAccessibleQuickWidget(QQuickWidget* widget) : QAccessibleWidget(widget) -, m_accessibleWindow(QQuickWidgetPrivate::get(widget)->offscreenWindow) { // NOTE: m_accessibleWindow is a QAccessibleQuickWindow, and not a // QAccessibleQuickWidgetOffscreenWindow (defined below). This means // it will return the Quick item child interfaces, which is what's needed here // (unlike QAccessibleQuickWidgetOffscreenWindow, which will report 0 children). + repairWindow(); +} + +void QAccessibleQuickWidget::repairWindow() +{ + if (!m_accessibleWindow || !m_accessibleWindow->object()) { + QQuickWidget *theWidget = static_cast<QQuickWidget *>(object()); + QQuickWindow *newOffscreen = QQuickWidgetPrivate::get(theWidget)->offscreenWindow; + // We use the qobject_cast here to detect that the newOffscreen is + // not the one getting destroyed right now. + if (qobject_cast<QQuickWindow *>(newOffscreen)) { + m_accessibleWindow.reset(new QAccessibleQuickWindow(newOffscreen)); + QObject::connect(newOffscreen, &QObject::destroyed, theWidget, [this]{ + repairWindow(); + }); + } + } } QAccessibleInterface *QAccessibleQuickWidget::child(int index) const { - return m_accessibleWindow.child(index); + return m_accessibleWindow->child(index); } int QAccessibleQuickWidget::childCount() const { - return m_accessibleWindow.childCount(); + return m_accessibleWindow->childCount(); } int QAccessibleQuickWidget::indexOfChild(const QAccessibleInterface *iface) const { - return m_accessibleWindow.indexOfChild(iface); + return m_accessibleWindow->indexOfChild(iface); } QAccessibleInterface *QAccessibleQuickWidget::childAt(int x, int y) const { - return m_accessibleWindow.childAt(x, y); + return m_accessibleWindow->childAt(x, y); } QAccessibleQuickWidgetOffscreenWindow::QAccessibleQuickWidgetOffscreenWindow(QQuickWindow *window) diff --git a/src/quickwidgets/qaccessiblequickwidget_p.h b/src/quickwidgets/qaccessiblequickwidget_p.h index ea86433c41..b81a57ba3f 100644 --- a/src/quickwidgets/qaccessiblequickwidget_p.h +++ b/src/quickwidgets/qaccessiblequickwidget_p.h @@ -38,7 +38,9 @@ public: QAccessibleInterface *childAt(int x, int y) const override; private: - QAccessibleQuickWindow m_accessibleWindow; + void repairWindow(); + + std::unique_ptr<QAccessibleQuickWindow> m_accessibleWindow; Q_DISABLE_COPY(QAccessibleQuickWidget) }; |