summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJüri Valdmann <juri.valdmann@qt.io>2018-03-08 17:37:03 +0100
committerJüri Valdmann <juri.valdmann@qt.io>2018-04-03 08:14:00 +0000
commitcce649b65d31353754af9e34148c0e8d4068d3cf (patch)
tree3125ee37a7a6a30e2c1aa1fe9fb990e5253aae86
parentef0bebc89a4d716d4bd3467bcbc971ca4101d974 (diff)
Stabilize tst_qwebenginedownloads
The waitForRequest/waitForSignal function used by these tests is broken: it assumes that calling QEventLoop::exit() will immediately exit from the event loop. In actuality this is not immediate and the event loop may continue to execute more signals handlers even after exit() has been called. This means that in e.g. waitForRequest the 'result' variable may be assigned to twice. Additionally there is a race condition in downloadTwoLinks, where we sometimes skip the first download. This is not a bug but simply one pending navigation being aborted in favor of another. Changes - Delete waitForRequest. Define one HTTP request handler per test, using state variables to communicate with the main body of the test. Ignore unknown requests (including favicon requests). Same for downloadRequested signals. - Expand downloadTwoLinks and fix expectations. - Add logging to HTTPServer. - Unblacklist. Task-number: QTBUG-66888 Change-Id: I718cac6c4b32a8cc68400fa8ee5b853686c77fcb Reviewed-by: Peter Varga <pvarga@inf.u-szeged.hu>
-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()