diff options
author | Liang Qi <liang.qi@qt.io> | 2019-09-06 22:43:43 +0200 |
---|---|---|
committer | Liang Qi <liang.qi@qt.io> | 2019-09-06 22:44:35 +0200 |
commit | 7268797ab9e32de7497f13ffa9820c49d8cb3e47 (patch) | |
tree | fcaa0a0c7967ed7cc2c35ca4bef5eef123dc22af | |
parent | 8992d21bfde7dc609cd7e235c5a1b543f6f47412 (diff) | |
parent | 9d0b12351718f74a40ec619dc1fa8356211a9ab8 (diff) |
Merge "Merge remote-tracking branch 'origin/5.14' into dev"
25 files changed, 614 insertions, 87 deletions
diff --git a/examples/webenginewidgets/simplebrowser/webpage.cpp b/examples/webenginewidgets/simplebrowser/webpage.cpp index 99849c77d..2e49f651f 100644 --- a/examples/webenginewidgets/simplebrowser/webpage.cpp +++ b/examples/webenginewidgets/simplebrowser/webpage.cpp @@ -57,6 +57,7 @@ #include <QAuthenticator> #include <QMessageBox> #include <QStyle> +#include <QTimer> #include <QWebEngineCertificateError> WebPage::WebPage(QWebEngineProfile *profile, QObject *parent) @@ -74,22 +75,33 @@ WebPage::WebPage(QWebEngineProfile *profile, QObject *parent) bool WebPage::certificateError(const QWebEngineCertificateError &error) { QWidget *mainWindow = view()->window(); - if (error.isOverridable()) { - QDialog dialog(mainWindow); - dialog.setModal(true); - dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint); - Ui::CertificateErrorDialog certificateDialog; - certificateDialog.setupUi(&dialog); - certificateDialog.m_iconLabel->setText(QString()); - QIcon icon(mainWindow->style()->standardIcon(QStyle::SP_MessageBoxWarning, 0, mainWindow)); - certificateDialog.m_iconLabel->setPixmap(icon.pixmap(32, 32)); - certificateDialog.m_errorLabel->setText(error.errorDescription()); - dialog.setWindowTitle(tr("Certificate Error")); - return dialog.exec() == QDialog::Accepted; - } - QMessageBox::critical(mainWindow, tr("Certificate Error"), error.errorDescription()); - return false; + QWebEngineCertificateError deferredError = error; + deferredError.defer(); + + QTimer::singleShot(0, mainWindow, [mainWindow, error = std::move(deferredError)] () mutable { + if (!error.deferred()) { + QMessageBox::critical(mainWindow, tr("Certificate Error"), error.errorDescription()); + } else { + QDialog dialog(mainWindow); + dialog.setModal(true); + dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint); + + Ui::CertificateErrorDialog certificateDialog; + certificateDialog.setupUi(&dialog); + certificateDialog.m_iconLabel->setText(QString()); + QIcon icon(mainWindow->style()->standardIcon(QStyle::SP_MessageBoxWarning, 0, mainWindow)); + certificateDialog.m_iconLabel->setPixmap(icon.pixmap(32, 32)); + certificateDialog.m_errorLabel->setText(error.errorDescription()); + dialog.setWindowTitle(tr("Certificate Error")); + + if (dialog.exec() == QDialog::Accepted) + error.ignoreCertificateError(); + else + error.rejectCertificate(); + } + }); + return true; } void WebPage::handleAuthenticationRequired(const QUrl &requestUrl, QAuthenticator *auth) diff --git a/src/core/api/qwebengineurlrequestinfo.cpp b/src/core/api/qwebengineurlrequestinfo.cpp index e2101fd02..2aa43a318 100644 --- a/src/core/api/qwebengineurlrequestinfo.cpp +++ b/src/core/api/qwebengineurlrequestinfo.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWebEngine module of the Qt Toolkit. @@ -185,7 +185,9 @@ QWebEngineUrlRequestInfo::QWebEngineUrlRequestInfo(QWebEngineUrlRequestInfoPriva \value ResourceTypeCspReport A report of Content Security Policy (CSP) violations. CSP reports are in JSON format and they are delivered by HTTP POST requests to specified servers. (Added in Qt 5.7) - \value ResourceTypePluginResource A resource requested by a plugin. (Added in Qt 5.7) + \value ResourceTypePluginResource A resource requested by a plugin. (Added in Qt 5.7) + \value ResourceTypeNavigationPreload A service worker navigation preload + request. (Added in Qt 5.14) \value ResourceTypeUnknown Unknown request type. \note For forward compatibility all values not matched should be treated as unknown, @@ -248,8 +250,10 @@ QUrl QWebEngineUrlRequestInfo::firstPartyUrl() const } /*! - Returns the origin url of the document which initiated - the navigation when a frame navigates another frame. + Returns the origin URL of the document that initiated + the navigation of a frame to another frame. + + \since 5.14 */ QUrl QWebEngineUrlRequestInfo::initiator() const diff --git a/src/core/certificate_error_controller.cpp b/src/core/certificate_error_controller.cpp index a747451df..71465a1af 100644 --- a/src/core/certificate_error_controller.cpp +++ b/src/core/certificate_error_controller.cpp @@ -90,9 +90,10 @@ CertificateErrorControllerPrivate::CertificateErrorControllerPrivate(int cert_er , strictEnforcement(strict_enforcement) , callback(cb) { - if (ssl_info.cert.get()) { - validStart = toQt(ssl_info.cert->valid_start()); - validExpiry = toQt(ssl_info.cert->valid_expiry()); + if (auto cert = ssl_info.cert.get()) { + validStart = toQt(cert->valid_start()); + validExpiry = toQt(cert->valid_expiry()); + chain = toCertificateChain(cert); } } @@ -186,4 +187,9 @@ QString CertificateErrorController::errorString() const return getQStringForMessageId(IDS_CERT_ERROR_UNKNOWN_ERROR_DESCRIPTION); } +QList<QSslCertificate> CertificateErrorController::chain() const +{ + return d->chain; +} + QT_END_NAMESPACE diff --git a/src/core/certificate_error_controller.h b/src/core/certificate_error_controller.h index 5bea61c9b..7f5300dc8 100644 --- a/src/core/certificate_error_controller.h +++ b/src/core/certificate_error_controller.h @@ -55,6 +55,7 @@ #include <QtCore/QDateTime> #include <QtCore/QUrl> +#include <QtNetwork/QSslCertificate> QT_BEGIN_NAMESPACE @@ -95,6 +96,7 @@ public: QString errorString() const; QDateTime validStart() const; QDateTime validExpiry() const; + QList<QSslCertificate> chain() const; void accept(bool); diff --git a/src/core/certificate_error_controller_p.h b/src/core/certificate_error_controller_p.h index abde9a7d5..f3b0c23fa 100644 --- a/src/core/certificate_error_controller_p.h +++ b/src/core/certificate_error_controller_p.h @@ -71,6 +71,7 @@ public: bool overridable; bool strictEnforcement; const base::Callback<void(content::CertificateRequestResultType)> callback; + QList<QSslCertificate> chain; }; QT_END_NAMESPACE diff --git a/src/core/type_conversion.cpp b/src/core/type_conversion.cpp index 02d2db448..ddadeb9f2 100644 --- a/src/core/type_conversion.cpp +++ b/src/core/type_conversion.cpp @@ -40,11 +40,14 @@ #include "type_conversion.h" #include <content/public/common/favicon_url.h> +#include <net/cert/x509_certificate.h> +#include <net/cert/x509_util.h> #include <ui/events/event_constants.h> #include <ui/gfx/image/image_skia.h> #include <QtCore/qcoreapplication.h> #include <QtGui/qmatrix4x4.h> +#include <QtNetwork/qsslcertificate.h> namespace QtWebEngineCore { @@ -256,4 +259,20 @@ void convertToQt(const SkMatrix44 &m, QMatrix4x4 &c) c = qtMatrix; } +static QSslCertificate toCertificate(CRYPTO_BUFFER *buffer) +{ + auto derCert = net::x509_util::CryptoBufferAsStringPiece(buffer); + return QSslCertificate(QByteArray::fromRawData(derCert.data(), derCert.size()), QSsl::Der); +} + +QList<QSslCertificate> toCertificateChain(net::X509Certificate *certificate) +{ + // from leaf to root as in QtNetwork + QList<QSslCertificate> chain; + chain.append(toCertificate(certificate->cert_buffer())); + for (auto &&buffer : certificate->intermediate_buffers()) + chain.append(toCertificate(buffer.get())); + return chain; +} + } // namespace QtWebEngineCore diff --git a/src/core/type_conversion.h b/src/core/type_conversion.h index 7b1f1b4d6..dfd8e8fef 100644 --- a/src/core/type_conversion.h +++ b/src/core/type_conversion.h @@ -64,6 +64,7 @@ #include "url/gurl.h" QT_FORWARD_DECLARE_CLASS(QMatrix4x4) +QT_FORWARD_DECLARE_CLASS(QSslCertificate) namespace content { struct FaviconURL; @@ -73,6 +74,10 @@ namespace gfx { class ImageSkiaRep; } +namespace net { +class X509Certificate; +} + namespace QtWebEngineCore { inline QString toQt(const base::string16 &string) @@ -291,6 +296,8 @@ inline QStringList fromVector(const std::vector<base::string16> &vector) FaviconInfo toFaviconInfo(const content::FaviconURL &); +QList<QSslCertificate> toCertificateChain(net::X509Certificate *certificate); + } // namespace QtWebEngineCore #endif // TYPE_CONVERSION_H diff --git a/src/webenginewidgets/api/qwebenginecertificateerror.cpp b/src/webenginewidgets/api/qwebenginecertificateerror.cpp index f04b73615..a61d98f94 100644 --- a/src/webenginewidgets/api/qwebenginecertificateerror.cpp +++ b/src/webenginewidgets/api/qwebenginecertificateerror.cpp @@ -39,6 +39,8 @@ #include "qwebenginecertificateerror.h" +#include "certificate_error_controller.h" + QT_BEGIN_NAMESPACE /*! @@ -51,14 +53,38 @@ QT_BEGIN_NAMESPACE QWebEnginePage::certificateError(). */ -class QWebEngineCertificateErrorPrivate { +class QWebEngineCertificateErrorPrivate : public QSharedData { public: QWebEngineCertificateErrorPrivate(int error, QUrl url, bool overridable, QString errorDescription); + ~QWebEngineCertificateErrorPrivate() { + if (deferred && !answered) + rejectCertificate(); + } + + void resolveError(bool accept) { + if (answered) + return; + answered = true; + if (overridable) { + if (auto ctl = controller.lock()) + ctl->accept(accept); + } + } + + void ignoreCertificateError() { resolveError(true); } + void rejectCertificate() { resolveError(false); } + QWebEngineCertificateError::Error error; QUrl url; bool overridable; QString errorDescription; + QList<QSslCertificate> chain; + + bool answered = false, deferred = false; + QWeakPointer<CertificateErrorController> controller; + + Q_DISABLE_COPY(QWebEngineCertificateErrorPrivate) }; QWebEngineCertificateErrorPrivate::QWebEngineCertificateErrorPrivate(int error, QUrl url, bool overridable, QString errorDescription) @@ -68,17 +94,31 @@ QWebEngineCertificateErrorPrivate::QWebEngineCertificateErrorPrivate(int error, , errorDescription(errorDescription) { } - /*! \internal */ QWebEngineCertificateError::QWebEngineCertificateError(int error, QUrl url, bool overridable, QString errorDescription) - : d_ptr(new QWebEngineCertificateErrorPrivate(error, url, overridable, errorDescription)) + : d(new QWebEngineCertificateErrorPrivate(error, url, overridable, errorDescription)) { } /*! \internal */ +QWebEngineCertificateError::QWebEngineCertificateError(const QSharedPointer<CertificateErrorController> &controller) + : d(new QWebEngineCertificateErrorPrivate(controller->error(), controller->url(), + controller->overridable(), controller->errorString())) +{ + d->controller = controller; + d->chain = controller->chain(); +} + +QWebEngineCertificateError::QWebEngineCertificateError(const QWebEngineCertificateError &other) = default; + +QWebEngineCertificateError& QWebEngineCertificateError::operator=(const QWebEngineCertificateError &other) = default; + +/*! \internal +*/ QWebEngineCertificateError::~QWebEngineCertificateError() { + } /*! @@ -116,7 +156,6 @@ QWebEngineCertificateError::~QWebEngineCertificateError() */ bool QWebEngineCertificateError::isOverridable() const { - const Q_D(QWebEngineCertificateError); return d->overridable; } @@ -127,7 +166,6 @@ bool QWebEngineCertificateError::isOverridable() const */ QUrl QWebEngineCertificateError::url() const { - const Q_D(QWebEngineCertificateError); return d->url; } @@ -138,7 +176,6 @@ QUrl QWebEngineCertificateError::url() const */ QWebEngineCertificateError::Error QWebEngineCertificateError::error() const { - const Q_D(QWebEngineCertificateError); return d->error; } @@ -149,8 +186,66 @@ QWebEngineCertificateError::Error QWebEngineCertificateError::error() const */ QString QWebEngineCertificateError::errorDescription() const { - const Q_D(QWebEngineCertificateError); return d->errorDescription; } +/*! + Marks the certificate error for delayed handling. + + This function should be called when there is a need to postpone the decision whether to ignore a + certificate error, for example, while waiting for user input. When called, the function pauses the + URL request until ignoreCertificateError() or rejectCertificate() is called. + + \note It is only possible to defer overridable certificate errors. + + \sa isOverridable(), deferred() +*/ +void QWebEngineCertificateError::defer() +{ + if (isOverridable()) + d->deferred = true; +} + +/*! + Returns whether the decision for error handling was delayed and the URL load was halted. +*/ +bool QWebEngineCertificateError::deferred() const +{ + return d->deferred; +} + +/*! + Ignores the certificate error and continues the loading of the requested URL. +*/ +void QWebEngineCertificateError::ignoreCertificateError() +{ + d->ignoreCertificateError(); +} + +/*! + Rejects the certificate and aborts the loading of the requested URL. +*/ +void QWebEngineCertificateError::rejectCertificate() +{ + d->rejectCertificate(); +} + +/*! + Returns \c true if the error was explicitly rejected or accepted. +*/ +bool QWebEngineCertificateError::answered() const +{ + return d->answered; +} + +/*! + Returns the peer's chain of digital certificates + + Chain starts with the peer's immediate certificate and ending with the CA's certificate. +*/ +QList<QSslCertificate> QWebEngineCertificateError::chain() const +{ + return d->chain; +} + QT_END_NAMESPACE diff --git a/src/webenginewidgets/api/qwebenginecertificateerror.h b/src/webenginewidgets/api/qwebenginecertificateerror.h index 82ac281be..b2dd65a9f 100644 --- a/src/webenginewidgets/api/qwebenginecertificateerror.h +++ b/src/webenginewidgets/api/qwebenginecertificateerror.h @@ -42,11 +42,13 @@ #include <QtWebEngineWidgets/qtwebenginewidgetsglobal.h> -#include <QtCore/qscopedpointer.h> +#include <QtCore/qsharedpointer.h> #include <QtCore/qurl.h> +#include <QtNetwork/QSslCertificate> QT_BEGIN_NAMESPACE +class CertificateErrorController; class QWebEngineCertificateErrorPrivate; class QWEBENGINEWIDGETS_EXPORT QWebEngineCertificateError { @@ -78,10 +80,22 @@ public: bool isOverridable() const; QString errorDescription() const; + QWebEngineCertificateError(const QWebEngineCertificateError &other); + QWebEngineCertificateError& operator=(const QWebEngineCertificateError &other); + + void defer(); + bool deferred() const; + + void rejectCertificate(); + void ignoreCertificateError(); + bool answered() const; + + QList<QSslCertificate> chain() const; + private: - Q_DISABLE_COPY(QWebEngineCertificateError) - Q_DECLARE_PRIVATE(QWebEngineCertificateError) - QScopedPointer<QWebEngineCertificateErrorPrivate> d_ptr; + friend class QWebEnginePagePrivate; + QWebEngineCertificateError(const QSharedPointer<CertificateErrorController> &controller); + QExplicitlySharedDataPointer<QWebEngineCertificateErrorPrivate> d; }; QT_END_NAMESPACE diff --git a/src/webenginewidgets/api/qwebenginedownloaditem.cpp b/src/webenginewidgets/api/qwebenginedownloaditem.cpp index 7ce572e2f..7b0cfe074 100644 --- a/src/webenginewidgets/api/qwebenginedownloaditem.cpp +++ b/src/webenginewidgets/api/qwebenginedownloaditem.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWebEngine module of the Qt Toolkit. @@ -124,8 +124,9 @@ static inline QWebEngineDownloadItem::DownloadInterruptReason toDownloadInterrup then the download request will be automatically rejected and nothing will be written to disk. - \note Some properties, like the \l path under which the file will be saved, - can only be changed before calling accept(). + \note Some properties, such as setting the path and file name where the file + will be saved (see \l downloadDirectory() and \l downloadFileName()), can + only be changed before calling accept(). \section2 Object Life Cycle @@ -510,6 +511,10 @@ QString QWebEngineDownloadItem::mimeType() const /*! \obsolete + + Use \l suggestedFileName(), \l downloadDirectory(), and + \l downloadFileName() instead. + Returns the full target path where data is being downloaded to. The path includes the file name. The default suggested path is the standard download location @@ -523,6 +528,10 @@ QString QWebEngineDownloadItem::path() const } /*! + \obsolete + + Use \l setDownloadDirectory() and \l setDownloadFileName() instead. + Sets the full target path to download the file to. The \a path should also include the file name. The download path can only be set in response @@ -572,7 +581,7 @@ QString QWebEngineDownloadItem::downloadDirectory() const /*! \since 5.14 - Sets the directory path to download the file to. + Sets \a directory as the directory path to download the file to. The download directory path can only be set in response to the QWebEngineProfile::downloadRequested() signal before the download is accepted. Past that point, this function has no effect on the @@ -598,7 +607,7 @@ void QWebEngineDownloadItem::setDownloadDirectory(QString directory) /*! \since 5.14 - Returns the suggested file name. + Returns the file name to download the file to. */ QString QWebEngineDownloadItem::downloadFileName() const @@ -610,7 +619,7 @@ QString QWebEngineDownloadItem::downloadFileName() const /*! \since 5.14 - Sets the file name to download the file to. + Sets \a fileName as the file name to download the file to. The download file name can only be set in response to the QWebEngineProfile::downloadRequested() signal before the download is accepted. Past that point, this function has no effect on the diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index c9e9177b7..b22d47916 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -294,6 +294,7 @@ void QWebEnginePagePrivate::loadStarted(const QUrl &provisionalUrl, bool isError return; isLoading = true; + m_certificateErrorControllers.clear(); QTimer::singleShot(0, q, &QWebEnginePage::loadStarted); } @@ -1715,10 +1716,11 @@ void QWebEnginePagePrivate::allowCertificateError(const QSharedPointer<Certifica Q_Q(QWebEnginePage); bool accepted = false; - QWebEngineCertificateError error(controller->error(), controller->url(), controller->overridable() && !controller->strictEnforcement(), controller->errorString()); + QWebEngineCertificateError error(controller); accepted = q->certificateError(error); - - if (error.isOverridable()) + if (error.deferred() && !error.answered()) + m_certificateErrorControllers.append(controller); + else if (!error.answered() && error.isOverridable()) controller->accept(accepted); } diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h index fae97b9fa..2843f69c4 100644 --- a/src/webenginewidgets/api/qwebenginepage_p.h +++ b/src/webenginewidgets/api/qwebenginepage_p.h @@ -210,6 +210,8 @@ public: #if QT_CONFIG(webengine_printing_and_pdf) QPrinter *currentPrinter; #endif + + QList<QSharedPointer<CertificateErrorController>> m_certificateErrorControllers; }; class QContextMenuBuilder : public QtWebEngineCore::RenderViewContextMenuQt diff --git a/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc b/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc index 64fe4c9cd..5536c0058 100644 --- a/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc +++ b/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc @@ -522,6 +522,8 @@ Return \c true to ignore the error and complete the request. Return \c false to stop loading the request. + \note If the error was successfully deferred then the returned value will be ignored. + \sa QWebEngineCertificateError */ diff --git a/tests/auto/shared/https.pri b/tests/auto/shared/https.pri new file mode 100644 index 000000000..ce4c147f7 --- /dev/null +++ b/tests/auto/shared/https.pri @@ -0,0 +1,4 @@ +include($$PWD/http.pri) + +HEADERS += $$PWD/httpsserver.h +RESOURCES += $$PWD/httpsserver.qrc diff --git a/tests/auto/shared/httpserver.cpp b/tests/auto/shared/httpserver.cpp index b85af9901..e282fc8b8 100644 --- a/tests/auto/shared/httpserver.cpp +++ b/tests/auto/shared/httpserver.cpp @@ -31,9 +31,21 @@ Q_LOGGING_CATEGORY(gHttpServerLog, "HttpServer") -HttpServer::HttpServer(QObject *parent) : QObject(parent) +HttpServer::HttpServer(QObject *parent) : HttpServer(new QTcpServer, "http", parent) { - connect(&m_tcpServer, &QTcpServer::newConnection, this, &HttpServer::handleNewConnection); +} + +HttpServer::HttpServer(QTcpServer *tcpServer, const QString &protocol, QObject *parent) + : QObject(parent), m_tcpServer(tcpServer) +{ + m_url.setHost(QStringLiteral("127.0.0.1")); + m_url.setScheme(protocol); + connect(tcpServer, &QTcpServer::newConnection, this, &HttpServer::handleNewConnection); +} + +HttpServer::~HttpServer() +{ + delete m_tcpServer; } bool HttpServer::start() @@ -41,21 +53,18 @@ bool HttpServer::start() m_error = false; m_expectingError = false; - if (!m_tcpServer.listen()) { - qCWarning(gHttpServerLog).noquote() << m_tcpServer.errorString(); + 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()); - + m_url.setPort(m_tcpServer->serverPort()); return true; } bool HttpServer::stop() { - m_tcpServer.close(); + m_tcpServer->close(); return m_error == m_expectingError; } @@ -73,12 +82,12 @@ QUrl HttpServer::url(const QString &path) const void HttpServer::handleNewConnection() { - auto rr = new HttpReqRep(m_tcpServer.nextPendingConnection(), this); + 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]() { + connect(rr, &HttpReqRep::responseSent, [rr]() { qCInfo(gHttpServerLog).noquote() << rr->requestMethod() << rr->requestPath() << rr->responseStatus() << rr->responseBody().size(); }); diff --git a/tests/auto/shared/httpserver.h b/tests/auto/shared/httpserver.h index b4649244e..57f824bb5 100644 --- a/tests/auto/shared/httpserver.h +++ b/tests/auto/shared/httpserver.h @@ -59,19 +59,22 @@ class HttpServer : public QObject Q_OBJECT public: explicit HttpServer(QObject *parent = nullptr); + explicit HttpServer(QTcpServer *server, const QString &protocol, QObject *parent = nullptr); + + ~HttpServer() override; // Must be called to start listening. // // Returns true if a TCP port has been successfully bound. - Q_REQUIRED_RESULT bool start(); + Q_INVOKABLE Q_REQUIRED_RESULT bool start(); // Stops listening and performs final error checks. - Q_REQUIRED_RESULT bool stop(); + Q_INVOKABLE Q_REQUIRED_RESULT bool stop(); - void setExpectError(bool b); + Q_INVOKABLE void setExpectError(bool b); // Full URL for given relative path - QUrl url(const QString &path = QStringLiteral("/")) const; + Q_INVOKABLE QUrl url(const QString &path = QStringLiteral("/")) const; Q_SIGNALS: // Emitted after a HTTP request has been successfully parsed. @@ -81,7 +84,7 @@ private Q_SLOTS: void handleNewConnection(); private: - QTcpServer m_tcpServer; + QTcpServer *m_tcpServer; QUrl m_url; bool m_error = false; bool m_expectingError = false; diff --git a/tests/auto/shared/httpsserver.h b/tests/auto/shared/httpsserver.h new file mode 100644 index 000000000..32c8e8345 --- /dev/null +++ b/tests/auto/shared/httpsserver.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 HTTPSSERVER_H +#define HTTPSSERVER_H + +#include "httpreqrep.h" +#include "httpserver.h" + +#include <QDebug> +#include <QFile> +#include <QSslKey> +#include <QSslSocket> +#include <QSslConfiguration> +#include <QTcpServer> + +struct SslTcpServer : QTcpServer +{ + SslTcpServer() { + sslconf.setLocalCertificateChain(QSslCertificate::fromPath(":/resources/cert.pem")); + sslconf.setPrivateKey(readKey(":/resources/key.pem")); + } + + void incomingConnection(qintptr d) override { + auto socket = new QSslSocket(this); + socket->setSslConfiguration(sslconf); + + if (!socket->setSocketDescriptor(d)) { + qWarning() << "Failed to setup ssl socket!"; + delete socket; + return; + } + + connect(socket, QOverload<QSslSocket::SocketError>::of(&QSslSocket::error), + [] (QSslSocket::SocketError e) { qWarning() << "! Socket Error:" << e; }); + connect(socket, QOverload<const QList<QSslError> &>::of(&QSslSocket::sslErrors), + [] (const QList<QSslError> &le) { qWarning() << "! SSL Errors:\n" << le; }); + + addPendingConnection(socket); + socket->startServerEncryption(); + } + + QSslKey readKey(const QString &path) const { + QFile file(path); + file.open(QIODevice::ReadOnly); + return QSslKey(file.readAll(), QSsl::Rsa, QSsl::Pem); + } + + QSslConfiguration sslconf; +}; + +struct HttpsServer : HttpServer +{ + HttpsServer(QObject *parent = nullptr) : HttpServer(new SslTcpServer, "https", parent) { } +}; + +#endif diff --git a/tests/auto/shared/httpsserver.qrc b/tests/auto/shared/httpsserver.qrc new file mode 100644 index 000000000..ec57a1983 --- /dev/null +++ b/tests/auto/shared/httpsserver.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>resources/cert.pem</file> + <file>resources/key.pem</file> +</qresource> +</RCC> diff --git a/tests/auto/shared/resources/cert.pem b/tests/auto/shared/resources/cert.pem new file mode 100644 index 000000000..3aaaf289c --- /dev/null +++ b/tests/auto/shared/resources/cert.pem @@ -0,0 +1,64 @@ +-----BEGIN CERTIFICATE----- +MIIEpDCCAoygAwIBAgIUO90aty9AMjvBvzfUhr1WwdBrKkMwDQYJKoZIhvcNAQEL +BQAwfzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM +DVNhbiBGcmFuY2lzY28xDzANBgNVBAoMBkJhZFNTTDEyMDAGA1UEAwwpQmFkU1NM +IEludGVybWVkaWF0ZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTkwODI2MTQ0 +NDIxWhcNMTkwODI3MTQ0NDIxWjBjMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2Fs +aWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEPMA0GA1UECgwGQmFkU1NM +MRYwFAYDVQQDDA0qLmJhZHNzbC50ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAkybT/L4zJCqefpd+eYT6aQ0PtobQfFgP+n+z5wWoUxIAJnjb5ZW4 +7IJxka/2/ggzJOfrUBur54LkTfFQ+yX85eKYCuH0GLz+Rve50LDn0ya6qSgmEhDG +0bend2tMZY+Nl3B+5Ane1vua8hdJjv3ZO3e5UgpQwysL54eYyhEWWlbFWF11LhEd +MYp953UGLqoV4Mlw+Li8TmFwdKQx6icgBTuloXLzk9aUU+b6NbXdadNXkmzg09IC +sb8pnMXiF2P9Xm5rK0IoiRkSHxVnU12nQXh65Ns/2Dj5DcbHmVdvallfr4wnLeFP +UotysZnvFmE7FLMSr/eQfkTG+Jlb7ZhoGwIDAQABozQwMjAJBgNVHRMEAjAAMCUG +A1UdEQQeMByCDSouYmFkc3NsLnRlc3SCC2JhZHNzbC50ZXN0MA0GCSqGSIb3DQEB +CwUAA4ICAQA7Yc+QQzqSK15ibmaYrkqq+cumggsWLCprW8jvzhpWBt9IjToP5nsy +sKinYPoZR8jvZ1YVotcts7uQT7DkqeWkB+l+88c7gQdgujvBo6v9/g+jrXFKgsJD +IBmkho8hpd63Slqv2Yp4bYT20O5EvR9CQvwSkwTs+ylBNEs1Q+AbekxmBjuYUxHn +9xL4/GZ6ufoNv676iCoXo4mnDrCD8e8MRiZoU9Lq4G41HGiLWV0tM/M6BdVJYGzl +FcBg0ZKnQT9OCWEPRe3zyRS6a+MivPAzxS8z/kYaRN+C7H68Mib3xPDsEETz1MnO +uzGAPHAAgtYWYJi+CaaNWkgAv4n+UIQa0oyqPn4z5hLcsO+nMBws2Sg0mkQLilBX +N1ciCdVMi7sHKuLa7GVksq/RQrXnZcQhoYQRrZAaAHKbxyo/M2pNqmDiFJppdH7a +6Rj2vYf6ig/FXAzDGsDvf/tsGCxgJTFzGly+GsWVe40vyjfWHxWWDU/eGjfGO05k +Xzjm+kYGJnH2hfiIlX1Jeu/jjIodiSy31F0hvuKlJu8PfaQ7oo5neRzwRO6Wq9rR +7DMsQN6OtXGnnA+ogC0korA+aXev6wzbwYUhzMf1YTzEjrFNIXeIHsQSzq6lPcIE +JOly5wjyO/eNF7mpHyDX8brY6Hn+bgyDeKAmsUvhOCEXgaPpKlP4gQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- diff --git a/tests/auto/shared/resources/key.pem b/tests/auto/shared/resources/key.pem new file mode 100644 index 000000000..89922679a --- /dev/null +++ b/tests/auto/shared/resources/key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAkybT/L4zJCqefpd+eYT6aQ0PtobQfFgP+n+z5wWoUxIAJnjb +5ZW47IJxka/2/ggzJOfrUBur54LkTfFQ+yX85eKYCuH0GLz+Rve50LDn0ya6qSgm +EhDG0bend2tMZY+Nl3B+5Ane1vua8hdJjv3ZO3e5UgpQwysL54eYyhEWWlbFWF11 +LhEdMYp953UGLqoV4Mlw+Li8TmFwdKQx6icgBTuloXLzk9aUU+b6NbXdadNXkmzg +09ICsb8pnMXiF2P9Xm5rK0IoiRkSHxVnU12nQXh65Ns/2Dj5DcbHmVdvallfr4wn +LeFPUotysZnvFmE7FLMSr/eQfkTG+Jlb7ZhoGwIDAQABAoIBADRXy3BL98UVo+tD +2ClBtBFKJBy5N9ADQyvH4SZ8TLO/423L7+xqpaz7eYppHWKfaBHorTuBnFRtquhO +vo+Xo63iPFMirMFf+NMlq2MgilYBoMQrE9+5N//BZECGWlaGCcekrH5RRIMUXLlg +rzm98lfE7pbQNIo39bQV97NpAJqBWPuoIvCrbRCysGoA5j7ptZ/EhSlC00eA7ybD +CeYHmh8NrsapKOTGb5u1v3paV8X/mH6vKmsVs7n6LC0opBxzM8eAHEAQ6h8rmz9H +y99FWDYha3lOS4SLkTnuRnNHOMLJajPq3Isu+BgzLWuRGnKZ3rmuUFwPNkCZTvsV +dTdBE4ECgYEAw6jBEil0e8Pc9sGqnz93e8qrYE9wSPso4q3BNJgTbN48kon6mqh7 +gQVgEP/75Th5YrJUrY9Pd/8H9uoMOxbDXgOXG/xNnhC0L+7aM8nhKlxCLndY1e56 +/YymYYH4+D9ZD2u526mK/nmCg2QGOkCVYYp7NXe/mA0g34drKjefmj8CgYEAwIhq +rZhlfAvQThSOqQA9zA7NXPDh4KzIjr8htVu5YvVcv5W2uhsni9DXFaloPnhuLdJ7 +MnPF2WqzQ9YqFrGn/9/OTqeE23f60ed04qLGM4BApb45y5Kw6sCPnWu7dMYfny9i +XeZA2A+ODmqVkrU+ZNVzqzS1krYyUP3exd1voyUCgYEAqPRARH6np3gqhqoVvA4C +D1OjSTdPrrWzSIriG5h2rbv6ck/Tp1l1zKPnoMZrrjRmHWQA2x61cNk4926DwUKW +0cgn5HKqU6P49Ks8oRvi48FnJNjKTXHxoqChy/GAHF4Xecl8ZMKy06v5l5v4BLVg +SSpb2n/dYl9z05IMaBhAKeECgYBKB2n1S6ah1q0GiLL92mDoiDyAYwKG8AjBkk40 +vIsAuNUruTYkQvKmuOsqohO6CXZb2hWSpZ9KZNN+3ucaCL9PDE/4QEM+W9iuQu/X +gLzy6npxAD6avtGVweq2ncjbMp7QB1ksP69pJDn74xGV8miGPuiVyNOUEMgyChtR +Oz6EnQKBgEth0w80CBg6b3NKuASoc/vC08njZQvWpe5xrzY2DL8epVKb1qf6+8SE +eX34cIcSaonEZ2g67MAeIG6jtmPwxWk4EYAsO1u4XiyziABkoNyLKVH4hZg61BsV +jL7R5UrUvBbhKLFOwkcB4Kwdwu7COB/UKa5XJBTMbuw1UTyxlUeI +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/widgets/certificateerror/certificateerror.pro b/tests/auto/widgets/certificateerror/certificateerror.pro new file mode 100644 index 000000000..73ba7515b --- /dev/null +++ b/tests/auto/widgets/certificateerror/certificateerror.pro @@ -0,0 +1,3 @@ +include(../tests.pri) +include(../../shared/https.pri) +QT *= core-private diff --git a/tests/auto/widgets/certificateerror/tst_certificateerror.cpp b/tests/auto/widgets/certificateerror/tst_certificateerror.cpp new file mode 100644 index 000000000..5fd765ed5 --- /dev/null +++ b/tests/auto/widgets/certificateerror/tst_certificateerror.cpp @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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$ +** +****************************************************************************/ +#include <httpsserver.h> +#include <util.h> + +#include <QWebEngineCertificateError> +#include <QWebEnginePage> +#include <QWebEngineSettings> + +#include <QtTest/QtTest> + +class tst_CertificateError : public QObject +{ + Q_OBJECT +public: + tst_CertificateError() { } + +private Q_SLOTS: + void handleError_data(); + void handleError(); +}; + +struct PageWithCertificateErrorHandler : QWebEnginePage +{ + PageWithCertificateErrorHandler(bool defer, bool accept, QObject *p = nullptr) + : QWebEnginePage(p), deferError(defer), acceptCertificate(accept) { + connect(this, &QWebEnginePage::loadFinished, [&] (bool result) { spyLoad(result); }); + } + + bool deferError, acceptCertificate; + + CallbackSpy<bool> spyLoad; + QScopedPointer<QWebEngineCertificateError> error; + + bool certificateError(const QWebEngineCertificateError &e) override { + error.reset(new QWebEngineCertificateError(e)); + if (deferError) + error->defer(); + return acceptCertificate; + } +}; + +void tst_CertificateError::handleError_data() +{ + QTest::addColumn<bool>("deferError"); + QTest::addColumn<bool>("acceptCertificate"); + QTest::addColumn<QString>("expectedContent"); + QTest::addRow("Reject") << false << false << QString(); + QTest::addRow("DeferReject") << true << false << QString(); + QTest::addRow("DeferAccept") << true << true << "TEST"; +} + +void tst_CertificateError::handleError() +{ + HttpsServer server; + server.setExpectError(true); + QVERIFY(server.start()); + + connect(&server, &HttpsServer::newRequest, [&] (HttpReqRep *rr) { + rr->setResponseBody(QByteArrayLiteral("<html><body>TEST</body></html>")); + rr->sendResponse(); + }); + + QFETCH(bool, deferError); + QFETCH(bool, acceptCertificate); + QFETCH(QString, expectedContent); + + PageWithCertificateErrorHandler page(deferError, acceptCertificate); + page.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + + page.setUrl(server.url()); + QTRY_VERIFY(page.error); + QVERIFY(page.error->isOverridable()); + auto chain = page.error->chain(); + QCOMPARE(chain.size(), 2); + QCOMPARE(chain[0].serialNumber(), "3b:dd:1a:b7:2f:40:32:3b:c1:bf:37:d4:86:bd:56:c1:d0:6b:2a:43"); + QCOMPARE(chain[1].serialNumber(), "6d:52:fb:b4:57:3b:b2:03:c8:62:7b:7e:44:45:5c:d3:08:87:74:17"); + + if (deferError) { + QVERIFY(page.error->deferred()); + QVERIFY(!page.error->answered()); + QVERIFY(!page.spyLoad.wasCalled()); + QCOMPARE(toPlainTextSync(&page), QString()); + + if (acceptCertificate) + page.error->ignoreCertificateError(); + else + page.error->rejectCertificate(); + + QVERIFY(page.error->answered()); + page.error.reset(); + } + + bool loadResult = page.spyLoad.waitForResult(); + QVERIFY(page.spyLoad.wasCalled()); + QCOMPARE(loadResult, acceptCertificate); + QCOMPARE(toPlainTextSync(&page), expectedContent); +} + +QTEST_MAIN(tst_CertificateError) +#include <tst_certificateerror.moc> diff --git a/tests/auto/widgets/qwebenginepage/BLACKLIST b/tests/auto/widgets/qwebenginepage/BLACKLIST index 4e344c936..e6fad8b20 100644 --- a/tests/auto/widgets/qwebenginepage/BLACKLIST +++ b/tests/auto/widgets/qwebenginepage/BLACKLIST @@ -7,3 +7,6 @@ windows [runJavaScriptFromSlot] osx linux + +[fullScreenRequested] +windows diff --git a/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp b/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp index 1ba9c13e8..90361f2c9 100644 --- a/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp +++ b/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp @@ -30,6 +30,26 @@ #include <QWebChannel> #endif +static bool verifyOrder(QStringList orderList) +{ + QStringList expected = { + "DocumentCreation", + "DOMContentLoaded", + "DocumentReady", + "load", + "Deferred" + }; + + if (orderList.at(4) == "load (timeout)") { + if (orderList.at(3) != "Deferred") + return false; + expected[3] = "Deferred"; + expected[4] = "load (timeout)"; + } + + return orderList == expected; +} + class tst_QWebEngineScript: public QObject { Q_OBJECT @@ -101,15 +121,28 @@ void tst_QWebEngineScript::loadEvents() script.setRunsOnSubFrames(true); if (injectionPoint == QWebEngineScript::DocumentCreation) { script.setSourceCode(QStringLiteral(R"( - var log = ["DocumentCreation"]; - for (let type of ["DOMContentLoaded", "load"]) { - window.addEventListener(type, () => log.push(type)); + var log = ['DocumentCreation']; + var timestamps = {'DocumentCreation': Date.now()}; + for (let type of ['DOMContentLoaded', 'load']) { + window.addEventListener(type, () => { + timestamps[type] = Date.now(); + if (type === 'load' && log.includes('Deferred') && timestamps['Deferred'] - timestamps['DOMContentLoaded'] > 500) + log.push(type + ' (timeout)'); + else + log.push(type); + }); } )")); } else if (injectionPoint == QWebEngineScript::DocumentReady) { - script.setSourceCode(QStringLiteral(R"(log.push("DocumentReady"))")); + script.setSourceCode(QStringLiteral(R"( + timestamps['DocumentReady'] = Date.now(); + log.push('DocumentReady'); + )")); } else { - script.setSourceCode(QStringLiteral(R"(log.push("Deferred"))")); + script.setSourceCode(QStringLiteral(R"( + timestamps['Deferred'] = Date.now(); + log.push('Deferred'); + )")); } return script; } @@ -144,46 +177,38 @@ void tst_QWebEngineScript::loadEvents() profile.pages.emplace_back(profile); Page &page = profile.pages.back(); - const QStringList expected = { - "DocumentCreation", - "DOMContentLoaded", - "DocumentReady", - "load", - "Deferred" - }; - // Single frame / setHtml page.setHtml(QStringLiteral("<!DOCTYPE html><html><head><title>mr</title></head><body></body></html>")); QTRY_COMPARE(page.spy.count(), 1); QCOMPARE(page.spy.takeFirst().value(0).toBool(), true); - QCOMPARE(page.eval("window.log", QWebEngineScript::MainWorld).toStringList(), expected); - QCOMPARE(page.eval("window.log", QWebEngineScript::ApplicationWorld).toStringList(), expected); + QVERIFY(verifyOrder(page.eval("window.log", QWebEngineScript::MainWorld).toStringList())); + QVERIFY(verifyOrder(page.eval("window.log", QWebEngineScript::ApplicationWorld).toStringList())); // After discard page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); page.setLifecycleState(QWebEnginePage::LifecycleState::Active); QTRY_COMPARE(page.spy.count(), 1); QCOMPARE(page.spy.takeFirst().value(0).toBool(), true); - QCOMPARE(page.eval("window.log", QWebEngineScript::MainWorld).toStringList(), expected); - QCOMPARE(page.eval("window.log", QWebEngineScript::ApplicationWorld).toStringList(), expected); + QVERIFY(verifyOrder(page.eval("window.log", QWebEngineScript::MainWorld).toStringList())); + QVERIFY(verifyOrder(page.eval("window.log", QWebEngineScript::ApplicationWorld).toStringList())); // Multiple frames page.load(QUrl("qrc:/resources/test_iframe_main.html")); QTRY_COMPARE(page.spy.count(), 1); QCOMPARE(page.spy.takeFirst().value(0).toBool(), true); - QCOMPARE(page.eval("window.log", QWebEngineScript::MainWorld).toStringList(), expected); - QCOMPARE(page.eval("window.log", QWebEngineScript::ApplicationWorld).toStringList(), expected); - QCOMPARE(page.eval("window[0].log", QWebEngineScript::MainWorld).toStringList(), expected); - QCOMPARE(page.eval("window[0].log", QWebEngineScript::ApplicationWorld).toStringList(), expected); - QCOMPARE(page.eval("window[0][0].log", QWebEngineScript::MainWorld).toStringList(), expected); - QCOMPARE(page.eval("window[0][0].log", QWebEngineScript::ApplicationWorld).toStringList(), expected); + QVERIFY(verifyOrder(page.eval("window.log", QWebEngineScript::MainWorld).toStringList())); + QVERIFY(verifyOrder(page.eval("window.log", QWebEngineScript::ApplicationWorld).toStringList())); + QVERIFY(verifyOrder(page.eval("window[0].log", QWebEngineScript::MainWorld).toStringList())); + QVERIFY(verifyOrder(page.eval("window[0].log", QWebEngineScript::ApplicationWorld).toStringList())); + QVERIFY(verifyOrder(page.eval("window[0][0].log", QWebEngineScript::MainWorld).toStringList())); + QVERIFY(verifyOrder(page.eval("window[0][0].log", QWebEngineScript::ApplicationWorld).toStringList())); // Cross-process navigation page.load(QUrl("chrome://gpu")); QTRY_COMPARE_WITH_TIMEOUT(page.spy.count(), 1, 20000); QCOMPARE(page.spy.takeFirst().value(0).toBool(), true); - QCOMPARE(page.eval("window.log", QWebEngineScript::MainWorld).toStringList(), expected); - QCOMPARE(page.eval("window.log", QWebEngineScript::ApplicationWorld).toStringList(), expected); + QVERIFY(verifyOrder(page.eval("window.log", QWebEngineScript::MainWorld).toStringList())); + QVERIFY(verifyOrder(page.eval("window.log", QWebEngineScript::ApplicationWorld).toStringList())); // Using window.open from JS QVERIFY(profile.pages.size() == 1); @@ -191,10 +216,10 @@ void tst_QWebEngineScript::loadEvents() QTRY_VERIFY(profile.pages.size() == 2); QTRY_COMPARE(profile.pages.front().spy.count(), 1); QTRY_COMPARE(profile.pages.back().spy.count(), 1); - QCOMPARE(profile.pages.front().eval("window.log", QWebEngineScript::MainWorld).toStringList(), expected); - QCOMPARE(profile.pages.front().eval("window.log", QWebEngineScript::ApplicationWorld).toStringList(), expected); - QCOMPARE(profile.pages.back().eval("window.log", QWebEngineScript::MainWorld).toStringList(), expected); - QCOMPARE(profile.pages.back().eval("window.log", QWebEngineScript::ApplicationWorld).toStringList(), expected); + QVERIFY(verifyOrder(profile.pages.front().eval("window.log", QWebEngineScript::MainWorld).toStringList())); + QVERIFY(verifyOrder(profile.pages.front().eval("window.log", QWebEngineScript::ApplicationWorld).toStringList())); + QVERIFY(verifyOrder(profile.pages.back().eval("window.log", QWebEngineScript::MainWorld).toStringList())); + QVERIFY(verifyOrder(profile.pages.back().eval("window.log", QWebEngineScript::ApplicationWorld).toStringList())); } void tst_QWebEngineScript::scriptWorld_data() diff --git a/tests/auto/widgets/widgets.pro b/tests/auto/widgets/widgets.pro index 92159bf83..df553df55 100644 --- a/tests/auto/widgets/widgets.pro +++ b/tests/auto/widgets/widgets.pro @@ -4,6 +4,7 @@ QT_FOR_CONFIG += webenginecore webenginecore-private TEMPLATE = subdirs SUBDIRS += \ + certificateerror \ defaultsurfaceformat \ devtools \ faviconmanager \ |