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

123 lines
5.2 KiB
Diff
Raw Normal View History

2022-07-13 12:31:15 +00:00
From aca0c736c99439a8f4d297bc57c1fa5a2d2a6e88 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-07-13 12:31:15 +00:00
Subject: [PATCH 15/19] 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-07-13 12:31:15 +00:00
index 3df899d63d..83e52b12e5 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
index f65650cf9c..eeff768bb4 100644
--- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
+++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp
@@ -66,6 +66,7 @@ private slots:
void activeFocusOnTab10();
void activeFocusOnTab_infiniteLoop_data();
void activeFocusOnTab_infiniteLoop();
+ void activeFocusOnTab_infiniteLoopControls();
void nextItemInFocusChain();
void nextItemInFocusChain2();
@@ -1055,6 +1056,17 @@ void tst_QQuickItem::activeFocusOnTab_infiniteLoop()
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())
--
2.36.1