From 3e7463411e549100eee7abe2a8fae16fd965f8f6 Mon Sep 17 00:00:00 2001 From: David Faure Date: Mon, 29 Jul 2019 14:27:16 +0200 Subject: Fix crash when the focus widget gets a focus proxy after the fact QApplicationPrivate::focus_widget became a dangling pointer in the following scenario: A widget first gets focus and later on gets a focus proxy. QApplicationPrivate::focus_widget was still pointing to the initial widget. Upon destruction, QWidget::hasFocus() [which follows to the focus proxy and then compares with focus_widget] was therefore false for both widgets. So QWidget::clearFocus() didn't call QApplicationPrivate::setFocusWidget(0) for either of them. As a result, focus_widget remained set, and became dangling. In real life, this happened with a QWebEngineView, which the application gave focus to upon creation. At that time it doesn't have a focus proxy yet. That happens later, in QWebEngineViewPrivate::widgetChanged. https://bugs.kde.org/show_bug.cgi?id=381793 Change-Id: Ifee610bb76a2d4d2797b98ece9bffe5fffe3c6a6 Reviewed-by: Volker Hilsheimer --- tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp | 47 +++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp') diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index 73b1fd1865..a95d6e76b3 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -181,6 +181,8 @@ private slots: void tabOrderWithCompoundWidgets(); void tabOrderNoChange(); void tabOrderNoChange2(); + void appFocusWidgetWithFocusProxyLater(); + void appFocusWidgetWhenLosingFocusProxy(); #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) void activation(); #endif @@ -2031,6 +2033,51 @@ void tst_QWidget::tabOrderNoChange2() QCOMPARE(focusChainBackward, getFocusChain(&w, false)); } +void tst_QWidget::appFocusWidgetWithFocusProxyLater() +{ + // Given a lineedit without a focus proxy + QWidget window; + window.setWindowTitle(QTest::currentTestFunction()); + QLineEdit *lineEditFocusProxy = new QLineEdit(&window); + QLineEdit *lineEdit = new QLineEdit(&window); + lineEdit->setFocus(); + window.show(); + QApplication::setActiveWindow(&window); + QVERIFY(QTest::qWaitForWindowActive(&window)); + QCOMPARE(QApplication::focusWidget(), lineEdit); + + // When setting a focus proxy for the focus widget (like QWebEngineView does) + lineEdit->setFocusProxy(lineEditFocusProxy); + + // Then the focus widget should be updated + QCOMPARE(QApplication::focusWidget(), lineEditFocusProxy); + + // So that deleting the lineEdit and later the window, doesn't crash + delete lineEdit; + QCOMPARE(QApplication::focusWidget(), nullptr); +} + +void tst_QWidget::appFocusWidgetWhenLosingFocusProxy() +{ + // Given a lineedit with a focus proxy + QWidget window; + window.setWindowTitle(QTest::currentTestFunction()); + QLineEdit *lineEditFocusProxy = new QLineEdit(&window); + QLineEdit *lineEdit = new QLineEdit(&window); + lineEdit->setFocusProxy(lineEditFocusProxy); + lineEdit->setFocus(); + window.show(); + QApplication::setActiveWindow(&window); + QVERIFY(QTest::qWaitForWindowActive(&window)); + QCOMPARE(QApplication::focusWidget(), lineEditFocusProxy); + + // When unsetting the focus proxy + lineEdit->setFocusProxy(nullptr); + + // Then the application focus widget should be back to the lineedit + QCOMPARE(QApplication::focusWidget(), lineEdit); +} + #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) void tst_QWidget::activation() { -- cgit v1.2.3