From 91f3c468987a68bdcb5c5a2519ac5bd694a0432c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Arve=20S=C3=A6ther?= Date: Wed, 21 Sep 2022 09:46:40 +0200 Subject: Move StackLayout tests from qtquickcontrols.git (5.15) In 5.15 branch, there were still some autotests for StackLayout lurking around in the qtquickcontrols repo that wasn't moved to declarative for some reason. Move them to qtdeclarative repo. One of these tests also demonstrated a genuine failure, so we want to keep these tests. (It is currently skip()ped) (Moved from tests/auto/controls/data/tst_stacklayout.qml) Change-Id: I826228625c9f15b6028ad881adfc5370bdd17ca1 Reviewed-by: Mitch Curtis (cherry picked from commit aaf7f7c6b77ed75caea236322f799742466f0b64) --- .../quick/qquicklayouts/data/tst_stacklayout.qml | 414 +++++++++++++++++++++ 1 file changed, 414 insertions(+) diff --git a/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml b/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml index de335386cf..32b149336a 100644 --- a/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml +++ b/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml @@ -51,6 +51,7 @@ import QtQuick 2.2 import QtTest 1.0 import QtQuick.Layouts 1.3 +import QtQuick.Window 2.1 Item { id: container @@ -153,5 +154,418 @@ Item { compare(layout.secondItem.width, 200) // width should not be -1 layout.destroy() } + + function geometry(item) { + return [item.x, item.y, item.width, item.height] + } + + Component { + id: countGeometryChanges_Component + StackLayout { + id: stack + property alias col: _col + property alias row: _row + width: 100 + ColumnLayout { + id: _col + property alias r1: _r1 + property alias r2: _r2 + property alias r3: _r3 + spacing: 0 + property int counter : 0 + onWidthChanged: { ++counter; } + Rectangle { + id: _r1 + implicitWidth: 20 + implicitHeight: 20 + Layout.fillWidth: true + property int counter : 0 + onWidthChanged: { ++counter; } + } + Rectangle { + id: _r2 + implicitWidth: 50 + implicitHeight: 50 + Layout.fillWidth: true + property int counter : 0 + onWidthChanged: { ++counter; } + } + Rectangle { + id: _r3 + implicitWidth: 40 + implicitHeight: 40 + Layout.fillWidth: true + property int counter : 0 + onWidthChanged: { ++counter; } + } + } + RowLayout { + id: _row + property alias r5: _r5 + spacing: 0 + property int counter : 0 + onWidthChanged: { ++counter; } + Rectangle { + id: _r5 + implicitWidth: 100 + implicitHeight: 100 + Layout.fillWidth: true + property int counter : 0 + onWidthChanged: { ++counter; } + } + } + } + } + + function test_countGeometryChanges() { + + var stack = countGeometryChanges_Component.createObject(container) + compare(stack.currentIndex, 0) + compare(stack.col.width, 100) + compare(stack.col.height, 110) + compare(stack.row.width, 100) + compare(stack.row.height, 100) + verify(stack.col.r1.counter <= 2) + compare(stack.col.r2.counter, 1) + verify(stack.col.r3.counter <= 2) + verify(stack.col.counter <= 2) + compare(stack.row.counter, 1) // not visible, will only receive the initial geometry change + compare(stack.row.r5.counter, 0) + stack.destroy() + } + + + Component { + id: layoutItem_Component + Rectangle { + implicitWidth: 20 + implicitHeight: 20 + } + } + + Component { + id: emtpy_StackLayout_Component + StackLayout { + property int num_onCountChanged: 0 + property int num_onCurrentIndexChanged: 0 + onCountChanged: { ++num_onCountChanged; } + onCurrentIndexChanged: { ++num_onCurrentIndexChanged; } + } + } + + function test_addAndRemoveItems() + { + skip("There is a bug with the internal StackLayout cache that causes this to fail") + var stack = emtpy_StackLayout_Component.createObject(container) + stack.currentIndex = 2 + compare(stack.implicitWidth, 0) + compare(stack.implicitHeight, 0) + + var rect0 = layoutItem_Component.createObject(stack) + verify(waitForItemPolished(stack)) + compare(stack.implicitWidth, 20) + compare(stack.implicitHeight, 20) + compare(rect0.visible, false) + + var rect1 = layoutItem_Component.createObject(stack) + rect1.Layout.preferredWidth = 30 + rect1.Layout.preferredHeight = 10 + verify(waitForItemPolished(stack)) + compare(stack.implicitWidth, 30) + compare(stack.implicitHeight, 20) + compare(rect0.visible, false) + compare(rect1.visible, false) + + var rect2 = layoutItem_Component.createObject(stack) + rect2.x = 42 // ### items in a stacklayout will have their x and y positions discarded. + rect2.y = 42 + rect2.Layout.preferredWidth = 80 + rect2.Layout.preferredHeight = 30 + rect2.Layout.fillWidth = true + verify(waitForItemPolished(stack)) + compare(stack.implicitWidth, 80) + compare(stack.implicitHeight, 30) + compare(rect0.visible, false) + compare(rect1.visible, false) + compare(rect2.visible, true) + compare(geometry(rect2), geometry(stack)) + + rect2.destroy() + wait(0) + verify(waitForItemPolished(stack)) + compare(stack.implicitWidth, 30) + compare(stack.implicitHeight, 20) + + rect0.destroy() + wait(0) + verify(waitForItemPolished(stack)) + compare(stack.implicitWidth, 30) + compare(stack.implicitHeight, 10) + + rect1.destroy() + wait(0) + verify(waitForItemPolished(stack)) + compare(stack.implicitWidth, 0) + compare(stack.implicitHeight, 0) + + stack.destroy() + } + + function test_sizeHint_data() { + return [ + { tag: "propagateNone", layoutHints: [10, 20, 30], childHints: [11, 21, 31], expected:[10, 20, Number.POSITIVE_INFINITY]}, + { tag: "propagateMinimumWidth", layoutHints: [-1, 20, 30], childHints: [10, 21, 31], expected:[10, 20, Number.POSITIVE_INFINITY]}, + { tag: "propagatePreferredWidth", layoutHints: [10, -1, 30], childHints: [11, 20, 31], expected:[10, 20, Number.POSITIVE_INFINITY]}, + { tag: "propagateMaximumWidth", layoutHints: [10, 20, -1], childHints: [11, 21, 30], expected:[10, 20, Number.POSITIVE_INFINITY]}, + { tag: "propagateAll", layoutHints: [-1, -1, -1], childHints: [10, 20, 30], expected:[10, 20, Number.POSITIVE_INFINITY]}, + { tag: "propagateCrazy", layoutHints: [-1, -1, -1], childHints: [40, 21, 30], expected:[30, 30, Number.POSITIVE_INFINITY]}, + { tag: "expandMinToExplicitPref", layoutHints: [-1, 1, -1], childHints: [11, 21, 31], expected:[ 1, 1, Number.POSITIVE_INFINITY]}, + { tag: "expandMaxToExplicitPref", layoutHints: [-1, 99, -1], childHints: [11, 21, 31], expected:[11, 99, Number.POSITIVE_INFINITY]}, + { tag: "expandAllToExplicitMin", layoutHints: [99, -1, -1], childHints: [11, 21, 31], expected:[99, 99, Number.POSITIVE_INFINITY]}, + { tag: "expandPrefToExplicitMin", layoutHints: [24, -1, -1], childHints: [11, 21, 31], expected:[24, 24, Number.POSITIVE_INFINITY]}, + { tag: "boundPrefToExplicitMax", layoutHints: [-1, -1, 19], childHints: [11, 21, 31], expected:[11, 19, Number.POSITIVE_INFINITY]}, + { tag: "boundAllToExplicitMax", layoutHints: [-1, -1, 9], childHints: [11, 21, 31], expected:[ 9, 9, Number.POSITIVE_INFINITY]}, + ]; + } + + function itemSizeHints(item) { + return [item.Layout.minimumWidth, item.implicitWidth, item.Layout.maximumWidth] + } + Component { + id: stacklayout_sizeHint_Component + StackLayout { + property int implicitWidthChangedCount : 0 + onImplicitWidthChanged: { ++implicitWidthChangedCount } + ColumnLayout { + Rectangle { + id: r1 + color: "red" + Layout.minimumWidth: 1 + Layout.preferredWidth: 2 + Layout.maximumWidth: 3 + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + Layout.fillWidth: true + } + } + } + } + + function test_sizeHint(data) { + var layout = stacklayout_sizeHint_Component.createObject(container) + + var col = layout.children[0] + col.Layout.minimumWidth = data.layoutHints[0] + col.Layout.preferredWidth = data.layoutHints[1] + col.Layout.maximumWidth = data.layoutHints[2] + + var child = col.children[0] + if (data.implicitWidth !== undefined) { + child.implicitWidth = data.implicitWidth + } + child.Layout.minimumWidth = data.childHints[0] + child.Layout.preferredWidth = data.childHints[1] + child.Layout.maximumWidth = data.childHints[2] + + verify(waitForItemPolished(layout)) + var effectiveSizeHintResult = [layout.Layout.minimumWidth, layout.implicitWidth, layout.Layout.maximumWidth] + compare(effectiveSizeHintResult, data.expected) + layout.destroy() + } + + Component { + id: stacklayout_addIgnoredItem_Component + StackLayout { + Repeater { + id: rep + model: 1 + Rectangle { + id: r + } + } + } + } + + // Items with no size information is ignored. + function test_addIgnoredItem() + { + var stack = stacklayout_addIgnoredItem_Component.createObject(container) + compare(stack.count, 1) + compare(stack.implicitWidth, 0) + compare(stack.implicitHeight, 0) + var r = stack.children[0] + r.Layout.preferredWidth = 20 + r.Layout.preferredHeight = 30 + verify(waitForItemPolished(stack)) + compare(stack.count, 1) + compare(stack.implicitWidth, 20) + compare(stack.implicitHeight, 30) + stack.destroy(); + } + + function test_dontCrashWhenAnchoredToAWindow() { + var test_layoutStr = + 'import QtQuick 2.2; \ + import QtQuick.Window 2.1; \ + import QtQuick.Layouts 1.3; \ + Window { \ + visible: true; \ + width: stack.implicitWidth; \ + height: stack.implicitHeight; \ + StackLayout { \ + id: stack; \ + currentIndex: 0; \ + anchors.fill: parent; \ + Rectangle { \ + color: "red"; \ + implicitWidth: 300; \ + implicitHeight: 200; \ + } \ + } \ + } ' + + var win = Qt.createQmlObject(test_layoutStr, container, ''); + if (win.visibility === Window.Windowed) { + // on single-window systems (such as Android), the window geometry will be + // fullscreen, and most likely it will be set to screen size. Avoid this test for + // those systems, as the size of the window will not be determined by the layout + tryCompare(win, 'width', 300); + } + win.destroy() + } + + Component { + id: test_dontCrashWhenChildIsResizedToNull_Component + StackLayout { + property alias rect : _rect + Rectangle { + id: _rect; + color: "red" + implicitWidth: 200 + implicitHeight: 200 + } + } + } + + function test_dontCrashWhenChildIsResizedToNull() { + var layout = test_dontCrashWhenChildIsResizedToNull_Component.createObject(container) + layout.rect.width = 0 + layout.width = 222 // trigger a rearrange with a valid size + layout.height = 222 + } + + Component { + id: test_currentIndex_Component + StackLayout { + currentIndex: 1 + Text { + text: "0" + } + Text { + text: "1" + } + } + } + + function test_currentIndex() { + var layout = test_currentIndex_Component.createObject(container) + var c0 = layout.children[0] + var c1 = layout.children[1] + compare(layout.currentIndex, 1) + tryCompare(layout, 'visible', true) + compare(c0.visible, false) + compare(c1.visible, true) + layout.currentIndex = 0 + compare(c0.visible, true) + compare(c1.visible, false) + var c2 = layoutItem_Component.createObject(layout) + compare(c2.visible, false) + + /* + * destroy the current item and check if visibility advances to next + */ + c0.destroy() + tryCompare(c1, 'visible', true) + compare(c2.visible, false) + c1.destroy() + tryCompare(c2, 'visible', true) + c2.destroy() + tryCompare(layout, 'currentIndex', 0) + + layout.destroy() + + /* + * Test the default/implicit value of currentIndex, either -1 (if empty) or 0: + */ + layout = emtpy_StackLayout_Component.createObject(container) + tryCompare(layout, 'visible', true) + compare(layout.currentIndex, -1) + compare(layout.num_onCurrentIndexChanged, 0) + // make it non-empty + c0 = layoutItem_Component.createObject(layout) + compare(layout.currentIndex, 0) + compare(layout.num_onCurrentIndexChanged, 1) + compare(c0.visible, true) + // make it empty again + c0.destroy() + wait(0) + compare(layout.currentIndex, -1) + //tryCompare(layout, 'currentIndex', -1) + compare(layout.num_onCurrentIndexChanged, 2) + + /* + * Check that explicit value doesn't change, + * and that no items are visible if the index is invalid/out of range + */ + layout.currentIndex = 2 + compare(layout.currentIndex, 2) + compare(layout.num_onCurrentIndexChanged, 3) + c0 = layoutItem_Component.createObject(layout) + compare(layout.currentIndex, 2) + compare(c0.visible, false) + + c1 = layoutItem_Component.createObject(layout) + compare(layout.currentIndex, 2) + compare(c0.visible, false) + compare(c1.visible, false) + + c2 = layoutItem_Component.createObject(layout) + compare(layout.currentIndex, 2) + compare(c0.visible, false) + compare(c1.visible, false) + compare(c2.visible, true) + + c2.destroy() + wait(0) + compare(layout.currentIndex, 2) + compare(c0.visible, false) + compare(c1.visible, false) + c1.destroy() + wait(0) + compare(layout.currentIndex, 2) + compare(c0.visible, false) + c0.destroy() + wait(0) + compare(layout.currentIndex, 2) + compare(layout.num_onCurrentIndexChanged, 3) + } + + function test_count() { + var layout = emtpy_StackLayout_Component.createObject(container) + tryCompare(layout, 'visible', true) + compare(layout.count, 0) + compare(layout.currentIndex, -1) + compare(layout.num_onCountChanged, 0) + compare(layout.num_onCurrentIndexChanged, 0) + var c0 = layoutItem_Component.createObject(layout) + compare(layout.count, 1) + compare(layout.currentIndex, 0) + compare(layout.num_onCurrentIndexChanged, 1) + compare(layout.num_onCountChanged, 1) + } + + } } -- cgit v1.2.3 From 0a271d6e7157177054831a1f37a4943513126316 Mon Sep 17 00:00:00 2001 From: Tarja Sundqvist Date: Thu, 6 Oct 2022 17:48:52 +0300 Subject: Bump version to 5.15.12 Change-Id: I6c8ea6b1590343e04af975b63e0fcb3280958a46 --- .qmake.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.qmake.conf b/.qmake.conf index a0e2de0f37..703f7df915 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -4,4 +4,4 @@ CONFIG += warning_clean DEFINES += QT_NO_LINKED_LIST DEFINES += QT_NO_JAVA_STYLE_ITERATORS -MODULE_VERSION = 5.15.11 +MODULE_VERSION = 5.15.12 -- cgit v1.2.3 From 89fc2b573046e0c78c3f03a02e0f4f93f5964f03 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 28 Sep 2022 09:00:18 +0200 Subject: masm: Treat Android as generic Posix regarding mmap and friends Apparently we cannot rely on madvise() to work as we expect it on linux. Fixes: QTBUG-106864 Fixes: QTBUG-106269 Change-Id: Ie488ad788386c1a8c493d6bba632787f5282baaa Reviewed-by: Fabian Kosmale (cherry picked from commit ffecc122d785de9c4c5defd8724526b8dd4982dc) Reviewed-by: Qt Cherry-pick Bot --- src/3rdparty/masm/wtf/OSAllocatorPosix.cpp | 23 ++++++++++++++++------- src/qml/memory/qv4mm.cpp | 2 +- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp index d54b7e9492..ec8e030b14 100644 --- a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp +++ b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp @@ -37,7 +37,16 @@ #include #include -#if OS(LINUX) +// Android does not really behave like linux here. +// For example, madvise() randomly fails if we pass MADV_WILLNEED or MADV_DONTNEED. +// Treat it as generic Posix. +#if OS(LINUX) && !defined(__ANDROID__) +#define NON_ANDROID_LINUX 1 +#else +#define NON_ANDROID_LINUX 0 +#endif + +#if NON_ANDROID_LINUX #include #ifndef MFD_CLOEXEC #define MFD_CLOEXEC 0x0001U @@ -86,7 +95,7 @@ static int memfdForUsage(size_t bytes, OSAllocator::Usage usage) close(fd); return -1; } -#elif OS(LINUX) +#elif NON_ANDROID_LINUX static int memfdForUsage(size_t bytes, OSAllocator::Usage usage) { UNUSED_PARAM(bytes); @@ -102,7 +111,7 @@ void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable, void* result = mmap(0, bytes, PROT_NONE, MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0); if (result == MAP_FAILED) CRASH(); -#elif OS(LINUX) +#elif NON_ANDROID_LINUX UNUSED_PARAM(writable); UNUSED_PARAM(executable); int fd = memfdForUsage(bytes, usage); @@ -148,7 +157,7 @@ void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, bool writable, bo #if OS(DARWIN) int fd = usage; -#elif OS(LINUX) +#elif NON_ANDROID_LINUX int fd = memfdForUsage(bytes, usage); if (fd != -1) flags &= ~MAP_ANON; @@ -196,7 +205,7 @@ void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, bool writable, bo mmap(static_cast(result) + bytes - pageSize(), pageSize(), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, fd, 0); } -#if OS(LINUX) +#if NON_ANDROID_LINUX if (fd != -1) close(fd); #endif @@ -214,7 +223,7 @@ void OSAllocator::commit(void* address, size_t bytes, bool writable, bool execut protection |= PROT_EXEC; if (MAP_FAILED == mmap(address, bytes, protection, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0)) CRASH(); -#elif OS(LINUX) +#elif NON_ANDROID_LINUX int protection = PROT_READ; if (writable) protection |= PROT_WRITE; @@ -246,7 +255,7 @@ void OSAllocator::decommit(void* address, size_t bytes) #if OS(QNX) // Use PROT_NONE and MAP_LAZY to decommit the pages. mmap(address, bytes, PROT_NONE, MAP_FIXED | MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0); -#elif OS(LINUX) +#elif NON_ANDROID_LINUX while (madvise(address, bytes, MADV_DONTNEED)) { if (errno != EAGAIN) CRASH(); diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 216a82718b..9caedccbe7 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -177,7 +177,7 @@ struct MemorySegment { size_t pageSize = WTF::pageSize(); size = (size + pageSize - 1) & ~(pageSize - 1); -#if !defined(Q_OS_LINUX) && !defined(Q_OS_WIN) +#if (!defined(Q_OS_LINUX) && !defined(Q_OS_WIN)) || defined(Q_OS_ANDROID) // Linux and Windows zero out pages that have been decommitted and get committed again. // unfortunately that's not true on other OSes (e.g. BSD based ones), so zero out the // memory before decommit, so that we can be sure that all chunks we allocate will be -- cgit v1.2.3 From 5dffe98733ba05a8da801af0008f085ec2857ddd Mon Sep 17 00:00:00 2001 From: Alexey Edelev Date: Thu, 6 Oct 2022 20:43:29 +0200 Subject: Trim file names before adding them to qml components and scripts Fix the issue with the trailing spaces in the file paths of components and scripts in the json output. This specificly fixes androiddeployqt behavior since it was not able to find qml components mentioned in the qmlimportscanner's output and skip qml modules, because paths contained trailing spaces. Amends 2cc85e60d4313bd845bfdc233e5e75a8b7ccb01f Pick-to: 5.15.11 Fixes: QTBUG-107255 Change-Id: I20cc1b724aaea3a76932e82a6dc65eccbef3576d Reviewed-by: Assam Boudjelthia --- tools/qmlimportscanner/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/qmlimportscanner/main.cpp b/tools/qmlimportscanner/main.cpp index 52eebcdf65..6e2300a069 100644 --- a/tools/qmlimportscanner/main.cpp +++ b/tools/qmlimportscanner/main.cpp @@ -182,11 +182,11 @@ QVariantMap pluginsForModulePath(const QString &modulePath) { } else if (sections.size() == 2 && sectionType != "module" && sectionType != "typeinfo") { componentFiles.append(modulePath + QLatin1Char('/') - + QString::fromUtf8(sections.at(1))); + + QString::fromUtf8(sections.at(1)).trimmed()); } else if (sections.size() == 3 || (sectionType == "singleton" && sections.size() == 4)) { int fileNameIndex = (sectionType == "singleton") ? 3 : 2; - const QString fileName = QString::fromUtf8(sections.at(fileNameIndex)); + const QString fileName = QString::fromUtf8(sections.at(fileNameIndex)).trimmed(); const QString filePath = modulePath + QLatin1Char('/') + fileName; if (fileName.endsWith(QLatin1String(".js")) || fileName.endsWith(QLatin1String(".mjs"))) -- cgit v1.2.3 From e8c92d2675b4721bf8523da75f0d2f023bfdc4d6 Mon Sep 17 00:00:00 2001 From: Dominik Holland Date: Tue, 4 Oct 2022 09:07:10 +0200 Subject: Fix broken Text rendering when noantialiased NativeRendering is used In case antialiasing is disabled the QFontEngine::Format_Mono is used to render in the glyph cache. In this format the padding needs to be 8-bit aligned. Fixes: QTBUG-107038 Change-Id: Icc3c69fe0395cea9954c2fa07c39e7769fc91800 Reviewed-by: Eskil Abrahamsen Blomfeldt (cherry picked from commit cba96b3a97977f71931f311db9d5644d1d74d694) Reviewed-by: Qt Cherry-pick Bot --- src/quick/scenegraph/qsgrhitextureglyphcache.cpp | 5 ++++- .../text/text_nativerendering_no_antialiasing.qml | 25 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 tests/baseline/scenegraph/data/text/text_nativerendering_no_antialiasing.qml diff --git a/src/quick/scenegraph/qsgrhitextureglyphcache.cpp b/src/quick/scenegraph/qsgrhitextureglyphcache.cpp index 5a22103935..b812e26ec0 100644 --- a/src/quick/scenegraph/qsgrhitextureglyphcache.cpp +++ b/src/quick/scenegraph/qsgrhitextureglyphcache.cpp @@ -233,7 +233,10 @@ void QSGRhiTextureGlyphCache::endFillTexture() int QSGRhiTextureGlyphCache::glyphPadding() const { - return 1; + if (m_format == QFontEngine::Format_Mono) + return 8; + else + return 1; } int QSGRhiTextureGlyphCache::maxTextureWidth() const diff --git a/tests/baseline/scenegraph/data/text/text_nativerendering_no_antialiasing.qml b/tests/baseline/scenegraph/data/text/text_nativerendering_no_antialiasing.qml new file mode 100644 index 0000000000..92598eb490 --- /dev/null +++ b/tests/baseline/scenegraph/data/text/text_nativerendering_no_antialiasing.qml @@ -0,0 +1,25 @@ +import QtQuick 2.0 + +//vary font style, native rendering without antialiasing + +Item { + id: topLevel + width: 320 + height: 580 + + Repeater { + model: [Text.Normal, Text.Outline, Text.Raised, Text.Sunken] + Text { + y: 20 * index + clip: true + renderType: Text.NativeRendering + width: parent.width + wrapMode: Text.Wrap + font.pointSize: 10 + style: modelData + styleColor: "green" + antialiasing: false + text: "The quick fox jumps in style " + modelData + } + } +} -- cgit v1.2.3 From 1b165a141a8dc1d69a5f93f64e4c5ccdd594ad04 Mon Sep 17 00:00:00 2001 From: Sami Varanka Date: Mon, 17 Oct 2022 20:00:56 +0300 Subject: Fix TypeError in dynamicview1 example Rectangle's parent was null when setting left and right anchors. Check for parent null before binding. Fixes: QTBUG-106645 Change-Id: I79c1375d12d0cc901c931890beeff4fc0d701b79 Reviewed-by: Ulf Hermann --- examples/quick/tutorials/dynamicview/dynamicview1/dynamicview.qml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/quick/tutorials/dynamicview/dynamicview1/dynamicview.qml b/examples/quick/tutorials/dynamicview/dynamicview1/dynamicview.qml index 60167aa813..3db55a51a3 100644 --- a/examples/quick/tutorials/dynamicview/dynamicview1/dynamicview.qml +++ b/examples/quick/tutorials/dynamicview/dynamicview1/dynamicview.qml @@ -63,7 +63,11 @@ Rectangle { Rectangle { id: content - anchors { left: parent.left; right: parent.right } + anchors { + left: parent ? parent.left : undefined + right: parent ? parent.right : undefined + } + height: column.implicitHeight + 4 border.width: 1 -- cgit v1.2.3 From 26be677f84264d84fa6924f7cc4641552a6479f4 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 8 Sep 2022 17:21:10 +0200 Subject: Qml: Don't crash on bad grouped properties Amend and cherry-pick e061c934d5a6d93493ba2be7b61efdd055d1e164. This commit is required by the (already-merged) commit dc69a005755c4d4c1b3de6cd52321b543b49a1a1 to successfully fix the grouped property crash. Add the reproducer of the issue in the tests. Task-number: QTBUG-106457 Fixes: QTBUG-107795 Change-Id: I7da381d2c3b9c58d370c1ed754140637e065bdec Reviewed-by: Fabian Kosmale Reviewed-by: Qt CI Bot --- src/qml/qml/qqmlpropertycachecreator.cpp | 4 +++- tests/auto/qml/qqmllanguage/data/alias.15a.qml | 21 +++++++++++++++++++++ .../qml/qqmllanguage/data/badGroupedProperty.qml | 10 ++++++++++ tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 12 ++++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 tests/auto/qml/qqmllanguage/data/badGroupedProperty.qml diff --git a/src/qml/qml/qqmlpropertycachecreator.cpp b/src/qml/qml/qqmlpropertycachecreator.cpp index bb601cea9d..2dccf66df2 100644 --- a/src/qml/qml/qqmlpropertycachecreator.cpp +++ b/src/qml/qml/qqmlpropertycachecreator.cpp @@ -115,8 +115,10 @@ bool QQmlBindingInstantiationContext::resolveInstantiatingProperty() return true; } + if (!referencingObjectPropertyCache) + return false; + Q_ASSERT(referencingObjectIndex >= 0); - Q_ASSERT(referencingObjectPropertyCache); Q_ASSERT(instantiatingBinding->propertyNameIndex != 0); bool notInRevision = false; diff --git a/tests/auto/qml/qqmllanguage/data/alias.15a.qml b/tests/auto/qml/qqmllanguage/data/alias.15a.qml index ba8097c997..b5f96e15c1 100644 --- a/tests/auto/qml/qqmllanguage/data/alias.15a.qml +++ b/tests/auto/qml/qqmllanguage/data/alias.15a.qml @@ -9,4 +9,25 @@ Item { Item { id: symbol } + + Rectangle { + id: txtElevationValue + + property Rectangle background: Rectangle { } + + state: "ValidatorInvalid" + + states: [ + State { + name: "ValidatorInvalid" + PropertyChanges { + target: txtElevationValue + background.border.color: "red" // this line caused the segfault in qtbug107795 + } + }, + State { + name: "ValidatorAcceptable" + } + ] + } } diff --git a/tests/auto/qml/qqmllanguage/data/badGroupedProperty.qml b/tests/auto/qml/qqmllanguage/data/badGroupedProperty.qml new file mode 100644 index 0000000000..34dcbf96fa --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/badGroupedProperty.qml @@ -0,0 +1,10 @@ +import QtQml 2.15 + +QtObject { + id: testItem + property rect rect + onComplete { + rect.x: 2 + rect.width: 22 + } +} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 1be1533b63..ac6634290a 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -341,6 +341,7 @@ private slots: void ambiguousContainingType(); void staticConstexprMembers(); void bindingAliasToComponentUrl(); + void badGroupedProperty(); private: QQmlEngine engine; @@ -5939,6 +5940,17 @@ void tst_qqmllanguage::bindingAliasToComponentUrl() } } +void tst_qqmllanguage::badGroupedProperty() +{ + QQmlEngine engine; + const QUrl url = testFileUrl("badGroupedProperty.qml"); + QQmlComponent c(&engine, url); + QVERIFY(c.isError()); + QCOMPARE(c.errorString(), + QStringLiteral("%1:6 Cannot assign to non-existent property \"onComplete\"\n") + .arg(url.toString())); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" -- cgit v1.2.3 From a2c4d27cf9b4992fca6a30e01d31c0a5305b7123 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 27 Oct 2022 08:18:25 -0700 Subject: QSGGeometry: add Q_DISABLE_COPY_MOVE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a polymorphic class, so that already tells us this should never be copied. Moreover, it has a reference member, so the default assignment operator cannot be instantiated. error: use of deleted function ‘QSGGeometry& QSGGeometry::operator=(const QSGGeometry&)’ qsggeometry.h:51:22: note: ‘QSGGeometry& QSGGeometry::operator=(const QSGGeometry&)’ is implicitly deleted because the default definition would be ill-formed: qsggeometry.h:51:22: error: non-static reference member ‘const QSGGeometry::AttributeSet& QSGGeometry::m_attributes’, cannot use default assignment operator Found while compiling PyQt, which is obviously wrong, but this should improve the error message output. Change-Id: I07ec23f3cb174fb197c3fffd1721f614929a18f4 Reviewed-by: Laszlo Agocs (cherry picked from commit 32b71a40cdbcd77956b309b032caafa02abcc13f) Reviewed-by: Qt Cherry-pick Bot --- src/quick/scenegraph/coreapi/qsggeometry.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/quick/scenegraph/coreapi/qsggeometry.h b/src/quick/scenegraph/coreapi/qsggeometry.h index 64085393f9..7ea2aa1027 100644 --- a/src/quick/scenegraph/coreapi/qsggeometry.h +++ b/src/quick/scenegraph/coreapi/qsggeometry.h @@ -202,6 +202,7 @@ public: void setLineWidth(float w); private: + Q_DISABLE_COPY_MOVE(QSGGeometry) friend class QSGGeometryData; int m_drawing_mode; -- cgit v1.2.3 From e4cd376599e368edd7e8132903f425530102bfae Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Tue, 3 May 2022 14:37:26 +0200 Subject: Avoid double applyDelegateChange in QQIV::setDelegate If a QQmlDelegateModel is used together with QQuickItemView, then we will already call applyDelegateChange when the DelegateModel's delegateChanged signal is emitted from QQmlDelegateModel::setDelegate. Calling it manually in QQuickItemView's setDelegate is thus superfluous. Fixes: QTBUG-102793 Fixes: QTBUG-107732 Change-Id: Ifffb23661813c4e71287538ec5342215dfbbdad6 Reviewed-by: Ulf Hermann (cherry picked from commit 2664b1988f031366c2616581e65b0e02e37e3ff1) Reviewed-by: Qt CI Bot Reviewed-by: Fabian Kosmale --- src/quick/items/qquickitemview.cpp | 2 - .../data/setDelegateNoDoubleChange.qml | 43 ++++++++++++++++++++++ .../tst_qquickvisualdatamodel.cpp | 15 ++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 tests/auto/quick/qquickvisualdatamodel/data/setDelegateNoDoubleChange.qml diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index fda2e6ff79..7146b8d1b7 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -290,8 +290,6 @@ void QQuickItemView::setDelegate(QQmlComponent *delegate) if (QQmlDelegateModel *dataModel = qobject_cast(d->model)) { int oldCount = dataModel->count(); dataModel->setDelegate(delegate); - if (isComponentComplete()) - d->applyDelegateChange(); if (oldCount != dataModel->count()) emit countChanged(); } diff --git a/tests/auto/quick/qquickvisualdatamodel/data/setDelegateNoDoubleChange.qml b/tests/auto/quick/qquickvisualdatamodel/data/setDelegateNoDoubleChange.qml new file mode 100644 index 0000000000..05623fbc5b --- /dev/null +++ b/tests/auto/quick/qquickvisualdatamodel/data/setDelegateNoDoubleChange.qml @@ -0,0 +1,43 @@ +import QtQuick 2.0 + +Item { + width: 200 + height: 200 + id: root + property int creationCount: 0 + property bool testStarted: false + + ListModel { + id: mymodel + ListElement {message: "This is my alarm"} + } + + Component { + id: aDelegate + Rectangle { + color: "blue" + width: 100 + height: 100 + } + } + + Component { + id: bDelegate + Rectangle { + color: "red" + width: 100 + height: 100 + Text {text: model.message } + Component.onCompleted: root.creationCount++ + } + } + + + ListView { + width: 200 + height: 200 + id: myListView + model: mymodel + delegate: testStarted ? bDelegate : aDelegate + } +} diff --git a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp index 27bd8aae49..83bdb1a5f8 100644 --- a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp +++ b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp @@ -434,6 +434,7 @@ private slots: void invalidContext(); void externalManagedModel(); void delegateModelChangeDelegate(); + void noDoubleDelegateUpdate(); void checkFilterGroupForDelegate(); void readFromProxyObject(); @@ -4345,6 +4346,20 @@ void tst_qquickvisualdatamodel::delegateModelChangeDelegate() QCOMPARE(visualModel->count(), 3); } +void tst_qquickvisualdatamodel::noDoubleDelegateUpdate() +{ + // changing a delegate only refreshes its instances once + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("setDelegateNoDoubleChange.qml")); + + QScopedPointer root(component.create()); + QVERIFY(root); + + bool ok = root->setProperty("testStarted", true); + QVERIFY(ok); + QCOMPARE(root->property("creationCount").toInt(), 1); +} + void tst_qquickvisualdatamodel::checkFilterGroupForDelegate() { QQuickView view; -- cgit v1.2.3 From 524da9ca5f2bde11c7879e8fb479700e4bbe3734 Mon Sep 17 00:00:00 2001 From: CI Insignificant Platforms Monitor Bot Date: Tue, 8 Nov 2022 07:02:14 +0000 Subject: Blacklist 1 tests in tst_qquickbehaviors on macos - currentValue Task-number: QTBUG-108258 Change-Id: I6a76a8dc588112063bdb383adb01b463d2b8ab36 Reviewed-by: CI Insignificant Platforms Monitor Bot --- tests/auto/quick/qquickbehaviors/BLACKLIST | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tests/auto/quick/qquickbehaviors/BLACKLIST diff --git a/tests/auto/quick/qquickbehaviors/BLACKLIST b/tests/auto/quick/qquickbehaviors/BLACKLIST new file mode 100644 index 0000000000..d2a11a6a32 --- /dev/null +++ b/tests/auto/quick/qquickbehaviors/BLACKLIST @@ -0,0 +1,3 @@ +# See qtbase/src/testlib/qtestblacklist.cpp for format +[currentValue] +ci macos # QTBUG-108258 -- cgit v1.2.3 From 7901a44a0efa8ef6b779bd5366ac87973015f899 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 3 Nov 2022 13:25:18 +0100 Subject: Do not crash if madvise() fails on MADV_WILLNEED MADV_WILLNEED is only advisory. The kernel may ignore it anyway. Any subsequent access to the pages in question will trigger them to be re-populated. Fixes: QTBUG-107774 Change-Id: I8d70003502fdeb3e53c169b28ea6826801d47c74 Reviewed-by: Qt CI Bot Reviewed-by: Fabian Kosmale (cherry picked from commit 2034e10c9378364ecc7aa1af27505562d86688de) Reviewed-by: Qt Cherry-pick Bot --- src/3rdparty/masm/wtf/OSAllocatorPosix.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp index ec8e030b14..5b5c0b23bb 100644 --- a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp +++ b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp @@ -234,7 +234,7 @@ void OSAllocator::commit(void* address, size_t bytes, bool writable, bool execut while (madvise(address, bytes, MADV_WILLNEED)) { if (errno != EAGAIN) - CRASH(); + break; // We don't have to crash here. MADV_WILLNEED is only advisory } #elif HAVE(MADV_FREE_REUSE) -- cgit v1.2.3 From fe896f524568ba8d9caad8308a1edb1c652b4da2 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 10 Nov 2022 14:35:01 +0100 Subject: Handle missing stops gracefully in Shape gradients Fixes: QTBUG-108298 Change-Id: Ib6e004a1518aec4c786c3aeebcd74e6cb11f45ef Reviewed-by: Andy Nichols (cherry picked from commit e52efc3defe102a55b45b013764ba1bae1fe5a1b) Reviewed-by: Qt Cherry-pick Bot --- src/quickshapes/qquickshape.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/quickshapes/qquickshape.cpp b/src/quickshapes/qquickshape.cpp index a56ead6fe2..549cd57444 100644 --- a/src/quickshapes/qquickshape.cpp +++ b/src/quickshapes/qquickshape.cpp @@ -1522,6 +1522,7 @@ static void generateGradientColorTable(const QQuickShapeGradientCacheKey &gradie { int pos = 0; const QGradientStops &s = gradient.stops; + Q_ASSERT(!s.isEmpty()); const bool colorInterpolation = true; uint alpha = qRound(opacity * 256); @@ -1559,8 +1560,6 @@ static void generateGradientColorTable(const QQuickShapeGradientCacheKey &gradie current_color = next_color; } - Q_ASSERT(s.size() > 0); - uint last_color = ARGB2RGBA(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha))); for ( ; pos < size; ++pos) colorTable[pos] = last_color; @@ -1595,7 +1594,10 @@ QSGTexture *QQuickShapeGradientCache::get(const QQuickShapeGradientCacheKey &gra if (!tx) { static const int W = 1024; // texture size is 1024x1 QImage gradTab(W, 1, QImage::Format_RGBA8888_Premultiplied); - generateGradientColorTable(grad, reinterpret_cast(gradTab.bits()), W, 1.0f); + if (!grad.stops.isEmpty()) + generateGradientColorTable(grad, reinterpret_cast(gradTab.bits()), W, 1.0f); + else + gradTab.fill(Qt::black); tx = new QSGPlainTexture; tx->setImage(gradTab); switch (grad.spread) { -- cgit v1.2.3 From a6273d93216503a2a8ce4a1b08a8ceb378627a16 Mon Sep 17 00:00:00 2001 From: Hatem ElKharashy Date: Mon, 14 Nov 2022 18:29:05 +0200 Subject: Doc: Add missing QQuickWindow constructor QQuickWindow::QQuickWindow(QQuickRenderControl *control) should be part of the public API. The user can use QQuickRenderControl with QQuickWindow to control the scenegraph rendering. This constructor is used in the QQuickRenderControl example. Change-Id: I1bb38d64173c24ba835137897ef915dfde83b1f3 Reviewed-by: Laszlo Agocs (cherry picked from commit 7454d3b612eab92b8e91741fc1c51311cf80c3a2) Reviewed-by: Qt Cherry-pick Bot --- src/quick/items/qquickwindow.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index e48b83d5fc..c6c7b6171e 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1494,7 +1494,11 @@ QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QWindow *parent) } /*! - \internal + Constructs a window for displaying a QML scene, whose rendering will + be controlled by the \a control object. + Please refer to QQuickRenderControl's documentation for more information. + + \since 5.4 */ QQuickWindow::QQuickWindow(QQuickRenderControl *control) : QWindow(*(new QQuickWindowPrivate), nullptr) -- cgit v1.2.3 From 52535d21f084bae67b845a1ff2cf70f03b5fa6a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Arve=20S=C3=A6ther?= Date: Wed, 21 Sep 2022 13:44:19 +0200 Subject: Fix wrong item-sizeHint-cache when StackLayout children were reordered This fixes the Tests_StackLayout::test_addAndRemoveItems() autotest The code uses an index to look up the cached size hints. This index corresponds to the layout's item child index (which doesn't have to correspond to the children index). The vector of sizeHints then had to be in sync with the index of the child layout items for this to work. The problem here was that if the first item in the stack was removed (or siblings was reordered), the vector was not adjusted for this (basically we could get the size hint of an previously removed item). In order to avoid to keep the QVector index in sync with the layout children index, we change it to use a QHash instead, where we look up by QQuickItem*. QHash Task-number: QTBUG-106520 Task-number: QTBUG-106521 Change-Id: I7c1f9fb018fcab093b074c45dfaba264f76749f4 Reviewed-by: Mitch Curtis (cherry picked from commit c41075d9e2808f636d793c93e237f80a8ecadb2e) --- src/imports/layouts/qquickstacklayout.cpp | 22 +++++++++++++--------- src/imports/layouts/qquickstacklayout_p.h | 2 +- .../quick/qquicklayouts/data/tst_stacklayout.qml | 1 - 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/imports/layouts/qquickstacklayout.cpp b/src/imports/layouts/qquickstacklayout.cpp index bd2f93c630..d3d140abc8 100644 --- a/src/imports/layouts/qquickstacklayout.cpp +++ b/src/imports/layouts/qquickstacklayout.cpp @@ -171,6 +171,7 @@ void QQuickStackLayout::itemChange(QQuickItem::ItemChange change, const QQuickIt QQuickLayout::itemChange(change, value); if (change == ItemChildRemovedChange) { + m_cachedItemSizeHints.remove(value.item); invalidate(); } else if (change == ItemChildAddedChange) { invalidate(); @@ -254,11 +255,12 @@ void QQuickStackLayout::setAlignment(QQuickItem * /*item*/, Qt::Alignment /*alig void QQuickStackLayout::invalidate(QQuickItem *childItem) { - const int indexOfChild = indexOf(childItem); - if (indexOfChild >= 0 && indexOfChild < m_cachedItemSizeHints.count()) { - m_cachedItemSizeHints[indexOfChild].min() = QSizeF(); - m_cachedItemSizeHints[indexOfChild].pref() = QSizeF(); - m_cachedItemSizeHints[indexOfChild].max() = QSizeF(); + ensureLayoutItemsUpdated(); + if (childItem) { + SizeHints &hints = m_cachedItemSizeHints[childItem]; + hints.min() = QSizeF(); + hints.pref() = QSizeF(); + hints.max() = QSizeF(); } for (int i = 0; i < Qt::NSizeHints; ++i) @@ -284,7 +286,6 @@ void QQuickStackLayout::updateLayoutItems() if (count != d->count) { d->count = count; emit countChanged(); - m_cachedItemSizeHints.resize(count); } for (int i = 0; i < count; ++i) { QQuickItem *child = itemAt(i); @@ -293,10 +294,13 @@ void QQuickStackLayout::updateLayoutItems() } } -QQuickStackLayout::SizeHints &QQuickStackLayout::cachedItemSizeHints(int index) const { - SizeHints &hints = m_cachedItemSizeHints[index]; +QQuickStackLayout::SizeHints &QQuickStackLayout::cachedItemSizeHints(int index) const +{ + QQuickItem *item = itemAt(index); + Q_ASSERT(item); + SizeHints &hints = m_cachedItemSizeHints[item]; // will create an entry if it doesn't exist if (!hints.min().isValid()) - QQuickStackLayout::collectItemSizeHints(itemAt(index), hints.array); + QQuickStackLayout::collectItemSizeHints(item, hints.array); return hints; } diff --git a/src/imports/layouts/qquickstacklayout_p.h b/src/imports/layouts/qquickstacklayout_p.h index e46c608926..41aac01ec8 100644 --- a/src/imports/layouts/qquickstacklayout_p.h +++ b/src/imports/layouts/qquickstacklayout_p.h @@ -95,7 +95,7 @@ private: QSizeF array[Qt::NSizeHints]; } SizeHints; - mutable QVector m_cachedItemSizeHints; + mutable QHash m_cachedItemSizeHints; mutable QSizeF m_cachedSizeHints[Qt::NSizeHints]; SizeHints &cachedItemSizeHints(int index) const; }; diff --git a/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml b/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml index 32b149336a..2536b9789f 100644 --- a/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml +++ b/tests/auto/quick/qquicklayouts/data/tst_stacklayout.qml @@ -255,7 +255,6 @@ Item { function test_addAndRemoveItems() { - skip("There is a bug with the internal StackLayout cache that causes this to fail") var stack = emtpy_StackLayout_Component.createObject(container) stack.currentIndex = 2 compare(stack.implicitWidth, 0) -- cgit v1.2.3 From cf8ff9df89fec1df805ddd85bd1d27c36bfece55 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 8 Nov 2022 08:20:04 +0100 Subject: QML: Check for stack overflows when creating objects Fixes: QTBUG-106875 Change-Id: I3b0abda6948b79a9e3cf263f27885037fff1804c Reviewed-by: Fabian Kosmale Reviewed-by: Sami Shalayel (cherry picked from commit edc01fbfa430d6f0ce66f1871ab28e0f691ee252) --- src/qml/jsruntime/qv4engine_p.h | 12 +++++++++--- src/qml/qml/qqmlincubator.cpp | 14 ++++++++++++++ tests/auto/quick/qquickloader/data/overflow.qml | 5 +++++ tests/auto/quick/qquickloader/tst_qquickloader.cpp | 13 +++++++++++++ 4 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 tests/auto/quick/qquickloader/data/overflow.qml diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index e033c5b067..0f42b411e8 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -723,6 +723,9 @@ public: QQmlRefPointer loadModule(const QUrl &_url, const ExecutableCompilationUnit *referrer = nullptr); private: + template + friend struct ExecutionEngineCallDepthRecorder; + #if QT_CONFIG(qml_debug) QScopedPointer m_debugger; QScopedPointer m_profiler; @@ -753,14 +756,17 @@ private: }; #define CHECK_STACK_LIMITS(v4) if ((v4)->checkStackLimits()) return Encode::undefined(); \ - ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4); + ExecutionEngineCallDepthRecorder<1> _executionEngineCallDepthRecorder(v4); +template struct ExecutionEngineCallDepthRecorder { ExecutionEngine *ee; - ExecutionEngineCallDepthRecorder(ExecutionEngine *e): ee(e) { ++ee->callDepth; } - ~ExecutionEngineCallDepthRecorder() { --ee->callDepth; } + ExecutionEngineCallDepthRecorder(ExecutionEngine *e): ee(e) { ee->callDepth += Frames; } + ~ExecutionEngineCallDepthRecorder() { ee->callDepth -= Frames; } + + bool hasOverflow() const { return ee->callDepth >= ExecutionEngine::maxCallDepth; } }; inline bool ExecutionEngine::checkStackLimits() diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp index caf419b778..b8227c2131 100644 --- a/src/qml/qml/qqmlincubator.cpp +++ b/src/qml/qml/qqmlincubator.cpp @@ -279,6 +279,20 @@ void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i) // get a copy of the engine pointer as it might get reset; QQmlEnginePrivate *enginePriv = this->enginePriv; + // Incubating objects takes quite a bit more stack space than our usual V4 function + enum { EstimatedSizeInV4Frames = 2 }; + QV4::ExecutionEngineCallDepthRecorder callDepthRecorder( + compilationUnit->engine); + if (callDepthRecorder.hasOverflow()) { + QQmlError error; + error.setMessageType(QtCriticalMsg); + error.setUrl(compilationUnit->url()); + error.setDescription(QQmlComponent::tr("Maximum call stack size exceeded.")); + errors << error; + progress = QQmlIncubatorPrivate::Completed; + goto finishIncubate; + } + if (!vmeGuard.isOK()) { QQmlError error; error.setMessageType(QtInfoMsg); diff --git a/tests/auto/quick/qquickloader/data/overflow.qml b/tests/auto/quick/qquickloader/data/overflow.qml new file mode 100644 index 0000000000..8ca196e2ed --- /dev/null +++ b/tests/auto/quick/qquickloader/data/overflow.qml @@ -0,0 +1,5 @@ +import QtQuick 2.15 + +Loader { + source: "overflow.qml" +} diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp index db678ae5a1..4539a89cb3 100644 --- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp +++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp @@ -134,6 +134,8 @@ private slots: void setSourceAndCheckStatus(); void asyncLoaderRace(); void noEngine(); + + void stackOverflow(); }; Q_DECLARE_METATYPE(QList) @@ -1530,6 +1532,17 @@ void tst_QQuickLoader::noEngine() QTRY_COMPARE(o->property("changes").toInt(), 1); } +void tst_QQuickLoader::stackOverflow() +{ + QQmlEngine engine; + const QUrl url = testFileUrl("overflow.qml"); + QQmlComponent component(&engine, url); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + const QString message = url.toString() + QStringLiteral(": Maximum call stack size exceeded."); + QTest::ignoreMessage(QtCriticalMsg, qPrintable(message)); + QScopedPointer o(component.create()); +} + QTEST_MAIN(tst_QQuickLoader) #include "tst_qquickloader.moc" -- cgit v1.2.3 From 760a37d60a79c8b4fcf91009d92e8fdfb7bdba6f Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 23 Nov 2022 09:22:19 +0100 Subject: Revert "masm: Treat Android as generic Posix regarding mmap and friends" This reverts commit ffecc122d785de9c4c5defd8724526b8dd4982dc. It turns out that madvise() only fails when given MADV_WILLNEED as argument on the affected devices. MADV_WILLNEED is indeed optional. Since commit 2034e10c9378364ecc7aa1af27505562d86688de we do not crash on a failed MADV_WILLNEED anymore. Therefore, we can re-enable the linux code path for android. Task-number: QTBUG-107774 Task-number: QTBUG-106864 Task-number: QTBUG-106269 Change-Id: If67a38e4fc206bd5d5ed0ef8bf66ededd09d8f59 Reviewed-by: Fabian Kosmale (cherry picked from commit ac4fea75379467dde9065825d3f15da3b86e9ad8) Reviewed-by: Qt Cherry-pick Bot --- src/3rdparty/masm/wtf/OSAllocatorPosix.cpp | 23 +++++++---------------- src/qml/memory/qv4mm.cpp | 2 +- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp index 5b5c0b23bb..b5c5f6a2b0 100644 --- a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp +++ b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp @@ -37,16 +37,7 @@ #include #include -// Android does not really behave like linux here. -// For example, madvise() randomly fails if we pass MADV_WILLNEED or MADV_DONTNEED. -// Treat it as generic Posix. -#if OS(LINUX) && !defined(__ANDROID__) -#define NON_ANDROID_LINUX 1 -#else -#define NON_ANDROID_LINUX 0 -#endif - -#if NON_ANDROID_LINUX +#if OS(LINUX) #include #ifndef MFD_CLOEXEC #define MFD_CLOEXEC 0x0001U @@ -95,7 +86,7 @@ static int memfdForUsage(size_t bytes, OSAllocator::Usage usage) close(fd); return -1; } -#elif NON_ANDROID_LINUX +#elif OS(LINUX) static int memfdForUsage(size_t bytes, OSAllocator::Usage usage) { UNUSED_PARAM(bytes); @@ -111,7 +102,7 @@ void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable, void* result = mmap(0, bytes, PROT_NONE, MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0); if (result == MAP_FAILED) CRASH(); -#elif NON_ANDROID_LINUX +#elif OS(LINUX) UNUSED_PARAM(writable); UNUSED_PARAM(executable); int fd = memfdForUsage(bytes, usage); @@ -157,7 +148,7 @@ void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, bool writable, bo #if OS(DARWIN) int fd = usage; -#elif NON_ANDROID_LINUX +#elif OS(LINUX) int fd = memfdForUsage(bytes, usage); if (fd != -1) flags &= ~MAP_ANON; @@ -205,7 +196,7 @@ void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, bool writable, bo mmap(static_cast(result) + bytes - pageSize(), pageSize(), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, fd, 0); } -#if NON_ANDROID_LINUX +#if OS(LINUX) if (fd != -1) close(fd); #endif @@ -223,7 +214,7 @@ void OSAllocator::commit(void* address, size_t bytes, bool writable, bool execut protection |= PROT_EXEC; if (MAP_FAILED == mmap(address, bytes, protection, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0)) CRASH(); -#elif NON_ANDROID_LINUX +#elif OS(LINUX) int protection = PROT_READ; if (writable) protection |= PROT_WRITE; @@ -255,7 +246,7 @@ void OSAllocator::decommit(void* address, size_t bytes) #if OS(QNX) // Use PROT_NONE and MAP_LAZY to decommit the pages. mmap(address, bytes, PROT_NONE, MAP_FIXED | MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0); -#elif NON_ANDROID_LINUX +#elif OS(LINUX) while (madvise(address, bytes, MADV_DONTNEED)) { if (errno != EAGAIN) CRASH(); diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 9caedccbe7..216a82718b 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -177,7 +177,7 @@ struct MemorySegment { size_t pageSize = WTF::pageSize(); size = (size + pageSize - 1) & ~(pageSize - 1); -#if (!defined(Q_OS_LINUX) && !defined(Q_OS_WIN)) || defined(Q_OS_ANDROID) +#if !defined(Q_OS_LINUX) && !defined(Q_OS_WIN) // Linux and Windows zero out pages that have been decommitted and get committed again. // unfortunately that's not true on other OSes (e.g. BSD based ones), so zero out the // memory before decommit, so that we can be sure that all chunks we allocate will be -- cgit v1.2.3 From bb68ee992464eededf50261a68b4199b61911f36 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 29 Nov 2022 21:30:24 +0100 Subject: doc: Add note to PinchHandler.translation property about macOS trackpad Translation doesn't happen with trackpad native gestures. Fixes: QTBUG-109002 Change-Id: I7d42b0d737d45405732a389560a6c77624831c9e Reviewed-by: Paul Wicking (cherry picked from commit 786e1748d4469c135a922a221024f3f9c421c0de) Reviewed-by: Qt Cherry-pick Bot --- src/quick/handlers/qquickpinchhandler.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp index 9985c85878..3111b6cfb3 100644 --- a/src/quick/handlers/qquickpinchhandler.cpp +++ b/src/quick/handlers/qquickpinchhandler.cpp @@ -543,6 +543,9 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event) The translation of the gesture \l centroid. It is \c (0, 0) when the gesture begins. + + \note On some touchpads, such as on a \macos trackpad, native gestures do + not generate any translation values, and this property stays at \c (0, 0). */ QT_END_NAMESPACE -- cgit v1.2.3 From 09cc572c62c8b3c8365978f8b4d69de7dc53bb25 Mon Sep 17 00:00:00 2001 From: CI Insignificant Platforms Monitor Bot Date: Fri, 25 Nov 2022 11:17:00 +0000 Subject: Blacklist 1 tests in tst_qquickanimations on macos - reparent Task-number: QTBUG-108880 Change-Id: I60403672b54a0d1afe88f14607b3add92bf7bf46 Reviewed-by: CI Insignificant Platforms Monitor Bot --- tests/auto/quick/qquickanimations/BLACKLIST | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tests/auto/quick/qquickanimations/BLACKLIST diff --git a/tests/auto/quick/qquickanimations/BLACKLIST b/tests/auto/quick/qquickanimations/BLACKLIST new file mode 100644 index 0000000000..6c1bec6520 --- /dev/null +++ b/tests/auto/quick/qquickanimations/BLACKLIST @@ -0,0 +1,3 @@ +# See qtbase/src/testlib/qtestblacklist.cpp for format +[reparent] +ci macos # QTBUG-108880 -- cgit v1.2.3 From 94fd52dbb83a4982e4a70e621f431b0bd0945b5d Mon Sep 17 00:00:00 2001 From: Fabian Kosmale Date: Wed, 12 Oct 2022 09:02:40 +0200 Subject: QV4: Avoid memory corruption in Reflect.apply This extracts the check from Function.prototype.apply into a shared function, and uses it in Reflect.apply, which has the same issue. Task-number: QTBUG-107619 Change-Id: I899464c86554f9bbb5270a95bbe3fe27531e9a27 Reviewed-by: Ulf Hermann (cherry picked from commit 0e963a53c04b0dbe172cfb495b4d62dc8e2f31a3) Reviewed-by: Qt Cherry-pick Bot --- src/qml/jsruntime/qv4engine.cpp | 19 +++++++++++++++++++ src/qml/jsruntime/qv4engine_p.h | 1 + src/qml/jsruntime/qv4functionobject.cpp | 11 +++-------- src/qml/jsruntime/qv4reflect.cpp | 5 ++++- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 17 +++++++++++++++++ 5 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index bf5d437d10..da683da952 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1892,6 +1892,25 @@ int ExecutionEngine::maxGCStackSize() const return m_maxGCStackSize; } +/*! + \internal + Returns \a length converted to int if its safe to + pass to \c Scope::alloc. + Otherwise it throws a RangeError, and returns 0. + */ +int ExecutionEngine::safeForAllocLength(qint64 len64) +{ + if (len64 < 0ll || len64 > qint64(std::numeric_limits::max())) { + this->throwRangeError(QStringLiteral("Invalid array length.")); + return 0; + } + if (len64 > qint64(this->jsStackLimit - this->jsStackTop)) { + this->throwRangeError(QStringLiteral("Array too large for apply().")); + return 0; + } + return len64; +} + ReturnedValue ExecutionEngine::global() { return globalObject->asReturnedValue(); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 0f42b411e8..639f387ca6 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -655,6 +655,7 @@ public: int maxGCStackSize() const; bool checkStackLimits(); + int safeForAllocLength(qint64 len64); bool canJIT(Function *f = nullptr) { diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 987355286d..efb4a10f11 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -364,15 +364,10 @@ ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, cons if (!arr) return v4->throwTypeError(); - const qint64 len64 = arr->getLength(); - if (len64 < 0ll || len64 > qint64(std::numeric_limits::max())) - return v4->throwRangeError(QStringLiteral("Invalid array length.")); - if (len64 > qint64(v4->jsStackLimit - v4->jsStackTop)) - return v4->throwRangeError(QStringLiteral("Array too large for apply().")); - - const uint len = uint(len64); - Scope scope(v4); + const uint len = v4->safeForAllocLength(arr->getLength()); + CHECK_EXCEPTION(); + Value *arguments = scope.alloc(len); if (len) { if (ArgumentsObject::isNonStrictArgumentsObject(arr) && !arr->cast()->fullyCreated()) { diff --git a/src/qml/jsruntime/qv4reflect.cpp b/src/qml/jsruntime/qv4reflect.cpp index 981f4ba6f3..395c637492 100644 --- a/src/qml/jsruntime/qv4reflect.cpp +++ b/src/qml/jsruntime/qv4reflect.cpp @@ -76,7 +76,10 @@ struct CallArgs { static CallArgs createListFromArrayLike(Scope &scope, const Object *o) { - int len = o->getLength(); + int len = scope.engine->safeForAllocLength(o->getLength()); + if (scope.engine->hasException) + return {nullptr, 0}; + Value *arguments = scope.alloc(len); for (int i = 0; i < len; ++i) { diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index c66e2dccf3..363070d7f8 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -260,6 +260,7 @@ private slots: void sortNonStringArray(); void iterateInvalidProxy(); void applyOnHugeArray(); + void reflectApplyOnHugeArray(); void tostringRecursionCheck(); void arrayIncludesWithLargeArray(); @@ -5174,6 +5175,22 @@ void tst_QJSEngine::applyOnHugeArray() QCOMPARE(value.toString(), "RangeError: Array too large for apply()."); } + +void tst_QJSEngine::reflectApplyOnHugeArray() +{ + QQmlEngine engine; + const QJSValue value = engine.evaluate(R"( +(function(){ +const v1 = []; +const v3 = []; +v3.length = 3900000000; +Reflect.apply(v1.reverse,v1,v3); +})() + )"); + QVERIFY(value.isError()); + QCOMPARE(value.toString(), QLatin1String("RangeError: Invalid array length.")); +} + void tst_QJSEngine::typedArraySet() { QJSEngine engine; -- cgit v1.2.3