Merge "Merge remote-tracking branch 'origin/5.13' into dev"
This commit is contained in:
commit
46cb35fccc
@ -3429,9 +3429,9 @@ MakefileGenerator::writePkgConfigFile()
|
||||
t << Qt::endl;
|
||||
|
||||
// requires
|
||||
const QString requires = project->values("QMAKE_PKGCONFIG_REQUIRES").join(' ');
|
||||
if (!requires.isEmpty()) {
|
||||
t << "Requires: " << requires << Qt::endl;
|
||||
const QString requiresString = project->values("QMAKE_PKGCONFIG_REQUIRES").join(' ');
|
||||
if (!requiresString.isEmpty()) {
|
||||
t << "Requires: " << requiresString << Qt::endl;
|
||||
}
|
||||
|
||||
t << Qt::endl;
|
||||
|
@ -433,11 +433,12 @@ public:
|
||||
ProFunctionDef(const ProFunctionDef &o) : m_pro(o.m_pro), m_offset(o.m_offset) { m_pro->ref(); }
|
||||
ProFunctionDef(ProFunctionDef &&other) noexcept
|
||||
: m_pro(other.m_pro), m_offset(other.m_offset) { other.m_pro = nullptr; }
|
||||
~ProFunctionDef() { m_pro->deref(); }
|
||||
~ProFunctionDef() { if (m_pro) m_pro->deref(); }
|
||||
ProFunctionDef &operator=(const ProFunctionDef &o)
|
||||
{
|
||||
if (this != &o) {
|
||||
m_pro->deref();
|
||||
if (m_pro)
|
||||
m_pro->deref();
|
||||
m_pro = o.m_pro;
|
||||
m_pro->ref();
|
||||
m_offset = o.m_offset;
|
||||
|
@ -468,8 +468,18 @@ inline bool QStorageIterator::next()
|
||||
size_t len = strlen(buffer.data());
|
||||
if (len == 0)
|
||||
return false;
|
||||
if (ptr[len - 1] == '\n')
|
||||
ptr[len - 1] = '\0';
|
||||
while (Q_UNLIKELY(ptr[len - 1] != '\n' && !feof(fp))) {
|
||||
// buffer wasn't large enough. Enlarge and try again.
|
||||
// (we're readidng from the kernel, so OOM is unlikely)
|
||||
buffer.resize((buffer.size() + 4096) & ~4095);
|
||||
ptr = buffer.data();
|
||||
if (fgets(ptr + len, buffer.size() - len, fp) == nullptr)
|
||||
return false;
|
||||
|
||||
len += strlen(ptr + len);
|
||||
Q_ASSERT(len < size_t(buffer.size()));
|
||||
}
|
||||
ptr[len - 1] = '\0';
|
||||
|
||||
// parse the line
|
||||
bool ok;
|
||||
|
@ -514,10 +514,9 @@ Q_CONSTRUCTOR_FUNCTION(qt_apple_check_os_version);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
void QMacKeyValueObserver::addObserver()
|
||||
void QMacKeyValueObserver::addObserver(NSKeyValueObservingOptions options)
|
||||
{
|
||||
[object addObserver:observer forKeyPath:keyPath
|
||||
options:NSKeyValueObservingOptionNew context:callback.get()];
|
||||
[object addObserver:observer forKeyPath:keyPath options:options context:callback.get()];
|
||||
}
|
||||
|
||||
void QMacKeyValueObserver::removeObserver() {
|
||||
|
@ -350,8 +350,12 @@ public:
|
||||
QMacKeyValueObserver() {}
|
||||
|
||||
// Note: QMacKeyValueObserver must not outlive the object observed!
|
||||
QMacKeyValueObserver(id object, NSString *keyPath, Callback callback)
|
||||
: object(object), keyPath(keyPath), callback(new Callback(callback)) { addObserver(); }
|
||||
QMacKeyValueObserver(id object, NSString *keyPath, Callback callback,
|
||||
NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew)
|
||||
: object(object), keyPath(keyPath), callback(new Callback(callback))
|
||||
{
|
||||
addObserver(options);
|
||||
}
|
||||
|
||||
QMacKeyValueObserver(const QMacKeyValueObserver &other)
|
||||
: QMacKeyValueObserver(other.object, other.keyPath, *other.callback.get()) {}
|
||||
@ -381,7 +385,7 @@ private:
|
||||
std::swap(first.callback, second.callback);
|
||||
}
|
||||
|
||||
void addObserver();
|
||||
void addObserver(NSKeyValueObservingOptions options);
|
||||
|
||||
id object = nil;
|
||||
NSString *keyPath = nullptr;
|
||||
|
@ -1972,7 +1972,15 @@ inline void QCborStreamReader::preparse()
|
||||
if (lastError() == QCborError::NoError) {
|
||||
type_ = cbor_value_get_type(&d->currentElement);
|
||||
|
||||
if (type_ != CborInvalidType) {
|
||||
if (type_ == CborInvalidType) {
|
||||
// We may have reached the end.
|
||||
if (d->device && d->containerStack.isEmpty()) {
|
||||
d->buffer.clear();
|
||||
if (d->bufferStart)
|
||||
d->device->skip(d->bufferStart);
|
||||
d->bufferStart = 0;
|
||||
}
|
||||
} else {
|
||||
d->lastError = {};
|
||||
// Undo the type mapping that TinyCBOR does (we have an explicit type
|
||||
// for negative integer and we don't have separate types for Boolean,
|
||||
|
@ -2053,6 +2053,23 @@ inline void fetchTransformed_pixelBounds(int max, int l1, int l2, int &v)
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool canUseFastMatrixPath(const qreal cx, const qreal cy, const qsizetype length, const QSpanData *data)
|
||||
{
|
||||
if (Q_UNLIKELY(!data->fast_matrix))
|
||||
return false;
|
||||
|
||||
qreal fx = (data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale;
|
||||
qreal fy = (data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale;
|
||||
qreal minc = std::min(fx, fy);
|
||||
qreal maxc = std::max(fx, fy);
|
||||
fx += std::trunc(data->m11 * fixed_scale) * length;
|
||||
fy += std::trunc(data->m12 * fixed_scale) * length;
|
||||
minc = std::min(minc, std::min(fx, fy));
|
||||
maxc = std::max(maxc, std::max(fx, fy));
|
||||
|
||||
return minc >= std::numeric_limits<int>::min() && maxc <= std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
template<TextureBlendType blendType, QPixelLayout::BPP bpp, typename T>
|
||||
static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *data,
|
||||
int y, int x, int length)
|
||||
@ -2070,7 +2087,7 @@ static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *dat
|
||||
// When templated 'fetch' should be inlined at compile time:
|
||||
const FetchPixelFunc fetch = (bpp == QPixelLayout::BPPNone) ? qFetchPixel[layout->bpp] : FetchPixelFunc(fetchPixel<bpp>);
|
||||
|
||||
if (data->fast_matrix) {
|
||||
if (canUseFastMatrixPath(cx, cy, length, data)) {
|
||||
// The increment pr x in the scanline
|
||||
int fdx = (int)(data->m11 * fixed_scale);
|
||||
int fdy = (int)(data->m12 * fixed_scale);
|
||||
@ -3026,7 +3043,7 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c
|
||||
|
||||
uint *end = buffer + length;
|
||||
uint *b = buffer;
|
||||
if (data->fast_matrix) {
|
||||
if (canUseFastMatrixPath(cx, cy, length, data)) {
|
||||
// The increment pr x in the scanline
|
||||
int fdx = (int)(data->m11 * fixed_scale);
|
||||
int fdy = (int)(data->m12 * fixed_scale);
|
||||
@ -3383,7 +3400,7 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
|
||||
const qreal cx = x + qreal(0.5);
|
||||
const qreal cy = y + qreal(0.5);
|
||||
|
||||
if (data->fast_matrix) {
|
||||
if (canUseFastMatrixPath(cx, cy, length, data)) {
|
||||
// The increment pr x in the scanline
|
||||
int fdx = (int)(data->m11 * fixed_scale);
|
||||
int fdy = (int)(data->m12 * fixed_scale);
|
||||
@ -3570,7 +3587,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint32(QRgba64 *buf
|
||||
QRgba64 *end = buffer + length;
|
||||
QRgba64 *b = buffer;
|
||||
|
||||
if (data->fast_matrix) {
|
||||
if (canUseFastMatrixPath(cx, cy, length, data)) {
|
||||
// The increment pr x in the scanline
|
||||
const int fdx = (int)(data->m11 * fixed_scale);
|
||||
const int fdy = (int)(data->m12 * fixed_scale);
|
||||
@ -3728,7 +3745,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint64(QRgba64 *buf
|
||||
QRgba64 *end = buffer + length;
|
||||
QRgba64 *b = buffer;
|
||||
|
||||
if (data->fast_matrix) {
|
||||
if (canUseFastMatrixPath(cx, cy, length, data)) {
|
||||
// The increment pr x in the scanline
|
||||
const int fdx = (int)(data->m11 * fixed_scale);
|
||||
const int fdy = (int)(data->m12 * fixed_scale);
|
||||
|
@ -6,6 +6,7 @@ SOURCES += main.mm \
|
||||
qcocoatheme.mm \
|
||||
qcocoabackingstore.mm \
|
||||
qcocoawindow.mm \
|
||||
qcocoawindowmanager.mm \
|
||||
qnsview.mm \
|
||||
qnswindow.mm \
|
||||
qnswindowdelegate.mm \
|
||||
@ -41,6 +42,7 @@ HEADERS += qcocoaintegration.h \
|
||||
qcocoatheme.h \
|
||||
qcocoabackingstore.h \
|
||||
qcocoawindow.h \
|
||||
qcocoawindowmanager.h \
|
||||
qnsview.h \
|
||||
qnswindow.h \
|
||||
qnswindowdelegate.h \
|
||||
|
@ -215,25 +215,6 @@ QCocoaIntegration::QCocoaIntegration(const QStringList ¶mList)
|
||||
|
||||
connect(qGuiApp, &QGuiApplication::focusWindowChanged,
|
||||
this, &QCocoaIntegration::focusWindowChanged);
|
||||
|
||||
static auto splashScreenHider = QMacKeyValueObserver(NSApp, @"modalWindow", []{
|
||||
const QWindowList allWindows = QGuiApplication::topLevelWindows();
|
||||
for (QWindow *window : allWindows) {
|
||||
if ((window->flags() & Qt::SplashScreen) == Qt::SplashScreen) {
|
||||
QCocoaWindow *platformWindow = static_cast<QCocoaWindow*>(window->handle());
|
||||
NSWindow *splashWindow = platformWindow->view().window;
|
||||
if (!splashWindow)
|
||||
continue;
|
||||
if (NSApp.modalWindow) {
|
||||
NSInteger originalLevel = splashWindow.level;
|
||||
splashWindow.level = NSNormalWindowLevel;
|
||||
window->setProperty("_q_levelBeforeModalSession", (qlonglong)originalLevel);
|
||||
} else if (NSInteger originalLevel = window->property("_q_levelBeforeModalSession").toLongLong()) {
|
||||
splashWindow.level = originalLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QCocoaIntegration::~QCocoaIntegration()
|
||||
|
@ -118,11 +118,13 @@ class QSystemTrayIconSys
|
||||
public:
|
||||
QSystemTrayIconSys(QCocoaSystemTrayIcon *sys) {
|
||||
item = [[QNSStatusItem alloc] initWithSysTray:sys];
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:item];
|
||||
NSUserNotificationCenter.defaultUserNotificationCenter.delegate = item;
|
||||
}
|
||||
~QSystemTrayIconSys() {
|
||||
[[[item item] view] setHidden: YES];
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:nil];
|
||||
NSUserNotificationCenter *center = NSUserNotificationCenter.defaultUserNotificationCenter;
|
||||
if (center.delegate == item)
|
||||
center.delegate = nil;
|
||||
[item release];
|
||||
}
|
||||
QNSStatusItem *item;
|
||||
@ -277,7 +279,10 @@ void QCocoaSystemTrayIcon::showMessage(const QString &title, const QString &mess
|
||||
notification.contentImage = [nsimage autorelease];
|
||||
}
|
||||
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
|
||||
NSUserNotificationCenter *center = NSUserNotificationCenter.defaultUserNotificationCenter;
|
||||
center.delegate = m_sys->item;
|
||||
[center deliverNotification:notification];
|
||||
[notification release];
|
||||
}
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
@ -454,13 +454,35 @@ NSInteger QCocoaWindow::windowLevel(Qt::WindowFlags flags)
|
||||
if (type == Qt::ToolTip)
|
||||
windowLevel = NSScreenSaverWindowLevel;
|
||||
|
||||
// Any "special" window should be in at least the same level as its parent.
|
||||
if (type != Qt::Window) {
|
||||
const QWindow * const transientParent = window()->transientParent();
|
||||
const QCocoaWindow * const transientParentWindow = transientParent ?
|
||||
static_cast<QCocoaWindow *>(transientParent->handle()) : nullptr;
|
||||
if (transientParentWindow)
|
||||
windowLevel = qMax([transientParentWindow->nativeWindow() level], windowLevel);
|
||||
auto *transientParent = window()->transientParent();
|
||||
if (transientParent && transientParent->handle()) {
|
||||
// We try to keep windows in at least the same window level as
|
||||
// their transient parent. Unfortunately this only works when the
|
||||
// window is created. If the window level changes after that, as
|
||||
// a result of a call to setWindowFlags, or by changing the level
|
||||
// of the native window, we will not pick this up, and the window
|
||||
// will be left behind (or in a different window level than) its
|
||||
// parent. We could KVO-observe the window level of our transient
|
||||
// parent, but that requires us to know when the parent goes away
|
||||
// so that we can unregister the observation before the parent is
|
||||
// dealloced, something we can't do for generic NSWindows. Another
|
||||
// way would be to override [NSWindow setLevel:] and notify child
|
||||
// windows about the change, but that doesn't work for foreign
|
||||
// windows, which can still be transient parents via fromWinId().
|
||||
// One area where this problem is apparent is when AppKit tweaks
|
||||
// the window level of modal windows during application activation
|
||||
// and deactivation. Since we don't pick up on these window level
|
||||
// changes in a generic way, we need to add logic explicitly to
|
||||
// re-evaluate the window level after AppKit has done its tweaks.
|
||||
|
||||
auto *transientCocoaWindow = static_cast<QCocoaWindow *>(transientParent->handle());
|
||||
auto *nsWindow = transientCocoaWindow->nativeWindow();
|
||||
|
||||
// We only upgrade the window level for "special" windows, to work
|
||||
// around Qt Designer parenting the designer windows to the widget
|
||||
// palette window (QTBUG-31779). This should be fixed in designer.
|
||||
if (type != Qt::Window)
|
||||
windowLevel = qMax(windowLevel, nsWindow.level);
|
||||
}
|
||||
|
||||
return windowLevel;
|
||||
|
63
src/plugins/platforms/cocoa/qcocoawindowmanager.h
Normal file
63
src/plugins/platforms/cocoa/qcocoawindowmanager.h
Normal file
@ -0,0 +1,63 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the plugins of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QCOCOAWINDOWMANAGER_H
|
||||
#define QCOCOAWINDOWMANAGER_H
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#include <AppKit/AppKit.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QCocoaWindowManager
|
||||
{
|
||||
public:
|
||||
static QCocoaWindowManager *instance();
|
||||
|
||||
private:
|
||||
QCocoaWindowManager();
|
||||
void initialize();
|
||||
|
||||
void modalSessionChanged();
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QCOCOAWINDOWMANAGER_H
|
108
src/plugins/platforms/cocoa/qcocoawindowmanager.mm
Normal file
108
src/plugins/platforms/cocoa/qcocoawindowmanager.mm
Normal file
@ -0,0 +1,108 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the plugins of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qcocoawindowmanager.h"
|
||||
#include "qcocoawindow.h"
|
||||
|
||||
#include <QtCore/private/qcore_mac_p.h>
|
||||
|
||||
#include <QtGui/qguiapplication.h>
|
||||
#include <QtGui/qwindow.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QCocoaWindowManager *QCocoaWindowManager::instance()
|
||||
{
|
||||
static auto *instance = new QCocoaWindowManager;
|
||||
return instance;
|
||||
}
|
||||
|
||||
QCocoaWindowManager::QCocoaWindowManager()
|
||||
{
|
||||
if (NSApp) {
|
||||
initialize();
|
||||
} else {
|
||||
static auto applicationDidFinishLaunching(QMacNotificationObserver(nil,
|
||||
NSApplicationDidFinishLaunchingNotification, [this] { initialize(); }));
|
||||
}
|
||||
}
|
||||
|
||||
void QCocoaWindowManager::initialize()
|
||||
{
|
||||
Q_ASSERT(NSApp);
|
||||
|
||||
// Whenever the modalWindow property of NSApplication changes we have a new
|
||||
// modal session running. Observing the app modal window instead of our own
|
||||
// event dispatcher sessions allows us to track session started by native
|
||||
// APIs as well. We need to check the initial state as well, in case there
|
||||
// is already a modal session running.
|
||||
static auto modalSessionObserver(QMacKeyValueObserver(
|
||||
NSApp, @"modalWindow", [this] { modalSessionChanged(); },
|
||||
NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew));
|
||||
}
|
||||
|
||||
void QCocoaWindowManager::modalSessionChanged()
|
||||
{
|
||||
// Make sure that no window is overlapping the modal window. This can
|
||||
// happen for e.g. splash screens, which have the NSPopUpMenuWindowLevel.
|
||||
for (auto *window : QGuiApplication::topLevelWindows()) {
|
||||
auto *platformWindow = static_cast<QCocoaWindow*>(window->handle());
|
||||
if (!platformWindow)
|
||||
continue;
|
||||
|
||||
auto naturalWindowLevel = platformWindow->windowLevel(window->flags());
|
||||
if (naturalWindowLevel > NSModalPanelWindowLevel) {
|
||||
NSWindow *nativeWindow = platformWindow->nativeWindow();
|
||||
if (NSApp.modalWindow) {
|
||||
// Lower window to that of the modal windows, but no less
|
||||
nativeWindow.level = NSModalPanelWindowLevel;
|
||||
[nativeWindow orderBack:nil];
|
||||
} else {
|
||||
// Restore window's natural window level, whatever that was
|
||||
nativeWindow.level = naturalWindowLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void initializeWindowManager() { Q_UNUSED(QCocoaWindowManager::instance()); }
|
||||
Q_CONSTRUCTOR_FUNCTION(initializeWindowManager)
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -80,6 +80,11 @@ private Q_SLOTS:
|
||||
void addData_singleElement();
|
||||
void addData_complex_data() { arrays_data(); }
|
||||
void addData_complex();
|
||||
|
||||
void duplicatedData_data() { arrays_data(); }
|
||||
void duplicatedData();
|
||||
void extraData_data() { arrays_data(); }
|
||||
void extraData();
|
||||
};
|
||||
|
||||
#define FOR_CBOR_TYPE(F) \
|
||||
@ -480,6 +485,28 @@ static QString parseOne(QCborStreamReader &reader)
|
||||
return result;
|
||||
}
|
||||
|
||||
static QString parse(QCborStreamReader &reader, const QByteArray &data)
|
||||
{
|
||||
qint64 oldPos = 0;
|
||||
if (QIODevice *dev = reader.device())
|
||||
oldPos = dev->pos();
|
||||
|
||||
QString r = parseOne(reader);
|
||||
if (r.isEmpty())
|
||||
return r;
|
||||
|
||||
if (reader.currentOffset() - oldPos != data.size())
|
||||
r = QString("Number of parsed bytes (%1) not expected (%2)")
|
||||
.arg(reader.currentOffset()).arg(data.size());
|
||||
if (QIODevice *dev = reader.device()) {
|
||||
if (dev->pos() - oldPos != data.size())
|
||||
r = QString("QIODevice not advanced (%1) as expected (%2)")
|
||||
.arg(dev->pos()).arg(data.size());
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
bool parseNonRecursive(QString &result, bool &printingStringChunks, QCborStreamReader &reader)
|
||||
{
|
||||
while (reader.lastError() == QCborError::NoError) {
|
||||
@ -612,13 +639,13 @@ void tst_QCborStreamReader::fixed()
|
||||
}
|
||||
QVERIFY(reader.isValid());
|
||||
QCOMPARE(reader.lastError(), QCborError::NoError);
|
||||
QCOMPARE(parseOne(reader), expected);
|
||||
QCOMPARE(parse(reader, data), expected);
|
||||
|
||||
// verify that we can re-read
|
||||
reader.reset();
|
||||
QVERIFY(reader.isValid());
|
||||
QCOMPARE(reader.lastError(), QCborError::NoError);
|
||||
QCOMPARE(parseOne(reader), expected);
|
||||
QCOMPARE(parse(reader, data), expected);
|
||||
}
|
||||
|
||||
void tst_QCborStreamReader::strings_data()
|
||||
@ -721,7 +748,7 @@ void tst_QCborStreamReader::emptyContainers()
|
||||
QCOMPARE(reader.lastError(), QCborError::NoError);
|
||||
if (reader.isLengthKnown())
|
||||
QCOMPARE(reader.length(), 0U);
|
||||
QCOMPARE(parseOne(reader), expected);
|
||||
QCOMPARE(parse(reader, data), expected);
|
||||
|
||||
// verify that we can re-read
|
||||
reader.reset();
|
||||
@ -729,7 +756,7 @@ void tst_QCborStreamReader::emptyContainers()
|
||||
QCOMPARE(reader.lastError(), QCborError::NoError);
|
||||
if (reader.isLengthKnown())
|
||||
QCOMPARE(reader.length(), 0U);
|
||||
QCOMPARE(parseOne(reader), expected);
|
||||
QCOMPARE(parse(reader, data), expected);
|
||||
}
|
||||
|
||||
void tst_QCborStreamReader::arrays_data()
|
||||
@ -758,7 +785,7 @@ static void checkContainer(int len, const QByteArray &data, const QString &expec
|
||||
QVERIFY(reader.isLengthKnown());
|
||||
QCOMPARE(reader.length(), uint(len));
|
||||
}
|
||||
QCOMPARE(parseOne(reader), expected);
|
||||
QCOMPARE(parse(reader, data), expected);
|
||||
|
||||
// verify that we can re-read
|
||||
reader.reset();
|
||||
@ -768,7 +795,7 @@ static void checkContainer(int len, const QByteArray &data, const QString &expec
|
||||
QVERIFY(reader.isLengthKnown());
|
||||
QCOMPARE(reader.length(), uint(len));
|
||||
}
|
||||
QCOMPARE(parseOne(reader), expected);
|
||||
QCOMPARE(parse(reader, data), expected);
|
||||
}
|
||||
|
||||
void tst_QCborStreamReader::arrays()
|
||||
@ -892,7 +919,7 @@ void tst_QCborStreamReader::validation()
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
reader.setDevice(&buffer);
|
||||
}
|
||||
parseOne(reader);
|
||||
parse(reader, data);
|
||||
QVERIFY(reader.lastError() != QCborError::NoError);
|
||||
|
||||
// next() should fail
|
||||
@ -997,7 +1024,7 @@ void tst_QCborStreamReader::addData_singleElement()
|
||||
reader.addData(data.constData() + i, 1);
|
||||
}
|
||||
|
||||
parseOne(reader);
|
||||
parse(reader, data);
|
||||
QCOMPARE(reader.lastError(), QCborError::EndOfFile);
|
||||
}
|
||||
|
||||
@ -1009,7 +1036,7 @@ void tst_QCborStreamReader::addData_singleElement()
|
||||
reader.addData(data.right(1));
|
||||
}
|
||||
QCOMPARE(reader.lastError(), QCborError::NoError);
|
||||
QCOMPARE(parseOne(reader), expected);
|
||||
QCOMPARE(parse(reader, data), expected);
|
||||
}
|
||||
|
||||
void tst_QCborStreamReader::addData_complex()
|
||||
@ -1085,6 +1112,69 @@ void tst_QCborStreamReader::addData_complex()
|
||||
"{1, [" + expected + ", " + expected + "]}");
|
||||
}
|
||||
|
||||
void tst_QCborStreamReader::duplicatedData()
|
||||
{
|
||||
QFETCH_GLOBAL(bool, useDevice);
|
||||
QFETCH(QByteArray, data);
|
||||
QFETCH(QString, expected);
|
||||
removeIndicators(expected);
|
||||
|
||||
// double the data up
|
||||
QByteArray doubledata = data + data;
|
||||
|
||||
QBuffer buffer(&doubledata);
|
||||
QCborStreamReader reader(doubledata);
|
||||
if (useDevice) {
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
reader.setDevice(&buffer);
|
||||
}
|
||||
QVERIFY(reader.isValid());
|
||||
QCOMPARE(reader.lastError(), QCborError::NoError);
|
||||
QCOMPARE(parse(reader, data), expected); // yes, data
|
||||
|
||||
QVERIFY(reader.currentOffset() < doubledata.size());
|
||||
if (useDevice) {
|
||||
reader.setDevice(&buffer);
|
||||
QVERIFY(reader.isValid());
|
||||
QCOMPARE(reader.lastError(), QCborError::NoError);
|
||||
QCOMPARE(parse(reader, data), expected);
|
||||
QCOMPARE(buffer.pos(), doubledata.size());
|
||||
} else {
|
||||
// there's no reader.setData()
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QCborStreamReader::extraData()
|
||||
{
|
||||
QFETCH_GLOBAL(bool, useDevice);
|
||||
QFETCH(QByteArray, data);
|
||||
QFETCH(QString, expected);
|
||||
removeIndicators(expected);
|
||||
|
||||
QByteArray extension(9, '\0');
|
||||
|
||||
// stress test everything with extra bytes (just one byte changing;
|
||||
// TinyCBOR used to have a bug where the next byte got sometimes read)
|
||||
for (int c = '\0'; c < 0x100; ++c) {
|
||||
extension[0] = c;
|
||||
QByteArray extendeddata = data + extension;
|
||||
|
||||
QBuffer buffer(&extendeddata);
|
||||
QCborStreamReader reader(extendeddata);
|
||||
if (useDevice) {
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
reader.setDevice(&buffer);
|
||||
}
|
||||
QVERIFY(reader.isValid());
|
||||
QCOMPARE(reader.lastError(), QCborError::NoError);
|
||||
QCOMPARE(parse(reader, data), expected); // yes, data
|
||||
|
||||
// if we were a parser, we could parse the next payload
|
||||
if (useDevice)
|
||||
QCOMPARE(buffer.readAll(), extension);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QTEST_MAIN(tst_QCborStreamReader)
|
||||
|
||||
|
@ -91,8 +91,14 @@ private slots:
|
||||
|
||||
void toCbor_data();
|
||||
void toCbor();
|
||||
void toCborStreamWriter_data() { toCbor_data(); }
|
||||
void toCborStreamWriter();
|
||||
void fromCbor_data();
|
||||
void fromCbor();
|
||||
void fromCborStreamReaderByteArray_data() { fromCbor_data(); }
|
||||
void fromCborStreamReaderByteArray();
|
||||
void fromCborStreamReaderIODevice_data() { fromCbor_data(); }
|
||||
void fromCborStreamReaderIODevice();
|
||||
void validation_data();
|
||||
void validation();
|
||||
void toDiagnosticNotation_data();
|
||||
@ -1454,6 +1460,22 @@ void tst_QCborValue::toCbor()
|
||||
"\xa1\x01\xd9\xd9\xf7" + result);
|
||||
}
|
||||
|
||||
void tst_QCborValue::toCborStreamWriter()
|
||||
{
|
||||
QFETCH(QCborValue, v);
|
||||
QFETCH(QByteArray, result);
|
||||
QFETCH(QCborValue::EncodingOptions, options);
|
||||
|
||||
QByteArray output;
|
||||
QBuffer buffer(&output);
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
QCborStreamWriter writer(&buffer);
|
||||
|
||||
v.toCbor(writer, options);
|
||||
QCOMPARE(buffer.pos(), result.size());
|
||||
QCOMPARE(output, result);
|
||||
}
|
||||
|
||||
void tst_QCborValue::fromCbor_data()
|
||||
{
|
||||
addCommonCborData();
|
||||
@ -1484,20 +1506,11 @@ void tst_QCborValue::fromCbor_data()
|
||||
<< raw("\xd8\x25\x51" "\1\2\3\4""\4\3\2\0""\0\0\0\0""\0\0\0\1""\2");
|
||||
}
|
||||
|
||||
void tst_QCborValue::fromCbor()
|
||||
void fromCbor_common(void (*doCheck)(const QCborValue &, const QByteArray &))
|
||||
{
|
||||
QFETCH(QCborValue, v);
|
||||
QFETCH(QByteArray, result);
|
||||
|
||||
auto doCheck = [](const QCborValue &v, const QByteArray &result) {
|
||||
QCborParserError error;
|
||||
QCborValue decoded = QCborValue::fromCbor(result, &error);
|
||||
QVERIFY2(error.error == QCborError(), qPrintable(error.errorString()));
|
||||
QCOMPARE(error.offset, result.size());
|
||||
QVERIFY(decoded == v);
|
||||
QVERIFY(v == decoded);
|
||||
};
|
||||
|
||||
doCheck(v, result);
|
||||
if (QTest::currentTestFailed())
|
||||
return;
|
||||
@ -1548,6 +1561,52 @@ void tst_QCborValue::fromCbor()
|
||||
return;
|
||||
}
|
||||
|
||||
void tst_QCborValue::fromCbor()
|
||||
{
|
||||
auto doCheck = [](const QCborValue &v, const QByteArray &result) {
|
||||
QCborParserError error;
|
||||
QCborValue decoded = QCborValue::fromCbor(result, &error);
|
||||
QVERIFY2(error.error == QCborError(), qPrintable(error.errorString()));
|
||||
QCOMPARE(error.offset, result.size());
|
||||
QVERIFY(decoded == v);
|
||||
QVERIFY(v == decoded);
|
||||
};
|
||||
|
||||
fromCbor_common(doCheck);
|
||||
}
|
||||
|
||||
void tst_QCborValue::fromCborStreamReaderByteArray()
|
||||
{
|
||||
auto doCheck = [](const QCborValue &expected, const QByteArray &data) {
|
||||
QCborStreamReader reader(data);
|
||||
QCborValue decoded = QCborValue::fromCbor(reader);
|
||||
QCOMPARE(reader.lastError(), QCborError());
|
||||
QCOMPARE(reader.currentOffset(), data.size());
|
||||
QVERIFY(decoded == expected);
|
||||
QVERIFY(expected == decoded);
|
||||
};
|
||||
|
||||
fromCbor_common(doCheck);
|
||||
}
|
||||
|
||||
void tst_QCborValue::fromCborStreamReaderIODevice()
|
||||
{
|
||||
auto doCheck = [](const QCborValue &expected, const QByteArray &data) {
|
||||
QBuffer buffer;
|
||||
buffer.setData(data);
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
QCborStreamReader reader(&buffer);
|
||||
QCborValue decoded = QCborValue::fromCbor(reader);
|
||||
QCOMPARE(reader.lastError(), QCborError());
|
||||
QCOMPARE(reader.currentOffset(), data.size());
|
||||
QVERIFY(decoded == expected);
|
||||
QVERIFY(expected == decoded);
|
||||
QCOMPARE(buffer.pos(), reader.currentOffset());
|
||||
};
|
||||
|
||||
fromCbor_common(doCheck);
|
||||
}
|
||||
|
||||
void tst_QCborValue::validation_data()
|
||||
{
|
||||
addValidationColumns();
|
||||
|
@ -46,6 +46,7 @@
|
||||
#endif // NO_SSL
|
||||
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "emulationdetector.h"
|
||||
@ -117,7 +118,7 @@ private:
|
||||
|
||||
quint16 serverPort = 0;
|
||||
QThread *workerThread = nullptr;
|
||||
QNetworkAccessManager manager;
|
||||
std::unique_ptr<QNetworkAccessManager> manager;
|
||||
|
||||
QTestEventLoop eventLoop;
|
||||
|
||||
@ -131,6 +132,10 @@ private:
|
||||
static const Http2::RawSettings defaultServerSettings;
|
||||
};
|
||||
|
||||
#define STOP_ON_FAILURE \
|
||||
if (QTest::currentTestFailed()) \
|
||||
return;
|
||||
|
||||
const Http2::RawSettings tst_Http2::defaultServerSettings{{Http2::Settings::MAX_CONCURRENT_STREAMS_ID, 100}};
|
||||
|
||||
namespace {
|
||||
@ -178,7 +183,7 @@ tst_Http2::~tst_Http2()
|
||||
|
||||
void tst_Http2::init()
|
||||
{
|
||||
manager.clearConnectionCache();
|
||||
manager.reset(new QNetworkAccessManager);
|
||||
}
|
||||
|
||||
void tst_Http2::singleRequest_data()
|
||||
@ -235,13 +240,14 @@ void tst_Http2::singleRequest()
|
||||
QFETCH(const QNetworkRequest::Attribute, h2Attribute);
|
||||
request.setAttribute(h2Attribute, QVariant(true));
|
||||
|
||||
auto reply = manager.get(request);
|
||||
auto reply = manager->get(request);
|
||||
connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished);
|
||||
// Since we're using self-signed certificates,
|
||||
// ignore SSL errors:
|
||||
reply->ignoreSslErrors();
|
||||
|
||||
runEventLoop();
|
||||
STOP_ON_FAILURE
|
||||
|
||||
QVERIFY(nRequests == 0);
|
||||
QVERIFY(prefaceOK);
|
||||
@ -277,6 +283,7 @@ void tst_Http2::multipleRequests()
|
||||
sendRequest(i, priorities[QRandomGenerator::global()->bounded(3)]);
|
||||
|
||||
runEventLoop();
|
||||
STOP_ON_FAILURE
|
||||
|
||||
QVERIFY(nRequests == 0);
|
||||
QVERIFY(prefaceOK);
|
||||
@ -306,7 +313,7 @@ void tst_Http2::flowControlClientSide()
|
||||
params.maxSessionReceiveWindowSize = Http2::defaultSessionWindowSize * 5;
|
||||
params.settingsFrameData[Settings::INITIAL_WINDOW_SIZE_ID] = Http2::defaultSessionWindowSize;
|
||||
// Inform our manager about non-default settings:
|
||||
manager.setProperty(Http2::http2ParametersPropertyName, QVariant::fromValue(params));
|
||||
manager->setProperty(Http2::http2ParametersPropertyName, QVariant::fromValue(params));
|
||||
|
||||
const Http2::RawSettings serverSettings = {{Settings::MAX_CONCURRENT_STREAMS_ID, quint32(3)}};
|
||||
ServerPtr srv(newServer(serverSettings, defaultConnectionType(), params));
|
||||
@ -324,6 +331,7 @@ void tst_Http2::flowControlClientSide()
|
||||
sendRequest(i);
|
||||
|
||||
runEventLoop(120000);
|
||||
STOP_ON_FAILURE
|
||||
|
||||
QVERIFY(nRequests == 0);
|
||||
QVERIFY(prefaceOK);
|
||||
@ -364,6 +372,7 @@ void tst_Http2::flowControlServerSide()
|
||||
sendRequest(i, QNetworkRequest::NormalPriority, payload);
|
||||
|
||||
runEventLoop(120000);
|
||||
STOP_ON_FAILURE
|
||||
|
||||
QVERIFY(nRequests == 0);
|
||||
QVERIFY(prefaceOK);
|
||||
@ -384,7 +393,7 @@ void tst_Http2::pushPromise()
|
||||
Http2::ProtocolParameters params;
|
||||
// Defaults are good, except ENABLE_PUSH:
|
||||
params.settingsFrameData[Settings::ENABLE_PUSH_ID] = 1;
|
||||
manager.setProperty(Http2::http2ParametersPropertyName, QVariant::fromValue(params));
|
||||
manager->setProperty(Http2::http2ParametersPropertyName, QVariant::fromValue(params));
|
||||
|
||||
ServerPtr srv(newServer(defaultServerSettings, defaultConnectionType(), params));
|
||||
srv->enablePushPromise(true, QByteArray("/script.js"));
|
||||
@ -400,12 +409,13 @@ void tst_Http2::pushPromise()
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true));
|
||||
|
||||
auto reply = manager.get(request);
|
||||
auto reply = manager->get(request);
|
||||
connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished);
|
||||
// Since we're using self-signed certificates, ignore SSL errors:
|
||||
reply->ignoreSslErrors();
|
||||
|
||||
runEventLoop();
|
||||
STOP_ON_FAILURE
|
||||
|
||||
QVERIFY(nRequests == 0);
|
||||
QVERIFY(prefaceOK);
|
||||
@ -423,7 +433,7 @@ void tst_Http2::pushPromise()
|
||||
url.setPath("/script.js");
|
||||
QNetworkRequest promisedRequest(url);
|
||||
promisedRequest.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true));
|
||||
reply = manager.get(promisedRequest);
|
||||
reply = manager->get(promisedRequest);
|
||||
connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished);
|
||||
reply->ignoreSslErrors();
|
||||
|
||||
@ -474,7 +484,7 @@ void tst_Http2::goaway()
|
||||
url.setPath(QString("/%1").arg(i));
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true));
|
||||
replies[i] = manager.get(request);
|
||||
replies[i] = manager->get(request);
|
||||
QCOMPARE(replies[i]->error(), QNetworkReply::NoError);
|
||||
void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) =
|
||||
&QNetworkReply::error;
|
||||
@ -484,6 +494,7 @@ void tst_Http2::goaway()
|
||||
}
|
||||
|
||||
runEventLoop(5000 + responseTimeoutMS);
|
||||
STOP_ON_FAILURE
|
||||
|
||||
// No request processed, no 'replyFinished' slot calls:
|
||||
QCOMPARE(nRequests, 0);
|
||||
@ -527,6 +538,7 @@ void tst_Http2::earlyResponse()
|
||||
sendRequest(1, QNetworkRequest::NormalPriority, {1000000, Qt::Uninitialized});
|
||||
|
||||
runEventLoop();
|
||||
STOP_ON_FAILURE
|
||||
|
||||
QVERIFY(nRequests == 0);
|
||||
QVERIFY(prefaceOK);
|
||||
@ -544,7 +556,7 @@ void tst_Http2::clearHTTP2State()
|
||||
windowUpdates = 0;
|
||||
prefaceOK = false;
|
||||
serverGotSettingsACK = false;
|
||||
manager.setProperty(Http2::http2ParametersPropertyName, QVariant());
|
||||
manager->setProperty(Http2::http2ParametersPropertyName, QVariant());
|
||||
}
|
||||
|
||||
void tst_Http2::runEventLoop(int ms)
|
||||
@ -597,9 +609,9 @@ void tst_Http2::sendRequest(int streamNumber,
|
||||
|
||||
QNetworkReply *reply = nullptr;
|
||||
if (payload.size())
|
||||
reply = manager.post(request, payload);
|
||||
reply = manager->post(request, payload);
|
||||
else
|
||||
reply = manager.get(request);
|
||||
reply = manager->get(request);
|
||||
|
||||
reply->ignoreSslErrors();
|
||||
connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished);
|
||||
@ -691,14 +703,29 @@ void tst_Http2::replyFinished()
|
||||
QVERIFY(nRequests);
|
||||
|
||||
if (const auto reply = qobject_cast<QNetworkReply *>(sender())) {
|
||||
if (reply->error() != QNetworkReply::NoError)
|
||||
stopEventLoop();
|
||||
|
||||
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
||||
|
||||
const QVariant http2Used(reply->attribute(QNetworkRequest::HTTP2WasUsedAttribute));
|
||||
if (!http2Used.isValid() || !http2Used.toBool())
|
||||
stopEventLoop();
|
||||
|
||||
QVERIFY(http2Used.isValid());
|
||||
QVERIFY(http2Used.toBool());
|
||||
|
||||
const QVariant spdyUsed(reply->attribute(QNetworkRequest::SpdyWasUsedAttribute));
|
||||
if (!spdyUsed.isValid() || spdyUsed.toBool())
|
||||
stopEventLoop();
|
||||
|
||||
QVERIFY(spdyUsed.isValid());
|
||||
QVERIFY(!spdyUsed.toBool());
|
||||
|
||||
const QVariant code(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute));
|
||||
if (!code.isValid() || !code.canConvert<int>() || code.value<int>() != 200)
|
||||
stopEventLoop();
|
||||
|
||||
QVERIFY(code.isValid());
|
||||
QVERIFY(code.canConvert<int>());
|
||||
QCOMPARE(code.value<int>(), 200);
|
||||
@ -716,6 +743,8 @@ void tst_Http2::replyFinishedWithError()
|
||||
if (const auto reply = qobject_cast<QNetworkReply *>(sender())) {
|
||||
// For now this is a 'generic' code, it just verifies some error was
|
||||
// reported without testing its type.
|
||||
if (reply->error() == QNetworkReply::NoError)
|
||||
stopEventLoop();
|
||||
QVERIFY(reply->error() != QNetworkReply::NoError);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user