summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tests/auto/shared/httpreqrep.cpp93
-rw-r--r--tests/auto/shared/httpreqrep.h36
-rw-r--r--tests/auto/shared/httpserver.cpp61
-rw-r--r--tests/auto/shared/httpserver.h46
-rw-r--r--tests/auto/shared/waitforsignal.h90
-rw-r--r--tests/auto/widgets/qwebenginedownloads/BLACKLIST4
-rw-r--r--tests/auto/widgets/qwebenginedownloads/tst_qwebenginedownloads.cpp811
-rw-r--r--tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp36
8 files changed, 619 insertions, 558 deletions
diff --git a/tests/auto/shared/httpreqrep.cpp b/tests/auto/shared/httpreqrep.cpp
index eb2db6890..15a86631c 100644
--- a/tests/auto/shared/httpreqrep.cpp
+++ b/tests/auto/shared/httpreqrep.cpp
@@ -31,11 +31,14 @@ HttpReqRep::HttpReqRep(QTcpSocket *socket, QObject *parent)
: QObject(parent), m_socket(socket)
{
m_socket->setParent(this);
- connect(m_socket, &QIODevice::readyRead, this, &HttpReqRep::handleReadyRead);
+ connect(m_socket, &QTcpSocket::readyRead, this, &HttpReqRep::handleReadyRead);
+ connect(m_socket, &QTcpSocket::disconnected, this, &HttpReqRep::handleDisconnected);
}
void HttpReqRep::sendResponse()
{
+ if (m_state != State::REQUEST_RECEIVED)
+ return;
m_socket->write("HTTP/1.1 ");
m_socket->write(QByteArray::number(m_responseStatusCode));
m_socket->write(" OK?\r\n");
@@ -45,9 +48,21 @@ void HttpReqRep::sendResponse()
m_socket->write(kv.second);
m_socket->write("\r\n");
}
+ m_socket->write("Connection: close\r\n");
m_socket->write("\r\n");
m_socket->write(m_responseBody);
- m_socket->close();
+ m_state = State::DISCONNECTING;
+ m_socket->disconnectFromHost();
+ Q_EMIT responseSent();
+}
+
+void HttpReqRep::close()
+{
+ if (m_state != State::REQUEST_RECEIVED)
+ return;
+ m_state = State::DISCONNECTING;
+ m_socket->disconnectFromHost();
+ Q_EMIT error(QStringLiteral("missing response"));
}
QByteArray HttpReqRep::requestHeader(const QByteArray &key) const
@@ -60,32 +75,60 @@ QByteArray HttpReqRep::requestHeader(const QByteArray &key) const
void HttpReqRep::handleReadyRead()
{
- const auto requestLine = m_socket->readLine();
- const auto requestLineParts = requestLine.split(' ');
- if (requestLineParts.size() != 3 || !requestLineParts[2].toUpper().startsWith("HTTP/")) {
- qWarning("HttpReqRep: invalid request line");
- Q_EMIT readFinished(false);
- return;
- }
-
- decltype(m_requestHeaders) headers;
- for (;;) {
- const auto headerLine = m_socket->readLine();
- if (headerLine == QByteArrayLiteral("\r\n"))
+ while (m_socket->canReadLine()) {
+ switch (m_state) {
+ case State::RECEIVING_REQUEST: {
+ const auto requestLine = m_socket->readLine();
+ const auto requestLineParts = requestLine.split(' ');
+ if (requestLineParts.size() != 3 || !requestLineParts[2].toUpper().startsWith("HTTP/")) {
+ m_state = State::DISCONNECTING;
+ m_socket->disconnectFromHost();
+ Q_EMIT error(QStringLiteral("invalid request line"));
+ return;
+ }
+ m_requestMethod = requestLineParts[0];
+ m_requestPath = requestLineParts[1];
+ m_state = State::RECEIVING_HEADERS;
+ break;
+ }
+ case State::RECEIVING_HEADERS: {
+ const auto headerLine = m_socket->readLine();
+ if (headerLine == QByteArrayLiteral("\r\n")) {
+ m_state = State::REQUEST_RECEIVED;
+ Q_EMIT requestReceived();
+ return;
+ }
+ int colonIndex = headerLine.indexOf(':');
+ if (colonIndex < 0) {
+ m_state = State::DISCONNECTING;
+ m_socket->disconnectFromHost();
+ Q_EMIT error(QStringLiteral("invalid header line"));
+ return;
+ }
+ auto headerKey = headerLine.left(colonIndex).trimmed().toLower();
+ auto headerValue = headerLine.mid(colonIndex + 1).trimmed().toLower();
+ m_requestHeaders.emplace(headerKey, headerValue);
break;
- int colonIndex = headerLine.indexOf(':');
- if (colonIndex < 0) {
- qWarning("HttpReqRep: invalid header line");
- Q_EMIT readFinished(false);
+ }
+ default:
return;
}
- auto headerKey = headerLine.left(colonIndex).trimmed().toLower();
- auto headerValue = headerLine.mid(colonIndex + 1).trimmed().toLower();
- headers.emplace(headerKey, headerValue);
}
+}
- m_requestMethod = requestLineParts[0];
- m_requestPath = requestLineParts[1];
- m_requestHeaders = headers;
- Q_EMIT readFinished(true);
+void HttpReqRep::handleDisconnected()
+{
+ switch (m_state) {
+ case State::RECEIVING_REQUEST:
+ case State::RECEIVING_HEADERS:
+ case State::REQUEST_RECEIVED:
+ Q_EMIT error(QStringLiteral("unexpected disconnect"));
+ break;
+ case State::DISCONNECTING:
+ break;
+ case State::DISCONNECTED:
+ Q_UNREACHABLE();
+ }
+ m_state = State::DISCONNECTED;
+ Q_EMIT closed();
}
diff --git a/tests/auto/shared/httpreqrep.h b/tests/auto/shared/httpreqrep.h
index 4e9f10dff..bee8119eb 100644
--- a/tests/auto/shared/httpreqrep.h
+++ b/tests/auto/shared/httpreqrep.h
@@ -30,6 +30,7 @@
#include <QTcpSocket>
+#include <map>
#include <utility>
// Represents an HTTP request-response exchange.
@@ -37,11 +38,20 @@ class HttpReqRep : public QObject
{
Q_OBJECT
public:
- HttpReqRep(QTcpSocket *socket, QObject *parent = nullptr);
+ explicit HttpReqRep(QTcpSocket *socket, QObject *parent = nullptr);
+
void sendResponse();
+ void close();
+
+ // Request parameters (only valid after requestReceived())
+
QByteArray requestMethod() const { return m_requestMethod; }
QByteArray requestPath() const { return m_requestPath; }
QByteArray requestHeader(const QByteArray &key) const;
+
+ // Response parameters (can be set until sendResponse()/close()).
+
+ int responseStatus() const { return m_responseStatusCode; }
void setResponseStatus(int statusCode)
{
m_responseStatusCode = statusCode;
@@ -50,6 +60,7 @@ public:
{
m_responseHeaders[key.toLower()] = std::move(value);
}
+ QByteArray responseBody() const { return m_responseBody; }
void setResponseBody(QByteArray content)
{
m_responseHeaders["content-length"] = QByteArray::number(content.size());
@@ -57,13 +68,34 @@ public:
}
Q_SIGNALS:
- void readFinished(bool ok);
+ // Emitted when the request has been correctly parsed.
+ void requestReceived();
+ // Emitted on first call to sendResponse().
+ void responseSent();
+ // Emitted when something goes wrong.
+ void error(const QString &error);
+ // Emitted during or some time after sendResponse() or close().
+ void closed();
private Q_SLOTS:
void handleReadyRead();
+ void handleDisconnected();
private:
+ enum class State {
+ // Waiting for first line of request.
+ RECEIVING_REQUEST, // Next: RECEIVING_HEADERS or DISCONNECTING.
+ // Waiting for header lines.
+ RECEIVING_HEADERS, // Next: REQUEST_RECEIVED or DISCONNECTING.
+ // Request parsing succeeded, waiting for sendResponse() or close().
+ REQUEST_RECEIVED, // Next: DISCONNECTING.
+ // Waiting for network.
+ DISCONNECTING, // Next: DISCONNECTED.
+ // Connection is dead.
+ DISCONNECTED, // Next: -
+ };
QTcpSocket *m_socket = nullptr;
+ State m_state = State::RECEIVING_REQUEST;
QByteArray m_requestMethod;
QByteArray m_requestPath;
std::map<QByteArray, QByteArray> m_requestHeaders;
diff --git a/tests/auto/shared/httpserver.cpp b/tests/auto/shared/httpserver.cpp
index 6012379f2..8d14c18ff 100644
--- a/tests/auto/shared/httpserver.cpp
+++ b/tests/auto/shared/httpserver.cpp
@@ -27,44 +27,59 @@
****************************************************************************/
#include "httpserver.h"
-#include "waitforsignal.h"
+#include <QLoggingCategory>
+
+Q_LOGGING_CATEGORY(gHttpServerLog, "HttpServer")
HttpServer::HttpServer(QObject *parent) : QObject(parent)
{
connect(&m_tcpServer, &QTcpServer::newConnection, this, &HttpServer::handleNewConnection);
- if (!m_tcpServer.listen())
- qWarning("HttpServer: listen() failed");
- m_url = QStringLiteral("http://127.0.0.1:") + QString::number(m_tcpServer.serverPort());
}
-QUrl HttpServer::url(const QString &path) const
+bool HttpServer::start()
{
- auto copy = m_url;
- copy.setPath(path);
- return copy;
+ m_error = false;
+
+ if (!m_tcpServer.listen()) {
+ qCWarning(gHttpServerLog).noquote() << m_tcpServer.errorString();
+ return false;
+ }
+
+ m_url.setScheme(QStringLiteral("http"));
+ m_url.setHost(QStringLiteral("127.0.0.1"));
+ m_url.setPort(m_tcpServer.serverPort());
+
+ return true;
}
-void HttpServer::handleNewConnection()
+bool HttpServer::stop()
{
- auto reqRep = new HttpReqRep(m_tcpServer.nextPendingConnection(), this);
- connect(reqRep, &HttpReqRep::readFinished, this, &HttpServer::handleReadFinished);
+ m_tcpServer.close();
+ return !m_error;
}
-void HttpServer::handleReadFinished(bool ok)
+QUrl HttpServer::url(const QString &path) const
{
- auto reqRep = qobject_cast<HttpReqRep *>(sender());
- if (ok)
- Q_EMIT newRequest(reqRep);
- else
- reqRep->deleteLater();
+ auto copy = m_url;
+ copy.setPath(path);
+ return copy;
}
-std::unique_ptr<HttpReqRep> waitForRequest(HttpServer *server)
+void HttpServer::handleNewConnection()
{
- std::unique_ptr<HttpReqRep> result;
- waitForSignal(server, &HttpServer::newRequest, [&](HttpReqRep *rr) {
- rr->setParent(nullptr);
- result.reset(rr);
+ auto rr = new HttpReqRep(m_tcpServer.nextPendingConnection(), this);
+ connect(rr, &HttpReqRep::requestReceived, [this, rr]() {
+ Q_EMIT newRequest(rr);
+ rr->close();
+ });
+ connect(rr, &HttpReqRep::responseSent, [this, rr]() {
+ qCInfo(gHttpServerLog).noquote() << rr->requestMethod() << rr->requestPath()
+ << rr->responseStatus() << rr->responseBody().size();
+ });
+ connect(rr, &HttpReqRep::error, [this, rr](const QString &error) {
+ qCWarning(gHttpServerLog).noquote() << rr->requestMethod() << rr->requestPath()
+ << error;
+ m_error = true;
});
- return result;
+ connect(rr, &HttpReqRep::closed, rr, &QObject::deleteLater);
}
diff --git a/tests/auto/shared/httpserver.h b/tests/auto/shared/httpserver.h
index e45743b7b..ddbab433c 100644
--- a/tests/auto/shared/httpserver.h
+++ b/tests/auto/shared/httpserver.h
@@ -33,29 +33,55 @@
#include <QTcpServer>
#include <QUrl>
-#include <memory>
-
// Listens on a TCP socket and creates HttpReqReps for each connection.
+//
+// Usage:
+//
+// HttpServer server;
+// connect(&server, &HttpServer::newRequest, [](HttpReqRep *rr) {
+// if (rr->requestPath() == "/myPage.html") {
+// rr->setResponseBody("<html><body>Hello, World!</body></html>");
+// rr->sendResponse();
+// }
+// });
+// QVERIFY(server.start());
+// /* do stuff */
+// QVERIFY(server.stop());
+//
+// HttpServer owns the HttpReqRep objects. The signal handler should not store
+// references to HttpReqRep objects.
+//
+// Only if a handler calls sendResponse() will a response be actually sent. This
+// means that multiple handlers can be connected to the signal, with different
+// handlers responsible for different paths.
class HttpServer : public QObject
{
Q_OBJECT
+public:
+ explicit HttpServer(QObject *parent = nullptr);
- QTcpServer m_tcpServer;
- QUrl m_url;
+ // Must be called to start listening.
+ //
+ // Returns true if a TCP port has been successfully bound.
+ Q_REQUIRED_RESULT bool start();
-public:
- HttpServer(QObject *parent = nullptr);
+ // Stops listening and performs final error checks.
+ Q_REQUIRED_RESULT bool stop();
+
+ // Full URL for given relative path
QUrl url(const QString &path = QStringLiteral("/")) const;
Q_SIGNALS:
+ // Emitted after a HTTP request has been successfully parsed.
void newRequest(HttpReqRep *reqRep);
private Q_SLOTS:
void handleNewConnection();
- void handleReadFinished(bool ok);
-};
-// Wait for an HTTP request to be received.
-std::unique_ptr<HttpReqRep> waitForRequest(HttpServer *server);
+private:
+ QTcpServer m_tcpServer;
+ QUrl m_url;
+ bool m_error = false;
+};
#endif // !HTTPSERVER_H
diff --git a/tests/auto/shared/waitforsignal.h b/tests/auto/shared/waitforsignal.h
deleted file mode 100644
index 9b2daf7af..000000000
--- a/tests/auto/shared/waitforsignal.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtWebEngine module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** 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 General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** 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-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef WAITFORSIGNAL_H
-#define WAITFORSIGNAL_H
-
-#include <QObject>
-#include <QTestEventLoop>
-
-#include <utility>
-
-// Implementation details of waitForSignal.
-namespace {
- // Wraps a functor to set a flag and exit from event loop if called.
- template <class SignalHandler>
- struct waitForSignal_SignalHandlerWrapper {
- waitForSignal_SignalHandlerWrapper(SignalHandler &&sh)
- : signalHandler(std::forward<SignalHandler>(sh)) {}
-
- template <class... Args>
- void operator()(Args && ... args) {
- signalHandler(std::forward<Args>(args)...);
- hasBeenCalled = true;
- loop.exitLoop();
- }
-
- SignalHandler &&signalHandler;
- QTestEventLoop loop;
- bool hasBeenCalled = false;
- };
-
- // No-op functor.
- struct waitForSignal_DefaultSignalHandler {
- template <class... Args>
- void operator()(Args && ...) {}
- };
-} // namespace
-
-// Wait for a signal to be emitted.
-//
-// The given functor is called with the signal arguments allowing the arguments
-// to be modified before returning from the signal handler (unlike QSignalSpy).
-template <class Sender, class Signal, class SignalHandler>
-bool waitForSignal(Sender &&sender, Signal &&signal, SignalHandler &&signalHandler, int timeout = 15000)
-{
- waitForSignal_SignalHandlerWrapper<SignalHandler> signalHandlerWrapper(
- std::forward<SignalHandler>(signalHandler));
- auto connection = QObject::connect(
- std::forward<Sender>(sender),
- std::forward<Signal>(signal),
- std::ref(signalHandlerWrapper));
- signalHandlerWrapper.loop.enterLoopMSecs(timeout);
- QObject::disconnect(connection);
- return signalHandlerWrapper.hasBeenCalled;
-}
-
-template <class Sender, class Signal>
-bool waitForSignal(Sender &&sender, Signal &&signal, int timeout = 15000)
-{
- return waitForSignal(std::forward<Sender>(sender),
- std::forward<Signal>(signal),
- waitForSignal_DefaultSignalHandler(),
- timeout);
-}
-
-#endif // !WAITFORSIGNAL_H
diff --git a/tests/auto/widgets/qwebenginedownloads/BLACKLIST b/tests/auto/widgets/qwebenginedownloads/BLACKLIST
deleted file mode 100644
index 1bf8c8b1f..000000000
--- a/tests/auto/widgets/qwebenginedownloads/BLACKLIST
+++ /dev/null
@@ -1,4 +0,0 @@
-[downloadLink]
-osx
-[downloadTwoLinks]
-*
diff --git a/tests/auto/widgets/qwebenginedownloads/tst_qwebenginedownloads.cpp b/tests/auto/widgets/qwebenginedownloads/tst_qwebenginedownloads.cpp
index e8ac9676f..4848038df 100644
--- a/tests/auto/widgets/qwebenginedownloads/tst_qwebenginedownloads.cpp
+++ b/tests/auto/widgets/qwebenginedownloads/tst_qwebenginedownloads.cpp
@@ -36,50 +36,150 @@
#include <QWebEngineProfile>
#include <QWebEngineView>
#include <httpserver.h>
-#include <waitforsignal.h>
-
-static std::unique_ptr<HttpReqRep> waitForFaviconRequest(HttpServer *server)
-{
- auto rr = waitForRequest(server);
- if (!rr ||
- rr->requestMethod() != QByteArrayLiteral("GET") ||
- rr->requestPath() != QByteArrayLiteral("/favicon.ico"))
- return nullptr;
- rr->setResponseStatus(404);
- rr->sendResponse();
- return std::move(rr);
-}
class tst_QWebEngineDownloads : public QObject
{
Q_OBJECT
+
+public:
+ enum UserAction {
+ SaveLink,
+ ClickLink,
+ };
+
+ enum FileAction {
+ FileIsDownloaded,
+ FileIsDisplayed,
+ };
+
private Q_SLOTS:
+ void initTestCase();
+ void init();
+ void cleanup();
+ void cleanupTestCase();
+
void downloadLink_data();
void downloadLink();
+ void downloadTwoLinks_data();
void downloadTwoLinks();
void downloadPage_data();
void downloadPage();
void downloadViaSetUrl();
void downloadFileNot1();
void downloadFileNot2();
-};
-enum DownloadTestUserAction {
- SaveLink,
- Navigate,
+private:
+ void saveLink(QPoint linkPos);
+ void clickLink(QPoint linkPos);
+ void simulateUserAction(QPoint linkPos, UserAction action);
+
+ QWebEngineDownloadItem::DownloadType expectedDownloadType(
+ UserAction userAction,
+ const QByteArray &contentDisposition = QByteArray());
+
+ HttpServer *m_server;
+ QWebEngineProfile *m_profile;
+ QWebEnginePage *m_page;
+ QWebEngineView *m_view;
+ QSet<QWebEngineDownloadItem *> m_downloads;
};
-enum DownloadTestFileAction {
- FileIsDownloaded,
- FileIsDisplayed,
+class ScopedConnection {
+public:
+ ScopedConnection(QMetaObject::Connection connection) : m_connection(std::move(connection)) {}
+ ~ScopedConnection() { QObject::disconnect(m_connection); }
+private:
+ QMetaObject::Connection m_connection;
};
-Q_DECLARE_METATYPE(DownloadTestUserAction);
-Q_DECLARE_METATYPE(DownloadTestFileAction);
+Q_DECLARE_METATYPE(tst_QWebEngineDownloads::UserAction)
+Q_DECLARE_METATYPE(tst_QWebEngineDownloads::FileAction)
+
+void tst_QWebEngineDownloads::initTestCase()
+{
+ m_server = new HttpServer();
+ m_profile = new QWebEngineProfile;
+ m_profile->setHttpCacheType(QWebEngineProfile::NoCache);
+ connect(m_profile, &QWebEngineProfile::downloadRequested, [this](QWebEngineDownloadItem *item) {
+ m_downloads.insert(item);
+ connect(item, &QWebEngineDownloadItem::destroyed, [this, item](){
+ m_downloads.remove(item);
+ });
+ connect(item, &QWebEngineDownloadItem::finished, [this, item](){
+ m_downloads.remove(item);
+ });
+ });
+ m_page = new QWebEnginePage(m_profile);
+ m_view = new QWebEngineView;
+ m_view->setPage(m_page);
+ m_view->show();
+}
+
+void tst_QWebEngineDownloads::init()
+{
+ QVERIFY(m_server->start());
+}
+
+void tst_QWebEngineDownloads::cleanup()
+{
+ QCOMPARE(m_downloads.count(), 0);
+ QVERIFY(m_server->stop());
+}
+
+void tst_QWebEngineDownloads::cleanupTestCase()
+{
+ delete m_view;
+ delete m_page;
+ delete m_profile;
+ delete m_server;
+}
+
+void tst_QWebEngineDownloads::saveLink(QPoint linkPos)
+{
+ // Simulate right-clicking on link and choosing "save link as" from menu.
+ QSignalSpy menuSpy(m_view, &QWebEngineView::customContextMenuRequested);
+ m_view->setContextMenuPolicy(Qt::CustomContextMenu);
+ auto event1 = new QContextMenuEvent(QContextMenuEvent::Mouse, linkPos);
+ auto event2 = new QMouseEvent(QEvent::MouseButtonPress, linkPos, Qt::RightButton, {}, {});
+ auto event3 = new QMouseEvent(QEvent::MouseButtonRelease, linkPos, Qt::RightButton, {}, {});
+ QTRY_VERIFY(m_view->focusWidget());
+ QWidget *renderWidget = m_view->focusWidget();
+ QCoreApplication::postEvent(renderWidget, event1);
+ QCoreApplication::postEvent(renderWidget, event2);
+ QCoreApplication::postEvent(renderWidget, event3);
+ QTRY_COMPARE(menuSpy.count(), 1);
+ m_page->triggerAction(QWebEnginePage::DownloadLinkToDisk);
+}
+
+void tst_QWebEngineDownloads::clickLink(QPoint linkPos)
+{
+ // Simulate left-clicking on link.
+ QTRY_VERIFY(m_view->focusWidget());
+ QWidget *renderWidget = m_view->focusWidget();
+ QTest::mouseClick(renderWidget, Qt::LeftButton, {}, linkPos);
+}
+
+void tst_QWebEngineDownloads::simulateUserAction(QPoint linkPos, UserAction action)
+{
+ switch (action) {
+ case SaveLink: return saveLink(linkPos);
+ case ClickLink: return clickLink(linkPos);
+ }
+}
+
+QWebEngineDownloadItem::DownloadType tst_QWebEngineDownloads::expectedDownloadType(
+ UserAction userAction, const QByteArray &contentDisposition)
+{
+ if (userAction == SaveLink)
+ return QWebEngineDownloadItem::UserRequested;
+ if (contentDisposition == QByteArrayLiteral("attachment"))
+ return QWebEngineDownloadItem::Attachment;
+ return QWebEngineDownloadItem::DownloadAttribute;
+}
void tst_QWebEngineDownloads::downloadLink_data()
{
- QTest::addColumn<DownloadTestUserAction>("userAction");
+ QTest::addColumn<UserAction>("userAction");
QTest::addColumn<bool>("anchorHasDownloadAttribute");
QTest::addColumn<QByteArray>("fileName");
QTest::addColumn<QByteArray>("fileContents");
@@ -87,7 +187,7 @@ void tst_QWebEngineDownloads::downloadLink_data()
QTest::addColumn<QByteArray>("fileMimeTypeDetected");
QTest::addColumn<QByteArray>("fileDisposition");
QTest::addColumn<bool>("fileHasReferer");
- QTest::addColumn<DownloadTestFileAction>("fileAction");
+ QTest::addColumn<FileAction>("fileAction");
QTest::addColumn<QWebEngineDownloadItem::DownloadType>("downloadType");
// SaveLink should always trigger a download, even for empty files.
@@ -100,8 +200,7 @@ void tst_QWebEngineDownloads::downloadLink_data()
/* fileMimeTypeDetected */ << QByteArrayLiteral("")
/* fileDisposition */ << QByteArrayLiteral("")
/* fileHasReferer */ << true
- /* fileAction */ << FileIsDownloaded
- /* downloadType */ << QWebEngineDownloadItem::UserRequested;
+ /* fileAction */ << FileIsDownloaded;
// SaveLink should always trigger a download, also for text files.
QTest::newRow("save link to text file")
@@ -113,8 +212,7 @@ void tst_QWebEngineDownloads::downloadLink_data()
/* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain")
/* fileDisposition */ << QByteArrayLiteral("")
/* fileHasReferer */ << true
- /* fileAction */ << FileIsDownloaded
- /* downloadType */ << QWebEngineDownloadItem::UserRequested;
+ /* fileAction */ << FileIsDownloaded;
// ... adding the "download" attribute should have no effect.
QTest::newRow("save link to text file (attribute)")
@@ -126,8 +224,7 @@ void tst_QWebEngineDownloads::downloadLink_data()
/* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain")
/* fileDisposition */ << QByteArrayLiteral("")
/* fileHasReferer */ << true
- /* fileAction */ << FileIsDownloaded
- /* downloadType */ << QWebEngineDownloadItem::UserRequested;
+ /* fileAction */ << FileIsDownloaded;
// ... adding the "attachment" content disposition should also have no effect.
QTest::newRow("save link to text file (attachment)")
@@ -139,8 +236,7 @@ void tst_QWebEngineDownloads::downloadLink_data()
/* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain")
/* fileDisposition */ << QByteArrayLiteral("attachment")
/* fileHasReferer */ << true
- /* fileAction */ << FileIsDownloaded
- /* downloadType */ << QWebEngineDownloadItem::UserRequested;
+ /* fileAction */ << FileIsDownloaded;
// ... even adding both should have no effect.
QTest::newRow("save link to text file (attribute+attachment)")
@@ -152,12 +248,11 @@ void tst_QWebEngineDownloads::downloadLink_data()
/* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain")
/* fileDisposition */ << QByteArrayLiteral("attachment")
/* fileHasReferer */ << true
- /* fileAction */ << FileIsDownloaded
- /* downloadType */ << QWebEngineDownloadItem::UserRequested;
+ /* fileAction */ << FileIsDownloaded;
// Navigating to an empty file should show an empty page.
QTest::newRow("navigate to empty file")
- /* userAction */ << Navigate
+ /* userAction */ << ClickLink
/* anchorHasDownloadAttribute */ << false
/* fileName */ << QByteArrayLiteral("foo.txt")
/* fileContents */ << QByteArrayLiteral("")
@@ -165,12 +260,11 @@ void tst_QWebEngineDownloads::downloadLink_data()
/* fileMimeTypeDetected */ << QByteArrayLiteral("")
/* fileDisposition */ << QByteArrayLiteral("")
/* fileHasReferer */ << true
- /* fileAction */ << FileIsDisplayed
- /* downloadType */ << QWebEngineDownloadItem::UserRequested;
+ /* fileAction */ << FileIsDisplayed;
// Navigating to a text file should show the text file.
QTest::newRow("navigate to text file")
- /* userAction */ << Navigate
+ /* userAction */ << ClickLink
/* anchorHasDownloadAttribute */ << false
/* fileName */ << QByteArrayLiteral("foo.txt")
/* fileContents */ << QByteArrayLiteral("bar")
@@ -178,12 +272,11 @@ void tst_QWebEngineDownloads::downloadLink_data()
/* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain")
/* fileDisposition */ << QByteArrayLiteral("")
/* fileHasReferer */ << true
- /* fileAction */ << FileIsDisplayed
- /* downloadType */ << QWebEngineDownloadItem::UserRequested;
+ /* fileAction */ << FileIsDisplayed;
// ... unless the link has the "download" attribute: then the file should be downloaded.
QTest::newRow("navigate to text file (attribute)")
- /* userAction */ << Navigate
+ /* userAction */ << ClickLink
/* anchorHasDownloadAttribute */ << true
/* fileName */ << QByteArrayLiteral("foo.txt")
/* fileContents */ << QByteArrayLiteral("bar")
@@ -191,12 +284,11 @@ void tst_QWebEngineDownloads::downloadLink_data()
/* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain")
/* fileDisposition */ << QByteArrayLiteral("")
/* fileHasReferer */ << true
- /* fileAction */ << FileIsDownloaded
- /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute;
+ /* fileAction */ << FileIsDownloaded;
// ... same with the content disposition header save for the download type.
QTest::newRow("navigate to text file (attachment)")
- /* userAction */ << Navigate
+ /* userAction */ << ClickLink
/* anchorHasDownloadAttribute */ << false
/* fileName */ << QByteArrayLiteral("foo.txt")
/* fileContents */ << QByteArrayLiteral("bar")
@@ -204,12 +296,11 @@ void tst_QWebEngineDownloads::downloadLink_data()
/* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain")
/* fileDisposition */ << QByteArrayLiteral("attachment")
/* fileHasReferer */ << true
- /* fileAction */ << FileIsDownloaded
- /* downloadType */ << QWebEngineDownloadItem::Attachment;
+ /* fileAction */ << FileIsDownloaded;
// ... and both.
QTest::newRow("navigate to text file (attribute+attachment)")
- /* userAction */ << Navigate
+ /* userAction */ << ClickLink
/* anchorHasDownloadAttribute */ << true
/* fileName */ << QByteArrayLiteral("foo.txt")
/* fileContents */ << QByteArrayLiteral("bar")
@@ -217,12 +308,11 @@ void tst_QWebEngineDownloads::downloadLink_data()
/* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain")
/* fileDisposition */ << QByteArrayLiteral("attachment")
/* fileHasReferer */ << true
- /* fileAction */ << FileIsDownloaded
- /* downloadType */ << QWebEngineDownloadItem::Attachment;
+ /* fileAction */ << FileIsDownloaded;
// The file's extension has no effect.
QTest::newRow("navigate to supposed zip file")
- /* userAction */ << Navigate
+ /* userAction */ << ClickLink
/* anchorHasDownloadAttribute */ << false
/* fileName */ << QByteArrayLiteral("foo.zip")
/* fileContents */ << QByteArrayLiteral("bar")
@@ -230,12 +320,11 @@ void tst_QWebEngineDownloads::downloadLink_data()
/* fileMimeTypeDetected */ << QByteArrayLiteral("")
/* fileDisposition */ << QByteArrayLiteral("")
/* fileHasReferer */ << true
- /* fileAction */ << FileIsDisplayed
- /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute;
+ /* fileAction */ << FileIsDisplayed;
// ... the file's mime type however does.
QTest::newRow("navigate to supposed zip file (application/zip)")
- /* userAction */ << Navigate
+ /* userAction */ << ClickLink
/* anchorHasDownloadAttribute */ << false
/* fileName */ << QByteArrayLiteral("foo.zip")
/* fileContents */ << QByteArrayLiteral("bar")
@@ -243,12 +332,11 @@ void tst_QWebEngineDownloads::downloadLink_data()
/* fileMimeTypeDetected */ << QByteArrayLiteral("application/zip")
/* fileDisposition */ << QByteArrayLiteral("")
/* fileHasReferer */ << true
- /* fileAction */ << FileIsDownloaded
- /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute;
+ /* fileAction */ << FileIsDownloaded;
// ... but we're not very picky about the exact type.
QTest::newRow("navigate to supposed zip file (application/octet-stream)")
- /* userAction */ << Navigate
+ /* userAction */ << ClickLink
/* anchorHasDownloadAttribute */ << false
/* fileName */ << QByteArrayLiteral("foo.zip")
/* fileContents */ << QByteArrayLiteral("bar")
@@ -256,15 +344,14 @@ void tst_QWebEngineDownloads::downloadLink_data()
/* fileMimeTypeDetected */ << QByteArrayLiteral("application/octet-stream")
/* fileDisposition */ << QByteArrayLiteral("")
/* fileHasReferer */ << true
- /* fileAction */ << FileIsDownloaded
- /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute;
+ /* fileAction */ << FileIsDownloaded;
// empty zip file (consisting only of "end of central directory record")
QByteArray zipFile = QByteArrayLiteral("PK\x05\x06") + QByteArray(18, 0);
// The mime type is guessed automatically if not provided by the server.
QTest::newRow("navigate to actual zip file")
- /* userAction */ << Navigate
+ /* userAction */ << ClickLink
/* anchorHasDownloadAttribute */ << false
/* fileName */ << QByteArrayLiteral("foo.zip")
/* fileContents */ << zipFile
@@ -272,12 +359,11 @@ void tst_QWebEngineDownloads::downloadLink_data()
/* fileMimeTypeDetected */ << QByteArrayLiteral("application/octet-stream")
/* fileDisposition */ << QByteArrayLiteral("")
/* fileHasReferer */ << true
- /* fileAction */ << FileIsDownloaded
- /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute;
+ /* fileAction */ << FileIsDownloaded;
// The mime type is not guessed automatically if provided by the server.
QTest::newRow("navigate to actual zip file (application/zip)")
- /* userAction */ << Navigate
+ /* userAction */ << ClickLink
/* anchorHasDownloadAttribute */ << false
/* fileName */ << QByteArrayLiteral("foo.zip")
/* fileContents */ << zipFile
@@ -285,13 +371,12 @@ void tst_QWebEngineDownloads::downloadLink_data()
/* fileMimeTypeDetected */ << QByteArrayLiteral("application/zip")
/* fileDisposition */ << QByteArrayLiteral("")
/* fileHasReferer */ << true
- /* fileAction */ << FileIsDownloaded
- /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute;
+ /* fileAction */ << FileIsDownloaded;
}
void tst_QWebEngineDownloads::downloadLink()
{
- QFETCH(DownloadTestUserAction, userAction);
+ QFETCH(UserAction, userAction);
QFETCH(bool, anchorHasDownloadAttribute);
QFETCH(QByteArray, fileName);
QFETCH(QByteArray, fileContents);
@@ -299,245 +384,225 @@ void tst_QWebEngineDownloads::downloadLink()
QFETCH(QByteArray, fileMimeTypeDetected);
QFETCH(QByteArray, fileDisposition);
QFETCH(bool, fileHasReferer);
- QFETCH(DownloadTestFileAction, fileAction);
- QFETCH(QWebEngineDownloadItem::DownloadType, downloadType);
-
- HttpServer server;
- QWebEngineProfile profile;
- profile.setHttpCacheType(QWebEngineProfile::NoCache);
- QWebEnginePage page(&profile);
- QWebEngineView view;
- view.setPage(&page);
-
- // 1. Load an HTML page with a link
- //
- // The only variation being whether the <a> element has a "download"
- // attribute or not.
- view.load(server.url());
- view.show();
- auto indexRR = waitForRequest(&server);
- QVERIFY(indexRR);
- QCOMPARE(indexRR->requestMethod(), QByteArrayLiteral("GET"));
- QCOMPARE(indexRR->requestPath(), QByteArrayLiteral("/"));
- indexRR->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/html"));
- QByteArray html;
- html += QByteArrayLiteral("<html><body><a href=\"");
- html += fileName;
- html += QByteArrayLiteral("\" ");
- if (anchorHasDownloadAttribute)
- html += QByteArrayLiteral("download");
- html += QByteArrayLiteral(">Link</a></body></html>");
- indexRR->setResponseBody(html);
- indexRR->sendResponse();
- bool loadOk = false;
- QVERIFY(waitForSignal(&page, &QWebEnginePage::loadFinished, [&](bool ok) { loadOk = ok; }));
- QVERIFY(loadOk);
-
- // 1.1. Ignore favicon request
- auto favIconRR = waitForFaviconRequest(&server);
- QVERIFY(favIconRR);
-
- // 2. Simulate user action
- //
- // - Navigate: user left-clicks on link
- // - SaveLink: user right-clicks on link and chooses "save link as" from menu
- QTRY_VERIFY(view.focusWidget());
- QWidget *renderWidget = view.focusWidget();
- QPoint linkPos(10, 10);
- if (userAction == SaveLink) {
- view.setContextMenuPolicy(Qt::CustomContextMenu);
- auto event1 = new QContextMenuEvent(QContextMenuEvent::Mouse, linkPos);
- auto event2 = new QMouseEvent(QEvent::MouseButtonPress, linkPos, Qt::RightButton, {}, {});
- auto event3 = new QMouseEvent(QEvent::MouseButtonRelease, linkPos, Qt::RightButton, {}, {});
- QCoreApplication::postEvent(renderWidget, event1);
- QCoreApplication::postEvent(renderWidget, event2);
- QCoreApplication::postEvent(renderWidget, event3);
- QVERIFY(waitForSignal(&view, &QWidget::customContextMenuRequested));
- page.triggerAction(QWebEnginePage::DownloadLinkToDisk);
- } else
- QTest::mouseClick(renderWidget, Qt::LeftButton, {}, linkPos);
-
- // 3. Deliver requested file
- //
- // Request/response headers vary.
- auto fileRR = waitForRequest(&server);
- QVERIFY(fileRR);
- QCOMPARE(fileRR->requestMethod(), QByteArrayLiteral("GET"));
- QCOMPARE(fileRR->requestPath(), QByteArrayLiteral("/") + fileName);
- if (fileHasReferer)
- QCOMPARE(fileRR->requestHeader(QByteArrayLiteral("referer")), server.url().toEncoded());
- else
- QCOMPARE(fileRR->requestHeader(QByteArrayLiteral("referer")), QByteArray());
- if (!fileDisposition.isEmpty())
- fileRR->setResponseHeader(QByteArrayLiteral("content-disposition"), fileDisposition);
- if (!fileMimeTypeDeclared.isEmpty())
- fileRR->setResponseHeader(QByteArrayLiteral("content-type"), fileMimeTypeDeclared);
- fileRR->setResponseBody(fileContents);
- fileRR->sendResponse();
-
- // 4a. File is displayed and not downloaded - end test
- if (fileAction == FileIsDisplayed) {
- QVERIFY(waitForSignal(&page, &QWebEnginePage::loadFinished, [&](bool ok) { loadOk = ok; }));
- QVERIFY(loadOk);
- return;
- }
+ QFETCH(FileAction, fileAction);
+
+ // Set up HTTP server
+ int indexRequestCount = 0;
+ int fileRequestCount = 0;
+ QByteArray fileRequestReferer;
+ ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) {
+ if (rr->requestMethod() == "GET" && rr->requestPath() == "/") {
+ indexRequestCount++;
+
+ rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/html"));
+ QByteArray html;
+ html += QByteArrayLiteral("<html><body><a href=\"");
+ html += fileName;
+ html += QByteArrayLiteral("\" ");
+ if (anchorHasDownloadAttribute)
+ html += QByteArrayLiteral("download");
+ html += QByteArrayLiteral(">Link</a></body></html>");
+ rr->setResponseBody(html);
+ rr->sendResponse();
+ } else if (rr->requestMethod() == "GET" && rr->requestPath() == "/" + fileName) {
+ fileRequestCount++;
+ fileRequestReferer = rr->requestHeader(QByteArrayLiteral("referer"));
+
+ if (!fileDisposition.isEmpty())
+ rr->setResponseHeader(QByteArrayLiteral("content-disposition"), fileDisposition);
+ if (!fileMimeTypeDeclared.isEmpty())
+ rr->setResponseHeader(QByteArrayLiteral("content-type"), fileMimeTypeDeclared);
+ rr->setResponseBody(fileContents);
+ rr->sendResponse();
+ } else {
+ rr->setResponseStatus(404);
+ rr->sendResponse();
+ }
+ });
- // 4b. File is downloaded - check QWebEngineDownloadItem attributes
+ // Set up profile and download handler
QTemporaryDir tmpDir;
QVERIFY(tmpDir.isValid());
QByteArray slashFileName = QByteArrayLiteral("/") + fileName;
QString suggestedPath =
QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + slashFileName;
QString downloadPath = tmpDir.path() + slashFileName;
- QUrl downloadUrl = server.url(slashFileName);
- QWebEngineDownloadItem *downloadItem = nullptr;
- QVERIFY(waitForSignal(&profile, &QWebEngineProfile::downloadRequested,
- [&](QWebEngineDownloadItem *item) {
+ QUrl downloadUrl = m_server->url(slashFileName);
+ int acceptedCount = 0;
+ int finishedCount = 0;
+ ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) {
QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadRequested);
QCOMPARE(item->isFinished(), false);
QCOMPARE(item->totalBytes(), -1);
QCOMPARE(item->receivedBytes(), 0);
QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason);
- QCOMPARE(item->type(), downloadType);
+ QCOMPARE(item->type(), expectedDownloadType(userAction, fileDisposition));
QCOMPARE(item->isSavePageDownload(), false);
QCOMPARE(item->mimeType(), QString(fileMimeTypeDetected));
QCOMPARE(item->path(), suggestedPath);
QCOMPARE(item->savePageFormat(), QWebEngineDownloadItem::UnknownSaveFormat);
QCOMPARE(item->url(), downloadUrl);
+
+ connect(item, &QWebEngineDownloadItem::finished, [&, item]() {
+ QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadCompleted);
+ QCOMPARE(item->isFinished(), true);
+ QCOMPARE(item->totalBytes(), fileContents.size());
+ QCOMPARE(item->receivedBytes(), fileContents.size());
+ QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason);
+ QCOMPARE(item->type(), expectedDownloadType(userAction, fileDisposition));
+ QCOMPARE(item->isSavePageDownload(), false);
+ QCOMPARE(item->mimeType(), QString(fileMimeTypeDetected));
+ QCOMPARE(item->path(), downloadPath);
+ QCOMPARE(item->savePageFormat(), QWebEngineDownloadItem::UnknownSaveFormat);
+ QCOMPARE(item->url(), downloadUrl);
+
+ finishedCount++;
+ });
item->setPath(downloadPath);
item->accept();
- downloadItem = item;
- }));
- QVERIFY(downloadItem);
- bool finishOk = false;
- QVERIFY(waitForSignal(downloadItem, &QWebEngineDownloadItem::finished, [&]() {
- auto item = downloadItem;
- QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadCompleted);
- QCOMPARE(item->isFinished(), true);
- QCOMPARE(item->totalBytes(), fileContents.size());
- QCOMPARE(item->receivedBytes(), fileContents.size());
- QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason);
- QCOMPARE(item->type(), downloadType);
- QCOMPARE(item->isSavePageDownload(), false);
- QCOMPARE(item->mimeType(), QString(fileMimeTypeDetected));
- QCOMPARE(item->path(), downloadPath);
- QCOMPARE(item->savePageFormat(), QWebEngineDownloadItem::UnknownSaveFormat);
- QCOMPARE(item->url(), downloadUrl);
- finishOk = true;
- }));
- QVERIFY(finishOk);
- // 5. Check actual file contents
+ acceptedCount++;
+ });
+
+ // Load an HTML page with a link
+ //
+ // The only variation being whether the <a> element has a "download"
+ // attribute or not.
+ QSignalSpy loadSpy(m_page, &QWebEnginePage::loadFinished);
+ m_view->load(m_server->url());
+ QTRY_COMPARE(loadSpy.count(), 1);
+ QCOMPARE(loadSpy.takeFirst().value(0).toBool(), true);
+ QCOMPARE(indexRequestCount, 1);
+
+ simulateUserAction(QPoint(10, 10), userAction);
+
+ // If file is expected to be displayed and not downloaded then end test
+ if (fileAction == FileIsDisplayed) {
+ QTRY_COMPARE(loadSpy.count(), 1);
+ QCOMPARE(loadSpy.takeFirst().value(0).toBool(), true);
+ QCOMPARE(acceptedCount, 0);
+ return;
+ }
+
+ // Otherwise file is downloaded
+ QTRY_COMPARE(acceptedCount, 1);
+ QTRY_COMPARE(finishedCount, 1);
+ QCOMPARE(fileRequestCount, 1);
+ if (fileHasReferer)
+ QCOMPARE(fileRequestReferer, m_server->url().toEncoded());
+ else
+ QCOMPARE(fileRequestReferer, QByteArray());
QFile file(downloadPath);
QVERIFY(file.open(QIODevice::ReadOnly));
QCOMPARE(file.readAll(), fileContents);
}
+void tst_QWebEngineDownloads::downloadTwoLinks_data()
+{
+ QTest::addColumn<UserAction>("action1");
+ QTest::addColumn<UserAction>("action2");
+ QTest::newRow("Save+Save") << SaveLink << SaveLink;
+ QTest::newRow("Save+Click") << SaveLink << ClickLink;
+ QTest::newRow("Click+Save") << ClickLink << SaveLink;
+ QTest::newRow("Click+Click") << ClickLink << ClickLink;
+}
+
void tst_QWebEngineDownloads::downloadTwoLinks()
{
- HttpServer server;
- QSignalSpy requestSpy(&server, &HttpServer::newRequest);
- QList<HttpReqRep*> results;
- connect(&server, &HttpServer::newRequest, [&](HttpReqRep *rr) {
- rr->setParent(nullptr);
- results.append(rr);
+ QFETCH(UserAction, action1);
+ QFETCH(UserAction, action2);
+
+ // Set up HTTP server
+ int file1RequestCount = 0;
+ int file2RequestCount = 0;
+ ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) {
+ if (rr->requestMethod() == "GET" && rr->requestPath() == "/") {
+ rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/html"));
+ rr->setResponseBody(QByteArrayLiteral("<html><body><a href=\"file1\" download>Link1</a><br/><a href=\"file2\">Link2</a></body></html>"));
+ rr->sendResponse();
+ } else if (rr->requestMethod() == "GET" && rr->requestPath() == "/file1") {
+ file1RequestCount++;
+ rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/plain"));
+ rr->setResponseBody(QByteArrayLiteral("file1"));
+ rr->sendResponse();
+ } else if (rr->requestMethod() == "GET" && rr->requestPath() == "/file2") {
+ file2RequestCount++;
+ rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/plain"));
+ rr->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment"));
+ rr->setResponseBody(QByteArrayLiteral("file2"));
+ rr->sendResponse();
+ } else {
+ rr->setResponseStatus(404);
+ rr->sendResponse();
+ }
});
+ // Set up profile and download handler
QTemporaryDir tmpDir;
QVERIFY(tmpDir.isValid());
QString standardDir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
-
- QWebEngineProfile profile;
- profile.setHttpCacheType(QWebEngineProfile::NoCache);
- QList<QPointer<QWebEngineDownloadItem>> downloadItems;
- connect(&profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) {
+ int acceptedCount = 0;
+ int finishedCount = 0;
+ ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) {
QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadRequested);
QCOMPARE(item->isFinished(), false);
QCOMPARE(item->totalBytes(), -1);
QCOMPARE(item->receivedBytes(), 0);
QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason);
+ QCOMPARE(item->savePageFormat(), QWebEngineDownloadItem::UnknownSaveFormat);
+ QCOMPARE(item->mimeType(), QStringLiteral("text/plain"));
QString filePart = QChar('/') + item->url().fileName();
QCOMPARE(item->path(), standardDir + filePart);
+
+ // type() is broken due to race condition in DownloadManagerDelegateQt
+ if (action1 == ClickLink && action2 == ClickLink) {
+ if (filePart == QStringLiteral("/file1"))
+ QCOMPARE(item->type(), expectedDownloadType(action1));
+ else if (filePart == QStringLiteral("/file2"))
+ QCOMPARE(item->type(), expectedDownloadType(action2, QByteArrayLiteral("attachment")));
+ else
+ QFAIL(qPrintable("Unexpected file name: " + filePart));
+ }
+
+ connect(item, &QWebEngineDownloadItem::finished, [&]() {
+ finishedCount++;
+ });
item->setPath(tmpDir.path() + filePart);
item->accept();
- downloadItems.append(item);
+
+ acceptedCount++;
});
- QWebEnginePage page(&profile);
- QWebEngineView view;
- view.setPage(&page);
-
- view.load(server.url());
- view.show();
- QTRY_COMPARE(requestSpy.count(), 1);
- std::unique_ptr<HttpReqRep> indexRR(results.takeFirst());
- QVERIFY(indexRR);
- QCOMPARE(indexRR->requestMethod(), QByteArrayLiteral("GET"));
- QCOMPARE(indexRR->requestPath(), QByteArrayLiteral("/"));
- indexRR->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/html"));
- indexRR->setResponseBody(QByteArrayLiteral("<html><body><a href=\"file1\" download>Link1</a><br/><a href=\"file2\">Link2</a></body></html>"));
- indexRR->sendResponse();
- bool loadOk = false;
- QVERIFY(waitForSignal(&page, &QWebEnginePage::loadFinished, [&](bool ok){ loadOk = ok; }));
- QVERIFY(loadOk);
-
- QTRY_COMPARE(requestSpy.count(), 2);
- std::unique_ptr<HttpReqRep> favIconRR(results.takeFirst());
- QVERIFY(favIconRR);
- favIconRR->setResponseStatus(404);
- favIconRR->sendResponse();
-
- QTRY_VERIFY(view.focusWidget());
- QWidget *renderWidget = view.focusWidget();
- QTest::mouseClick(renderWidget, Qt::LeftButton, {}, QPoint(10, 10));
- QTest::mouseClick(renderWidget, Qt::LeftButton, {}, QPoint(10, 30));
-
- QTRY_VERIFY(requestSpy.count() >= 3);
- std::unique_ptr<HttpReqRep> file1RR(results.takeFirst());
- QVERIFY(file1RR);
- QCOMPARE(file1RR->requestMethod(), QByteArrayLiteral("GET"));
- QTRY_COMPARE(requestSpy.count(), 4);
- std::unique_ptr<HttpReqRep> file2RR(results.takeFirst());
- QVERIFY(file2RR);
- QCOMPARE(file2RR->requestMethod(), QByteArrayLiteral("GET"));
-
- // Handle one request overtaking the other
- if (file1RR->requestPath() == QByteArrayLiteral("/file2"))
- std::swap(file1RR, file2RR);
-
- QCOMPARE(file1RR->requestPath(), QByteArrayLiteral("/file1"));
- QCOMPARE(file2RR->requestPath(), QByteArrayLiteral("/file2"));
-
- file1RR->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/plain"));
- file1RR->setResponseBody(QByteArrayLiteral("file1"));
- file1RR->sendResponse();
- file2RR->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/plain"));
- file2RR->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment"));
- file2RR->setResponseBody(QByteArrayLiteral("file2"));
- file2RR->sendResponse();
-
- // Now wait for downloadRequested signals:
- QTRY_VERIFY(downloadItems.count() >= 2);
- QScopedPointer<QWebEngineDownloadItem> item1(downloadItems.takeFirst());
- QScopedPointer<QWebEngineDownloadItem> item2(downloadItems.takeFirst());
- QVERIFY(item1);
- QVERIFY(item2);
-
- // Handle one request overtaking the other
- if (item1->url().fileName() == QByteArrayLiteral("file2"))
- qSwap(item1, item2);
-
- QCOMPARE(item1->type(), QWebEngineDownloadItem::DownloadAttribute);
- QCOMPARE(item1->mimeType(), QStringLiteral("text/plain"));
- QCOMPARE(item1->savePageFormat(), QWebEngineDownloadItem::UnknownSaveFormat);
- QCOMPARE(item1->url(), server.url(QByteArrayLiteral("/file1")));
- QTRY_COMPARE(item1->state(), QWebEngineDownloadItem::DownloadCompleted);
-
- QCOMPARE(item2->type(), QWebEngineDownloadItem::Attachment);
- QCOMPARE(item2->mimeType(), QStringLiteral("text/plain"));
- QCOMPARE(item2->savePageFormat(), QWebEngineDownloadItem::UnknownSaveFormat);
- QCOMPARE(item2->url(), server.url(QByteArrayLiteral("/file2")));
- QTRY_COMPARE(item2->state(), QWebEngineDownloadItem::DownloadCompleted);
+ QSignalSpy loadSpy(m_page, &QWebEnginePage::loadFinished);
+ m_view->load(m_server->url());
+ QTRY_COMPARE(loadSpy.count(), 1);
+ QCOMPARE(loadSpy.takeFirst().value(0).toBool(), true);
+
+ // Trigger downloads
+ simulateUserAction(QPoint(10, 10), action1);
+ simulateUserAction(QPoint(10, 30), action2);
+
+ // Wait for downloads
+ if (action1 == action2 && action1 == ClickLink) {
+ // With two clicks, sometimes both files get downloaded, sometimes only
+ // the second file, depending on the timing. This is expected and
+ // follows Chromium's behavior. We check here only that the second file
+ // is downloaded correctly (and that we do not crash).
+ //
+ // The first download may be aborted before or after the HTTP request is
+ // made. In the latter case we will have both a file1 and a file2
+ // request, but still only one accepted download.
+ QTRY_COMPARE(file2RequestCount, 1);
+ QTRY_VERIFY(acceptedCount >= 1);
+ QTRY_VERIFY(finishedCount >= 1);
+ QTRY_COMPARE(m_downloads.count(), 0);
+ } else {
+ // Otherwise both files should always be downloaded correctly.
+ QTRY_COMPARE(file1RequestCount, 1);
+ QTRY_COMPARE(file2RequestCount, 1);
+ QTRY_COMPARE(acceptedCount, 2);
+ QTRY_COMPARE(finishedCount, 2);
+ }
}
void tst_QWebEngineDownloads::downloadPage_data()
@@ -552,37 +617,28 @@ void tst_QWebEngineDownloads::downloadPage()
{
QFETCH(QWebEngineDownloadItem::SavePageFormat, savePageFormat);
- HttpServer server;
- QWebEngineProfile profile;
- QWebEnginePage page(&profile);
- QWebEngineView view;
- view.setPage(&page);
-
- view.load(server.url());
- view.show();
- auto indexRR = waitForRequest(&server);
- QVERIFY(indexRR);
- QCOMPARE(indexRR->requestMethod(), QByteArrayLiteral("GET"));
- QCOMPARE(indexRR->requestPath(), QByteArrayLiteral("/"));
- indexRR->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/html"));
- indexRR->setResponseBody(QByteArrayLiteral("<html><body>Hello</body></html>"));
- indexRR->sendResponse();
- bool loadOk = false;
- QVERIFY(waitForSignal(&page, &QWebEnginePage::loadFinished, [&](bool ok){ loadOk = ok; }));
- QVERIFY(loadOk);
-
- auto favIconRR = waitForFaviconRequest(&server);
- QVERIFY(favIconRR);
+ // Set up HTTP server
+ int indexRequestCount = 0;
+ ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) {
+ if (rr->requestMethod() == "GET" && rr->requestPath() == "/") {
+ indexRequestCount++;
+ rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/html"));
+ rr->setResponseBody(QByteArrayLiteral("<html><body>Hello</body></html>"));
+ rr->sendResponse();
+ } else {
+ rr->setResponseStatus(404);
+ rr->sendResponse();
+ }
+ });
+ // Set up profile and download handler
QTemporaryDir tmpDir;
QVERIFY(tmpDir.isValid());
QString downloadPath = tmpDir.path() + QStringLiteral("/test.html");
- page.save(downloadPath, savePageFormat);
-
- QWebEngineDownloadItem *downloadItem = nullptr;
- QUrl downloadUrl = server.url("/");
- QVERIFY(waitForSignal(&profile, &QWebEngineProfile::downloadRequested,
- [&](QWebEngineDownloadItem *item) {
+ QUrl downloadUrl = m_server->url("/");
+ int acceptedCount = 0;
+ int finishedCount = 0;
+ ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) {
QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadInProgress);
QCOMPARE(item->isFinished(), false);
QCOMPARE(item->totalBytes(), -1);
@@ -590,33 +646,43 @@ void tst_QWebEngineDownloads::downloadPage()
QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason);
QCOMPARE(item->type(), QWebEngineDownloadItem::SavePage);
QCOMPARE(item->isSavePageDownload(), true);
- // FIXME why is mimeType always the same?
+ // FIXME(juvaldma): why is mimeType always the same?
QCOMPARE(item->mimeType(), QStringLiteral("application/x-mimearchive"));
QCOMPARE(item->path(), downloadPath);
QCOMPARE(item->savePageFormat(), savePageFormat);
QCOMPARE(item->url(), downloadUrl);
// no need to call item->accept()
- downloadItem = item;
- }));
- QVERIFY(downloadItem);
- bool finishOk = false;
- QVERIFY(waitForSignal(downloadItem, &QWebEngineDownloadItem::finished, [&]() {
- auto item = downloadItem;
- QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadCompleted);
- QCOMPARE(item->isFinished(), true);
- QCOMPARE(item->totalBytes(), item->receivedBytes());
- QVERIFY(item->receivedBytes() > 0);
- QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason);
- QCOMPARE(item->type(), QWebEngineDownloadItem::SavePage);
- QCOMPARE(item->isSavePageDownload(), true);
- QCOMPARE(item->mimeType(), QStringLiteral("application/x-mimearchive"));
- QCOMPARE(item->path(), downloadPath);
- QCOMPARE(item->savePageFormat(), savePageFormat);
- QCOMPARE(item->url(), downloadUrl);
- finishOk = true;
- }));
- QVERIFY(finishOk);
+ connect(item, &QWebEngineDownloadItem::finished, [&, item]() {
+ QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadCompleted);
+ QCOMPARE(item->isFinished(), true);
+ QCOMPARE(item->totalBytes(), item->receivedBytes());
+ QVERIFY(item->receivedBytes() > 0);
+ QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason);
+ QCOMPARE(item->type(), QWebEngineDownloadItem::SavePage);
+ QCOMPARE(item->isSavePageDownload(), true);
+ QCOMPARE(item->mimeType(), QStringLiteral("application/x-mimearchive"));
+ QCOMPARE(item->path(), downloadPath);
+ QCOMPARE(item->savePageFormat(), savePageFormat);
+ QCOMPARE(item->url(), downloadUrl);
+
+ finishedCount++;
+ });
+
+ acceptedCount++;
+ });
+
+ // Load some HTML
+ QSignalSpy loadSpy(m_page, &QWebEnginePage::loadFinished);
+ m_page->load(m_server->url());
+ QTRY_COMPARE(loadSpy.count(), 1);
+ QCOMPARE(loadSpy.takeFirst().value(0).toBool(), true);
+ QCOMPARE(indexRequestCount, 1);
+
+ // Save some HTML
+ m_page->save(downloadPath, savePageFormat);
+ QTRY_COMPARE(acceptedCount, 1);
+ QTRY_COMPARE(finishedCount, 1);
QFile file(downloadPath);
QVERIFY(file.exists());
}
@@ -626,31 +692,33 @@ void tst_QWebEngineDownloads::downloadViaSetUrl()
// Reproduce the scenario described in QTBUG-63388 by triggering downloads
// of the same file multiple times via QWebEnginePage::setUrl
- HttpServer server;
- QWebEngineProfile profile;
- QWebEnginePage page(&profile);
- QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished);
- QSignalSpy urlSpy(&page, &QWebEnginePage::urlChanged);
- const QUrl indexUrl = server.url();
- const QUrl fileUrl = server.url(QByteArrayLiteral("/file"));
-
- // Set up the test scenario by trying to load some unrelated HTML.
-
- page.setUrl(indexUrl);
-
- auto indexRR = waitForRequest(&server);
- QVERIFY(indexRR);
- QCOMPARE(indexRR->requestMethod(), QByteArrayLiteral("GET"));
- QCOMPARE(indexRR->requestPath(), QByteArrayLiteral("/"));
- indexRR->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/html"));
- indexRR->setResponseBody(QByteArrayLiteral("<html><body>Hello</body></html>"));
- indexRR->sendResponse();
+ // Set up HTTP server
+ ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) {
+ if (rr->requestMethod() == "GET" && rr->requestPath() == "/") {
+ rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/html"));
+ rr->setResponseBody(QByteArrayLiteral("<html><body>Hello</body></html>"));
+ rr->sendResponse();
+ } else if (rr->requestMethod() == "GET" && rr->requestPath() == "/file") {
+ rr->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment"));
+ rr->setResponseBody(QByteArrayLiteral("redacted"));
+ rr->sendResponse();
+ } else {
+ rr->setResponseStatus(404);
+ rr->sendResponse();
+ }
+ });
- auto indexFavRR = waitForFaviconRequest(&server);
- QVERIFY(indexFavRR);
- indexRR.reset();
- indexFavRR.reset();
+ // Set up profile and download handler
+ QVector<QUrl> downloadUrls;
+ ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) {
+ downloadUrls.append(item->url());
+ });
+ // Set up the test scenario by trying to load some unrelated HTML.
+ QSignalSpy loadSpy(m_page, &QWebEnginePage::loadFinished);
+ QSignalSpy urlSpy(m_page, &QWebEnginePage::urlChanged);
+ const QUrl indexUrl = m_server->url();
+ m_page->setUrl(indexUrl);
QTRY_COMPARE(loadSpy.count(), 1);
QTRY_COMPARE(urlSpy.count(), 1);
QCOMPARE(loadSpy.takeFirst().value(0).toBool(), true);
@@ -658,37 +726,18 @@ void tst_QWebEngineDownloads::downloadViaSetUrl()
// Download files via setUrl. With QTBUG-63388 after the first iteration the
// downloads would be triggered for indexUrl and not fileUrl.
-
- QVector<QUrl> downloadUrls;
- QObject::connect(&profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) {
- downloadUrls.append(item->url());
- });
-
+ const QUrl fileUrl = m_server->url(QByteArrayLiteral("/file"));
for (int i = 0; i != 3; ++i) {
- page.setUrl(fileUrl);
- QCOMPARE(page.url(), fileUrl);
-
- auto fileRR = waitForRequest(&server);
- QVERIFY(fileRR);
- QCOMPARE(fileRR->requestMethod(), QByteArrayLiteral("GET"));
- QCOMPARE(fileRR->requestPath(), QByteArrayLiteral("/file"));
- fileRR->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment"));
- fileRR->setResponseBody(QByteArrayLiteral("redacted"));
- fileRR->sendResponse();
-
-// Since 63 we no longer get favicon requests here:
-// auto fileFavRR = waitForFaviconRequest(&server);
-// QVERIFY(fileFavRR);
-
+ m_page->setUrl(fileUrl);
+ QCOMPARE(m_page->url(), fileUrl);
QTRY_COMPARE(loadSpy.count(), 1);
QTRY_COMPARE(urlSpy.count(), 2);
QTRY_COMPARE(downloadUrls.count(), 1);
- fileRR.reset();
QCOMPARE(loadSpy.takeFirst().value(0).toBool(), false);
QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), fileUrl);
QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), indexUrl);
QCOMPARE(downloadUrls.takeFirst(), fileUrl);
- QCOMPARE(page.url(), indexUrl);
+ QCOMPARE(m_page->url(), indexUrl);
}
}
@@ -696,26 +745,22 @@ void tst_QWebEngineDownloads::downloadFileNot1()
{
// Trigger file download via download() but don't accept().
- HttpServer server;
- QWebEngineProfile profile;
- QWebEnginePage page(&profile);
- const auto filePath = QByteArrayLiteral("/file");
- const auto fileUrl = server.url(filePath);
-
- page.download(fileUrl);
- auto fileRR = waitForRequest(&server);
- QVERIFY(fileRR);
- QCOMPARE(fileRR->requestMethod(), QByteArrayLiteral("GET"));
- QCOMPARE(fileRR->requestPath(), filePath);
- fileRR->sendResponse();
+ ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) {
+ rr->setResponseStatus(404);
+ rr->sendResponse();
+ });
QPointer<QWebEngineDownloadItem> downloadItem;
- QVERIFY(waitForSignal(&profile, &QWebEngineProfile::downloadRequested,
- [&](QWebEngineDownloadItem *item) {
+ int downloadCount = 0;
+ ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) {
QVERIFY(item);
QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadRequested);
downloadItem = item;
- }));
+ downloadCount++;
+ });
+
+ m_page->download(m_server->url(QByteArrayLiteral("/file")));
+ QTRY_COMPARE(downloadCount, 1);
QVERIFY(!downloadItem);
}
@@ -723,27 +768,23 @@ void tst_QWebEngineDownloads::downloadFileNot2()
{
// Trigger file download via download() but call cancel() instead of accept().
- HttpServer server;
- QWebEngineProfile profile;
- QWebEnginePage page(&profile);
- const auto filePath = QByteArrayLiteral("/file");
- const auto fileUrl = server.url(filePath);
-
- page.download(fileUrl);
- auto fileRR = waitForRequest(&server);
- QVERIFY(fileRR);
- QCOMPARE(fileRR->requestMethod(), QByteArrayLiteral("GET"));
- QCOMPARE(fileRR->requestPath(), filePath);
- fileRR->sendResponse();
+ ScopedConnection sc1 = connect(m_server, &HttpServer::newRequest, [&](HttpReqRep *rr) {
+ rr->setResponseStatus(404);
+ rr->sendResponse();
+ });
QPointer<QWebEngineDownloadItem> downloadItem;
- QVERIFY(waitForSignal(&profile, &QWebEngineProfile::downloadRequested,
- [&](QWebEngineDownloadItem *item) {
+ int downloadCount = 0;
+ ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) {
QVERIFY(item);
QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadRequested);
item->cancel();
downloadItem = item;
- }));
+ downloadCount++;
+ });
+
+ m_page->download(m_server->url(QByteArrayLiteral("/file")));
+ QTRY_COMPARE(downloadCount, 1);
QVERIFY(downloadItem);
QCOMPARE(downloadItem->state(), QWebEngineDownloadItem::DownloadCancelled);
}
diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
index 6b729d8f2..81877657b 100644
--- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
+++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
@@ -4247,21 +4247,26 @@ void tst_QWebEnginePage::registerProtocolHandler()
QFETCH(bool, permission);
HttpServer server;
+ int mailRequestCount = 0;
+ connect(&server, &HttpServer::newRequest, [&](HttpReqRep *rr) {
+ if (rr->requestMethod() == "GET" && rr->requestPath() == "/") {
+ rr->setResponseBody(QByteArrayLiteral("<html><body><a id=\"link\" href=\"mailto:foo@bar.com\">some text here</a></body></html>"));
+ rr->sendResponse();
+ } else if (rr->requestMethod() == "GET" && rr->requestPath() == "/mail?uri=mailto%3Afoo%40bar.com") {
+ mailRequestCount++;
+ rr->sendResponse();
+ } else {
+ rr->setResponseStatus(404);
+ rr->sendResponse();
+ }
+ });
+ QVERIFY(server.start());
+
QWebEnginePage page;
QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished);
QSignalSpy permissionSpy(&page, &QWebEnginePage::registerProtocolHandlerPermissionRequested);
page.setUrl(server.url("/"));
- auto rr1 = waitForRequest(&server);
- QVERIFY(rr1);
- rr1->setResponseBody(QByteArrayLiteral("<html><body><a id=\"link\" href=\"mailto:foo@bar.com\">some text here</a></body></html>"));
- rr1->sendResponse();
- auto rr2 = waitForRequest(&server);
- QVERIFY(rr2);
- QCOMPARE(rr2->requestMethod(), QByteArrayLiteral("GET"));
- QCOMPARE(rr2->requestPath(), QByteArrayLiteral("/favicon.ico"));
- rr2->setResponseStatus(404);
- rr2->sendResponse();
QTRY_COMPARE(loadSpy.count(), 1);
QCOMPARE(loadSpy.takeFirst().value(0).toBool(), true);
@@ -4283,17 +4288,10 @@ void tst_QWebEnginePage::registerProtocolHandler()
page.runJavaScript(QStringLiteral("document.getElementById(\"link\").click()"));
- std::unique_ptr<HttpReqRep> rr3;
- if (permission) {
- rr3 = waitForRequest(&server);
- QVERIFY(rr3);
- QCOMPARE(rr3->requestMethod(), QByteArrayLiteral("GET"));
- QCOMPARE(rr3->requestPath(), QByteArrayLiteral("/mail?uri=mailto%3Afoo%40bar.com"));
- rr3->sendResponse();
- }
-
QTRY_COMPARE(loadSpy.count(), 1);
QCOMPARE(loadSpy.takeFirst().value(0).toBool(), permission);
+ QCOMPARE(mailRequestCount, permission ? 1 : 0);
+ QVERIFY(server.stop());
}
void tst_QWebEnginePage::dataURLFragment()