From ce2d68ebe1aefeae78ff2fd8ec5ff7e20790ef69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Tue, 24 Mar 2020 10:08:49 +0100 Subject: macOS: Flush sublayers via separate IOSurface backingstores MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flushing sublayers via QImage copies of the root IOSurface was causing performance regressions due to the constant allocations of new images each frame. We now re-use the QCALayerBackingStore implementation for sublayers, which gives a dynamic swap-chain. We're still paying the CPU cost of the copy from the root backingstore to the layered backingstores, as well as the memory cost, but at least improves the situation. We do not try to be smart and paint directly into the sublayers, as that would leave the root backingstore stale, potentially causing glitches when views are repositioned. Investigating this is left for future work. Fixes: QTBUG-82986 Change-Id: I758a3d8e1e40e2ed4fe6bc590a4a5a988d87a3a7 Reviewed-by: Morten Johan Sørvig Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/cocoa/qcocoabackingstore.h | 10 ++- src/plugins/platforms/cocoa/qcocoabackingstore.mm | 83 ++++++++++++++++------- 2 files changed, 68 insertions(+), 25 deletions(-) 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 #include "qiosurfacegraphicsbuffer.h" +#include + 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> m_buffers; + + void flushSubWindow(QWindow *window); + std::unordered_map> 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 94eb471f5d..6aa0c0182f 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -394,7 +394,7 @@ void QCALayerBackingStore::beginPaint(const QRegion ®ion) // 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); @@ -523,9 +523,13 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, if (!prepareForFlush()) return; + if (flushedWindow != window()) { + flushSubWindow(flushedWindow); + return; + } + QMacAutoReleasePool pool; - NSView *backingStoreView = static_cast(window()->handle())->view(); NSView *flushedView = static_cast(flushedWindow->handle())->view(); // If the backingstore is just flushed, without being painted to first, then we may @@ -560,7 +564,7 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, // 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, @@ -568,28 +572,10 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, 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 cgImage = CGImageCreateCopyWithColorSpace( - QCFType(subImage.toCGImage()), colorSpace()); - flushedView.layer.contents = (__bridge id)static_cast(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 @@ -601,6 +587,53 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, // 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(window()->handle())->view(); + NSView *flushedView = static_cast(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(object); + qCInfo(lcQpaBackingStore) << "Removing backingstore for sub-window" << window; + m_subWindowBackingstores.erase(window); +} + #ifndef QT_NO_OPENGL void QCALayerBackingStore::composeAndFlush(QWindow *window, const QRegion ®ion, const QPoint &offset, QPlatformTextureList *textures, bool translucentBackground) @@ -734,4 +767,6 @@ QImage *QCALayerBackingStore::GraphicsBuffer::asImage() return &m_image; } +#include "moc_qcocoabackingstore.cpp" + QT_END_NAMESPACE -- cgit v1.2.3 From f432c08882ffebe5074ea28de871559a98a4d094 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 26 Feb 2020 10:42:10 +0100 Subject: Add an expansion limit for entities Recursively defined entities can easily exhaust all available memory. Limit entity expansion to a default of 4096 characters to avoid DoS attacks when a user loads untrusted content. [ChangeLog][QtCore][QXmlStream] QXmlStreamReader does now limit the expansion of entities to 4096 characters. Documents where a single entity expands to more characters than the limit are not considered well formed. The limit is there to avoid DoS attacks through recursively expanding entities when loading untrusted content. Qt 5.15 will add methods that allow changing that limit. Fixes: QTBUG-47417 Change-Id: I94387815d74fcf34783e136387ee57fac5ded0c9 Reviewed-by: Oswald Buddenhagen Reviewed-by: Volker Hilsheimer (cherry picked from commit fd4be84d23a0db4186cb42e736a9de3af722c7f7) Reviewed-by: Eirik Aavitsland --- src/corelib/serialization/qxmlstream.g | 14 ++++++++++++- src/corelib/serialization/qxmlstream_p.h | 14 ++++++++++++- .../serialization/qxmlstream/tst_qxmlstream.cpp | 23 ++++++++++++++++++++-- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/corelib/serialization/qxmlstream.g b/src/corelib/serialization/qxmlstream.g index 10bfcd491c..5726bafb26 100644 --- a/src/corelib/serialization/qxmlstream.g +++ b/src/corelib/serialization/qxmlstream.g @@ -277,9 +277,19 @@ public: QHash entityHash; QHash parameterEntityHash; QXmlStreamSimpleStackentityReferenceStack; + int entityExpansionLimit = 4096; + int entityLength = 0; inline bool referenceEntity(Entity &entity) { if (entity.isCurrentlyReferenced) { - raiseWellFormedError(QXmlStream::tr("Recursive entity detected.")); + raiseWellFormedError(QXmlStream::tr("Self-referencing entity detected.")); + return false; + } + // entityLength represents the amount of additional characters the + // entity expands into (can be negative for e.g. &). It's used to + // avoid DoS attacks through recursive entity expansions + entityLength += entity.value.size() - entity.name.size() - 2; + if (entityLength > entityExpansionLimit) { + raiseWellFormedError(QXmlStream::tr("Entity expands to more characters than the entity expansion limit.")); return false; } entity.isCurrentlyReferenced = true; @@ -830,6 +840,8 @@ entity_done ::= ENTITY_DONE; /. case $rule_number: entityReferenceStack.pop()->isCurrentlyReferenced = false; + if (entityReferenceStack.isEmpty()) + entityLength = 0; clearSym(); break; ./ diff --git a/src/corelib/serialization/qxmlstream_p.h b/src/corelib/serialization/qxmlstream_p.h index 61f501f81b..31053f8e0b 100644 --- a/src/corelib/serialization/qxmlstream_p.h +++ b/src/corelib/serialization/qxmlstream_p.h @@ -774,9 +774,19 @@ public: QHash entityHash; QHash parameterEntityHash; QXmlStreamSimpleStackentityReferenceStack; + int entityExpansionLimit = 4096; + int entityLength = 0; inline bool referenceEntity(Entity &entity) { if (entity.isCurrentlyReferenced) { - raiseWellFormedError(QXmlStream::tr("Recursive entity detected.")); + raiseWellFormedError(QXmlStream::tr("Self-referencing entity detected.")); + return false; + } + // entityLength represents the amount of additional characters the + // entity expands into (can be negative for e.g. &). It's used to + // avoid DoS attacks through recursive entity expansions + entityLength += entity.value.size() - entity.name.size() - 2; + if (entityLength > entityExpansionLimit) { + raiseWellFormedError(QXmlStream::tr("Entity expands to more characters than the entity expansion limit.")); return false; } entity.isCurrentlyReferenced = true; @@ -1308,6 +1318,8 @@ bool QXmlStreamReaderPrivate::parse() case 10: entityReferenceStack.pop()->isCurrentlyReferenced = false; + if (entityReferenceStack.isEmpty()) + entityLength = 0; clearSym(); break; diff --git a/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp b/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp index 8fdf91b090..1f9a0d575d 100644 --- a/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp +++ b/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp @@ -393,8 +393,6 @@ public: return true; } - QXmlStreamReader reader(&inputFile); - /* See testcases.dtd which reads: 'Nonvalidating parsers * must also accept "invalid" testcases, but validating ones must reject them.' */ if(type == QLatin1String("invalid") || type == QLatin1String("valid")) @@ -580,6 +578,8 @@ private slots: void roundTrip() const; void roundTrip_data() const; + void entityExpansionLimit() const; + private: static QByteArray readFile(const QString &filename); @@ -1756,6 +1756,25 @@ void tst_QXmlStream::roundTrip_data() const "\n"; } +void tst_QXmlStream::entityExpansionLimit() const +{ + QString xml = QStringLiteral("" + "" + "" + "" + "" + "]>" + "&d;&d;&d;"); + { + QXmlStreamReader reader(xml); + do { + reader.readNext(); + } while (!reader.atEnd()); + QCOMPARE(reader.error(), QXmlStreamReader::NotWellFormedError); + } +} + void tst_QXmlStream::roundTrip() const { QFETCH(QString, in); -- cgit v1.2.3 From 6a60192ac03d0b4ab542191065122243cebcd1ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 27 Mar 2020 11:53:35 +0100 Subject: Pass SDK root to the linker as -isysroot, not -Wl,-syslibroot The former option to clang will result in more options to the linker, such as the newly introduced -platform_version, which writes the SDK version to the resulting binary. By using the syslibroot flag directly we were missing the platform version, and binaries were left without an SDK version set, resulting in failed validation of the binary. Going with the clang driver gives us the right behavior for free. Fixes: QTBUG-83100 Change-Id: I98bc9ba644dae4bcc7a6a88481556bae185ce5fa Reviewed-by: Simon Hausmann Reviewed-by: Timur Pocheptsov --- configure | 5 +---- mkspecs/features/mac/default_post.prf | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/configure b/configure index ef7bad1bfc..f9407ef587 100755 --- a/configure +++ b/configure @@ -271,12 +271,9 @@ macSDKify() val=$(echo $sdk_val $(echo $val | cut -s -d ' ' -f 2-)) echo "$var=$val" ;; - QMAKE_CFLAGS=*|QMAKE_CXXFLAGS=*) + QMAKE_CFLAGS=*|QMAKE_CXXFLAGS=*|QMAKE_LFLAGS=*) echo "$line -isysroot $sysroot $version_min_flag" ;; - QMAKE_LFLAGS=*) - echo "$line -Wl,-syslibroot,$sysroot $version_min_flag" - ;; *) echo "$line" ;; diff --git a/mkspecs/features/mac/default_post.prf b/mkspecs/features/mac/default_post.prf index 993f4d56a9..d052808c14 100644 --- a/mkspecs/features/mac/default_post.prf +++ b/mkspecs/features/mac/default_post.prf @@ -197,7 +197,7 @@ macx-xcode { -isysroot$$xcodeSDKInfo(Path, $$sdk) QMAKE_XARCH_LFLAGS_$${arch} = $$version_min_flags \ -Xarch_$${arch} \ - -Wl,-syslibroot,$$xcodeSDKInfo(Path, $$sdk) + -isysroot$$xcodeSDKInfo(Path, $$sdk) QMAKE_XARCH_CFLAGS += $(EXPORT_QMAKE_XARCH_CFLAGS_$${arch}) QMAKE_XARCH_LFLAGS += $(EXPORT_QMAKE_XARCH_LFLAGS_$${arch}) @@ -218,7 +218,7 @@ macx-xcode { version_min_flag = -m$${version_identifier}-version-min=$$deployment_target QMAKE_CFLAGS += -isysroot $$QMAKE_MAC_SDK_PATH $$version_min_flag QMAKE_CXXFLAGS += -isysroot $$QMAKE_MAC_SDK_PATH $$version_min_flag - QMAKE_LFLAGS += -Wl,-syslibroot,$$QMAKE_MAC_SDK_PATH $$version_min_flag + QMAKE_LFLAGS += -isysroot $$QMAKE_MAC_SDK_PATH $$version_min_flag } # Enable precompiled headers for multiple architectures -- cgit v1.2.3 From c92ca4a41fdde11fee478eebba5702726e1bf3e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Mon, 7 Oct 2019 16:56:19 +0200 Subject: iOS: Remove assert when doing GL rendering in the background MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: QTBUG-76961 Change-Id: If2212601dbb867dd7ceb826b867bb24d302f86df Reviewed-by: Volker Hilsheimer Reviewed-by: Timur Pocheptsov Reviewed-by: Tor Arne Vestbø (cherry picked from commit b7aaee002677caf411190bbc6ea7f18a28a71360) --- src/plugins/platforms/ios/qioscontext.mm | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/plugins/platforms/ios/qioscontext.mm b/src/plugins/platforms/ios/qioscontext.mm index 535e7d7aa6..cecbb17039 100644 --- a/src/plugins/platforms/ios/qioscontext.mm +++ b/src/plugins/platforms/ios/qioscontext.mm @@ -332,11 +332,8 @@ bool QIOSContext::verifyGraphicsHardwareAvailability() ); }); - if (applicationBackgrounded) { - static const char warning[] = "OpenGL ES calls are not allowed while an application is backgrounded"; - Q_ASSERT_X(!applicationBackgrounded, "QIOSContext", warning); - qCWarning(lcQpaGLContext, warning); - } + if (applicationBackgrounded) + qCWarning(lcQpaGLContext, "OpenGL ES calls are not allowed while an application is backgrounded"); return !applicationBackgrounded; } -- cgit v1.2.3 From 7593cbc474f08f7f2d0331f60f28ea467a8de8c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Klitzing?= Date: Thu, 26 Mar 2020 12:52:22 +0100 Subject: Fix build with macOS 10.15 and deployment 10.12 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit io/qfilesystemengine_unix.cpp:1420:9: error: 'futimens' is only available on macOS 10.13 or newer [-Werror,-Wunguarded-availability-new] if (futimens(fd, ts) == -1) { ^~~~~~~~ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include/sys/stat.h:396:9: note: 'futimens' has been marked as being introduced in macOS 10.13 here, but the deployment target is macOS 10.12.0 int futimens(int __fd, const struct timespec __times[2]) __API_AVAILABLE(macosx(10.13), ios(11.0), tvos(11.0), watchos(4.0)); ^ io/qfilesystemengine_unix.cpp:1420:9: note: enclose 'futimens' in a __builtin_available check to silence this warning if (futimens(fd, ts) == -1) { ^~~~~~~~ Change-Id: Ib52adf7b1ec4f1057d8cb260a00da509429cfaed Reviewed-by: Tor Arne Vestbø (cherry picked from commit 2f030c2cf3fe368be217c0e0b157e050d1c27afc) --- src/corelib/configure.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/corelib/configure.json b/src/corelib/configure.json index de8d26a12b..4de6cc19f3 100644 --- a/src/corelib/configure.json +++ b/src/corelib/configure.json @@ -343,7 +343,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" ] } }, -- cgit v1.2.3 From ca0dedf9ea8e3746ce81946dc40c23d6a3507d41 Mon Sep 17 00:00:00 2001 From: Antti Kokko Date: Mon, 16 Mar 2020 13:57:45 +0200 Subject: Add changes file for Qt 5.12.8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I22227e4e671e509d6208f30220c00f026a09eb13 Reviewed-by: Lars Knoll Reviewed-by: Richard Moe Gustavsen Reviewed-by: Shawn Rutledge Reviewed-by: Tor Arne Vestbø --- dist/changes-5.12.8 | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 dist/changes-5.12.8 diff --git a/dist/changes-5.12.8 b/dist/changes-5.12.8 new file mode 100644 index 0000000000..0f75d167cf --- /dev/null +++ b/dist/changes-5.12.8 @@ -0,0 +1,70 @@ +Qt 5.12.8 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.12.0 through 5.12.7. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +https://doc.qt.io/qt-5/index.html + +The Qt version 5.12 series is binary compatible with the 5.11.x series. +Applications compiled for 5.11 will continue to run with 5.12. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* QtCore * +**************************************************************************** + + - QLockFile: + * Suppressed the warning on QNX that said 'setNativeLocks failed: + "Function not implemented"'. There is no difference in behavior: Qt + will continue not to be able to apply an OS- level file lock, which + means the lock could be accidentally stolen by buggy software. Correct + software using QLockFile should not be affected. + + - QXmlStream: + * QTBUG-47417: QXmlStreamReader does now limit the expansion of + entities to 4096 characters. Documents where a single entity + expands to more characters than the limit are not considered well + formed. The limit is there to avoid DoS attacks through + recursively expanding entities when loading untrusted content. Qt + 5.15 will add methods that allow changing that limit. + +**************************************************************************** +* QtSQL * +**************************************************************************** + + - sqlite: + * Updated to v3.31.1 + * [QTBUG-82533] Fixed CVE-2020-9327 + +**************************************************************************** +* QtNetwork * +**************************************************************************** + + - QSslCertificate: + * Fix a potential heap buffer overflow when parsing certificates + - QTBUG-81762: Fix SSL symbol resolving for OPENSSL_NO_NEXPROTONEG + +**************************************************************************** +* Platform specific changes * +**************************************************************************** + + - macOS: + * QTBUG-82986: Improve performance when flushing sublayers + * QTBUG-79139: Avoid repainting OpenGL layers when resizing the window + + - xcb/X11: + * QTBUG-76147, QTBUG-76354, QTBUG-68864: Fix handling of minimized state + + - QNX: + * QTBUG-81701: QLockFile: Disable flock() on QNX + + + -- cgit v1.2.3 From 08d6cb7673aa51bc0532d71db4134f4912e14769 Mon Sep 17 00:00:00 2001 From: Mike Achtelik Date: Wed, 22 Jan 2020 13:17:20 +0100 Subject: Handle exceptions when accessing android clipboard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In some circumstances android throws an exception or returns null, when trying to access the clipboard. Fixes: QTBUG-80689 Change-Id: I92c134e2a002fc648ff966e15a19eb3307c428a1 Reviewed-by: BogDan Vatra (cherry picked from commit 287b570ad5c00eb491f86eab0c4b8d3f6d96f666) Reviewed-by: Tor Arne Vestbø --- .../src/org/qtproject/qt5/android/QtNative.java | 128 +++++++++++++-------- 1 file changed, 78 insertions(+), 50 deletions(-) diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java index 1d2b70ab5f..a13c233712 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java @@ -724,54 +724,66 @@ public class QtNative public static boolean hasClipboardText() { - if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { - ClipData primaryClip = m_clipboardManager.getPrimaryClip(); - for (int i = 0; i < primaryClip.getItemCount(); ++i) - if (primaryClip.getItemAt(i).getText() != null) - return true; + try { + if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { + ClipData primaryClip = m_clipboardManager.getPrimaryClip(); + for (int i = 0; i < primaryClip.getItemCount(); ++i) + if (primaryClip.getItemAt(i).getText() != null) + return true; + } + } catch (Exception e) { + Log.e(QtTAG, "Failed to get clipboard data", e); } return false; } private static String getClipboardText() { - if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { - ClipData primaryClip = m_clipboardManager.getPrimaryClip(); - for (int i = 0; i < primaryClip.getItemCount(); ++i) - if (primaryClip.getItemAt(i).getText() != null) - return primaryClip.getItemAt(i).getText().toString(); + try { + if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { + ClipData primaryClip = m_clipboardManager.getPrimaryClip(); + for (int i = 0; i < primaryClip.getItemCount(); ++i) + if (primaryClip.getItemAt(i).getText() != null) + return primaryClip.getItemAt(i).getText().toString(); + } + } catch (Exception e) { + Log.e(QtTAG, "Failed to get clipboard data", e); } return ""; } private static void updatePrimaryClip(ClipData clipData) { - if (m_usePrimaryClip) { - ClipData clip = m_clipboardManager.getPrimaryClip(); - if (Build.VERSION.SDK_INT >= 26) { - if (m_addItemMethod == null) { - Class[] cArg = new Class[2]; - cArg[0] = ContentResolver.class; - cArg[1] = ClipData.Item.class; + try { + if (m_usePrimaryClip) { + ClipData clip = m_clipboardManager.getPrimaryClip(); + if (Build.VERSION.SDK_INT >= 26) { + if (m_addItemMethod == null) { + Class[] cArg = new Class[2]; + cArg[0] = ContentResolver.class; + cArg[1] = ClipData.Item.class; + try { + m_addItemMethod = m_clipboardManager.getClass().getMethod("addItem", cArg); + } catch (Exception e) { + } + } + } + if (m_addItemMethod != null) { try { - m_addItemMethod = m_clipboardManager.getClass().getMethod("addItem", cArg); + m_addItemMethod.invoke(m_activity.getContentResolver(), clipData.getItemAt(0)); } catch (Exception e) { + e.printStackTrace(); } + } else { + clip.addItem(clipData.getItemAt(0)); } - } - if (m_addItemMethod != null) { - try { - m_addItemMethod.invoke(m_activity.getContentResolver(), clipData.getItemAt(0)); - } catch (Exception e) { - e.printStackTrace(); - } + m_clipboardManager.setPrimaryClip(clip); } else { - clip.addItem(clipData.getItemAt(0)); + m_clipboardManager.setPrimaryClip(clipData); + m_usePrimaryClip = true; } - m_clipboardManager.setPrimaryClip(clip); - } else { - m_clipboardManager.setPrimaryClip(clipData); - m_usePrimaryClip = true; + } catch (Exception e) { + Log.e(QtTAG, "Failed to set clipboard data", e); } } @@ -785,22 +797,30 @@ public class QtNative public static boolean hasClipboardHtml() { - if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { - ClipData primaryClip = m_clipboardManager.getPrimaryClip(); - for (int i = 0; i < primaryClip.getItemCount(); ++i) - if (primaryClip.getItemAt(i).getHtmlText() != null) - return true; + try { + if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { + ClipData primaryClip = m_clipboardManager.getPrimaryClip(); + for (int i = 0; i < primaryClip.getItemCount(); ++i) + if (primaryClip.getItemAt(i).getHtmlText() != null) + return true; + } + } catch (Exception e) { + Log.e(QtTAG, "Failed to get clipboard data", e); } return false; } private static String getClipboardHtml() { - if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { - ClipData primaryClip = m_clipboardManager.getPrimaryClip(); - for (int i = 0; i < primaryClip.getItemCount(); ++i) - if (primaryClip.getItemAt(i).getHtmlText() != null) - return primaryClip.getItemAt(i).getHtmlText().toString(); + try { + if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { + ClipData primaryClip = m_clipboardManager.getPrimaryClip(); + for (int i = 0; i < primaryClip.getItemCount(); ++i) + if (primaryClip.getItemAt(i).getHtmlText() != null) + return primaryClip.getItemAt(i).getHtmlText().toString(); + } + } catch (Exception e) { + Log.e(QtTAG, "Failed to get clipboard data", e); } return ""; } @@ -816,11 +836,15 @@ public class QtNative public static boolean hasClipboardUri() { - if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { - ClipData primaryClip = m_clipboardManager.getPrimaryClip(); - for (int i = 0; i < primaryClip.getItemCount(); ++i) - if (primaryClip.getItemAt(i).getUri() != null) - return true; + try { + if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { + ClipData primaryClip = m_clipboardManager.getPrimaryClip(); + for (int i = 0; i < primaryClip.getItemCount(); ++i) + if (primaryClip.getItemAt(i).getUri() != null) + return true; + } + } catch (Exception e) { + Log.e(QtTAG, "Failed to get clipboard data", e); } return false; } @@ -828,11 +852,15 @@ public class QtNative private static String[] getClipboardUris() { ArrayList uris = new ArrayList(); - if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { - ClipData primaryClip = m_clipboardManager.getPrimaryClip(); - for (int i = 0; i < primaryClip.getItemCount(); ++i) - if (primaryClip.getItemAt(i).getUri() != null) - uris.add(primaryClip.getItemAt(i).getUri().toString()); + try { + if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) { + ClipData primaryClip = m_clipboardManager.getPrimaryClip(); + for (int i = 0; i < primaryClip.getItemCount(); ++i) + if (primaryClip.getItemAt(i).getUri() != null) + uris.add(primaryClip.getItemAt(i).getUri().toString()); + } + } catch (Exception e) { + Log.e(QtTAG, "Failed to get clipboard data", e); } String[] strings = new String[uris.size()]; strings = uris.toArray(strings); -- cgit v1.2.3