qt5-qtdeclarative/0012-QQuickItem-Guard-against-cycles-in-nextPrevItemInTab.patch

123 lines
5.2 KiB
Diff
Raw Normal View History

2022-10-31 10:19:31 +00:00
From 74c8c421763597f778313ea976fffdc03183226b Mon Sep 17 00:00:00 2001
2022-05-17 06:19:51 +00:00
From: Fabian Kosmale <fabian.kosmale@qt.io>
Date: Wed, 4 May 2022 09:10:54 +0200
2022-10-31 10:19:31 +00:00
Subject: [PATCH 12/18] QQuickItem: Guard against cycles in
2022-05-17 06:19:51 +00:00
nextPrevItemInTabFocusChain
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
nextPrevItemInTabFocusChain already had a check to prevent running into
cycles, it would however only detect if we reached the original item. If
our cycle instead would loop between reachable items without ever
returning to the initial one, as in the diagram below, then we would
never terminate the loop.
/-->other item<---next item
initial-item \ ^
\ |
--->different item
To prevent this from happening, we keep track of all items we've seen so
far. One last complications arises due to the fact that we do visit the
parent twice under some cicrcumstances, but we already have the skip
variable to indicate that case we simply skip the duplicate check if
it is set to true.
Pick-to: 6.2 6.3
Fixes: QTBUG-87190
Change-Id: I1449a7ebf8f325f00c296e8a8db4360faf1049e4
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
(cherry picked from commit e74bcf751495d9fe27efd195bc04e2a6ae6732a4)
---
src/quick/items/qquickitem.cpp | 7 ++++++-
.../data/activeFocusOnTab_infiniteLoop3.qml | 13 +++++++++++++
tests/auto/quick/qquickitem2/tst_qquickitem.cpp | 12 ++++++++++++
3 files changed, 31 insertions(+), 1 deletion(-)
create mode 100644 tests/auto/quick/qquickitem2/data/activeFocusOnTab_infiniteLoop3.qml
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
2022-10-31 10:19:31 +00:00
index 75f1457816..9de244ed9e 100644
2022-05-17 06:19:51 +00:00
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -59,6 +59,7 @@
#include <QtCore/private/qnumeric_p.h>
#include <QtGui/qpa/qplatformtheme.h>
#include <QtCore/qloggingcategory.h>
+#include <QtCore/private/qduplicatetracker_p.h>
#include <private/qqmlglobal_p.h>
#include <private/qqmlengine_p.h>
2022-07-13 12:31:15 +00:00
@@ -2526,6 +2527,7 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo
2022-05-17 06:19:51 +00:00
QQuickItem *current = item;
qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: startItem:" << startItem;
qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: firstFromItem:" << firstFromItem;
+ QDuplicateTracker<QQuickItem *> cycleDetector;
do {
qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: current:" << current;
qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: from:" << from;
2022-07-13 12:31:15 +00:00
@@ -2592,7 +2594,10 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo
2022-05-17 06:19:51 +00:00
// traversed all of the chain (by compare the [current] item with [startItem])
// Since the [startItem] might be promoted to its parent if it is invisible,
// we still have to check [current] item with original start item
- if ((current == startItem || current == originalStartItem) && from == firstFromItem) {
+ // We might also run into a cycle before we reach firstFromItem again
+ // but note that we have to ignore current if we are meant to skip it
+ if (((current == startItem || current == originalStartItem) && from == firstFromItem) ||
+ (!skip && cycleDetector.hasSeen(current))) {
// wrapped around, avoid endless loops
if (item == contentItem) {
qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: looped, return contentItem";
diff --git a/tests/auto/quick/qquickitem2/data/activeFocusOnTab_infiniteLoop3.qml b/tests/auto/quick/qquickitem2/data/activeFocusOnTab_infiniteLoop3.qml
new file mode 100644
index 0000000000..889e480f3b
--- /dev/null
+++ b/tests/auto/quick/qquickitem2/data/activeFocusOnTab_infiniteLoop3.qml
@@ -0,0 +1,13 @@
+import QtQuick 2.6
+
+Item {
+ visible: true
+ Item {
+ visible: false
+ Item {
+ objectName: "hiddenChild"
+ activeFocusOnTab: true
+ focus: true
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
2022-09-20 10:13:42 +00:00
index c8f251dbe1..c8ef36ee68 100644
2022-05-17 06:19:51 +00:00
--- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
+++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
2022-09-20 10:13:42 +00:00
@@ -67,6 +67,7 @@ private slots:
2022-05-17 06:19:51 +00:00
void activeFocusOnTab10();
void activeFocusOnTab_infiniteLoop_data();
void activeFocusOnTab_infiniteLoop();
+ void activeFocusOnTab_infiniteLoopControls();
void nextItemInFocusChain();
void nextItemInFocusChain2();
2022-09-20 10:13:42 +00:00
@@ -1057,6 +1058,17 @@ void tst_QQuickItem::activeFocusOnTab_infiniteLoop()
2022-05-17 06:19:51 +00:00
QCOMPARE(item, window->rootObject());
}
+
+void tst_QQuickItem::activeFocusOnTab_infiniteLoopControls()
+{
+ auto source = testFileUrl("activeFocusOnTab_infiniteLoop3.qml");
+ QScopedPointer<QQuickView>window(new QQuickView());
+ window->setSource(source);
+ window->show();
+ QVERIFY(window->errors().isEmpty());
+ QTest::keyClick(window.get(), Qt::Key_Tab); // should not hang
+}
+
void tst_QQuickItem::nextItemInFocusChain()
{
if (!qt_tab_all_widgets())
--
2022-09-20 10:13:42 +00:00
2.37.3
2022-05-17 06:19:51 +00:00