Merge "Merge remote-tracking branch 'origin/5.9' into 5.10" into refs/staging/5.10

This commit is contained in:
Liang Qi 2017-10-23 14:21:08 +00:00 committed by The Qt Project
commit ae03f905d6
24 changed files with 407 additions and 106 deletions

View File

@ -4296,6 +4296,7 @@ bool QInternal::activateCallbacks(Callback cb, void **parameters)
/*!
\macro qDebug(const char *message, ...)
\relates <QtGlobal>
\threadsafe
Calls the message handler with the debug message \a message. If no
message handler has been installed, the message is printed to
@ -4332,6 +4333,7 @@ bool QInternal::activateCallbacks(Callback cb, void **parameters)
/*!
\macro qInfo(const char *message, ...)
\relates <QtGlobal>
\threadsafe
\since 5.5
Calls the message handler with the informational message \a message. If no
@ -4369,6 +4371,7 @@ bool QInternal::activateCallbacks(Callback cb, void **parameters)
/*!
\macro qWarning(const char *message, ...)
\relates <QtGlobal>
\threadsafe
Calls the message handler with the warning message \a message. If no
message handler has been installed, the message is printed to
@ -4406,6 +4409,7 @@ bool QInternal::activateCallbacks(Callback cb, void **parameters)
/*!
\macro qCritical(const char *message, ...)
\relates <QtGlobal>
\threadsafe
Calls the message handler with the critical message \a message. If no
message handler has been installed, the message is printed to

View File

@ -595,6 +595,9 @@ QByteArray QFileSystemEngine::id(const QFileSystemEntry &entry)
params.dwSize = sizeof(params);
params.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
params.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
params.dwSecurityQosFlags = SECURITY_ANONYMOUS;
params.lpSecurityAttributes = NULL;
params.hTemplateFile = NULL;
const HANDLE handle =
CreateFile2((const wchar_t*)entry.nativeFilePath().utf16(), 0,
FILE_SHARE_READ, OPEN_EXISTING, &params);

View File

@ -445,6 +445,7 @@ void QLoggingCategory::setFilterRules(const QString &rules)
/*!
\macro qCDebug(category)
\relates QLoggingCategory
\threadsafe
\since 5.2
Returns an output stream for debug messages in the logging category
@ -469,6 +470,7 @@ void QLoggingCategory::setFilterRules(const QString &rules)
/*!
\macro qCDebug(category, const char *message, ...)
\relates QLoggingCategory
\threadsafe
\since 5.3
Logs a debug message \a message in the logging category \a category.
@ -490,6 +492,7 @@ void QLoggingCategory::setFilterRules(const QString &rules)
/*!
\macro qCInfo(category)
\relates QLoggingCategory
\threadsafe
\since 5.5
Returns an output stream for informational messages in the logging category
@ -514,6 +517,7 @@ void QLoggingCategory::setFilterRules(const QString &rules)
/*!
\macro qCInfo(category, const char *message, ...)
\relates QLoggingCategory
\threadsafe
\since 5.5
Logs an informational message \a message in the logging category \a category.
@ -535,6 +539,7 @@ void QLoggingCategory::setFilterRules(const QString &rules)
/*!
\macro qCWarning(category)
\relates QLoggingCategory
\threadsafe
\since 5.2
Returns an output stream for warning messages in the logging category
@ -559,6 +564,7 @@ void QLoggingCategory::setFilterRules(const QString &rules)
/*!
\macro qCWarning(category, const char *message, ...)
\relates QLoggingCategory
\threadsafe
\since 5.3
Logs a warning message \a message in the logging category \a category.
@ -580,6 +586,7 @@ void QLoggingCategory::setFilterRules(const QString &rules)
/*!
\macro qCCritical(category)
\relates QLoggingCategory
\threadsafe
\since 5.2
Returns an output stream for critical messages in the logging category
@ -604,6 +611,7 @@ void QLoggingCategory::setFilterRules(const QString &rules)
/*!
\macro qCCritical(category, const char *message, ...)
\relates QLoggingCategory
\threadsafe
\since 5.3
Logs a critical message \a message in the logging category \a category.

View File

@ -2335,6 +2335,13 @@ QVariant QInputMethodQueryEvent::value(Qt::InputMethodQuery query) const
cursor and touchpad. Qt recognizes these by their names. Otherwise, if the
tablet is configured to use the evdev driver, there will be only one device
and applications may not be able to distinguish the stylus from the eraser.
\section1 Notes for Windows Users
Tablet support currently requires the WACOM windows driver providing the DLL
\c{wintab32.dll} to be installed. It is contained in older packages,
for example \c{pentablet_5.3.5-3.exe}.
*/
/*!

View File

@ -476,6 +476,8 @@ bool QHttpMultiPartIODevice::isSequential() const
bool QHttpMultiPartIODevice::reset()
{
// Reset QIODevice's data
QIODevice::reset();
for (int a = 0; a < multiPart->parts.count(); a++)
if (!multiPart->parts[a].d->reset())
return false;

View File

@ -607,8 +607,14 @@ void QHttpNetworkConnectionChannel::handleStatus()
if (redirectUrl.isValid())
reply->setRedirectUrl(redirectUrl);
if (qobject_cast<QHttpNetworkConnection *>(connection))
if ((statusCode == 307 || statusCode == 308) && !resetUploadData()) {
// Couldn't reset the upload data, which means it will be unable to POST the data -
// this would lead to a long wait until it eventually failed and then retried.
// Instead of doing that we fail here instead, resetUploadData will already have emitted
// a ContentReSendError, so we're done.
} else if (qobject_cast<QHttpNetworkConnection *>(connection)) {
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
}
break;
}
case 401: // auth required

View File

@ -140,7 +140,7 @@ void QNetworkCookieJar::setAllCookies(const QList<QNetworkCookie> &cookieList)
static inline bool isParentPath(const QString &path, const QString &reference)
{
if (path.startsWith(reference)) {
if ((path.isEmpty() && reference == QLatin1String("/")) || path.startsWith(reference)) {
//The cookie-path and the request-path are identical.
if (path.length() == reference.length())
return true;

View File

@ -165,6 +165,16 @@ static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &hea
}
}
#if QT_CONFIG(bearermanagement)
static bool isSessionNeeded(const QUrl &url)
{
// Connections to the local machine does not require a session
QString host = url.host().toLower();
return !QHostAddress(host).isLoopback() && host != QLatin1String("localhost")
&& host != QSysInfo::machineHostName().toLower();
}
#endif // bearer management
QNetworkReplyHttpImpl::QNetworkReplyHttpImpl(QNetworkAccessManager* const manager,
const QNetworkRequest& request,
QNetworkAccessManager::Operation& operation,
@ -1109,10 +1119,15 @@ QNetworkAccessManager::Operation QNetworkReplyHttpImplPrivate::getRedirectOperat
switch (currentOp) {
case QNetworkAccessManager::HeadOperation:
return QNetworkAccessManager::HeadOperation;
case QNetworkAccessManager::PostOperation:
// We MUST keep using POST when being redirected with 307 or 308.
if (statusCode == 307 || statusCode == 308)
return QNetworkAccessManager::PostOperation;
break;
default:
break;
}
// For now, we're always returning GET for anything other than HEAD
// Use GET for everything else.
return QNetworkAccessManager::GetOperation;
}
@ -1187,11 +1202,30 @@ void QNetworkReplyHttpImplPrivate::followRedirect()
{
Q_Q(QNetworkReplyHttpImpl);
rawHeaders.clear();
cookedHeaders.clear();
if (managerPrivate->thread)
managerPrivate->thread->disconnect();
#if QT_CONFIG(bearermanagement)
// If the original request didn't need a session (i.e. it was to localhost)
// then we might not have a session open, to which to redirect, if the
// new URL is remote. When this happens, we need to open the session now:
if (managerPrivate && isSessionNeeded(url)) {
if (auto session = managerPrivate->getNetworkSession()) {
if (session->state() != QNetworkSession::State::Connected || !session->isOpen()) {
startWaitForSession(session);
// Need to set 'request' to the redirectRequest so that when QNAM restarts
// the request after the session starts it will not repeat the previous request.
request = redirectRequest;
// Return now, QNAM will start the request when the session has started.
return;
}
}
}
#endif // bearer management
QMetaObject::invokeMethod(q, "start", Qt::QueuedConnection,
Q_ARG(QNetworkRequest, redirectRequest));
}
@ -1776,10 +1810,8 @@ bool QNetworkReplyHttpImplPrivate::start(const QNetworkRequest &newHttpRequest)
}
// This is not ideal.
const QString host = url.host();
if (host == QLatin1String("localhost") ||
QHostAddress(host).isLoopback()) {
// Don't need an open session for localhost access.
if (!isSessionNeeded(url)) {
// Don't need to check for an open session if we don't need one.
postRequest(newHttpRequest);
return true;
}
@ -1803,6 +1835,34 @@ bool QNetworkReplyHttpImplPrivate::start(const QNetworkRequest &newHttpRequest)
#endif
}
#if QT_CONFIG(bearermanagement)
bool QNetworkReplyHttpImplPrivate::startWaitForSession(QSharedPointer<QNetworkSession> &session)
{
Q_Q(QNetworkReplyHttpImpl);
state = WaitingForSession;
if (session) {
QObject::connect(session.data(), SIGNAL(error(QNetworkSession::SessionError)),
q, SLOT(_q_networkSessionFailed()), Qt::QueuedConnection);
if (!session->isOpen()) {
QVariant isBackground = request.attribute(QNetworkRequest::BackgroundRequestAttribute,
QVariant::fromValue(false));
session->setSessionProperty(QStringLiteral("ConnectInBackground"), isBackground);
session->open();
}
return true;
}
const Qt::ConnectionType connection = synchronous ? Qt::DirectConnection : Qt::QueuedConnection;
qWarning("Backend is waiting for QNetworkSession to connect, but there is none!");
QMetaObject::invokeMethod(q, "_q_error", connection,
Q_ARG(QNetworkReply::NetworkError, QNetworkReply::NetworkSessionFailedError),
Q_ARG(QString, QCoreApplication::translate("QNetworkReply", "Network session error.")));
QMetaObject::invokeMethod(q, "_q_finished", connection);
return false;
}
#endif // QT_CONFIG(bearermanagement)
void QNetworkReplyHttpImplPrivate::_q_startOperation()
{
Q_Q(QNetworkReplyHttpImpl);
@ -1830,24 +1890,8 @@ void QNetworkReplyHttpImplPrivate::_q_startOperation()
// backend failed to start because the session state is not Connected.
// QNetworkAccessManager will call reply->backend->start() again for us when the session
// state changes.
state = WaitingForSession;
if (session) {
QObject::connect(session.data(), SIGNAL(error(QNetworkSession::SessionError)),
q, SLOT(_q_networkSessionFailed()), Qt::QueuedConnection);
if (!session->isOpen()) {
session->setSessionProperty(QStringLiteral("ConnectInBackground"), isBackground);
session->open();
}
} else {
qWarning("Backend is waiting for QNetworkSession to connect, but there is none!");
QMetaObject::invokeMethod(q, "_q_error", synchronous ? Qt::DirectConnection : Qt::QueuedConnection,
Q_ARG(QNetworkReply::NetworkError, QNetworkReply::NetworkSessionFailedError),
Q_ARG(QString, QCoreApplication::translate("QNetworkReply", "Network session error.")));
QMetaObject::invokeMethod(q, "_q_finished", synchronous ? Qt::DirectConnection : Qt::QueuedConnection);
if (!startWaitForSession(session))
return;
}
} else if (session) {
QObject::connect(session.data(), SIGNAL(stateChanged(QNetworkSession::State)),
q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State)),

View File

@ -161,6 +161,10 @@ signals:
class QNetworkReplyHttpImplPrivate: public QNetworkReplyPrivate
{
#if QT_CONFIG(bearermanagement)
bool startWaitForSession(QSharedPointer<QNetworkSession> &session);
#endif
public:
static QHttpNetworkRequest::Priority convert(const QNetworkRequest::Priority& prio);

View File

@ -520,7 +520,7 @@ static inline int fromBase8(const char *s, const char *end)
{
int result = 0;
while (*s && s != end) {
if (*s <= '0' || *s >= '7')
if (*s < '0' || *s > '7')
return 0;
result *= 8;
result += *s - '0';

View File

@ -104,11 +104,11 @@ namespace QtAndroidAccessibility
static jintArray childIdListForAccessibleObject(JNIEnv *env, jobject /*thiz*/, jint objectId)
{
QAccessibleInterface *iface = interfaceFromId(objectId);
if (iface) {
if (iface && iface->isValid()) {
jintArray jArray = env->NewIntArray(jsize(iface->childCount()));
for (int i = 0; i < iface->childCount(); ++i) {
QAccessibleInterface *child = iface->child(i);
if (child) {
if (child && child->isValid()) {
QAccessible::Id ifaceId = QAccessible::uniqueId(child);
jint jid = ifaceId;
env->SetIntArrayRegion(jArray, i, 1, &jid);
@ -123,9 +123,9 @@ namespace QtAndroidAccessibility
static jint parentId(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
{
QAccessibleInterface *iface = interfaceFromId(objectId);
if (iface) {
if (iface && iface->isValid()) {
QAccessibleInterface *parent = iface->parent();
if (parent) {
if (parent && parent->isValid()) {
if (parent->role() == QAccessible::Application)
return -1;
return QAccessible::uniqueId(parent);
@ -151,7 +151,7 @@ namespace QtAndroidAccessibility
static jint hitTest(JNIEnv */*env*/, jobject /*thiz*/, jfloat x, jfloat y)
{
QAccessibleInterface *root = interfaceFromId(-1);
if (root) {
if (root && root->isValid()) {
QPoint pos = QHighDpi::fromNativePixels(QPoint(int(x), int(y)), root->window());
QAccessibleInterface *child = root->childAt(pos.x(), pos.y());
@ -170,7 +170,7 @@ namespace QtAndroidAccessibility
{
// qDebug() << "A11Y: CLICK: " << objectId;
QAccessibleInterface *iface = interfaceFromId(objectId);
if (iface && iface->actionInterface()) {
if (iface && iface->isValid() && iface->actionInterface()) {
if (iface->actionInterface()->actionNames().contains(QAccessibleActionInterface::pressAction()))
iface->actionInterface()->doAction(QAccessibleActionInterface::pressAction());
else
@ -182,13 +182,17 @@ namespace QtAndroidAccessibility
static jboolean scrollForward(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
{
QAccessibleInterface *iface = interfaceFromId(objectId);
return QAccessibleBridgeUtils::performEffectiveAction(iface, QAccessibleActionInterface::increaseAction());
if (iface && iface->isValid())
return QAccessibleBridgeUtils::performEffectiveAction(iface, QAccessibleActionInterface::increaseAction());
return false;
}
static jboolean scrollBackward(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
{
QAccessibleInterface *iface = interfaceFromId(objectId);
return QAccessibleBridgeUtils::performEffectiveAction(iface, QAccessibleActionInterface::decreaseAction());
if (iface && iface->isValid())
return QAccessibleBridgeUtils::performEffectiveAction(iface, QAccessibleActionInterface::decreaseAction());
return false;
}

View File

@ -1,6 +1,6 @@
TARGET = qcocoa
OBJECTIVE_SOURCES += main.mm \
SOURCES += main.mm \
qcocoaintegration.mm \
qcocoascreen.mm \
qcocoatheme.mm \
@ -32,9 +32,8 @@ OBJECTIVE_SOURCES += main.mm \
qcocoasystemtrayicon.mm \
qcocoaintrospection.mm \
qcocoakeymapper.mm \
qcocoamimetypes.mm
SOURCES += messages.cpp
qcocoamimetypes.mm \
messages.cpp
HEADERS += qcocoaintegration.h \
qcocoascreen.h \
@ -70,7 +69,7 @@ HEADERS += qcocoaintegration.h \
qcocoamimetypes.h
qtConfig(opengl.*) {
OBJECTIVE_SOURCES += qcocoaglcontext.mm
SOURCES += qcocoaglcontext.mm
HEADERS += qcocoaglcontext.h
}
@ -89,7 +88,7 @@ CONFIG += no_app_extension_api_only
qtHaveModule(widgets) {
QT_FOR_CONFIG += widgets
OBJECTIVE_SOURCES += \
SOURCES += \
qpaintengine_mac.mm \
qprintengine_mac.mm \
qcocoaprintersupport.mm \

View File

@ -92,7 +92,6 @@
QT_USE_NAMESPACE
@class QT_MANGLE_NAMESPACE(QNSMenu);
@class QT_MANGLE_NAMESPACE(QNSImageView);
@interface QT_MANGLE_NAMESPACE(QNSStatusItem) : NSObject <NSUserNotificationCenterDelegate>
@ -123,16 +122,8 @@ QT_USE_NAMESPACE
-(void)mousePressed:(NSEvent *)mouseEvent button:(Qt::MouseButton)mouseButton;
@end
@interface QT_MANGLE_NAMESPACE(QNSMenu) : NSMenu <NSMenuDelegate> {
QPlatformMenu *qmenu;
}
-(QPlatformMenu*)menu;
-(id)initWithQMenu:(QPlatformMenu*)qmenu;
@end
QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSStatusItem);
QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSImageView);
QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSMenu);
QT_BEGIN_NAMESPACE
class QSystemTrayIconSys
@ -447,26 +438,4 @@ QT_END_NAMESPACE
@end
class QSystemTrayIconQMenu : public QPlatformMenu
{
public:
void doAboutToShow() { emit aboutToShow(); }
private:
QSystemTrayIconQMenu();
};
@implementation QNSMenu
-(id)initWithQMenu:(QPlatformMenu*)qm {
self = [super init];
if (self) {
self->qmenu = qm;
[self setDelegate:self];
}
return self;
}
-(QPlatformMenu*)menu {
return qmenu;
}
@end
#endif // QT_NO_SYSTEMTRAYICON

View File

@ -2,9 +2,10 @@ TARGET = qdirect2d
QT += \
core-private gui-private \
eventdispatcher_support-private accessibility_support-private \
eventdispatcher_support-private \
fontdatabase_support-private theme_support-private
qtConfig(accessibility): QT += accessibility_support-private
qtConfig(vulkan): QT += vulkan_support-private
LIBS += -ldwmapi -ld2d1 -ld3d11 -ldwrite -lVersion -lgdi32

View File

@ -47,7 +47,9 @@
#include "qwindowsmenu.h"
#include "qwindowsmime.h"
#include "qwindowsinputcontext.h"
#include "qwindowstabletsupport.h"
#if QT_CONFIG(tabletevent)
# include "qwindowstabletsupport.h"
#endif
#include "qwindowstheme.h"
#include <private/qguiapplication_p.h>
#ifndef QT_NO_ACCESSIBILITY

View File

@ -352,16 +352,26 @@ bool QWindowsTabletSupport::translateTabletProximityEvent(WPARAM /* wParam */, L
{
PACKET proximityBuffer[1]; // we are only interested in the first packet in this case
const int totalPacks = QWindowsTabletSupport::m_winTab32DLL.wTPacketsGet(m_context, 1, proximityBuffer);
if (!totalPacks)
return false;
if (!LOWORD(lParam)) {
qCDebug(lcQpaTablet) << "leave proximity for device #" << m_currentDevice;
QWindowSystemInterface::handleTabletLeaveProximityEvent(proximityBuffer[0].pkTime,
m_devices.at(m_currentDevice).currentDevice,
m_devices.at(m_currentDevice).currentPointerType,
m_devices.at(m_currentDevice).uniqueId);
if (totalPacks > 0) {
QWindowSystemInterface::handleTabletLeaveProximityEvent(proximityBuffer[0].pkTime,
m_devices.at(m_currentDevice).currentDevice,
m_devices.at(m_currentDevice).currentPointerType,
m_devices.at(m_currentDevice).uniqueId);
} else {
QWindowSystemInterface::handleTabletLeaveProximityEvent(m_devices.at(m_currentDevice).currentDevice,
m_devices.at(m_currentDevice).currentPointerType,
m_devices.at(m_currentDevice).uniqueId);
}
return true;
}
if (!totalPacks)
return false;
const UINT currentCursor = proximityBuffer[0].pkCursor;
UINT physicalCursorId;
QWindowsTabletSupport::m_winTab32DLL.wTInfo(WTI_CURSORS + currentCursor, CSR_PHYSID, &physicalCursorId);

View File

@ -2,9 +2,10 @@ TARGET = qwindows
QT += \
core-private gui-private \
eventdispatcher_support-private accessibility_support-private \
eventdispatcher_support-private \
fontdatabase_support-private theme_support-private
qtConfig(accessibility): QT += accessibility_support-private
qtConfig(vulkan): QT += vulkan_support-private
LIBS += -lgdi32 -ldwmapi

View File

@ -915,7 +915,9 @@ void QXcbWindow::hide()
if (QWindow *childWindow = childWindowAt(enterWindow, cursorPos))
enterWindow = childWindow;
const QPoint localPos = enterWindow->mapFromGlobal(cursorPos);
QWindowSystemInterface::handleEnterEvent(enterWindow, localPos, cursorPos);
QWindowSystemInterface::handleEnterEvent(enterWindow,
localPos * QHighDpiScaling::factor(enterWindow),
nativePos);
}
}
}

View File

@ -289,8 +289,10 @@ static bool correctActionContext(Qt::ShortcutContext context, QAction *a, QWidge
// not the QMenu.) Since we can also reach this code by climbing the menu
// hierarchy (see below), or when the shortcut is not a key-equivalent, we
// need to check whether the QPA menu is actually disabled.
// When there is no QPA menu, there will be no QCocoaMenuDelegate checking
// for the actual shortcuts. We can then fallback to our own logic.
QPlatformMenu *pm = menu->platformMenu();
if (!pm || !pm->isEnabled())
if (pm && !pm->isEnabled())
continue;
#endif
QAction *a = menu->menuAction();

View File

@ -43,3 +43,8 @@ else:!qtConfig(process): SUBDIRS -= tools
SUBDIRS -= dbus
}
}
# QTBUG-63915
boot2qt: {
contains(QT_ARCH, arm64): SUBDIRS -= dbus
}

View File

@ -0,0 +1,3 @@
[qMessagePattern:backtrace depth,separator]
# QTBUG-63915
b2qt 64bit

View File

@ -340,6 +340,17 @@ void tst_QNetworkCookieJar::cookiesForUrl_data()
QTest::newRow("no-match-domain-dot") << allCookies << "http://example.com" << result;
result += cookieDot;
QTest::newRow("match-domain-dot") << allCookies << "http://example.com." << result;
// Root path in cookie, empty url path
allCookies.clear();
QNetworkCookie rootCookie;
rootCookie.setName("a");
rootCookie.setPath("/");
rootCookie.setDomain("qt-project.org");
allCookies += rootCookie;
result.clear();
result += rootCookie;
QTest::newRow("root-path-match") << allCookies << "http://qt-project.org" << result;
}
void tst_QNetworkCookieJar::cookiesForUrl()

View File

@ -490,6 +490,11 @@ private Q_SLOTS:
void ioHttpCookiesDuringRedirect();
void ioHttpRedirect_data();
void ioHttpRedirect();
void ioHttpRedirectFromLocalToRemote();
void ioHttpRedirectPost_data();
void ioHttpRedirectPost();
void ioHttpRedirectMultipartPost_data();
void ioHttpRedirectMultipartPost();
#ifndef QT_NO_SSL
void putWithServerClosingConnectionImmediately();
#endif
@ -516,6 +521,17 @@ bool tst_QNetworkReply::seedCreated = false;
QFAIL(qPrintable(errorMsg)); \
} while (0)
static bool validateRedirectedResponseHeaders(QNetworkReplyPtr reply)
{
// QTBUG-61300: previously we were mixing 'raw' headers from all responses
// along the redirect chain. The simplest test is to check/verify we have
// no 'location' header anymore.
Q_ASSERT(reply.data());
return !reply->hasRawHeader("location")
&& !reply->header(QNetworkRequest::LocationHeader).isValid();
}
#ifndef QT_NO_SSL
static void setupSslServer(QSslSocket* serverSocket)
{
@ -530,7 +546,7 @@ static void setupSslServer(QSslSocket* serverSocket)
}
#endif
// Does not work for POST/PUT!
// Does not work for PUT! Limited support for POST.
class MiniHttpServer: public QTcpServer
{
Q_OBJECT
@ -545,6 +561,10 @@ public:
bool multiple;
int totalConnections;
bool hasContent = false;
int contentRead = 0;
int contentLength = 0;
MiniHttpServer(const QByteArray &data, bool ssl = false, QThread *thread = 0, bool useipv6 = false)
: dataToTransmit(data), doClose(true), doSsl(ssl), ipv6(useipv6),
multiple(false), totalConnections(0)
@ -621,6 +641,18 @@ private:
this, SLOT(slotError(QAbstractSocket::SocketError)));
}
void parseContentLength()
{
int index = receivedData.indexOf("Content-Length:");
index += sizeof("Content-Length:") - 1;
const auto end = std::find(receivedData.cbegin() + index, receivedData.cend(), '\r');
auto num = receivedData.mid(index, std::distance(receivedData.cbegin() + index, end));
bool ok;
contentLength = num.toInt(&ok);
if (!ok)
contentLength = -1;
}
private slots:
#ifndef QT_NO_SSL
void slotSslErrors(const QList<QSslError>& errors)
@ -642,12 +674,20 @@ public slots:
{
Q_ASSERT(!client.isNull());
receivedData += client->readAll();
int doubleEndlPos = receivedData.indexOf("\r\n\r\n");
const int doubleEndlPos = receivedData.indexOf("\r\n\r\n");
if (doubleEndlPos != -1) {
const int endOfHeader = doubleEndlPos + 4;
hasContent = receivedData.startsWith("POST");
if (hasContent && contentLength == 0)
parseContentLength();
contentRead = receivedData.length() - endOfHeader;
if (hasContent && contentRead < contentLength)
return;
// multiple requests incoming. remove the bytes of the current one
if (multiple)
receivedData.remove(0, doubleEndlPos+4);
receivedData.remove(0, endOfHeader);
reply();
}
@ -8106,6 +8146,7 @@ void tst_QNetworkReply::ioHttpSingleRedirect()
// Reply url is set to the redirect url
QCOMPARE(reply->url(), redirectUrl);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QVERIFY(validateRedirectedResponseHeaders(reply));
}
void tst_QNetworkReply::ioHttpChangeMaxRedirects()
@ -8154,6 +8195,7 @@ void tst_QNetworkReply::ioHttpChangeMaxRedirects()
QCOMPARE(redSpy2.count(), 2);
QCOMPARE(reply2->url(), server3Url);
QCOMPARE(reply2->error(), QNetworkReply::NoError);
QVERIFY(validateRedirectedResponseHeaders(reply2));
}
void tst_QNetworkReply::ioHttpRedirectErrors_data()
@ -8167,7 +8209,9 @@ void tst_QNetworkReply::ioHttpRedirectErrors_data()
"location: http://localhost:%1\r\n\r\n");
QTest::newRow("too-many-redirects") << "http://localhost" << tempRedirectReply << QNetworkReply::TooManyRedirectsError;
#if QT_CONFIG(ssl)
QTest::newRow("insecure-redirect") << "https://localhost" << tempRedirectReply << QNetworkReply::InsecureRedirectError;
#endif
QTest::newRow("unknown-redirect") << "http://localhost"<< tempRedirectReply.replace("http", "bad_protocol") << QNetworkReply::ProtocolUnknownError;
}
@ -8178,7 +8222,7 @@ void tst_QNetworkReply::ioHttpRedirectErrors()
QFETCH(QNetworkReply::NetworkError, error);
QUrl localhost(url);
MiniHttpServer server("", localhost.scheme() == "https");
MiniHttpServer server("", localhost.scheme() == QLatin1String("https"));
localhost.setPort(server.serverPort());
@ -8228,11 +8272,13 @@ void tst_QNetworkReply::ioHttpRedirectPolicy_data()
QTest::addColumn<int>("statusCode");
QTest::newRow("manual-nossl") << QNetworkRequest::ManualRedirectPolicy << false << 0 << 307;
QTest::newRow("manual-ssl") << QNetworkRequest::ManualRedirectPolicy << true << 0 << 307;
QTest::newRow("nolesssafe-nossl") << QNetworkRequest::NoLessSafeRedirectPolicy << false << 1 << 200;
QTest::newRow("nolesssafe-ssl") << QNetworkRequest::NoLessSafeRedirectPolicy << true << 1 << 200;
QTest::newRow("same-origin-nossl") << QNetworkRequest::SameOriginRedirectPolicy << false << 1 << 200;
#if QT_CONFIG(ssl)
QTest::newRow("manual-ssl") << QNetworkRequest::ManualRedirectPolicy << true << 0 << 307;
QTest::newRow("nolesssafe-ssl") << QNetworkRequest::NoLessSafeRedirectPolicy << true << 1 << 200;
QTest::newRow("same-origin-ssl") << QNetworkRequest::SameOriginRedirectPolicy << true << 1 << 200;
#endif
}
void tst_QNetworkReply::ioHttpRedirectPolicy()
@ -8240,10 +8286,6 @@ void tst_QNetworkReply::ioHttpRedirectPolicy()
QFETCH(const QNetworkRequest::RedirectPolicy, policy);
QFETCH(const bool, ssl);
#ifdef QT_NO_SSL
if (ssl)
QSKIP("SSL is not supported");
#endif
QFETCH(const int, redirectCount);
QFETCH(const int, statusCode);
@ -8251,11 +8293,7 @@ void tst_QNetworkReply::ioHttpRedirectPolicy()
// Setup HTTP server.
SameOriginRedirector redirectServer("", ssl);
QUrl url(QLatin1String(
#ifndef QT_NO_SSL
ssl ? "https://localhost" :
#endif
"http://localhost"));
QUrl url(QLatin1String(ssl ? "https://localhost" : "http://localhost"));
url.setPort(redirectServer.serverPort());
redirectServer.responses.push_back(httpEmpty200Response);
@ -8278,6 +8316,7 @@ void tst_QNetworkReply::ioHttpRedirectPolicy()
QCOMPARE(finishedSpy.count(), 1);
QCOMPARE(redirectSpy.count(), redirectCount);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), statusCode);
QVERIFY(validateRedirectedResponseHeaders(reply) || statusCode != 200);
}
void tst_QNetworkReply::ioHttpRedirectPolicyErrors_data()
@ -8291,27 +8330,35 @@ void tst_QNetworkReply::ioHttpRedirectPolicyErrors_data()
// 1. NoLessSafeRedirectsPolicy
QTest::newRow("nolesssafe-nossl-nossl-too-many") << QNetworkRequest::NoLessSafeRedirectPolicy
<< false << QString("http://localhost:%1") << 0 << QNetworkReply::TooManyRedirectsError;
#if QT_CONFIG(ssl)
QTest::newRow("nolesssafe-ssl-ssl-too-many") << QNetworkRequest::NoLessSafeRedirectPolicy
<< true << QString("https:/localhost:%1") << 0 << QNetworkReply::TooManyRedirectsError;
QTest::newRow("nolesssafe-ssl-nossl-insecure-redirect") << QNetworkRequest::NoLessSafeRedirectPolicy
<< true << QString("http://localhost:%1") << 50 << QNetworkReply::InsecureRedirectError;
#endif
// 2. SameOriginRedirectsPolicy
QTest::newRow("same-origin-nossl-nossl-too-many") << QNetworkRequest::SameOriginRedirectPolicy
<< false << QString("http://localhost:%1") << 0 << QNetworkReply::TooManyRedirectsError;
#if QT_CONFIG(ssl)
QTest::newRow("same-origin-ssl-ssl-too-many") << QNetworkRequest::SameOriginRedirectPolicy
<< true << QString("https://localhost:%1") << 0 << QNetworkReply::TooManyRedirectsError;
QTest::newRow("same-origin-https-http-wrong-protocol") << QNetworkRequest::SameOriginRedirectPolicy
<< true << QString("http://localhost:%1") << 50 << QNetworkReply::InsecureRedirectError;
#endif
QTest::newRow("same-origin-http-https-wrong-protocol") << QNetworkRequest::SameOriginRedirectPolicy
<< false << QString("https://localhost:%1") << 50 << QNetworkReply::InsecureRedirectError;
QTest::newRow("same-origin-http-http-wrong-host") << QNetworkRequest::SameOriginRedirectPolicy
<< false << QString("http://not-so-localhost:%1") << 50 << QNetworkReply::InsecureRedirectError;
#if QT_CONFIG(ssl)
QTest::newRow("same-origin-https-https-wrong-host") << QNetworkRequest::SameOriginRedirectPolicy
<< true << QString("https://not-so-localhost:%1") << 50 << QNetworkReply::InsecureRedirectError;
#endif
QTest::newRow("same-origin-http-http-wrong-port") << QNetworkRequest::SameOriginRedirectPolicy
<< false << QString("http://localhost/%1") << 50 << QNetworkReply::InsecureRedirectError;
#if QT_CONFIG(ssl)
QTest::newRow("same-origin-https-https-wrong-port") << QNetworkRequest::SameOriginRedirectPolicy
<< true << QString("https://localhost/%1") << 50 << QNetworkReply::InsecureRedirectError;
#endif
}
void tst_QNetworkReply::ioHttpRedirectPolicyErrors()
@ -8325,20 +8372,11 @@ void tst_QNetworkReply::ioHttpRedirectPolicyErrors()
QFETCH(const int, maxRedirects);
QFETCH(const QNetworkReply::NetworkError, expectedError);
#ifdef QT_NO_SSL
if (ssl || location.contains("https"))
QSKIP("SSL required to run this test");
#endif
// Setup the server.
MiniHttpServer server("", ssl);
server.setDataToTransmit(tempRedirectReplyStr().arg(location.arg(server.serverPort())).toLatin1());
QUrl url(QLatin1String(
#ifndef QT_NO_SSL
ssl ? "https://localhost" :
#endif
"http://localhost"));
QUrl url(QLatin1String(ssl ? "https://localhost" : "http://localhost"));
url.setPort(server.serverPort());
QNetworkRequest request(url);
@ -8411,6 +8449,7 @@ void tst_QNetworkReply::ioHttpUserVerifiedRedirect()
waitForFinish(reply);
QCOMPARE(finishedSpy.count(), 1);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), statusCode);
QVERIFY(validateRedirectedResponseHeaders(reply) || statusCode != 200);
}
void tst_QNetworkReply::ioHttpCookiesDuringRedirect()
@ -8438,6 +8477,7 @@ void tst_QNetworkReply::ioHttpCookiesDuringRedirect()
QVERIFY(waitForFinish(reply) == Success);
QVERIFY(target.receivedData.contains("\r\nCookie: hello=world\r\n"));
QVERIFY(validateRedirectedResponseHeaders(reply));
}
void tst_QNetworkReply::ioHttpRedirect_data()
@ -8476,6 +8516,152 @@ void tst_QNetworkReply::ioHttpRedirect()
QCOMPARE(waitForFinish(reply), int(Success));
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QVERIFY(validateRedirectedResponseHeaders(reply));
}
void tst_QNetworkReply::ioHttpRedirectFromLocalToRemote()
{
QUrl targetUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt");
QString redirectReply = tempRedirectReplyStr().arg(targetUrl.toString());
MiniHttpServer redirectServer(redirectReply.toLatin1(), false);
QUrl url("http://localhost/");
url.setPort(redirectServer.serverPort());
QFile reference(testDataDir + "/rfc3252.txt");
QVERIFY(reference.open(QIODevice::ReadOnly));
QNetworkRequest request(url);
auto oldRedirectPolicy = manager.redirectPolicy();
manager.setRedirectPolicy(QNetworkRequest::RedirectPolicy::NoLessSafeRedirectPolicy);
QNetworkReplyPtr reply(manager.get(request));
// Restore previous policy
manager.setRedirectPolicy(oldRedirectPolicy);
QCOMPARE(waitForFinish(reply), int(Success));
QCOMPARE(reply->url(), targetUrl);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), reference.size());
QCOMPARE(reply->readAll(), reference.readAll());
}
void tst_QNetworkReply::ioHttpRedirectPost_data()
{
QTest::addColumn<QString>("status");
QTest::addColumn<QByteArray>("data");
QTest::addColumn<QString>("contentType");
QByteArray data;
data = "hello world";
QTest::addRow("307") << "307 Temporary Redirect" << data << "text/plain";
QString permanentRedirect = "308 Permanent Redirect";
QTest::addRow("308") << permanentRedirect << data << "text/plain";
// Some data from ::putToFile_data
data = "";
QTest::newRow("empty") << permanentRedirect << data << "application/octet-stream";
data = QByteArray("abcd\0\1\2\abcd",12);
QTest::newRow("with-nul") << permanentRedirect << data << "application/octet-stream";
data = QByteArray(4097, '\4');
QTest::newRow("4k+1") << permanentRedirect << data << "application/octet-stream";
data = QByteArray(128*1024+1, '\177');
QTest::newRow("128k+1") << permanentRedirect << data << "application/octet-stream";
data = QByteArray(2*1024*1024+1, '\177');
QTest::newRow("2MB+1") << permanentRedirect << data << "application/octet-stream";
}
void tst_QNetworkReply::ioHttpRedirectPost()
{
QFETCH(QString, status);
QFETCH(QByteArray, data);
QFETCH(QString, contentType);
QUrl targetUrl("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi");
QString redirectReply = QStringLiteral("HTTP/1.1 %1\r\n"
"Content-Type: text/plain\r\n"
"location: %2\r\n"
"\r\n").arg(status, targetUrl.toString());
MiniHttpServer redirectServer(redirectReply.toLatin1());
QUrl url("http://localhost/");
url.setPort(redirectServer.serverPort());
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, contentType);
auto oldRedirectPolicy = manager.redirectPolicy();
manager.setRedirectPolicy(QNetworkRequest::RedirectPolicy::NoLessSafeRedirectPolicy);
QNetworkReplyPtr reply(manager.post(request, data));
// Restore previous policy:
manager.setRedirectPolicy(oldRedirectPolicy);
QCOMPARE(waitForFinish(reply), int(Success));
QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex());
}
void tst_QNetworkReply::ioHttpRedirectMultipartPost_data()
{
postToHttpMultipart_data();
}
void tst_QNetworkReply::ioHttpRedirectMultipartPost()
{
// Note: This code is heavily based on postToHttpMultipart
QFETCH(QUrl, url);
static QSet<QByteArray> boundaries;
QNetworkReplyPtr reply;
QFETCH(QHttpMultiPart *, multiPart);
QFETCH(QByteArray, expectedReplyData);
QFETCH(QByteArray, contentType);
QString redirectReply = tempRedirectReplyStr().arg(url.toString());
MiniHttpServer redirectServer(redirectReply.toLatin1());
QUrl redirectUrl("http://localhost/");
redirectUrl.setPort(redirectServer.serverPort());
QNetworkRequest request(redirectUrl);
// Restore policy when we leave this scope:
struct PolicyRestorer
{
QNetworkAccessManager &qnam;
QNetworkRequest::RedirectPolicy policy;
PolicyRestorer(QNetworkAccessManager &qnam)
: qnam(qnam), policy(qnam.redirectPolicy())
{ qnam.setRedirectPolicy(QNetworkRequest::RedirectPolicy::NoLessSafeRedirectPolicy); }
~PolicyRestorer() { qnam.setRedirectPolicy(policy); }
} policyRestorer(manager);
// hack for testing the setting of the content-type header by hand:
if (contentType == "custom") {
QByteArray contentType("multipart/custom; boundary=\"" + multiPart->boundary() + "\"");
request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
}
QVERIFY2(! boundaries.contains(multiPart->boundary()), "boundary '" + multiPart->boundary() + "' has been created twice");
boundaries.insert(multiPart->boundary());
RUN_REQUEST(runMultipartRequest(request, reply, multiPart, "POST"));
multiPart->deleteLater();
QCOMPARE(reply->url(), url);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 OK
QVERIFY(multiPart->boundary().count() > 20); // check that there is randomness after the "boundary_.oOo._" string
QVERIFY(multiPart->boundary().count() < 70);
QByteArray replyData = reply->readAll();
expectedReplyData.prepend("content type: multipart/" + contentType + "; boundary=\"" + multiPart->boundary() + "\"\n");
// QEXPECT_FAIL("nested", "the server does not understand nested multipart messages", Continue); // see above
QCOMPARE(replyData, expectedReplyData);
}
#ifndef QT_NO_SSL

