summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVolker Hilsheimer <volker.hilsheimer@qt.io>2023-04-08 18:53:14 +0200
committerVolker Hilsheimer <volker.hilsheimer@qt.io>2023-04-18 01:23:15 +0200
commit645aaa25b17ad3437e63877576ef7dac7966a934 (patch)
treec94db9472bd35f575a1bffae6c1794eaf8383b5d /src
parent75a45dfbbf499641bd68ae4d719ffb83d9072882 (diff)
downloadqtdeclarative-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.cpp10
-rw-r--r--src/quickwidgets/qaccessiblequickwidget.cpp26
-rw-r--r--src/quickwidgets/qaccessiblequickwidget_p.h4
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)
};