summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h2
-rw-r--r--src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp2
-rw-r--r--src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp4
-rw-r--r--src/3rdparty/sqlite/patches/0002-sqlite-Fix-CVE-2020-11655.patch30
-rw-r--r--src/3rdparty/sqlite/patches/0003-sqlite-Fix-CVE-2020-11656.patch63
-rw-r--r--src/3rdparty/sqlite/sqlite3.c19
-rw-r--r--src/3rdparty/tinycbor/tests/parser/data.cpp37
-rw-r--r--src/angle/patches/0016-ANGLE-Fix-severe-performance-regression.patch37
-rw-r--r--src/angle/patches/0017-ANGLE-Fix-resizing-of-windows-Take-2.patch27
-rw-r--r--src/angle/patches/0018-ANGLE-d3d11-Do-not-register-windows-message-hooks-fo.patch45
-rw-r--r--src/corelib/configure.json3
-rw-r--r--src/corelib/doc/src/containers.qdoc10
-rw-r--r--src/corelib/global/qnamespace.qdoc2
-rw-r--r--src/corelib/global/qsystemdetection.h2
-rw-r--r--src/corelib/io/qfileinfo.cpp2
-rw-r--r--src/corelib/kernel/qeventdispatcher_win.cpp82
-rw-r--r--src/corelib/kernel/qeventdispatcher_win_p.h1
-rw-r--r--src/corelib/plugin/qlibrary.cpp2
-rw-r--r--src/corelib/plugin/qlibrary_unix.cpp12
-rw-r--r--src/corelib/plugin/qlibrary_win.cpp3
-rw-r--r--src/corelib/serialization/qcborstream.cpp35
-rw-r--r--src/corelib/serialization/qcborvalue.cpp133
-rw-r--r--src/corelib/serialization/qcborvalue_p.h4
-rw-r--r--src/corelib/text/qbytearray_p.h7
-rw-r--r--src/corelib/time/qdatetime.cpp16
-rw-r--r--src/corelib/time/qtimezoneprivate_android.cpp2
-rw-r--r--src/corelib/time/qtimezoneprivate_p.h16
-rw-r--r--src/corelib/time/qtimezoneprivate_tz.cpp182
-rw-r--r--src/corelib/tools/qline.cpp2
-rw-r--r--src/gui/doc/src/richtext.qdoc8
-rw-r--r--src/gui/image/qiconloader.cpp8
-rw-r--r--src/gui/text/qtextlayout.cpp29
-rw-r--r--src/network/ssl/qsslsocket.cpp2
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp23
-rw-r--r--src/network/ssl/qsslsocket_openssl11_symbols_p.h7
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp26
-rw-r--r--src/network/ssl/qsslsocket_opensslpre11_symbols_p.h2
-rw-r--r--src/network/ssl/qsslsocket_p.h1
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.h10
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.mm83
-rw-r--r--src/plugins/platforms/wasm/qwasmeventdispatcher.cpp3
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp7
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.h2
-rw-r--r--src/plugins/platforms/windows/qwindowsmenu.cpp11
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.cpp12
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.h1
-rw-r--r--src/plugins/platforms/winrt/qwinrtservices.cpp33
-rw-r--r--src/plugins/platforms/xcb/qxcbatom.cpp1
-rw-r--r--src/plugins/platforms/xcb/qxcbatom.h1
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.cpp108
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.h15
-rw-r--r--src/testlib/qabstractitemmodeltester.cpp6
-rw-r--r--src/widgets/itemviews/qabstractitemview.cpp12
-rw-r--r--src/widgets/itemviews/qtreeview.cpp3
-rw-r--r--src/widgets/kernel/qwidgetwindow.cpp8
55 files changed, 885 insertions, 319 deletions
diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h
index ddbeeb90d2..f92a68454b 100644
--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h
+++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h
@@ -31,7 +31,6 @@ struct TranslatedAttribute;
// The order of this enum governs priority of 'getLatestBufferStorage'.
enum BufferUsage
{
- BUFFER_USAGE_SYSTEM_MEMORY,
BUFFER_USAGE_STAGING,
BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK,
BUFFER_USAGE_INDEX,
@@ -40,6 +39,7 @@ enum BufferUsage
BUFFER_USAGE_PIXEL_UNPACK,
BUFFER_USAGE_PIXEL_PACK,
BUFFER_USAGE_UNIFORM,
+ BUFFER_USAGE_SYSTEM_MEMORY,
BUFFER_USAGE_EMULATED_INDEXED_VERTEX,
BUFFER_USAGE_COUNT,
diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp
index e8f13b388f..9ece77ecbc 100644
--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp
+++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp
@@ -845,7 +845,7 @@ EGLint SwapChain11::copyOffscreenToBackbuffer(const gl::Context *context,
stateManager->setRenderTarget(mBackBufferRTView.get(), nullptr);
// Set the viewport
- stateManager->setSimpleViewport(mWidth, mHeight);
+ stateManager->setSimpleViewport(width, height);
// Apply textures
stateManager->setSimplePixelTextureAndSampler(mOffscreenSRView, mPassThroughSampler);
diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp
index 5394e3d3e7..f5e6c93813 100644
--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp
+++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp
@@ -168,7 +168,7 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device,
nullptr, nullptr, &swapChain1);
if (SUCCEEDED(result))
{
- factory2->MakeWindowAssociation(getNativeWindow(), DXGI_MWA_NO_ALT_ENTER);
+ factory2->MakeWindowAssociation(getNativeWindow(), DXGI_MWA_NO_WINDOW_CHANGES);
*swapChain = static_cast<IDXGISwapChain *>(swapChain1);
}
SafeRelease(factory2);
@@ -196,7 +196,7 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device,
HRESULT result = factory->CreateSwapChain(device, &swapChainDesc, swapChain);
if (SUCCEEDED(result))
{
- factory->MakeWindowAssociation(getNativeWindow(), DXGI_MWA_NO_ALT_ENTER);
+ factory->MakeWindowAssociation(getNativeWindow(), DXGI_MWA_NO_WINDOW_CHANGES);
}
return result;
}
diff --git a/src/3rdparty/sqlite/patches/0002-sqlite-Fix-CVE-2020-11655.patch b/src/3rdparty/sqlite/patches/0002-sqlite-Fix-CVE-2020-11655.patch
new file mode 100644
index 0000000000..c47e68c4a9
--- /dev/null
+++ b/src/3rdparty/sqlite/patches/0002-sqlite-Fix-CVE-2020-11655.patch
@@ -0,0 +1,30 @@
+From fa3ea2350c0367aa7cfd796b31214e2dcf574360 Mon Sep 17 00:00:00 2001
+From: Andy Shaw <andy.shaw@qt.io>
+Date: Mon, 20 Apr 2020 10:43:29 +0200
+Subject: [PATCH] sqlite: Fix CVE-2020-11655
+
+This was taken from 4a302b42c7bf5e11 in SQLite, ref:
+https://www3.sqlite.org/cgi/src/info/4a302b42c7bf5e11
+
+[ChangeLog][QtSQL][sqlite] Fixed CVE-2020-11655
+
+Change-Id: I5ead78d9ee63aa0f12f1c1014c79373728569f30
+---
+ src/3rdparty/sqlite/sqlite3.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/3rdparty/sqlite/sqlite3.c b/src/3rdparty/sqlite/sqlite3.c
+index dfe5323a59..054be43d95 100644
+--- a/src/3rdparty/sqlite/sqlite3.c
++++ b/src/3rdparty/sqlite/sqlite3.c
+@@ -133226,6 +133226,7 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
+ struct AggInfo_func *pFunc;
+ int nReg = pAggInfo->nFunc + pAggInfo->nColumn;
+ if( nReg==0 ) return;
++ if( pParse->nErr ) return;
+ #ifdef SQLITE_DEBUG
+ /* Verify that all AggInfo registers are within the range specified by
+ ** AggInfo.mnReg..AggInfo.mxReg */
+--
+2.24.2 (Apple Git-127)
+
diff --git a/src/3rdparty/sqlite/patches/0003-sqlite-Fix-CVE-2020-11656.patch b/src/3rdparty/sqlite/patches/0003-sqlite-Fix-CVE-2020-11656.patch
new file mode 100644
index 0000000000..c5ceb0a00c
--- /dev/null
+++ b/src/3rdparty/sqlite/patches/0003-sqlite-Fix-CVE-2020-11656.patch
@@ -0,0 +1,63 @@
+From 99cdbed3bb5368ae2ec80d15635a2dd57961310c Mon Sep 17 00:00:00 2001
+From: Andy Shaw <andy.shaw@qt.io>
+Date: Mon, 20 Apr 2020 10:49:57 +0200
+Subject: [PATCH] sqlite: Fix CVE-2020-11656
+
+This was taken from d09f8c3621d5f7f8 and b64674919f673602 in SQLite,
+ref: https://www3.sqlite.org/cgi/src/info/d09f8c3621d5f7f8
+https://www.sqlite.org/cgi/src/info/b64674919f673602
+
+[ChangeLog][QtSQL][sqlite] Fixed CVE-2020-11656
+
+Fixes: QTBUG-83652
+Change-Id: I99bd59dc10b753ff19822c902dff1fc339d330a8
+---
+ src/3rdparty/sqlite/sqlite3.c | 18 +++++++++++++++++-
+ 1 file changed, 17 insertions(+), 1 deletion(-)
+
+diff --git a/src/3rdparty/sqlite/sqlite3.c b/src/3rdparty/sqlite/sqlite3.c
+index 054be43d95..6ff9ba42aa 100644
+--- a/src/3rdparty/sqlite/sqlite3.c
++++ b/src/3rdparty/sqlite/sqlite3.c
+@@ -97945,7 +97945,7 @@ static int resolveOrderByTermToExprList(
+ nc.nErr = 0;
+ db = pParse->db;
+ savedSuppErr = db->suppressErr;
+- db->suppressErr = 1;
++ if( IN_RENAME_OBJECT==0 ) db->suppressErr = 1;
+ rc = sqlite3ResolveExprNames(&nc, pE);
+ db->suppressErr = savedSuppErr;
+ if( rc ) return 0;
+@@ -105383,6 +105383,21 @@ static void renameWalkWith(Walker *pWalker, Select *pSelect){
+ }
+ }
+
++/*
++** Unmap all tokens in the IdList object passed as the second argument.
++*/
++static void unmapColumnIdlistNames(
++ Parse *pParse,
++ IdList *pIdList
++){
++ if( pIdList ){
++ int ii;
++ for(ii=0; ii<pIdList->nId; ii++){
++ sqlite3RenameTokenRemap(pParse, 0, (void*)pIdList->a[ii].zName);
++ }
++ }
++}
++
+ /*
+ ** Walker callback used by sqlite3RenameExprUnmap().
+ */
+@@ -105404,6 +105419,7 @@ static int renameUnmapSelectCb(Walker *pWalker, Select *p){
+ for(i=0; i<pSrc->nSrc; i++){
+ sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName);
+ if( sqlite3WalkExpr(pWalker, pSrc->a[i].pOn) ) return WRC_Abort;
++ unmapColumnIdlistNames(pParse, pSrc->a[i].pUsing);
+ }
+ }
+
+--
+2.24.2 (Apple Git-127)
+
diff --git a/src/3rdparty/sqlite/sqlite3.c b/src/3rdparty/sqlite/sqlite3.c
index dfe5323a59..6ff9ba42aa 100644
--- a/src/3rdparty/sqlite/sqlite3.c
+++ b/src/3rdparty/sqlite/sqlite3.c
@@ -97945,7 +97945,7 @@ static int resolveOrderByTermToExprList(
nc.nErr = 0;
db = pParse->db;
savedSuppErr = db->suppressErr;
- db->suppressErr = 1;
+ if( IN_RENAME_OBJECT==0 ) db->suppressErr = 1;
rc = sqlite3ResolveExprNames(&nc, pE);
db->suppressErr = savedSuppErr;
if( rc ) return 0;
@@ -105384,6 +105384,21 @@ static void renameWalkWith(Walker *pWalker, Select *pSelect){
}
/*
+** Unmap all tokens in the IdList object passed as the second argument.
+*/
+static void unmapColumnIdlistNames(
+ Parse *pParse,
+ IdList *pIdList
+){
+ if( pIdList ){
+ int ii;
+ for(ii=0; ii<pIdList->nId; ii++){
+ sqlite3RenameTokenRemap(pParse, 0, (void*)pIdList->a[ii].zName);
+ }
+ }
+}
+
+/*
** Walker callback used by sqlite3RenameExprUnmap().
*/
static int renameUnmapSelectCb(Walker *pWalker, Select *p){
@@ -105404,6 +105419,7 @@ static int renameUnmapSelectCb(Walker *pWalker, Select *p){
for(i=0; i<pSrc->nSrc; i++){
sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName);
if( sqlite3WalkExpr(pWalker, pSrc->a[i].pOn) ) return WRC_Abort;
+ unmapColumnIdlistNames(pParse, pSrc->a[i].pUsing);
}
}
@@ -133226,6 +133242,7 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
struct AggInfo_func *pFunc;
int nReg = pAggInfo->nFunc + pAggInfo->nColumn;
if( nReg==0 ) return;
+ if( pParse->nErr ) return;
#ifdef SQLITE_DEBUG
/* Verify that all AggInfo registers are within the range specified by
** AggInfo.mnReg..AggInfo.mxReg */
diff --git a/src/3rdparty/tinycbor/tests/parser/data.cpp b/src/3rdparty/tinycbor/tests/parser/data.cpp
index 0ab0e47be4..3523c32167 100644
--- a/src/3rdparty/tinycbor/tests/parser/data.cpp
+++ b/src/3rdparty/tinycbor/tests/parser/data.cpp
@@ -338,7 +338,7 @@ void addValidationColumns()
QTest::addColumn<CborError>("expectedError");
}
-void addValidationData()
+void addValidationData(size_t minInvalid = ~size_t(0))
{
// illegal numbers are future extension points
QTest::newRow("illegal-number-in-unsigned-1") << raw("\x81\x1c") << 0 << CborErrorIllegalNumber;
@@ -488,26 +488,35 @@ void addValidationData()
QTest::newRow("map-break-after-value-tag2") << raw("\x81\xbf\0\xd8\x20\xff") << 0 << CborErrorUnexpectedBreak;
// check for pointer additions wrapping over the limit of the address space
- CborError tooLargeOn32bit = (sizeof(void *) == 4) ? CborErrorDataTooLarge : CborErrorUnexpectedEOF;
+ auto wraparoundError = [minInvalid](uint64_t encodedSize) {
+ if (encodedSize > minInvalid)
+ return CborErrorDataTooLarge;
+ return CborErrorUnexpectedEOF;
+ };
+ constexpr uint64_t FourGB = UINT32_MAX + UINT64_C(1);
// on 32-bit systems, this is a -1
- QTest::newRow("bytearray-wraparound1") << raw("\x81\x5a\xff\xff\xff\xff") << 0 << CborErrorUnexpectedEOF;
- QTest::newRow("string-wraparound1") << raw("\x81\x7a\xff\xff\xff\xff") << 0 << CborErrorUnexpectedEOF;
+ QTest::newRow("bytearray-wraparound1") << raw("\x81\x5a\xff\xff\xff\xff") << 0 << wraparoundError(UINT32_MAX);
+ QTest::newRow("string-wraparound1") << raw("\x81\x7a\xff\xff\xff\xff") << 0 << wraparoundError(UINT32_MAX);
// on 32-bit systems, a 4GB addition could be dropped
- QTest::newRow("bytearray-wraparound2") << raw("\x81\x5b\0\0\0\1\0\0\0\0") << 0 << tooLargeOn32bit;
- QTest::newRow("string-wraparound2") << raw("\x81\x7b\0\0\0\1\0\0\0\0") << 0 << tooLargeOn32bit;
+ QTest::newRow("bytearray-wraparound2") << raw("\x81\x5b\0\0\0\1\0\0\0\0") << 0 << wraparoundError(FourGB);
+ QTest::newRow("string-wraparound2") << raw("\x81\x7b\0\0\0\1\0\0\0\0") << 0 << wraparoundError(FourGB);
// on 64-bit systems, this could be a -1
- QTest::newRow("bytearray-wraparound3") << raw("\x81\x5b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 << tooLargeOn32bit;
- QTest::newRow("string-wraparound3") << raw("\x81\x7b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 << tooLargeOn32bit;
+ QTest::newRow("bytearray-wraparound3") << raw("\x81\x5b\xff\xff\xff\xff\xff\xff\xff\xff") << 0
+ << wraparoundError(UINT64_MAX);
+ QTest::newRow("string-wraparound3") << raw("\x81\x7b\xff\xff\xff\xff\xff\xff\xff\xff") << 0
+ << wraparoundError(UINT64_MAX);
// ditto on chunks
- QTest::newRow("bytearray-chunk-wraparound1") << raw("\x81\x5f\x5a\xff\xff\xff\xff") << 0 << CborErrorUnexpectedEOF;
- QTest::newRow("string-chunk-wraparound1") << raw("\x81\x7f\x7a\xff\xff\xff\xff") << 0 << CborErrorUnexpectedEOF;
+ QTest::newRow("bytearray-chunk-wraparound1") << raw("\x81\x5f\x5a\xff\xff\xff\xff") << 0 << wraparoundError(UINT32_MAX);
+ QTest::newRow("string-chunk-wraparound1") << raw("\x81\x7f\x7a\xff\xff\xff\xff") << 0 << wraparoundError(UINT32_MAX);
// on 32-bit systems, a 4GB addition could be dropped
- QTest::newRow("bytearray-chunk-wraparound2") << raw("\x81\x5f\x5b\0\0\0\1\0\0\0\0") << 0 << tooLargeOn32bit;
- QTest::newRow("string-chunk-wraparound2") << raw("\x81\x7f\x7b\0\0\0\1\0\0\0\0") << 0 << tooLargeOn32bit;
+ QTest::newRow("bytearray-chunk-wraparound2") << raw("\x81\x5f\x5b\0\0\0\1\0\0\0\0") << 0 << wraparoundError(FourGB);
+ QTest::newRow("string-chunk-wraparound2") << raw("\x81\x7f\x7b\0\0\0\1\0\0\0\0") << 0 << wraparoundError(FourGB);
// on 64-bit systems, this could be a -1
- QTest::newRow("bytearray-chunk-wraparound3") << raw("\x81\x5f\x5b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 << tooLargeOn32bit;
- QTest::newRow("string-chunk-wraparound3") << raw("\x81\x7f\x7b\xff\xff\xff\xff\xff\xff\xff\xff") << 0 << tooLargeOn32bit;
+ QTest::newRow("bytearray-chunk-wraparound3") << raw("\x81\x5f\x5b\xff\xff\xff\xff\xff\xff\xff\xff") << 0
+ << wraparoundError(UINT64_MAX);
+ QTest::newRow("string-chunk-wraparound3") << raw("\x81\x7f\x7b\xff\xff\xff\xff\xff\xff\xff\xff") << 0
+ << wraparoundError(UINT64_MAX);
QTest::newRow("eof-after-array") << raw("\x81") << 0 << CborErrorUnexpectedEOF;
QTest::newRow("eof-after-array2") << raw("\x81\x78\x20") << 0 << CborErrorUnexpectedEOF;
diff --git a/src/angle/patches/0016-ANGLE-Fix-severe-performance-regression.patch b/src/angle/patches/0016-ANGLE-Fix-severe-performance-regression.patch
new file mode 100644
index 0000000000..e9cda1337f
--- /dev/null
+++ b/src/angle/patches/0016-ANGLE-Fix-severe-performance-regression.patch
@@ -0,0 +1,37 @@
+From b215999d63d6e6b087e53e24a47b8b60520ec9e4 Mon Sep 17 00:00:00 2001
+From: Oliver Wolff <oliver.wolff@qt.io>
+Date: Wed, 11 Mar 2020 13:59:39 +0100
+Subject: [PATCH] ANGLE: Fix severe performance regression
+
+The changed buffer usage priority that was introduced in our ANGLE
+update caused severe performance regressions for Qt applications.
+
+Fixes: QTBUG-73835
+Change-Id: I49839bb272cdeec0027264f2751b88bc149665ad
+---
+ src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h
+index ddbeeb90d2..f92a68454b 100644
+--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h
++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/Buffer11.h
+@@ -31,7 +31,6 @@ struct TranslatedAttribute;
+ // The order of this enum governs priority of 'getLatestBufferStorage'.
+ enum BufferUsage
+ {
+- BUFFER_USAGE_SYSTEM_MEMORY,
+ BUFFER_USAGE_STAGING,
+ BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK,
+ BUFFER_USAGE_INDEX,
+@@ -40,6 +39,7 @@ enum BufferUsage
+ BUFFER_USAGE_PIXEL_UNPACK,
+ BUFFER_USAGE_PIXEL_PACK,
+ BUFFER_USAGE_UNIFORM,
++ BUFFER_USAGE_SYSTEM_MEMORY,
+ BUFFER_USAGE_EMULATED_INDEXED_VERTEX,
+
+ BUFFER_USAGE_COUNT,
+--
+2.20.1.windows.1
+
diff --git a/src/angle/patches/0017-ANGLE-Fix-resizing-of-windows-Take-2.patch b/src/angle/patches/0017-ANGLE-Fix-resizing-of-windows-Take-2.patch
new file mode 100644
index 0000000000..abab74b192
--- /dev/null
+++ b/src/angle/patches/0017-ANGLE-Fix-resizing-of-windows-Take-2.patch
@@ -0,0 +1,27 @@
+From 029d42d1049dcde7950c11fb9adf07c07a8c4c02 Mon Sep 17 00:00:00 2001
+From: Oliver Wolff <oliver.wolff@qt.io>
+Date: Wed, 18 Mar 2020 10:56:53 +0100
+Subject: [PATCH] ANGLE: Fix resizing of windows (Take 2)
+
+Task-number: QTBUG-62475
+Change-Id: I0ea17e7875906508941ae64bb396a4236928b0f9
+---
+ .../angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp
+index e8f13b388f..9ece77ecbc 100644
+--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp
++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/SwapChain11.cpp
+@@ -845,7 +845,7 @@ EGLint SwapChain11::copyOffscreenToBackbuffer(const gl::Context *context,
+ stateManager->setRenderTarget(mBackBufferRTView.get(), nullptr);
+
+ // Set the viewport
+- stateManager->setSimpleViewport(mWidth, mHeight);
++ stateManager->setSimpleViewport(width, height);
+
+ // Apply textures
+ stateManager->setSimplePixelTextureAndSampler(mOffscreenSRView, mPassThroughSampler);
+--
+2.20.1.windows.1
+
diff --git a/src/angle/patches/0018-ANGLE-d3d11-Do-not-register-windows-message-hooks-fo.patch b/src/angle/patches/0018-ANGLE-d3d11-Do-not-register-windows-message-hooks-fo.patch
new file mode 100644
index 0000000000..03529c6531
--- /dev/null
+++ b/src/angle/patches/0018-ANGLE-d3d11-Do-not-register-windows-message-hooks-fo.patch
@@ -0,0 +1,45 @@
+From 3d23de2ad72968d0bf43dac4a9a0f237cc9e03e2 Mon Sep 17 00:00:00 2001
+From: Oliver Wolff <oliver.wolff@qt.io>
+Date: Wed, 1 Apr 2020 14:48:48 +0200
+Subject: [PATCH] ANGLE: d3d11: Do not register windows message hooks for d3d11
+ windows
+
+These message hooks are used to handle ALT+ENTER to enter/exit fullscreen
+mode and PRINTSCREEN to take screenshots. Qt is implementing these
+functionalities itself so we do not have to register these hooks.
+
+If too many of these hooks are registered, callbacks are no longer called
+and Qt's message queue is no longer handling messages. By saving these
+hooks we can make sure that more Qt windows at the same time are possible
+without getting unresponsive due to too many hooks being registered.
+
+Change-Id: I5354f91f08cbfeda5e8dc3ad7f824fbd5b3b2932
+---
+ .../src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp
+index 5394e3d..f5e6c93 100644
+--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp
++++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/d3d11/win32/NativeWindow11Win32.cpp
+@@ -168,7 +168,7 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device,
+ nullptr, nullptr, &swapChain1);
+ if (SUCCEEDED(result))
+ {
+- factory2->MakeWindowAssociation(getNativeWindow(), DXGI_MWA_NO_ALT_ENTER);
++ factory2->MakeWindowAssociation(getNativeWindow(), DXGI_MWA_NO_WINDOW_CHANGES);
+ *swapChain = static_cast<IDXGISwapChain *>(swapChain1);
+ }
+ SafeRelease(factory2);
+@@ -196,7 +196,7 @@ HRESULT NativeWindow11Win32::createSwapChain(ID3D11Device *device,
+ HRESULT result = factory->CreateSwapChain(device, &swapChainDesc, swapChain);
+ if (SUCCEEDED(result))
+ {
+- factory->MakeWindowAssociation(getNativeWindow(), DXGI_MWA_NO_ALT_ENTER);
++ factory->MakeWindowAssociation(getNativeWindow(), DXGI_MWA_NO_WINDOW_CHANGES);
+ }
+ return result;
+ }
+--
+2.7.4.windows.1
+
diff --git a/src/corelib/configure.json b/src/corelib/configure.json
index ae360239c6..37fa85cd3d 100644
--- a/src/corelib/configure.json
+++ b/src/corelib/configure.json
@@ -389,7 +389,8 @@
"# Block futimens() on Apple platforms unless it's available on ALL",
"# deployment targets. This simplifies the logic at the call site",
"# dramatically, as it isn't strictly needed compared to futimes().",
- "darwin: QMAKE_CXXFLAGS += -Werror=unguarded-availability"
+ "darwin: QMAKE_CXXFLAGS += -Werror=unguarded-availability -Werror=unguarded-availability-new",
+ "CONFIG += warn_on"
]
}
},
diff --git a/src/corelib/doc/src/containers.qdoc b/src/corelib/doc/src/containers.qdoc
index d0bb251e81..4c7cb32aac 100644
--- a/src/corelib/doc/src/containers.qdoc
+++ b/src/corelib/doc/src/containers.qdoc
@@ -123,6 +123,10 @@
a vector can be quite slow, because it can lead to large numbers
of items having to be moved by one position in memory.
+ \row \li \l{QVarLengthArray}<T, Prealloc>
+ \li This provides a low-level variable-length array. It can be used
+ instead of QVector in places where speed is particularly important.
+
\row \li \l{QStack}<T>
\li This is a convenience subclass of QVector that provides
"last in, first out" (LIFO) semantics. It adds the following
@@ -622,15 +626,11 @@
\section1 Other Container-Like Classes
- Qt includes three template classes that resemble containers in
+ Qt includes other template classes that resemble containers in
some respects. These classes don't provide iterators and cannot
be used with the \c foreach keyword.
\list
- \li QVarLengthArray<T, Prealloc> provides a low-level
- variable-length array. It can be used instead of QVector in
- places where speed is particularly important.
-
\li QCache<Key, T> provides a cache to store objects of a certain
type T associated with keys of type Key.
diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc
index 31e1bcc0da..ca3687a420 100644
--- a/src/corelib/global/qnamespace.qdoc
+++ b/src/corelib/global/qnamespace.qdoc
@@ -291,7 +291,7 @@
application on supported platforms, use of a session manager may be
redundant for system services.
This attribute must be set before QGuiApplication is constructed.
- This value was added in 5.13
+ This value was added in 5.14
The following values are deprecated or obsolete:
diff --git a/src/corelib/global/qsystemdetection.h b/src/corelib/global/qsystemdetection.h
index a020788b11..fe7d7d880b 100644
--- a/src/corelib/global/qsystemdetection.h
+++ b/src/corelib/global/qsystemdetection.h
@@ -76,6 +76,8 @@
The following operating systems have variants:
LINUX - both Q_OS_LINUX and Q_OS_ANDROID are defined when building for Android
- only Q_OS_LINUX is defined if building for other Linux systems
+ MACOS - both Q_OS_BSD4 and Q_OS_IOS are defined when building for iOS
+ - both Q_OS_BSD4 and Q_OS_MACOS are defined when building for macOS
FREEBSD - Q_OS_FREEBSD is defined only when building for FreeBSD with a BSD userland
- Q_OS_FREEBSD_KERNEL is always defined on FreeBSD, even if the userland is from GNU
*/
diff --git a/src/corelib/io/qfileinfo.cpp b/src/corelib/io/qfileinfo.cpp
index 89834de29f..9f58458d4f 100644
--- a/src/corelib/io/qfileinfo.cpp
+++ b/src/corelib/io/qfileinfo.cpp
@@ -272,7 +272,7 @@ QDateTime &QFileInfoPrivate::getFileTime(QAbstractFileEngine::FileTime request)
info objects, just append one to the file name given to the constructors
or setFile().
- The file's dates are returned by created(), lastModified(), lastRead() and
+ The file's dates are returned by birthTime(), lastModified(), lastRead() and
fileTime(). Information about the file's access permissions is
obtained with isReadable(), isWritable() and isExecutable(). The
file's ownership is available from owner(), ownerId(), group() and
diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp
index 87623f304a..56b60e67e0 100644
--- a/src/corelib/kernel/qeventdispatcher_win.cpp
+++ b/src/corelib/kernel/qeventdispatcher_win.cpp
@@ -84,10 +84,8 @@ enum {
WM_QT_ACTIVATENOTIFIERS = WM_USER + 2
};
-// WM_QT_SENDPOSTEDEVENTS message parameter
enum {
- WMWP_QT_TOFOREIGNLOOP = 0,
- WMWP_QT_FROMWAKEUP
+ SendPostedEventsTimerId = ~1u
};
class QEventDispatcherWin32Private;
@@ -100,8 +98,8 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA
QEventDispatcherWin32Private::QEventDispatcherWin32Private()
: threadId(GetCurrentThreadId()), interrupt(false), internalHwnd(0),
- getMessageHook(0), wakeUps(0), activateNotifiersPosted(false),
- winEventNotifierActivatedEvent(NULL)
+ getMessageHook(0), sendPostedEventsTimerId(0), wakeUps(0),
+ activateNotifiersPosted(false), winEventNotifierActivatedEvent(NULL)
{
}
@@ -129,6 +127,18 @@ void WINAPI QT_WIN_CALLBACK qt_fast_timer_proc(uint timerId, uint /*reserved*/,
QCoreApplication::postEvent(t->dispatcher, new QTimerEvent(t->timerId));
}
+static inline UINT inputQueueMask()
+{
+ UINT result = QS_ALLEVENTS;
+ // QTBUG 28513, QTBUG-29097, QTBUG-29435: QS_TOUCH, QS_POINTER became part of
+ // QS_INPUT in Windows Kit 8. They should not be used when running on pre-Windows 8.
+#if WINVER > 0x0601
+ if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8)
+ result &= ~(QS_TOUCH | QS_POINTER);
+#endif // WINVER > 0x0601
+ return result;
+}
+
LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
{
if (message == WM_NCCREATE)
@@ -240,47 +250,39 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA
case WM_TIMER:
Q_ASSERT(d != 0);
- d->sendTimerEvent(wp);
+ if (wp == d->sendPostedEventsTimerId)
+ q->sendPostedEvents();
+ else
+ d->sendTimerEvent(wp);
return 0;
case WM_QT_SENDPOSTEDEVENTS:
Q_ASSERT(d != 0);
// We send posted events manually, if the window procedure was invoked
// by the foreign event loop (e.g. from the native modal dialog).
- q->sendPostedEvents();
+ // Skip sending, if the message queue is not empty.
+ // sendPostedEventsTimer will deliver posted events later.
+ static const UINT mask = inputQueueMask();
+ if (HIWORD(GetQueueStatus(mask)) == 0)
+ q->sendPostedEvents();
return 0;
} // switch (message)
return DefWindowProc(hwnd, message, wp, lp);
}
-static inline UINT inputQueueMask()
-{
- UINT result = QS_ALLEVENTS;
- // QTBUG 28513, QTBUG-29097, QTBUG-29435: QS_TOUCH, QS_POINTER became part of
- // QS_INPUT in Windows Kit 8. They should not be used when running on pre-Windows 8.
-#if WINVER > 0x0601
- if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8)
- result &= ~(QS_TOUCH | QS_POINTER);
-#endif // WINVER > 0x0601
- return result;
-}
-
LRESULT QT_WIN_CALLBACK qt_GetMessageHook(int code, WPARAM wp, LPARAM lp)
{
QEventDispatcherWin32 *q = qobject_cast<QEventDispatcherWin32 *>(QAbstractEventDispatcher::instance());
Q_ASSERT(q != 0);
QEventDispatcherWin32Private *d = q->d_func();
MSG *msg = reinterpret_cast<MSG *>(lp);
- static const UINT mask = inputQueueMask();
-
- if (HIWORD(GetQueueStatus(mask)) == 0 && wp == PM_REMOVE) {
- // Allow posting WM_QT_SENDPOSTEDEVENTS message.
- d->wakeUps.storeRelaxed(0);
- if (!(msg->hwnd == d->internalHwnd && msg->message == WM_QT_SENDPOSTEDEVENTS)) {
- PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS,
- WMWP_QT_TOFOREIGNLOOP, 0);
- }
+
+ if (msg->hwnd == d->internalHwnd && msg->message == WM_QT_SENDPOSTEDEVENTS
+ && wp == PM_REMOVE && d->sendPostedEventsTimerId == 0) {
+ // Start a timer to deliver posted events when the message queue is emptied.
+ d->sendPostedEventsTimerId = SetTimer(d->internalHwnd, SendPostedEventsTimerId,
+ USER_TIMER_MINIMUM, NULL);
}
return d->getMessageHook ? CallNextHookEx(0, code, wp, lp) : 0;
}
@@ -571,12 +573,15 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
}
if (haveMessage) {
if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) {
- // Set result to 'true', if the message was sent by wakeUp().
- if (msg.wParam == WMWP_QT_FROMWAKEUP)
- retVal = true;
+ // Set result to 'true' because the message was sent by wakeUp().
+ retVal = true;
continue;
}
if (msg.message == WM_TIMER) {
+ // Skip timer event intended for use inside foreign loop.
+ if (d->internalHwnd == msg.hwnd && msg.wParam == d->sendPostedEventsTimerId)
+ continue;
+
// avoid live-lock by keeping track of the timers we've already sent
bool found = false;
for (int i = 0; !found && i < processedTimers.count(); ++i) {
@@ -964,8 +969,7 @@ void QEventDispatcherWin32::wakeUp()
Q_D(QEventDispatcherWin32);
if (d->internalHwnd && d->wakeUps.testAndSetAcquire(0, 1)) {
// post a WM_QT_SENDPOSTEDEVENTS to this thread if there isn't one already pending
- if (!PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS,
- WMWP_QT_FROMWAKEUP, 0))
+ if (!PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0))
qErrnoWarning("QEventDispatcherWin32::wakeUp: Failed to post a message");
}
}
@@ -1007,6 +1011,10 @@ void QEventDispatcherWin32::closingDown()
if (d->getMessageHook)
UnhookWindowsHookEx(d->getMessageHook);
d->getMessageHook = 0;
+
+ if (d->sendPostedEventsTimerId != 0)
+ KillTimer(d->internalHwnd, d->sendPostedEventsTimerId);
+ d->sendPostedEventsTimerId = 0;
}
bool QEventDispatcherWin32::event(QEvent *e)
@@ -1048,6 +1056,14 @@ bool QEventDispatcherWin32::event(QEvent *e)
void QEventDispatcherWin32::sendPostedEvents()
{
Q_D(QEventDispatcherWin32);
+
+ if (d->sendPostedEventsTimerId != 0)
+ KillTimer(d->internalHwnd, d->sendPostedEventsTimerId);
+ d->sendPostedEventsTimerId = 0;
+
+ // Allow posting WM_QT_SENDPOSTEDEVENTS message.
+ d->wakeUps.storeRelaxed(0);
+
QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
}
diff --git a/src/corelib/kernel/qeventdispatcher_win_p.h b/src/corelib/kernel/qeventdispatcher_win_p.h
index e6620178d8..8f6f05e0a7 100644
--- a/src/corelib/kernel/qeventdispatcher_win_p.h
+++ b/src/corelib/kernel/qeventdispatcher_win_p.h
@@ -170,6 +170,7 @@ public:
HHOOK getMessageHook;
// for controlling when to send posted events
+ UINT_PTR sendPostedEventsTimerId;
QAtomicInt wakeUps;
// timers
diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp
index ddb053c26f..be9d92b204 100644
--- a/src/corelib/plugin/qlibrary.cpp
+++ b/src/corelib/plugin/qlibrary.cpp
@@ -576,9 +576,7 @@ bool QLibraryPrivate::load()
Q_TRACE(QLibraryPrivate_load_entry, fileName);
- mutex.lock();
bool ret = load_sys();
- mutex.unlock();
if (qt_debug_component()) {
if (ret) {
qDebug() << "loaded library" << fileName;
diff --git a/src/corelib/plugin/qlibrary_unix.cpp b/src/corelib/plugin/qlibrary_unix.cpp
index 29813e5863..a5c72f81d9 100644
--- a/src/corelib/plugin/qlibrary_unix.cpp
+++ b/src/corelib/plugin/qlibrary_unix.cpp
@@ -123,6 +123,7 @@ QStringList QLibraryPrivate::prefixes_sys()
bool QLibraryPrivate::load_sys()
{
+ QMutexLocker locker(&mutex);
QString attempt;
QFileSystemEntry fsEntry(fileName);
@@ -213,6 +214,7 @@ bool QLibraryPrivate::load_sys()
}
#endif
+ locker.unlock();
bool retry = true;
Handle hnd = nullptr;
for (int prefix = 0; retry && !hnd && prefix < prefixes.size(); prefix++) {
@@ -235,15 +237,15 @@ bool QLibraryPrivate::load_sys()
hnd = dlopen(QFile::encodeName(attempt), dlFlags);
#ifdef Q_OS_ANDROID
- if (!pHnd) {
+ if (!hnd) {
auto attemptFromBundle = attempt;
- pHnd = dlopen(QFile::encodeName(attemptFromBundle.replace(QLatin1Char('/'), QLatin1Char('_'))), dlFlags);
+ hnd = dlopen(QFile::encodeName(attemptFromBundle.replace(QLatin1Char('/'), QLatin1Char('_'))), dlFlags);
}
- if (pHnd) {
+ if (hnd) {
using JniOnLoadPtr = jint (*)(JavaVM *vm, void *reserved);
JniOnLoadPtr jniOnLoad = reinterpret_cast<JniOnLoadPtr>(dlsym(pHnd, "JNI_OnLoad"));
if (jniOnLoad && jniOnLoad(QtAndroidPrivate::javaVM(), nullptr) == JNI_ERR) {
- dlclose(pHnd);
+ dlclose(hnd);
pHnd = nullptr;
}
}
@@ -273,6 +275,8 @@ bool QLibraryPrivate::load_sys()
}
}
#endif
+
+ locker.relock();
if (!hnd) {
errorString = QLibrary::tr("Cannot load library %1: %2").arg(fileName, qdlerror());
}
diff --git a/src/corelib/plugin/qlibrary_win.cpp b/src/corelib/plugin/qlibrary_win.cpp
index 000bf76276..ef58724be8 100644
--- a/src/corelib/plugin/qlibrary_win.cpp
+++ b/src/corelib/plugin/qlibrary_win.cpp
@@ -78,6 +78,7 @@ bool QLibraryPrivate::load_sys()
// fileName
//
// NB If it's a plugin we do not ever try the ".dll" extension
+ QMutexLocker locker(&mutex);
QStringList attempts;
if (pluginState != IsAPlugin)
@@ -95,6 +96,7 @@ bool QLibraryPrivate::load_sys()
attempts.prepend(QDir::rootPath() + fileName);
#endif
+ locker.unlock();
Handle hnd = nullptr;
for (const QString &attempt : qAsConst(attempts)) {
#ifndef Q_OS_WINRT
@@ -115,6 +117,7 @@ bool QLibraryPrivate::load_sys()
#ifndef Q_OS_WINRT
SetErrorMode(oldmode);
#endif
+ locker.relock();
if (!hnd) {
errorString = QLibrary::tr("Cannot load library %1: %2").arg(
QDir::toNativeSeparators(fileName), qt_error_string());
diff --git a/src/corelib/serialization/qcborstream.cpp b/src/corelib/serialization/qcborstream.cpp
index c598eee213..22748e49e1 100644
--- a/src/corelib/serialization/qcborstream.cpp
+++ b/src/corelib/serialization/qcborstream.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 Intel Corporation.
+** Copyright (C) 2020 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -39,6 +39,7 @@
#include "qcborstream.h"
+#include <private/qbytearray_p.h>
#include <private/qnumeric_p.h>
#include <private/qutfcodec_p.h>
#include <qbuffer.h>
@@ -2282,6 +2283,10 @@ bool QCborStreamReader::next(int maxRecursion)
} else if (isString() || isByteArray()) {
auto r = _readByteArray_helper();
while (r.status == Ok) {
+ if (isString() && r.data.size() > MaxStringSize) {
+ d->handleError(CborErrorDataTooLarge);
+ break;
+ }
if (isString() && !QUtf8::isValidUtf8(r.data, r.data.size()).isValidUtf8) {
d->handleError(CborErrorInvalidUtf8TextString);
break;
@@ -2564,15 +2569,23 @@ QCborStreamReader::StringResult<QString> QCborStreamReader::_readString_helper()
result.status = r.status;
if (r.status == Ok) {
- QTextCodec::ConverterState cs;
- result.data = QUtf8::convertToUnicode(r.data, r.data.size(), &cs);
- if (cs.invalidChars == 0 && cs.remainingChars == 0)
- return result;
+ // See QUtf8::convertToUnicode() a detailed explanation of why this
+ // conversion uses the same number of words or less.
+ CborError err = CborNoError;
+ if (r.data.size() > MaxStringSize) {
+ err = CborErrorDataTooLarge;
+ } else {
+ QTextCodec::ConverterState cs;
+ result.data = QUtf8::convertToUnicode(r.data, r.data.size(), &cs);
+ if (cs.invalidChars != 0 || cs.remainingChars != 0)
+ err = CborErrorInvalidUtf8TextString;
+ }
- d->handleError(CborErrorInvalidUtf8TextString);
- result.data.clear();
- result.status = Error;
- return result;
+ if (err) {
+ d->handleError(err);
+ result.data.clear();
+ result.status = Error;
+ }
}
return result;
}
@@ -2600,6 +2613,10 @@ QCborStreamReader::StringResult<QByteArray> QCborStreamReader::_readByteArray_he
qsizetype len = _currentStringChunkSize();
if (len < 0)
return result;
+ if (len > MaxByteArraySize) {
+ d->handleError(CborErrorDataTooLarge);
+ return result;
+ }
result.data.resize(len);
auto r = readStringChunk(result.data.data(), len);
diff --git a/src/corelib/serialization/qcborvalue.cpp b/src/corelib/serialization/qcborvalue.cpp
index 4052bfa22e..65abf8dba8 100644
--- a/src/corelib/serialization/qcborvalue.cpp
+++ b/src/corelib/serialization/qcborvalue.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 Intel Corporation.
+** Copyright (C) 2020 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -46,6 +46,7 @@
#include <qendian.h>
#include <qlocale.h>
+#include <private/qbytearray_p.h>
#include <private/qnumeric_p.h>
#include <private/qsimd_p.h>
@@ -833,8 +834,6 @@ static QCborValue::Type convertToExtendedType(QCborContainerPrivate *d)
return QCborValue::Tag;
}
-// in qcborstream.cpp
-extern void qt_cbor_stream_set_error(QCborStreamReaderPrivate *d, QCborError error);
static void writeDoubleToCbor(QCborStreamWriter &writer, double d, QCborValue::EncodingOptions opt)
{
@@ -1440,23 +1439,59 @@ static Element decodeBasicValueFromCbor(QCborStreamReader &reader)
return e;
}
-static inline QCborContainerPrivate *createContainerFromCbor(QCborStreamReader &reader)
+static inline QCborContainerPrivate *createContainerFromCbor(QCborStreamReader &reader, int remainingRecursionDepth)
{
- auto d = new QCborContainerPrivate;
- d->ref.storeRelaxed(1);
- d->decodeFromCbor(reader);
+ if (Q_UNLIKELY(remainingRecursionDepth == 0)) {
+ QCborContainerPrivate::setErrorInReader(reader, { QCborError::NestingTooDeep });
+ return nullptr;
+ }
+
+ QCborContainerPrivate *d = nullptr;
+ int mapShift = reader.isMap() ? 1 : 0;
+ if (reader.isLengthKnown()) {
+ quint64 len = reader.length();
+
+ // Clamp allocation to 1M elements (avoids crashing due to corrupt
+ // stream or loss of precision when converting from quint64 to
+ // QVector::size_type).
+ len = qMin(len, quint64(1024 * 1024 - 1));
+ if (len) {
+ d = new QCborContainerPrivate;
+ d->ref.storeRelaxed(1);
+ d->elements.reserve(qsizetype(len) << mapShift);
+ }
+ } else {
+ d = new QCborContainerPrivate;
+ d->ref.storeRelaxed(1);
+ }
+
+ reader.enterContainer();
+ if (reader.lastError() != QCborError::NoError)
+ return d;
+
+ while (reader.hasNext() && reader.lastError() == QCborError::NoError)
+ d->decodeValueFromCbor(reader, remainingRecursionDepth - 1);
+
+ if (reader.lastError() == QCborError::NoError)
+ reader.leaveContainer();
+
return d;
}
-static QCborValue taggedValueFromCbor(QCborStreamReader &reader)
+static QCborValue taggedValueFromCbor(QCborStreamReader &reader, int remainingRecursionDepth)
{
+ if (Q_UNLIKELY(remainingRecursionDepth == 0)) {
+ QCborContainerPrivate::setErrorInReader(reader, { QCborError::NestingTooDeep });
+ return QCborValue::Invalid;
+ }
+
auto d = new QCborContainerPrivate;
d->append(reader.toTag());
reader.next();
if (reader.lastError() == QCborError::NoError) {
// decode tagged value
- d->decodeValueFromCbor(reader);
+ d->decodeValueFromCbor(reader, remainingRecursionDepth - 1);
}
QCborValue::Type type;
@@ -1472,6 +1507,13 @@ static QCborValue taggedValueFromCbor(QCborStreamReader &reader)
return QCborContainerPrivate::makeValue(type, -1, d);
}
+// in qcborstream.cpp
+extern void qt_cbor_stream_set_error(QCborStreamReaderPrivate *d, QCborError error);
+inline void QCborContainerPrivate::setErrorInReader(QCborStreamReader &reader, QCborError error)
+{
+ qt_cbor_stream_set_error(reader.d.data(), error);
+}
+
void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader)
{
auto addByteData_local = [this](QByteArray::size_type len) -> qint64 {
@@ -1493,6 +1535,8 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader)
// and calculate the final size
if (add_overflow(offset, increment, &newSize))
return -1;
+ if (newSize > MaxByteArraySize)
+ return -1;
// since usedData <= data.size(), this can't overflow
usedData += increment;
@@ -1516,7 +1560,7 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader)
return; // error
if (len != rawlen) {
// truncation
- qt_cbor_stream_set_error(reader.d.data(), { QCborError::DataTooLarge });
+ setErrorInReader(reader, { QCborError::DataTooLarge });
return;
}
@@ -1526,7 +1570,7 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader)
e.value = addByteData_local(len);
if (e.value < 0) {
// overflow
- qt_cbor_stream_set_error(reader.d.data(), { QCborError::DataTooLarge });
+ setErrorInReader(reader, { QCborError::DataTooLarge });
return;
}
}
@@ -1540,7 +1584,7 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader)
auto utf8result = QUtf8::isValidUtf8(dataPtr() + data.size() - len, len);
if (!utf8result.isValidUtf8) {
r.status = QCborStreamReader::Error;
- qt_cbor_stream_set_error(reader.d.data(), { QCborError::InvalidUtf8String });
+ setErrorInReader(reader, { QCborError::InvalidUtf8String });
break;
}
isAscii = isAscii && utf8result.isValidAscii;
@@ -1564,14 +1608,7 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader)
// error
r.status = QCborStreamReader::Error;
- qt_cbor_stream_set_error(reader.d.data(), { QCborError::DataTooLarge });
- }
-
- if (r.status == QCborStreamReader::Error) {
- // There can only be errors if there was data to be read.
- Q_ASSERT(e.flags & Element::HasByteData);
- data.truncate(e.value);
- return;
+ setErrorInReader(reader, { QCborError::DataTooLarge });
}
// update size
@@ -1585,14 +1622,30 @@ void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader)
Q_ASSERT(e.type == QCborValue::String);
e.flags |= Element::StringIsAscii;
}
+
+ // check that this UTF-8 text string can be loaded onto a QString
+ if (e.type == QCborValue::String) {
+ if (Q_UNLIKELY(b->len > MaxStringSize)) {
+ setErrorInReader(reader, { QCborError::DataTooLarge });
+ r.status = QCborStreamReader::Error;
+ }
+ }
+ }
+
+ if (r.status == QCborStreamReader::Error) {
+ // There can only be errors if there was data to be read.
+ Q_ASSERT(e.flags & Element::HasByteData);
+ data.truncate(e.value);
+ return;
}
elements.append(e);
}
-void QCborContainerPrivate::decodeValueFromCbor(QCborStreamReader &reader)
+void QCborContainerPrivate::decodeValueFromCbor(QCborStreamReader &reader, int remainingRecursionDepth)
{
- switch (reader.type()) {
+ QCborStreamReader::Type t = reader.type();
+ switch (t) {
case QCborStreamReader::UnsignedInteger:
case QCborStreamReader::NegativeInteger:
case QCborStreamReader::SimpleType:
@@ -1609,38 +1662,18 @@ void QCborContainerPrivate::decodeValueFromCbor(QCborStreamReader &reader)
case QCborStreamReader::Array:
case QCborStreamReader::Map:
+ return append(makeValue(t == QCborStreamReader::Array ? QCborValue::Array : QCborValue::Map, -1,
+ createContainerFromCbor(reader, remainingRecursionDepth),
+ MoveContainer));
+
case QCborStreamReader::Tag:
- return append(QCborValue::fromCbor(reader));
+ return append(taggedValueFromCbor(reader, remainingRecursionDepth));
case QCborStreamReader::Invalid:
return; // probably a decode error
}
}
-void QCborContainerPrivate::decodeFromCbor(QCborStreamReader &reader)
-{
- int mapShift = reader.isMap() ? 1 : 0;
- if (reader.isLengthKnown()) {
- quint64 len = reader.length();
-
- // Clamp allocation to 1M elements (avoids crashing due to corrupt
- // stream or loss of precision when converting from quint64 to
- // QVector::size_type).
- len = qMin(len, quint64(1024 * 1024 - 1));
- elements.reserve(qsizetype(len) << mapShift);
- }
-
- reader.enterContainer();
- if (reader.lastError() != QCborError::NoError)
- return;
-
- while (reader.hasNext() && reader.lastError() == QCborError::NoError)
- decodeValueFromCbor(reader);
-
- if (reader.lastError() == QCborError::NoError)
- reader.leaveContainer();
-}
-
/*!
Creates a QCborValue with byte array value \a ba. The value can later be
retrieved using toByteArray().
@@ -2335,6 +2368,8 @@ QCborValueRef QCborValue::operator[](qint64 key)
return { container, index };
}
+enum { MaximumRecursionDepth = 1024 };
+
/*!
Decodes one item from the CBOR stream found in \a reader and returns the
equivalent representation. This function is recursive: if the item is a map
@@ -2395,12 +2430,12 @@ QCborValue QCborValue::fromCbor(QCborStreamReader &reader)
case QCborStreamReader::Map:
result.n = -1;
result.t = reader.isArray() ? Array : Map;
- result.container = createContainerFromCbor(reader);
+ result.container = createContainerFromCbor(reader, MaximumRecursionDepth);
break;
// tag
case QCborStreamReader::Tag:
- result = taggedValueFromCbor(reader);
+ result = taggedValueFromCbor(reader, MaximumRecursionDepth);
break;
}
diff --git a/src/corelib/serialization/qcborvalue_p.h b/src/corelib/serialization/qcborvalue_p.h
index a74ac2ba10..041a20e746 100644
--- a/src/corelib/serialization/qcborvalue_p.h
+++ b/src/corelib/serialization/qcborvalue_p.h
@@ -405,9 +405,9 @@ public:
elements.remove(idx);
}
- void decodeValueFromCbor(QCborStreamReader &reader);
- void decodeFromCbor(QCborStreamReader &reader);
+ void decodeValueFromCbor(QCborStreamReader &reader, int remainiingStackDepth);
void decodeStringFromCbor(QCborStreamReader &reader);
+ static inline void setErrorInReader(QCborStreamReader &reader, QCborError error);
};
QT_END_NAMESPACE
diff --git a/src/corelib/text/qbytearray_p.h b/src/corelib/text/qbytearray_p.h
index 3c6257f786..ffec6dca22 100644
--- a/src/corelib/text/qbytearray_p.h
+++ b/src/corelib/text/qbytearray_p.h
@@ -56,10 +56,9 @@
QT_BEGIN_NAMESPACE
-enum {
- // Define as enum to force inlining. Don't expose MaxAllocSize in a public header.
- MaxByteArraySize = MaxAllocSize - sizeof(std::remove_pointer<QByteArray::DataPtr>::type)
-};
+// -1 because of the terminating NUL
+constexpr qsizetype MaxByteArraySize = MaxAllocSize - sizeof(std::remove_pointer<QByteArray::DataPtr>::type) - 1;
+constexpr qsizetype MaxStringSize = (MaxAllocSize - sizeof(std::remove_pointer<QByteArray::DataPtr>::type)) / 2 - 1;
QT_END_NAMESPACE
diff --git a/src/corelib/time/qdatetime.cpp b/src/corelib/time/qdatetime.cpp
index a5761055ed..3a564d1b9a 100644
--- a/src/corelib/time/qdatetime.cpp
+++ b/src/corelib/time/qdatetime.cpp
@@ -277,10 +277,11 @@ static int fromOffsetString(QStringView offsetString, bool *valid) noexcept
\reentrant
\brief The QDate class provides date functions.
-
- A QDate object represents a particular date. This can be expressed as a
- calendar date, i.e. year, month, and day numbers, in the proleptic Gregorian
- calendar.
+ A QDate object represents a particular day, regardless of calendar,
+ locale or other settings used when creating it or supplied by the system.
+ It can report the year, month and day of the month that represent the
+ day with respect to the proleptic Gregorian calendar or any calendar supplied
+ as a QCalendar object.
A QDate object is typically created by giving the year, month, and day
numbers explicitly. Note that QDate interprets year numbers less than 100 as
@@ -1572,9 +1573,8 @@ qint64 QDate::daysTo(const QDate &d) const
/*!
\fn bool QDate::operator==(const QDate &d) const
- Returns \c true if this date is equal to \a d; otherwise returns
- false.
-
+ Returns \c true if this date and \a d represent the same day, otherwise
+ \c false.
*/
/*!
@@ -1582,6 +1582,8 @@ qint64 QDate::daysTo(const QDate &d) const
Returns \c true if this date is different from \a d; otherwise
returns \c false.
+
+ \sa operator==()
*/
/*!
diff --git a/src/corelib/time/qtimezoneprivate_android.cpp b/src/corelib/time/qtimezoneprivate_android.cpp
index 5cb8155dcc..fc3653752a 100644
--- a/src/corelib/time/qtimezoneprivate_android.cpp
+++ b/src/corelib/time/qtimezoneprivate_android.cpp
@@ -102,7 +102,7 @@ void QAndroidTimeZonePrivate::init(const QByteArray &ianaId)
for (int style = 1; m_id.isEmpty() && style-- > 0;) {
for (int dst = 1; m_id.isEmpty() && dst-- > 0;) {
m_id = match(androidTimeZone.callObjectMethod(
- "getDisplayName", "(ZI;)Ljava/lang/String;", bool(dst), style));
+ "getDisplayName", "(ZI)Ljava/lang/String;", bool(dst), style));
}
}
}
diff --git a/src/corelib/time/qtimezoneprivate_p.h b/src/corelib/time/qtimezoneprivate_p.h
index 5f6491ef81..c3e9064b8c 100644
--- a/src/corelib/time/qtimezoneprivate_p.h
+++ b/src/corelib/time/qtimezoneprivate_p.h
@@ -287,6 +287,16 @@ Q_DECL_CONSTEXPR inline bool operator==(const QTzTransitionRule &lhs, const QTzT
Q_DECL_CONSTEXPR inline bool operator!=(const QTzTransitionRule &lhs, const QTzTransitionRule &rhs) noexcept
{ return !operator==(lhs, rhs); }
+// These are stored separately from QTzTimeZonePrivate so that they can be
+// cached, avoiding the need to re-parse them from disk constantly.
+struct QTzTimeZoneCacheEntry
+{
+ QVector<QTzTransitionTime> m_tranTimes;
+ QVector<QTzTransitionRule> m_tranRules;
+ QList<QByteArray> m_abbreviations;
+ QByteArray m_posixRule;
+};
+
class Q_AUTOTEST_EXPORT QTzTimeZonePrivate final : public QTimeZonePrivate
{
QTzTimeZonePrivate(const QTzTimeZonePrivate &) = default;
@@ -334,13 +344,11 @@ private:
QVector<QTimeZonePrivate::Data> getPosixTransitions(qint64 msNear) const;
Data dataForTzTransition(QTzTransitionTime tran) const;
- QVector<QTzTransitionTime> m_tranTimes;
- QVector<QTzTransitionRule> m_tranRules;
- QList<QByteArray> m_abbreviations;
#if QT_CONFIG(icu)
mutable QSharedDataPointer<QTimeZonePrivate> m_icu;
#endif
- QByteArray m_posixRule;
+ QTzTimeZoneCacheEntry cached_data;
+ QVector<QTzTransitionTime> tranCache() const { return cached_data.m_tranTimes; }
};
#endif // Q_OS_UNIX
diff --git a/src/corelib/time/qtimezoneprivate_tz.cpp b/src/corelib/time/qtimezoneprivate_tz.cpp
index 3c2695a789..ada19f4eb4 100644
--- a/src/corelib/time/qtimezoneprivate_tz.cpp
+++ b/src/corelib/time/qtimezoneprivate_tz.cpp
@@ -1,5 +1,6 @@
/****************************************************************************
**
+** Copyright (C) 2019 Crimson AS <info@crimson.no>
** Copyright (C) 2013 John Layt <jlayt@kde.org>
** Contact: https://www.qt.io/licensing/
**
@@ -42,6 +43,7 @@
#include "private/qlocale_tools_p.h"
#include <QtCore/QFile>
+#include <QtCore/QMutex>
#include <QtCore/QHash>
#include <QtCore/QDataStream>
#include <QtCore/QDateTime>
@@ -637,7 +639,7 @@ QTzTimeZonePrivate::QTzTimeZonePrivate()
// Create a named time zone
QTzTimeZonePrivate::QTzTimeZonePrivate(const QByteArray &ianaId)
{
- init(ianaId);
+ init(ianaId.isEmpty() ? systemTimeZoneId() : ianaId);
}
QTzTimeZonePrivate::~QTzTimeZonePrivate()
@@ -649,14 +651,26 @@ QTzTimeZonePrivate *QTzTimeZonePrivate::clone() const
return new QTzTimeZonePrivate(*this);
}
-void QTzTimeZonePrivate::init(const QByteArray &ianaId)
+class QTzTimeZoneCache
+{
+public:
+ QTzTimeZoneCacheEntry fetchEntry(const QByteArray &ianaId);
+
+private:
+ QTzTimeZoneCacheEntry findEntry(const QByteArray &ianaId);
+ QHash<QByteArray, QTzTimeZoneCacheEntry> m_cache;
+ QMutex m_mutex;
+};
+
+QTzTimeZoneCacheEntry QTzTimeZoneCache::findEntry(const QByteArray &ianaId)
{
+ QTzTimeZoneCacheEntry ret;
QFile tzif;
if (ianaId.isEmpty()) {
// Open system tz
tzif.setFileName(QStringLiteral("/etc/localtime"));
if (!tzif.open(QIODevice::ReadOnly))
- return;
+ return ret;
} else {
// Open named tz, try modern path first, if fails try legacy path
tzif.setFileName(QLatin1String("/usr/share/zoneinfo/") + QString::fromLocal8Bit(ianaId));
@@ -669,9 +683,9 @@ void QTzTimeZonePrivate::init(const QByteArray &ianaId)
if (PosixZone::parse(begin, zoneInfo.constEnd()).hasValidOffset()
&& (begin == zoneInfo.constEnd()
|| PosixZone::parse(begin, zoneInfo.constEnd()).hasValidOffset())) {
- m_id = m_posixRule = ianaId;
+ ret.m_posixRule = ianaId;
}
- return;
+ return ret;
}
}
}
@@ -682,59 +696,59 @@ void QTzTimeZonePrivate::init(const QByteArray &ianaId)
bool ok = false;
QTzHeader hdr = parseTzHeader(ds, &ok);
if (!ok || ds.status() != QDataStream::Ok)
- return;
+ return ret;
QVector<QTzTransition> tranList = parseTzTransitions(ds, hdr.tzh_timecnt, false);
if (ds.status() != QDataStream::Ok)
- return;
+ return ret;
QVector<QTzType> typeList = parseTzTypes(ds, hdr.tzh_typecnt);
if (ds.status() != QDataStream::Ok)
- return;
+ return ret;
QMap<int, QByteArray> abbrevMap = parseTzAbbreviations(ds, hdr.tzh_charcnt, typeList);
if (ds.status() != QDataStream::Ok)
- return;
+ return ret;
parseTzLeapSeconds(ds, hdr.tzh_leapcnt, false);
if (ds.status() != QDataStream::Ok)
- return;
+ return ret;
typeList = parseTzIndicators(ds, typeList, hdr.tzh_ttisstdcnt, hdr.tzh_ttisgmtcnt);
if (ds.status() != QDataStream::Ok)
- return;
+ return ret;
// If version 2 then parse the second block of data
if (hdr.tzh_version == '2' || hdr.tzh_version == '3') {
ok = false;
QTzHeader hdr2 = parseTzHeader(ds, &ok);
if (!ok || ds.status() != QDataStream::Ok)
- return;
+ return ret;
tranList = parseTzTransitions(ds, hdr2.tzh_timecnt, true);
if (ds.status() != QDataStream::Ok)
- return;
+ return ret;
typeList = parseTzTypes(ds, hdr2.tzh_typecnt);
if (ds.status() != QDataStream::Ok)
- return;
+ return ret;
abbrevMap = parseTzAbbreviations(ds, hdr2.tzh_charcnt, typeList);
if (ds.status() != QDataStream::Ok)
- return;
+ return ret;
parseTzLeapSeconds(ds, hdr2.tzh_leapcnt, true);
if (ds.status() != QDataStream::Ok)
- return;
+ return ret;
typeList = parseTzIndicators(ds, typeList, hdr2.tzh_ttisstdcnt, hdr2.tzh_ttisgmtcnt);
if (ds.status() != QDataStream::Ok)
- return;
- m_posixRule = parseTzPosixRule(ds);
+ return ret;
+ ret.m_posixRule = parseTzPosixRule(ds);
if (ds.status() != QDataStream::Ok)
- return;
+ return ret;
}
// Translate the TZ file into internal format
// Translate the array index based tz_abbrind into list index
const int size = abbrevMap.size();
- m_abbreviations.clear();
- m_abbreviations.reserve(size);
+ ret.m_abbreviations.clear();
+ ret.m_abbreviations.reserve(size);
QVector<int> abbrindList;
abbrindList.reserve(size);
for (auto it = abbrevMap.cbegin(), end = abbrevMap.cend(); it != end; ++it) {
- m_abbreviations.append(it.value());
+ ret.m_abbreviations.append(it.value());
abbrindList.append(it.key());
}
for (int i = 0; i < typeList.size(); ++i)
@@ -752,7 +766,7 @@ void QTzTimeZonePrivate::init(const QByteArray &ianaId)
// Now for each transition time calculate and store our rule:
const int tranCount = tranList.count();;
- m_tranTimes.reserve(tranCount);
+ ret.m_tranTimes.reserve(tranCount);
// The DST offset when in effect: usually stable, usually an hour:
int lastDstOff = 3600;
for (int i = 0; i < tranCount; i++) {
@@ -806,24 +820,48 @@ void QTzTimeZonePrivate::init(const QByteArray &ianaId)
rule.abbreviationIndex = tz_type.tz_abbrind;
// If the rule already exist then use that, otherwise add it
- int ruleIndex = m_tranRules.indexOf(rule);
+ int ruleIndex = ret.m_tranRules.indexOf(rule);
if (ruleIndex == -1) {
- m_tranRules.append(rule);
- tran.ruleIndex = m_tranRules.size() - 1;
+ ret.m_tranRules.append(rule);
+ tran.ruleIndex = ret.m_tranRules.size() - 1;
} else {
tran.ruleIndex = ruleIndex;
}
tran.atMSecsSinceEpoch = tz_tran.tz_time * 1000;
- m_tranTimes.append(tran);
+ ret.m_tranTimes.append(tran);
}
- if (m_tranTimes.isEmpty() && m_posixRule.isEmpty())
+
+ return ret;
+}
+
+QTzTimeZoneCacheEntry QTzTimeZoneCache::fetchEntry(const QByteArray &ianaId)
+{
+ QMutexLocker locker(&m_mutex);
+
+ // search the cache...
+ const auto& it = m_cache.find(ianaId);
+ if (it != m_cache.constEnd())
+ return *it;
+
+ // ... or build a new entry from scratch
+ QTzTimeZoneCacheEntry ret = findEntry(ianaId);
+ m_cache[ianaId] = ret;
+ return ret;
+}
+
+void QTzTimeZonePrivate::init(const QByteArray &ianaId)
+{
+ // System ID defaults to UTC, so is never empty; and our callers default to
+ // the system ID if what they're given is empty.
+ Q_ASSERT(!ianaId.isEmpty());
+ static QTzTimeZoneCache tzCache;
+ const auto &entry = tzCache.fetchEntry(ianaId);
+ if (entry.m_tranTimes.isEmpty() && entry.m_posixRule.isEmpty())
return; // Invalid after all !
- if (ianaId.isEmpty())
- m_id = systemTimeZoneId();
- else
- m_id = ianaId;
+ cached_data = std::move(entry);
+ m_id = ianaId;
}
QLocale::Country QTzTimeZonePrivate::country() const
@@ -903,12 +941,12 @@ QString QTzTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
}
// Otherwise is strange sequence, so work backwards through trans looking for first match, if any
- auto it = std::partition_point(m_tranTimes.cbegin(), m_tranTimes.cend(),
+ auto it = std::partition_point(tranCache().cbegin(), tranCache().cend(),
[currentMSecs](const QTzTransitionTime &at) {
return at.atMSecsSinceEpoch <= currentMSecs;
});
- while (it != m_tranTimes.cbegin()) {
+ while (it != tranCache().cbegin()) {
--it;
tran = dataForTzTransition(*it);
int offset = tran.daylightTimeOffset;
@@ -944,7 +982,7 @@ int QTzTimeZonePrivate::daylightTimeOffset(qint64 atMSecsSinceEpoch) const
bool QTzTimeZonePrivate::hasDaylightTime() const
{
// TODO Perhaps cache as frequently accessed?
- for (const QTzTransitionRule &rule : m_tranRules) {
+ for (const QTzTransitionRule &rule : cached_data.m_tranRules) {
if (rule.dstOffset != 0)
return true;
}
@@ -960,11 +998,11 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::dataForTzTransition(QTzTransitionTime
{
QTimeZonePrivate::Data data;
data.atMSecsSinceEpoch = tran.atMSecsSinceEpoch;
- QTzTransitionRule rule = m_tranRules.at(tran.ruleIndex);
+ QTzTransitionRule rule = cached_data.m_tranRules.at(tran.ruleIndex);
data.standardTimeOffset = rule.stdOffset;
data.daylightTimeOffset = rule.dstOffset;
data.offsetFromUtc = rule.stdOffset + rule.dstOffset;
- data.abbreviation = QString::fromUtf8(m_abbreviations.at(rule.abbreviationIndex));
+ data.abbreviation = QString::fromUtf8(cached_data.m_abbreviations.at(rule.abbreviationIndex));
return data;
}
@@ -972,37 +1010,37 @@ QVector<QTimeZonePrivate::Data> QTzTimeZonePrivate::getPosixTransitions(qint64 m
{
const int year = QDateTime::fromMSecsSinceEpoch(msNear, Qt::UTC).date().year();
// The Data::atMSecsSinceEpoch of the single entry if zone is constant:
- qint64 atTime = m_tranTimes.isEmpty() ? msNear : m_tranTimes.last().atMSecsSinceEpoch;
- return calculatePosixTransitions(m_posixRule, year - 1, year + 1, atTime);
+ qint64 atTime = tranCache().isEmpty() ? msNear : tranCache().last().atMSecsSinceEpoch;
+ return calculatePosixTransitions(cached_data.m_posixRule, year - 1, year + 1, atTime);
}
QTimeZonePrivate::Data QTzTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
{
// If the required time is after the last transition (or there were none)
// and we have a POSIX rule, then use it:
- if (!m_posixRule.isEmpty()
- && (m_tranTimes.isEmpty() || m_tranTimes.last().atMSecsSinceEpoch < forMSecsSinceEpoch)) {
+ if (!cached_data.m_posixRule.isEmpty()
+ && (tranCache().isEmpty() || tranCache().last().atMSecsSinceEpoch < forMSecsSinceEpoch)) {
QVector<QTimeZonePrivate::Data> posixTrans = getPosixTransitions(forMSecsSinceEpoch);
auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(),
[forMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) {
return at.atMSecsSinceEpoch <= forMSecsSinceEpoch;
});
// Use most recent, if any in the past; or the first if we have no other rules:
- if (it > posixTrans.cbegin() || (m_tranTimes.isEmpty() && it < posixTrans.cend())) {
+ if (it > posixTrans.cbegin() || (tranCache().isEmpty() && it < posixTrans.cend())) {
QTimeZonePrivate::Data data = *(it > posixTrans.cbegin() ? it - 1 : it);
data.atMSecsSinceEpoch = forMSecsSinceEpoch;
return data;
}
}
- if (m_tranTimes.isEmpty()) // Only possible if !isValid()
+ if (tranCache().isEmpty()) // Only possible if !isValid()
return invalidData();
// Otherwise, use the rule for the most recent or first transition:
- auto last = std::partition_point(m_tranTimes.cbegin(), m_tranTimes.cend(),
+ auto last = std::partition_point(tranCache().cbegin(), tranCache().cend(),
[forMSecsSinceEpoch] (const QTzTransitionTime &at) {
return at.atMSecsSinceEpoch <= forMSecsSinceEpoch;
});
- if (last > m_tranTimes.cbegin())
+ if (last > tranCache().cbegin())
--last;
Data data = dataForTzTransition(*last);
data.atMSecsSinceEpoch = forMSecsSinceEpoch;
@@ -1018,8 +1056,8 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::nextTransition(qint64 afterMSecsSince
{
// If the required time is after the last transition (or there were none)
// and we have a POSIX rule, then use it:
- if (!m_posixRule.isEmpty()
- && (m_tranTimes.isEmpty() || m_tranTimes.last().atMSecsSinceEpoch < afterMSecsSinceEpoch)) {
+ if (!cached_data.m_posixRule.isEmpty()
+ && (tranCache().isEmpty() || tranCache().last().atMSecsSinceEpoch < afterMSecsSinceEpoch)) {
QVector<QTimeZonePrivate::Data> posixTrans = getPosixTransitions(afterMSecsSinceEpoch);
auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(),
[afterMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) {
@@ -1030,19 +1068,19 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::nextTransition(qint64 afterMSecsSince
}
// Otherwise, if we can find a valid tran, use its rule:
- auto last = std::partition_point(m_tranTimes.cbegin(), m_tranTimes.cend(),
+ auto last = std::partition_point(tranCache().cbegin(), tranCache().cend(),
[afterMSecsSinceEpoch] (const QTzTransitionTime &at) {
return at.atMSecsSinceEpoch <= afterMSecsSinceEpoch;
});
- return last != m_tranTimes.cend() ? dataForTzTransition(*last) : invalidData();
+ return last != tranCache().cend() ? dataForTzTransition(*last) : invalidData();
}
QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const
{
// If the required time is after the last transition (or there were none)
// and we have a POSIX rule, then use it:
- if (!m_posixRule.isEmpty()
- && (m_tranTimes.isEmpty() || m_tranTimes.last().atMSecsSinceEpoch < beforeMSecsSinceEpoch)) {
+ if (!cached_data.m_posixRule.isEmpty()
+ && (tranCache().isEmpty() || tranCache().last().atMSecsSinceEpoch < beforeMSecsSinceEpoch)) {
QVector<QTimeZonePrivate::Data> posixTrans = getPosixTransitions(beforeMSecsSinceEpoch);
auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(),
[beforeMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) {
@@ -1051,15 +1089,15 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecs
if (it > posixTrans.cbegin())
return *--it;
// It fell between the last transition (if any) and the first of the POSIX rule:
- return m_tranTimes.isEmpty() ? invalidData() : dataForTzTransition(m_tranTimes.last());
+ return tranCache().isEmpty() ? invalidData() : dataForTzTransition(tranCache().last());
}
// Otherwise if we can find a valid tran then use its rule
- auto last = std::partition_point(m_tranTimes.cbegin(), m_tranTimes.cend(),
+ auto last = std::partition_point(tranCache().cbegin(), tranCache().cend(),
[beforeMSecsSinceEpoch] (const QTzTransitionTime &at) {
return at.atMSecsSinceEpoch < beforeMSecsSinceEpoch;
});
- return last > m_tranTimes.cbegin() ? dataForTzTransition(*--last) : invalidData();
+ return last > tranCache().cbegin() ? dataForTzTransition(*--last) : invalidData();
}
static long getSymloopMax()
@@ -1088,15 +1126,15 @@ QByteArray QTzTimeZonePrivate::systemTimeZoneId() const
{
// Check TZ env var first, if not populated try find it
QByteArray ianaId = qgetenv("TZ");
- if (!ianaId.isEmpty() && ianaId.at(0) == ':')
- ianaId = ianaId.mid(1);
// The TZ value can be ":/etc/localtime" which libc considers
// to be a "default timezone", in which case it will be read
// by one of the blocks below, so unset it here so it is not
// considered as a valid/found ianaId
- if (ianaId == "/etc/localtime")
+ if (ianaId == ":/etc/localtime")
ianaId.clear();
+ else if (ianaId.startsWith(':'))
+ ianaId = ianaId.mid(1);
// On most distros /etc/localtime is a symlink to a real file so extract name from the path
if (ianaId.isEmpty()) {
@@ -1115,36 +1153,6 @@ QByteArray QTzTimeZonePrivate::systemTimeZoneId() const
}
}
- // On Debian Etch up to Jessie, /etc/localtime is a regular file while the actual name is in /etc/timezone
- if (ianaId.isEmpty()) {
- QFile tzif(QStringLiteral("/etc/timezone"));
- if (tzif.open(QIODevice::ReadOnly)) {
- // TODO QTextStream inefficient, replace later
- QTextStream ts(&tzif);
- if (!ts.atEnd())
- ianaId = ts.readLine().toUtf8();
- }
- }
-
- // On some Red Hat distros /etc/localtime is real file with name held in /etc/sysconfig/clock
- // in a line like ZONE="Europe/Oslo" or TIMEZONE="Europe/Oslo"
- if (ianaId.isEmpty()) {
- QFile tzif(QStringLiteral("/etc/sysconfig/clock"));
- if (tzif.open(QIODevice::ReadOnly)) {
- // TODO QTextStream inefficient, replace later
- QTextStream ts(&tzif);
- QString line;
- while (ianaId.isEmpty() && !ts.atEnd() && ts.status() == QTextStream::Ok) {
- line = ts.readLine();
- if (line.startsWith(QLatin1String("ZONE="))) {
- ianaId = line.midRef(6, line.size() - 7).toUtf8();
- } else if (line.startsWith(QLatin1String("TIMEZONE="))) {
- ianaId = line.midRef(10, line.size() - 11).toUtf8();
- }
- }
- }
- }
-
// Some systems (e.g. uClibc) have a default value for $TZ in /etc/TZ:
if (ianaId.isEmpty()) {
QFile zone(QStringLiteral("/etc/TZ"));
diff --git a/src/corelib/tools/qline.cpp b/src/corelib/tools/qline.cpp
index dde66ed093..3afd23d76b 100644
--- a/src/corelib/tools/qline.cpp
+++ b/src/corelib/tools/qline.cpp
@@ -374,7 +374,7 @@ QDataStream &operator>>(QDataStream &stream, QLine &line)
*/
/*!
- \enum QLineF::IntersectType
+ \enum QLineF::IntersectionType
Describes the intersection between two lines.
diff --git a/src/gui/doc/src/richtext.qdoc b/src/gui/doc/src/richtext.qdoc
index 31a2ebf05b..1ef34455ab 100644
--- a/src/gui/doc/src/richtext.qdoc
+++ b/src/gui/doc/src/richtext.qdoc
@@ -1197,16 +1197,16 @@
\li none | dotted | dashed | dot-dash | dot-dot-dash | solid | double | groove | ridge | inset | outset
\li Border style for text tables and table cells.
\row \li \c border-top-style
- \li <color>
+ \li <border-style>
\li Top border style for table cells.
\row \li \c border-bottom-style
- \li <color>
+ \li <border-style>
\li Bottom border style for table cells.
\row \li \c border-left-style
- \li <color>
+ \li <border-style>
\li Left border style for table cells.
\row \li \c border-right-style
- \li <color>
+ \li <border-style>
\li Right border style for table cells.
\row \li \c border-width
\li <width>px
diff --git a/src/gui/image/qiconloader.cpp b/src/gui/image/qiconloader.cpp
index 27c82bc09f..2f907a7709 100644
--- a/src/gui/image/qiconloader.cpp
+++ b/src/gui/image/qiconloader.cpp
@@ -797,8 +797,12 @@ QPixmap ScalableEntry::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State
if (svgIcon.isNull())
svgIcon = QIcon(filename);
- // Simply reuse svg icon engine
- return svgIcon.pixmap(size, mode, state);
+ // Bypass QIcon API, as that will scale by device pixel ratio of the
+ // highest DPR screen since we're not passing on any QWindow.
+ if (QIconEngine *engine = svgIcon.data_ptr() ? svgIcon.data_ptr()->engine : nullptr)
+ return engine->pixmap(size, mode, state);
+
+ return QPixmap();
}
QPixmap QIconLoaderEngine::pixmap(const QSize &size, QIcon::Mode mode,
diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp
index a3e194f835..aaca1061b7 100644
--- a/src/gui/text/qtextlayout.cpp
+++ b/src/gui/text/qtextlayout.cpp
@@ -1667,7 +1667,8 @@ namespace {
QFontEngine *previousGlyphFontEngine;
QFixed minw;
- QFixed softHyphenWidth;
+ QFixed currentSoftHyphenWidth;
+ QFixed commitedSoftHyphenWidth;
QFixed rightBearing;
QFixed minimumRightBearing;
@@ -1681,7 +1682,7 @@ namespace {
QFixed calculateNewWidth(const QScriptLine &line) const {
return line.textWidth + tmpData.textWidth + spaceData.textWidth
- + softHyphenWidth + negativeRightBearing();
+ + (line.textWidth > 0 ? currentSoftHyphenWidth : QFixed()) + negativeRightBearing();
}
inline glyph_t currentGlyph() const
@@ -1755,6 +1756,7 @@ inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
if (line.length && !manualWrap && (newWidth > line.width || glyphCount > maxGlyphs))
return true;
+ const QFixed oldTextWidth = line.textWidth;
minw = qMax(minw, tmpData.textWidth);
line += tmpData;
line.textWidth += spaceData.textWidth;
@@ -1765,6 +1767,11 @@ inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
spaceData.textWidth = 0;
spaceData.length = 0;
+ if (oldTextWidth != line.textWidth || currentSoftHyphenWidth > 0) {
+ commitedSoftHyphenWidth = currentSoftHyphenWidth;
+ currentSoftHyphenWidth = 0;
+ }
+
return false;
}
@@ -1837,7 +1844,6 @@ void QTextLine::layout_helper(int maxGlyphs)
while (newItem < eng->layoutData->items.size()) {
lbh.resetRightBearing();
- lbh.softHyphenWidth = 0;
if (newItem != item) {
item = newItem;
const QScriptItem &current = eng->layoutData->items.at(item);
@@ -1975,9 +1981,9 @@ void QTextLine::layout_helper(int maxGlyphs)
} while (lbh.currentPosition < end);
lbh.minw = qMax(lbh.tmpData.textWidth, lbh.minw);
- if (lbh.currentPosition > 0 && lbh.currentPosition < end
- && attributes[lbh.currentPosition].lineBreak
- && eng->layoutData->string.at(lbh.currentPosition - 1).unicode() == QChar::SoftHyphen) {
+ if (lbh.currentPosition > 0 && lbh.currentPosition <= end
+ && (lbh.currentPosition == end || attributes[lbh.currentPosition].lineBreak)
+ && eng->layoutData->string.at(lbh.currentPosition - 1) == QChar::SoftHyphen) {
// if we are splitting up a word because of
// a soft hyphen then we ...
//
@@ -1994,10 +2000,7 @@ void QTextLine::layout_helper(int maxGlyphs)
// want the soft-hyphen to slip into the next line
// and thus become invisible again.
//
- if (line.length)
- lbh.softHyphenWidth = lbh.glyphs.advances[lbh.logClusters[lbh.currentPosition - 1]];
- else if (breakany)
- lbh.tmpData.textWidth += lbh.glyphs.advances[lbh.logClusters[lbh.currentPosition - 1]];
+ lbh.currentSoftHyphenWidth = lbh.glyphs.advances[lbh.logClusters[lbh.currentPosition - 1]];
}
if (sb_or_ws|breakany) {
@@ -2023,6 +2026,7 @@ void QTextLine::layout_helper(int maxGlyphs)
lbh.calculateRightBearing();
if (lbh.checkFullOtherwiseExtend(line)) {
+
// We are too wide to accept the next glyph with its bearing, so we restore the
// right bearing to that of the previous glyph (the one that was already accepted),
// so that the bearing can be be applied to the final width of the text below.
@@ -2031,9 +2035,7 @@ void QTextLine::layout_helper(int maxGlyphs)
else
lbh.calculateRightBearingForPreviousGlyph();
- if (!breakany) {
- line.textWidth += lbh.softHyphenWidth;
- }
+ line.textWidth += lbh.commitedSoftHyphenWidth;
goto found;
}
@@ -2045,6 +2047,7 @@ void QTextLine::layout_helper(int maxGlyphs)
}
LB_DEBUG("reached end of line");
lbh.checkFullOtherwiseExtend(line);
+ line.textWidth += lbh.commitedSoftHyphenWidth;
found:
line.textAdvance = line.textWidth;
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index 4e9e947263..5c9e589ec3 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -2166,7 +2166,7 @@ void QSslSocketPrivate::init()
pendingClose = false;
flushTriggered = false;
ocspResponses.clear();
-
+ systemOrSslErrorDetected = false;
// we don't want to clear the ignoreErrorsList, so
// that it is possible setting it before connecting
// ignoreErrorsList.clear();
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index 51510f1c60..855865209b 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -648,10 +648,16 @@ bool QSslSocketBackendPrivate::initSslContext()
void QSslSocketBackendPrivate::destroySslContext()
{
if (ssl) {
- // We do not send a shutdown alert here. Just mark the session as
- // resumable for qhttpnetworkconnection's "optimization", otherwise
- // OpenSSL won't start a session resumption.
- q_SSL_shutdown(ssl);
+ if (!q_SSL_in_init(ssl) && !systemOrSslErrorDetected) {
+ // We do not send a shutdown alert here. Just mark the session as
+ // resumable for qhttpnetworkconnection's "optimization", otherwise
+ // OpenSSL won't start a session resumption.
+ if (q_SSL_shutdown(ssl) != 1) {
+ // Some error may be queued, clear it.
+ const auto errors = getErrorsFromOpenSsl();
+ Q_UNUSED(errors);
+ }
+ }
q_SSL_free(ssl);
ssl = nullptr;
}
@@ -1084,6 +1090,7 @@ void QSslSocketBackendPrivate::transmit()
case SSL_ERROR_SSL: // error in the SSL library
// we do not know exactly what the error is, nor whether we can recover from it,
// so just return to prevent an endless loop in the outer "while" statement
+ systemOrSslErrorDetected = true;
{
const ScopedBool bg(inSetAndEmitError, true);
setErrorAndEmit(QAbstractSocket::SslInternalError,
@@ -1681,8 +1688,12 @@ bool QSslSocketBackendPrivate::checkOcspStatus()
void QSslSocketBackendPrivate::disconnectFromHost()
{
if (ssl) {
- if (!shutdown) {
- q_SSL_shutdown(ssl);
+ if (!shutdown && !q_SSL_in_init(ssl) && !systemOrSslErrorDetected) {
+ if (q_SSL_shutdown(ssl) != 1) {
+ // Some error may be queued, clear it.
+ const auto errors = getErrorsFromOpenSsl();
+ Q_UNUSED(errors);
+ }
shutdown = true;
transmit();
}
diff --git a/src/network/ssl/qsslsocket_openssl11_symbols_p.h b/src/network/ssl/qsslsocket_openssl11_symbols_p.h
index 0fe0899d4f..b7193ad180 100644
--- a/src/network/ssl/qsslsocket_openssl11_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl11_symbols_p.h
@@ -192,4 +192,11 @@ typedef int (*q_SSL_psk_use_session_cb_func_t)(SSL *, const EVP_MD *, const unsi
}
void q_SSL_set_psk_use_session_callback(SSL *s, q_SSL_psk_use_session_cb_func_t);
+#if OPENSSL_VERSION_NUMBER < 0x10101000L
+// What a mess!
+int q_SSL_in_init(SSL *s);
+#else
+int q_SSL_in_init(const SSL *s);
+#endif // 1.1.1 or 1.1.0
+
#endif
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
index 26336edd3d..d1bd84cf25 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols.cpp
+++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp
@@ -160,6 +160,11 @@ DEFINEFUNC(void, OPENSSL_sk_free, OPENSSL_STACK *a, a, return, DUMMYARG)
DEFINEFUNC2(void *, OPENSSL_sk_value, OPENSSL_STACK *a, a, int b, b, return nullptr, return)
DEFINEFUNC(int, SSL_session_reused, SSL *a, a, return 0, return)
DEFINEFUNC2(unsigned long, SSL_CTX_set_options, SSL_CTX *ctx, ctx, unsigned long op, op, return 0, return)
+#if OPENSSL_VERSION_NUMBER < 0x10101000L
+DEFINEFUNC(int, SSL_in_init, SSL *a, a, return 0, return)
+#else
+DEFINEFUNC(int, SSL_in_init, const SSL *a, a, return 0, return)
+#endif
#ifdef TLS1_3_VERSION
DEFINEFUNC2(int, SSL_CTX_set_ciphersuites, SSL_CTX *ctx, ctx, const char *str, str, return 0, return)
DEFINEFUNC2(void, SSL_set_psk_use_session_callback, SSL *ssl, ssl, q_SSL_psk_use_session_cb_func_t callback, callback, return, DUMMYARG)
@@ -242,6 +247,7 @@ DEFINEFUNC2(void, BIO_set_shutdown, BIO *a, a, int shut, shut, return, DUMMYARG)
// Functions below are either deprecated or removed in OpenSSL >= 1.1:
DEFINEFUNC(unsigned char *, ASN1_STRING_data, ASN1_STRING *a, a, return nullptr, return)
+DEFINEFUNC(int, SSL_state, const SSL *a, a, return 0, return)
#ifdef SSLEAY_MACROS
DEFINEFUNC3(void *, ASN1_dup, i2d_of_void *a, a, d2i_of_void *b, b, char *c, c, return nullptr, return)
@@ -971,6 +977,7 @@ bool q_resolveOpenSslSymbols()
#if QT_CONFIG(opensslv11)
RESOLVEFUNC(OPENSSL_init_ssl)
+ RESOLVEFUNC(SSL_in_init)
RESOLVEFUNC(OPENSSL_init_crypto)
RESOLVEFUNC(ASN1_STRING_get0_data)
RESOLVEFUNC(EVP_CIPHER_CTX_reset)
@@ -1066,6 +1073,7 @@ bool q_resolveOpenSslSymbols()
#else // !opensslv11
RESOLVEFUNC(ASN1_STRING_data)
+ RESOLVEFUNC(SSL_state)
#ifdef SSLEAY_MACROS
RESOLVEFUNC(ASN1_dup)
@@ -1428,6 +1436,9 @@ QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime)
{
size_t lTimeLength = aTime->length;
char *pString = (char *) aTime->data;
+ auto isValidPointer = [pString, lTimeLength](const char *const probe){
+ return size_t(probe - pString) < lTimeLength;
+ };
if (aTime->type == V_ASN1_UTCTIME) {
@@ -1446,12 +1457,21 @@ QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime)
*pBuffer++ = '0';
} else {
*pBuffer++ = *pString++;
+ if (!isValidPointer(pString)) // Nah.
+ return {};
*pBuffer++ = *pString++;
+ if (!isValidPointer(pString)) // Nah.
+ return {};
// Skip any fractional seconds...
if (*pString == '.') {
pString++;
- while ((*pString >= '0') && (*pString <= '9'))
+ if (!isValidPointer(pString)) // Oh no, cannot dereference (see below).
+ return {};
+ while ((*pString >= '0') && (*pString <= '9')) {
pString++;
+ if (!isValidPointer(pString)) // No and no.
+ return {};
+ }
}
}
@@ -1465,6 +1485,10 @@ QDateTime q_getTimeFromASN1(const ASN1_TIME *aTime)
if ((*pString != '+') && (*pString != '-'))
return QDateTime();
+ if (!isValidPointer(pString + 4)) {
+ // What kind of input parameters we were provided with? To hell with them!
+ return {};
+ }
lSecondsFromUCT = ((pString[1] - '0') * 10 + (pString[2] - '0')) * 60;
lSecondsFromUCT += (pString[3] - '0') * 10 + (pString[4] - '0');
lSecondsFromUCT *= 60;
diff --git a/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h b/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h
index f5626d5d16..9284101779 100644
--- a/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h
+++ b/src/network/ssl/qsslsocket_opensslpre11_symbols_p.h
@@ -121,6 +121,8 @@ SSL_CTX *q_SSL_CTX_new(const SSL_METHOD *a);
int q_SSL_library_init();
void q_SSL_load_error_strings();
+int q_SSL_state(const SSL *a);
+#define q_SSL_in_init(a) (q_SSL_state(a) & SSL_ST_INIT)
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
int q_SSL_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func);
diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h
index daa9be23f4..350b1f1fc1 100644
--- a/src/network/ssl/qsslsocket_p.h
+++ b/src/network/ssl/qsslsocket_p.h
@@ -208,6 +208,7 @@ protected:
bool verifyErrorsHaveBeenIgnored();
bool paused;
bool flushTriggered;
+ bool systemOrSslErrorDetected = false;
QVector<QOcspResponse> ocspResponses;
};
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h
index b57deacb57..3d9dfd8359 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.h
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h
@@ -47,6 +47,8 @@
#include <QScopedPointer>
#include "qiosurfacegraphicsbuffer.h"
+#include <unordered_map>
+
QT_BEGIN_NAMESPACE
class QCocoaBackingStore : public QRasterBackingStore
@@ -71,8 +73,9 @@ private:
void redrawRoundedBottomCorners(CGRect) const;
};
-class QCALayerBackingStore : public QCocoaBackingStore
+class QCALayerBackingStore : public QObject, public QCocoaBackingStore
{
+ Q_OBJECT
public:
QCALayerBackingStore(QWindow *window);
~QCALayerBackingStore();
@@ -119,6 +122,11 @@ private:
QMacNotificationObserver m_backingPropertiesObserver;
std::list<std::unique_ptr<GraphicsBuffer>> m_buffers;
+
+ void flushSubWindow(QWindow *window);
+ std::unordered_map<QWindow*, std::unique_ptr<QCALayerBackingStore>> m_subWindowBackingstores;
+ void windowDestroyed(QObject *object);
+ bool m_clearSurfaceOnPaint = true;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
index cb019c3775..2b4c71f279 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
@@ -382,7 +382,7 @@ void QCALayerBackingStore::beginPaint(const QRegion &region)
// Although undocumented, QBackingStore::beginPaint expects the painted region
// to be cleared before use if the window has a surface format with an alpha.
// Fresh IOSurfaces are already cleared, so we don't need to clear those.
- if (!bufferWasRecreated && window()->format().hasAlpha()) {
+ if (m_clearSurfaceOnPaint && !bufferWasRecreated && window()->format().hasAlpha()) {
qCDebug(lcQpaBackingStore) << "Clearing" << region << "before use";
QPainter painter(m_buffers.back()->asImage());
painter.setCompositionMode(QPainter::CompositionMode_Source);
@@ -511,9 +511,13 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion &region,
if (!prepareForFlush())
return;
+ if (flushedWindow != window()) {
+ flushSubWindow(flushedWindow);
+ return;
+ }
+
QMacAutoReleasePool pool;
- NSView *backingStoreView = static_cast<QCocoaWindow *>(window()->handle())->view();
NSView *flushedView = static_cast<QCocoaWindow *>(flushedWindow->handle())->view();
// If the backingstore is just flushed, without being painted to first, then we may
@@ -548,7 +552,7 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion &region,
// are committed as part of a display-cycle instead of on the next runloop pass. This
// means CA won't try to throttle us if we flush too fast, and we'll coalesce our flush
// with other pending view and layer updates.
- backingStoreView.window.viewsNeedDisplay = YES;
+ flushedView.window.viewsNeedDisplay = YES;
if (window()->format().swapBehavior() == QSurfaceFormat::SingleBuffer) {
// The private API [CALayer reloadValueForKeyPath:@"contents"] would be preferable,
@@ -556,28 +560,10 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion &region,
flushedView.layer.contents = nil;
}
- if (flushedView == backingStoreView) {
- qCInfo(lcQpaBackingStore) << "Flushing" << backBufferSurface
- << "to" << flushedView.layer << "of" << flushedView;
- flushedView.layer.contents = backBufferSurface;
- } else {
- auto subviewRect = [flushedView convertRect:flushedView.bounds toView:backingStoreView];
- auto scale = flushedView.layer.contentsScale;
- subviewRect = CGRectApplyAffineTransform(subviewRect, CGAffineTransformMakeScale(scale, scale));
-
- // We make a copy of the image data up front, which means we don't
- // need to mark the IOSurface as being in use. FIXME: Investigate
- // if there's a cheaper way to get sub-image data to a layer.
- m_buffers.back()->lock(QPlatformGraphicsBuffer::SWReadAccess);
- QImage subImage = m_buffers.back()->asImage()->copy(QRectF::fromCGRect(subviewRect).toRect());
- m_buffers.back()->unlock();
+ qCInfo(lcQpaBackingStore) << "Flushing" << backBufferSurface
+ << "to" << flushedView.layer << "of" << flushedView;
- qCInfo(lcQpaBackingStore) << "Flushing" << subImage
- << "to" << flushedView.layer << "of subview" << flushedView;
- QCFType<CGImageRef> cgImage = CGImageCreateCopyWithColorSpace(
- QCFType<CGImageRef>(subImage.toCGImage()), colorSpace());
- flushedView.layer.contents = (__bridge id)static_cast<CGImageRef>(cgImage);
- }
+ flushedView.layer.contents = backBufferSurface;
// Since we may receive multiple flushes before a new frame is started, we do not
// swap any buffers just yet. Instead we check in the next beginPaint if the layer's
@@ -589,6 +575,53 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion &region,
// the window server.
}
+void QCALayerBackingStore::flushSubWindow(QWindow *subWindow)
+{
+ qCInfo(lcQpaBackingStore) << "Flushing sub-window" << subWindow
+ << "via its own backingstore";
+
+ auto &subWindowBackingStore = m_subWindowBackingstores[subWindow];
+ if (!subWindowBackingStore) {
+ subWindowBackingStore.reset(new QCALayerBackingStore(subWindow));
+ QObject::connect(subWindow, &QObject::destroyed, this, &QCALayerBackingStore::windowDestroyed);
+ subWindowBackingStore->m_clearSurfaceOnPaint = false;
+ }
+
+ auto subWindowSize = subWindow->size();
+ static const auto kNoStaticContents = QRegion();
+ subWindowBackingStore->resize(subWindowSize, kNoStaticContents);
+
+ auto subWindowLocalRect = QRect(QPoint(), subWindowSize);
+ subWindowBackingStore->beginPaint(subWindowLocalRect);
+
+ QPainter painter(subWindowBackingStore->m_buffers.back()->asImage());
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+
+ NSView *backingStoreView = static_cast<QCocoaWindow *>(window()->handle())->view();
+ NSView *flushedView = static_cast<QCocoaWindow *>(subWindow->handle())->view();
+ auto subviewRect = [flushedView convertRect:flushedView.bounds toView:backingStoreView];
+ auto scale = flushedView.layer.contentsScale;
+ subviewRect = CGRectApplyAffineTransform(subviewRect, CGAffineTransformMakeScale(scale, scale));
+
+ m_buffers.back()->lock(QPlatformGraphicsBuffer::SWReadAccess);
+ const QImage *backingStoreImage = m_buffers.back()->asImage();
+ painter.drawImage(subWindowLocalRect, *backingStoreImage, QRectF::fromCGRect(subviewRect));
+ m_buffers.back()->unlock();
+
+ painter.end();
+ subWindowBackingStore->endPaint();
+ subWindowBackingStore->flush(subWindow, subWindowLocalRect, QPoint());
+
+ qCInfo(lcQpaBackingStore) << "Done flushing sub-window" << subWindow;
+}
+
+void QCALayerBackingStore::windowDestroyed(QObject *object)
+{
+ auto *window = static_cast<QWindow*>(object);
+ qCInfo(lcQpaBackingStore) << "Removing backingstore for sub-window" << window;
+ m_subWindowBackingstores.erase(window);
+}
+
#ifndef QT_NO_OPENGL
void QCALayerBackingStore::composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset,
QPlatformTextureList *textures, bool translucentBackground)
@@ -722,4 +755,6 @@ QImage *QCALayerBackingStore::GraphicsBuffer::asImage()
return &m_image;
}
+#include "moc_qcocoabackingstore.cpp"
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp b/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp
index ca8db9b215..2e1b083557 100644
--- a/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp
+++ b/src/plugins/platforms/wasm/qwasmeventdispatcher.cpp
@@ -194,7 +194,8 @@ void QWasmEventDispatcher::wakeUp()
{
#ifdef EMSCRIPTEN_HAS_ASYNC_RUN_IN_MAIN_RUNTIME_THREAD
if (!emscripten_is_main_runtime_thread())
- emscripten_async_run_in_main_runtime_thread_(EM_FUNC_SIG_VI, (void*)(&QWasmEventDispatcher::mainThreadWakeUp), this);
+ if (m_hasMainLoop)
+ emscripten_async_run_in_main_runtime_thread_(EM_FUNC_SIG_VI, (void*)(&QWasmEventDispatcher::mainThreadWakeUp), this);
#endif
QEventDispatcherUNIX::wakeUp();
}
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index d31352b854..293faf8a53 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -978,6 +978,13 @@ QByteArray QWindowsContext::comErrorString(HRESULT hr)
return result;
}
+void QWindowsContext::forceNcCalcSize(HWND hwnd)
+{
+ // Force WM_NCCALCSIZE to adjust margin
+ SetWindowPos(hwnd, nullptr, 0, 0, 0, 0,
+ SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
+}
+
bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void *out,
unsigned dpi)
{
diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h
index 8027f09389..07398bd61c 100644
--- a/src/plugins/platforms/windows/qwindowscontext.h
+++ b/src/plugins/platforms/windows/qwindowscontext.h
@@ -246,6 +246,8 @@ public:
bool asyncExpose() const;
void setAsyncExpose(bool value);
+ static void forceNcCalcSize(HWND hwnd);
+
static bool systemParametersInfo(unsigned action, unsigned param, void *out, unsigned dpi = 0);
static bool systemParametersInfoForScreen(unsigned action, unsigned param, void *out,
const QPlatformScreen *screen = nullptr);
diff --git a/src/plugins/platforms/windows/qwindowsmenu.cpp b/src/plugins/platforms/windows/qwindowsmenu.cpp
index d20edd685e..221e4ff6ec 100644
--- a/src/plugins/platforms/windows/qwindowsmenu.cpp
+++ b/src/plugins/platforms/windows/qwindowsmenu.cpp
@@ -794,20 +794,13 @@ QWindowsMenuBar *QWindowsMenuBar::menuBarOf(const QWindow *notYetCreatedWindow)
? qobject_cast<QWindowsMenuBar *>(menuBarV.value<QObject *>()) : nullptr;
}
-static inline void forceNcCalcSize(HWND hwnd)
-{
- // Force WM_NCCALCSIZE to adjust margin: Does not appear to work?
- SetWindowPos(hwnd, nullptr, 0, 0, 0, 0,
- SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
-}
-
void QWindowsMenuBar::install(QWindowsWindow *window)
{
const HWND hwnd = window->handle();
const BOOL result = SetMenu(hwnd, m_hMenuBar);
if (result) {
window->setMenuBar(this);
- forceNcCalcSize(hwnd);
+ QWindowsContext::forceNcCalcSize(hwnd);
}
}
@@ -817,7 +810,7 @@ void QWindowsMenuBar::removeFromWindow()
const HWND hwnd = window->handle();
SetMenu(hwnd, nullptr);
window->setMenuBar(nullptr);
- forceNcCalcSize(hwnd);
+ QWindowsContext::forceNcCalcSize(hwnd);
}
}
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
index a11da598fc..04478d5f1f 100644
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
@@ -2431,7 +2431,17 @@ void QWindowsWindow::setFullFrameMargins(const QMargins &newMargins)
void QWindowsWindow::updateFullFrameMargins()
{
- // Normally obtained from WM_NCCALCSIZE
+ // QTBUG-82580: If a native menu is present, force a WM_NCCALCSIZE.
+ if (GetMenu(m_data.hwnd))
+ QWindowsContext::forceNcCalcSize(m_data.hwnd);
+ else
+ calculateFullFrameMargins();
+}
+
+void QWindowsWindow::calculateFullFrameMargins()
+{
+ // Normally obtained from WM_NCCALCSIZE. This calculation only works
+ // when no native menu is present.
const auto systemMargins = testFlag(DisableNonClientScaling)
? QWindowsGeometryHint::frameOnPrimaryScreen(m_data.hwnd)
: frameMargins_sys();
diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h
index 1f8800272b..aaf02d2a81 100644
--- a/src/plugins/platforms/windows/qwindowswindow.h
+++ b/src/plugins/platforms/windows/qwindowswindow.h
@@ -370,6 +370,7 @@ private:
void handleWindowStateChange(Qt::WindowStates state);
inline void destroyIcon();
void fireExpose(const QRegion &region, bool force=false);
+ void calculateFullFrameMargins();
mutable QWindowsWindowData m_data;
QPointer<QWindowsMenuBar> m_menuBar;
diff --git a/src/plugins/platforms/winrt/qwinrtservices.cpp b/src/plugins/platforms/winrt/qwinrtservices.cpp
index b27c408f40..04d7417801 100644
--- a/src/plugins/platforms/winrt/qwinrtservices.cpp
+++ b/src/plugins/platforms/winrt/qwinrtservices.cpp
@@ -43,6 +43,7 @@
#include <QtCore/QDir>
#include <QtCore/QCoreApplication>
#include <QtCore/qfunctions_winrt.h>
+#include <private/qeventdispatcher_winrt_p.h>
#include <wrl.h>
#include <windows.foundation.h>
@@ -94,13 +95,17 @@ bool QWinRTServices::openUrl(const QUrl &url)
HRESULT hr = d->uriFactory->CreateUri(uriString.Get(), &uri);
RETURN_FALSE_IF_FAILED("Failed to create URI from QUrl.");
- ComPtr<IAsyncOperation<bool>> op;
- hr = d->launcher->LaunchUriAsync(uri.Get(), &op);
- RETURN_FALSE_IF_FAILED("Failed to start URI launch.");
-
boolean result;
- hr = QWinRTFunctions::await(op, &result);
- RETURN_FALSE_IF_FAILED("Failed to launch URI.");
+ hr = QEventDispatcherWinRT::runOnXamlThread([this, d, uri, &result]() {
+ ComPtr<IAsyncOperation<bool>> op;
+ HRESULT hr = d->launcher->LaunchUriAsync(uri.Get(), &op);
+ RETURN_HR_IF_FAILED("Failed to start URI launch.");
+
+ hr = QWinRTFunctions::await(op, &result);
+ RETURN_HR_IF_FAILED("Failed to launch URI.");
+ return hr;
+ });
+ RETURN_FALSE_IF_FAILED("Failed to launch URI from Xaml thread.");
return result;
}
@@ -131,12 +136,16 @@ bool QWinRTServices::openDocument(const QUrl &url)
boolean result;
{
- ComPtr<IAsyncOperation<bool>> op;
- hr = d->launcher->LaunchFileAsync(file.Get(), &op);
- RETURN_FALSE_IF_FAILED("Failed to start file launch.");
-
- hr = QWinRTFunctions::await(op, &result);
- RETURN_FALSE_IF_FAILED("Failed to launch file.");
+ hr = QEventDispatcherWinRT::runOnXamlThread([this, d, file, &result]() {
+ ComPtr<IAsyncOperation<bool>> op;
+ HRESULT hr = d->launcher->LaunchFileAsync(file.Get(), &op);
+ RETURN_HR_IF_FAILED("Failed to start file launch.");
+
+ hr = QWinRTFunctions::await(op, &result);
+ RETURN_HR_IF_FAILED("Failed to launch file.");
+ return hr;
+ });
+ RETURN_FALSE_IF_FAILED("Failed to launch file from Xaml thread.");
}
return result;
diff --git a/src/plugins/platforms/xcb/qxcbatom.cpp b/src/plugins/platforms/xcb/qxcbatom.cpp
index d366564dd6..a73d28319d 100644
--- a/src/plugins/platforms/xcb/qxcbatom.cpp
+++ b/src/plugins/platforms/xcb/qxcbatom.cpp
@@ -182,6 +182,7 @@ static const char *xcb_atomnames = {
"XdndActionCopy\0"
"XdndActionLink\0"
"XdndActionMove\0"
+ "XdndActionAsk\0"
"XdndActionPrivate\0"
// Xkb
diff --git a/src/plugins/platforms/xcb/qxcbatom.h b/src/plugins/platforms/xcb/qxcbatom.h
index 80b5887395..9cf93ec314 100644
--- a/src/plugins/platforms/xcb/qxcbatom.h
+++ b/src/plugins/platforms/xcb/qxcbatom.h
@@ -183,6 +183,7 @@ public:
XdndActionCopy,
XdndActionLink,
XdndActionMove,
+ XdndActionAsk,
XdndActionPrivate,
// Xkb
diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp
index 3d525598ca..d82129c6bc 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.cpp
+++ b/src/plugins/platforms/xcb/qxcbdrag.cpp
@@ -216,6 +216,22 @@ void QXcbDrag::endDrag()
initiatorWindow.clear();
}
+Qt::DropAction QXcbDrag::defaultAction(Qt::DropActions possibleActions, Qt::KeyboardModifiers modifiers) const
+{
+ if (currentDrag() || drop_actions.isEmpty())
+ return QBasicDrag::defaultAction(possibleActions, modifiers);
+
+ return toDropAction(drop_actions.first());
+}
+
+void QXcbDrag::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event)
+{
+ if (event->window != xdnd_dragsource || event->atom != atom(QXcbAtom::XdndActionList))
+ return;
+
+ readActionList();
+}
+
static
bool windowInteractsWithPosition(xcb_connection_t *connection, const QPoint & pos, xcb_window_t w, xcb_shape_sk_t shapeType)
{
@@ -470,16 +486,20 @@ void QXcbDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardMod
move.data.data32[1] = 0; // flags
move.data.data32[2] = (globalPos.x() << 16) + globalPos.y();
move.data.data32[3] = connection()->time();
- move.data.data32[4] = toXdndAction(defaultAction(currentDrag()->supportedActions(), mods));
+ const auto supportedActions = currentDrag()->supportedActions();
+ const auto requestedAction = defaultAction(supportedActions, mods);
+ move.data.data32[4] = toXdndAction(requestedAction);
qCDebug(lcQpaXDnd) << "sending XdndPosition to target:" << target;
source_time = connection()->time();
- if (w)
+ if (w) {
handle_xdnd_position(w, &move, b, mods);
- else
+ } else {
+ setActionList(requestedAction, supportedActions);
xcb_send_event(xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&move);
+ }
}
static const bool isUnity = qgetenv("XDG_CURRENT_DESKTOP").toLower() == "unity";
@@ -560,6 +580,16 @@ Qt::DropAction QXcbDrag::toDropAction(xcb_atom_t a) const
return Qt::CopyAction;
}
+Qt::DropActions QXcbDrag::toDropActions(const QVector<xcb_atom_t> &atoms) const
+{
+ Qt::DropActions actions;
+ for (const auto actionAtom : atoms) {
+ if (actionAtom != atom(QXcbAtom::XdndActionAsk))
+ actions |= toDropAction(actionAtom);
+ }
+ return actions;
+}
+
xcb_atom_t QXcbDrag::toXdndAction(Qt::DropAction a) const
{
switch (a) {
@@ -577,6 +607,60 @@ xcb_atom_t QXcbDrag::toXdndAction(Qt::DropAction a) const
}
}
+void QXcbDrag::readActionList()
+{
+ drop_actions.clear();
+ auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, xdnd_dragsource,
+ atom(QXcbAtom::XdndActionList), XCB_ATOM_ATOM,
+ 0, 1024);
+ if (reply && reply->type != XCB_NONE && reply->format == 32) {
+ int length = xcb_get_property_value_length(reply.get()) / 4;
+
+ xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply.get());
+ for (int i = 0; i < length; ++i)
+ drop_actions.append(atoms[i]);
+ }
+}
+
+void QXcbDrag::setActionList(Qt::DropAction requestedAction, Qt::DropActions supportedActions)
+{
+#ifndef QT_NO_CLIPBOARD
+ QVector<xcb_atom_t> actions;
+ if (requestedAction != Qt::IgnoreAction)
+ actions.append(toXdndAction(requestedAction));
+
+ auto checkAppend = [this, requestedAction, supportedActions, &actions](Qt::DropAction action) {
+ if (requestedAction != action && supportedActions & action)
+ actions.append(toXdndAction(action));
+ };
+
+ checkAppend(Qt::CopyAction);
+ checkAppend(Qt::MoveAction);
+ checkAppend(Qt::LinkAction);
+
+ if (current_actions != actions) {
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, connection()->clipboard()->owner(),
+ atom(QXcbAtom::XdndActionList),
+ XCB_ATOM_ATOM, 32, actions.size(), actions.constData());
+ current_actions = actions;
+ }
+#endif
+}
+
+void QXcbDrag::startListeningForActionListChanges()
+{
+ connection()->addWindowEventListener(xdnd_dragsource, this);
+ const uint32_t event_mask[] = { XCB_EVENT_MASK_PROPERTY_CHANGE };
+ xcb_change_window_attributes(xcb_connection(), xdnd_dragsource, XCB_CW_EVENT_MASK, event_mask);
+}
+
+void QXcbDrag::stopListeningForActionListChanges()
+{
+ const uint32_t event_mask[] = { XCB_EVENT_MASK_NO_EVENT };
+ xcb_change_window_attributes(xcb_connection(), xdnd_dragsource, XCB_CW_EVENT_MASK, event_mask);
+ connection()->removeWindowEventListener(xdnd_dragsource);
+}
+
int QXcbDrag::findTransactionByWindow(xcb_window_t window)
{
int at = -1;
@@ -657,6 +741,9 @@ void QXcbDrag::handleEnter(QPlatformWindow *, const xcb_client_message_event_t *
return;
xdnd_dragsource = event->data.data32[0];
+ startListeningForActionListChanges();
+ readActionList();
+
if (!proxy)
proxy = xdndProxy(connection(), xdnd_dragsource);
current_proxy_target = proxy ? proxy : xdnd_dragsource;
@@ -723,7 +810,9 @@ void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message
supported_actions = currentDrag()->supportedActions();
} else {
dropData = m_dropData;
- supported_actions = Qt::DropActions(toDropAction(e->data.data32[4]));
+ supported_actions = toDropActions(drop_actions);
+ if (e->data.data32[4] != atom(QXcbAtom::XdndActionAsk))
+ supported_actions |= Qt::DropActions(toDropAction(e->data.data32[4]));
}
auto buttons = currentDrag() ? b : connection()->queryMouseButtons();
@@ -867,8 +956,10 @@ void QXcbDrag::handleLeave(QPlatformWindow *w, const xcb_client_message_event_t
// If the target receives XdndLeave, it frees any cached data and forgets the whole incident.
qCDebug(lcQpaXDnd) << "target:" << event->window << "received XdndLeave";
- if (!currentWindow || w != currentWindow.data()->handle())
+ if (!currentWindow || w != currentWindow.data()->handle()) {
+ stopListeningForActionListChanges();
return; // sanity
+ }
// ###
// if (checkEmbedded(current_embedding_widget, event)) {
@@ -883,6 +974,8 @@ void QXcbDrag::handleLeave(QPlatformWindow *w, const xcb_client_message_event_t
event->data.data32[0], xdnd_dragsource);
}
+ stopListeningForActionListChanges();
+
QWindowSystemInterface::handleDrag(w->window(), nullptr, QPoint(), Qt::IgnoreAction, 0, 0);
}
@@ -929,6 +1022,7 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e
qCDebug(lcQpaXDnd) << "target:" << event->window << "received XdndDrop";
if (!currentWindow) {
+ stopListeningForActionListChanges();
xdnd_dragsource = 0;
return; // sanity
}
@@ -951,7 +1045,7 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e
supported_drop_actions = Qt::DropActions(l[4]);
} else {
dropData = m_dropData;
- supported_drop_actions = accepted_drop_action;
+ supported_drop_actions = accepted_drop_action | toDropActions(drop_actions);
}
if (!dropData)
@@ -986,6 +1080,8 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e
xcb_send_event(xcb_connection(), false, current_proxy_target,
XCB_EVENT_MASK_NO_EVENT, (char *)&finished);
+ stopListeningForActionListChanges();
+
dropped = true;
}
diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h
index 1388e68acc..b6371041e6 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.h
+++ b/src/plugins/platforms/xcb/qxcbdrag.h
@@ -68,7 +68,7 @@ class QXcbScreen;
class QDrag;
class QShapedPixmapWindow;
-class QXcbDrag : public QXcbObject, public QBasicDrag
+class QXcbDrag : public QXcbObject, public QBasicDrag, public QXcbWindowEventListener
{
public:
QXcbDrag(QXcbConnection *c);
@@ -82,6 +82,10 @@ public:
void drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override;
void endDrag() override;
+ Qt::DropAction defaultAction(Qt::DropActions possibleActions, Qt::KeyboardModifiers modifiers) const override;
+
+ void handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) override;
+
void handleEnter(QPlatformWindow *window, const xcb_client_message_event_t *event, xcb_window_t proxy = 0);
void handlePosition(QPlatformWindow *w, const xcb_client_message_event_t *event);
void handleLeave(QPlatformWindow *w, const xcb_client_message_event_t *event);
@@ -114,8 +118,14 @@ private:
void send_leave();
Qt::DropAction toDropAction(xcb_atom_t atom) const;
+ Qt::DropActions toDropActions(const QVector<xcb_atom_t> &atoms) const;
xcb_atom_t toXdndAction(Qt::DropAction a) const;
+ void readActionList();
+ void setActionList(Qt::DropAction requestedAction, Qt::DropActions supportedActions);
+ void startListeningForActionListChanges();
+ void stopListeningForActionListChanges();
+
QPointer<QWindow> initiatorWindow;
QPointer<QWindow> currentWindow;
QPoint currentPosition;
@@ -159,6 +169,9 @@ private:
QVector<xcb_atom_t> drag_types;
+ QVector<xcb_atom_t> current_actions;
+ QVector<xcb_atom_t> drop_actions;
+
struct Transaction
{
xcb_timestamp_t timestamp;
diff --git a/src/testlib/qabstractitemmodeltester.cpp b/src/testlib/qabstractitemmodeltester.cpp
index 859966c0e3..f651fd95be 100644
--- a/src/testlib/qabstractitemmodeltester.cpp
+++ b/src/testlib/qabstractitemmodeltester.cpp
@@ -288,6 +288,12 @@ QAbstractItemModelTester::FailureReportingMode QAbstractItemModelTester::failure
return d->failureReportingMode;
}
+bool QAbstractItemModelTester::verify(bool statement, const char *statementStr, const char *description, const char *file, int line)
+{
+ Q_D(QAbstractItemModelTester);
+ return d->verify(statement, statementStr, description, file, line);
+}
+
QAbstractItemModelTesterPrivate::QAbstractItemModelTesterPrivate(QAbstractItemModel *model, QAbstractItemModelTester::FailureReportingMode failureReportingMode)
: model(model),
failureReportingMode(failureReportingMode),
diff --git a/src/widgets/itemviews/qabstractitemview.cpp b/src/widgets/itemviews/qabstractitemview.cpp
index f879af6ad2..73604cb8f4 100644
--- a/src/widgets/itemviews/qabstractitemview.cpp
+++ b/src/widgets/itemviews/qabstractitemview.cpp
@@ -1844,10 +1844,16 @@ void QAbstractItemView::mouseMoveEvent(QMouseEvent *event)
|| edit(index, NoEditTriggers, event))
return;
- if (d->selectionMode != SingleSelection)
- topLeft = d->pressedPosition - d->offset();
- else
+ if (d->selectionMode != SingleSelection) {
+ // Use the current selection start index if it is valid as this will be based on the
+ // start of the selection and not the last item being pressed which can be different
+ // when in extended selection
+ topLeft = d->currentSelectionStartIndex.isValid()
+ ? visualRect(d->currentSelectionStartIndex).center()
+ : d->pressedPosition - d->offset();
+ } else {
topLeft = bottomRight;
+ }
d->checkMouseMove(index);
diff --git a/src/widgets/itemviews/qtreeview.cpp b/src/widgets/itemviews/qtreeview.cpp
index 442369c2ec..a9e6e90623 100644
--- a/src/widgets/itemviews/qtreeview.cpp
+++ b/src/widgets/itemviews/qtreeview.cpp
@@ -3755,7 +3755,8 @@ int QTreeViewPrivate::itemDecorationAt(const QPoint &pos) const
bool spanned = false;
if (!spanningIndexes.isEmpty()) {
const QModelIndex index = q->indexAt(pos);
- spanned = q->isFirstColumnSpanned(index.row(), index.parent());
+ if (index.isValid())
+ spanned = q->isFirstColumnSpanned(index.row(), index.parent());
}
const int column = spanned ? 0 : header->logicalIndexAt(pos.x());
if (!isTreePosition(column))
diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp
index 596343c52f..7b36699d5c 100644
--- a/src/widgets/kernel/qwidgetwindow.cpp
+++ b/src/widgets/kernel/qwidgetwindow.cpp
@@ -505,7 +505,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ContextMenuOnMouseRelease).toBool() ?
QEvent::MouseButtonRelease : QEvent::MouseButtonPress;
if (QApplicationPrivate::inPopupMode()) {
- QWidget *activePopupWidget = QApplication::activePopupWidget();
+ QPointer<QWidget> activePopupWidget = QApplication::activePopupWidget();
QPoint mapped = event->pos();
if (activePopupWidget != m_widget)
mapped = activePopupWidget->mapFromGlobal(event->globalPos());
@@ -565,9 +565,11 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
#endif
if ((event->type() != QEvent::MouseButtonPress)
|| !(event->flags().testFlag(Qt::MouseEventCreatedDoubleClick))) {
-
+ // if the widget that was pressed is gone, then deliver move events without buttons
+ const auto buttons = event->type() == QEvent::MouseMove && qt_button_down == nullptr
+ ? Qt::NoButton : event->buttons();
QMouseEvent e(event->type(), widgetPos, event->windowPos(), event->screenPos(),
- event->button(), event->buttons(), event->modifiers(), event->source());
+ event->button(), buttons, event->modifiers(), event->source());
e.setTimestamp(event->timestamp());
QApplicationPrivate::sendMouseEvent(receiver, &e, receiver, receiver->window(), &qt_button_down, qt_last_mouse_receiver);
qt_last_mouse_receiver = receiver;