View File

@ -135,6 +135,7 @@ private slots:
void menuSize_Scrolling_data();
void menuSize_Scrolling();
void tearOffMenuNotDisplayed();
void QTBUG_61039_menu_shortcuts();
protected slots:
void onActivated(QAction*);
@ -1644,5 +1645,32 @@ void tst_QMenu::tearOffMenuNotDisplayed()
QVERIFY(!torn->isVisible());
}
void tst_QMenu::QTBUG_61039_menu_shortcuts()
{
QAction *actionKamen = new QAction("Action Kamen");
actionKamen->setShortcut(QKeySequence(QLatin1String("K")));
QAction *actionJoe = new QAction("Action Joe");
actionJoe->setShortcut(QKeySequence(QLatin1String("Ctrl+J")));
QMenu menu;
menu.addAction(actionKamen);
menu.addAction(actionJoe);
QVERIFY(!menu.platformMenu());
QWidget widget;
widget.addAction(menu.menuAction());
widget.show();
QVERIFY(QTest::qWaitForWindowActive(&widget));
QSignalSpy actionKamenSpy(actionKamen, &QAction::triggered);
QTest::keyClick(&widget, Qt::Key_K);
QTRY_COMPARE(actionKamenSpy.count(), 1);
QSignalSpy actionJoeSpy(actionJoe, &QAction::triggered);
QTest::keyClick(&widget, Qt::Key_J, Qt::ControlModifier);
QTRY_COMPARE(actionJoeSpy.count(), 1);
}
QTEST_MAIN(tst_QMenu)
#include "tst_qmenu.moc"