diff options
Diffstat (limited to 'tests/auto')
180 files changed, 8183 insertions, 1158 deletions
diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt index 9b7672f67..1b0ff3e9d 100644 --- a/tests/auto/CMakeLists.txt +++ b/tests/auto/CMakeLists.txt @@ -1,8 +1,6 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -if(LINUX AND CMAKE_CROSSCOMPILING AND (${TEST_architecture_arch} STREQUAL "arm64")) - return()# FIXME -endif() +# SPDX-License-Identifier: BSD-3-Clause +add_subdirectory(cmake) if(TARGET Qt::WebEngineCore) add_subdirectory(httpserver) add_subdirectory(util) @@ -17,3 +15,6 @@ endif() if(TARGET Qt::Pdf) add_subdirectory(pdf) endif() +if(TARGET Qt::PdfQuick) + add_subdirectory(pdfquick) +endif() diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt index 1b6d3d01f..2fa1f915a 100644 --- a/tests/auto/cmake/CMakeLists.txt +++ b/tests/auto/cmake/CMakeLists.txt @@ -1,19 +1,30 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - +# SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 3.16) - -project(qmake_cmake_files) +project(qtwebengine_cmake_tests) enable_testing() -find_package(Qt5Core REQUIRED) +find_package(Qt6Core REQUIRED) -include("${_Qt5CTestMacros}") +include("${_Qt6CTestMacros}") -if (NOT NO_WIDGETS) - test_module_includes( - WebEngineWidgets QWebEngineView +set(module_includes "") +if(TARGET Qt6::WebEngineCore) + list(APPEND module_includes + WebEngineCore QWebEnginePage + ) +endif() +if(TARGET Qt6::WebEngineWidgets) + list(APPEND module_includes + WebEngineWidgets QWebEngineView ) endif() +if(TARGET Qt6::Pdf) + list(APPEND module_includes + Pdf QPdfDocument + ) +endif() + +_qt_internal_test_module_includes(${module_includes}) diff --git a/tests/auto/core/CMakeLists.txt b/tests/auto/core/CMakeLists.txt index 4a536c58e..eb8e9266f 100644 --- a/tests/auto/core/CMakeLists.txt +++ b/tests/auto/core/CMakeLists.txt @@ -1,13 +1,26 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause add_subdirectory(qwebenginecookiestore) +add_subdirectory(qwebengineframe) +add_subdirectory(qwebengineloadinginfo) add_subdirectory(qwebenginesettings) +if(QT_FEATURE_ssl) + # only tests doh, and requires ssl + add_subdirectory(qwebengineglobalsettings) +endif() add_subdirectory(qwebengineurlrequestinterceptor) +add_subdirectory(qwebengineurlrequestjob) add_subdirectory(origins) add_subdirectory(devtools) +add_subdirectory(getdomainandregistry) +add_subdirectory(qtversion) if(QT_FEATURE_ssl) add_subdirectory(qwebengineclientcertificatestore) add_subdirectory(certificateerror) endif() + +if(QT_FEATURE_webenginedriver) + add_subdirectory(webenginedriver) +endif() diff --git a/tests/auto/core/certificateerror/BLACKLIST b/tests/auto/core/certificateerror/BLACKLIST new file mode 100644 index 000000000..a8fd16bf3 --- /dev/null +++ b/tests/auto/core/certificateerror/BLACKLIST @@ -0,0 +1,2 @@ +[fatalError] +* diff --git a/tests/auto/core/certificateerror/CMakeLists.txt b/tests/auto/core/certificateerror/CMakeLists.txt index ab996d0db..6223ca25c 100644 --- a/tests/auto/core/certificateerror/CMakeLists.txt +++ b/tests/auto/core/certificateerror/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../httpserver/httpserver.cmake) include(../../util/util.cmake) diff --git a/tests/auto/core/certificateerror/tst_certificateerror.cpp b/tests/auto/core/certificateerror/tst_certificateerror.cpp index 4ae840e72..67e2d8ae4 100644 --- a/tests/auto/core/certificateerror/tst_certificateerror.cpp +++ b/tests/auto/core/certificateerror/tst_certificateerror.cpp @@ -67,8 +67,8 @@ void tst_CertificateError::handleError_data() void tst_CertificateError::handleError() { - HttpsServer server(":/resources/server.pem",":/resources/server.key"); - server.setExpectError(true); + HttpsServer server(":/resources/server.pem", ":/resources/server.key", ""); + server.setExpectError(false); QVERIFY(server.start()); connect(&server, &HttpsServer::newRequest, [&] (HttpReqRep *rr) { @@ -92,7 +92,7 @@ void tst_CertificateError::handleError() QCOMPARE(chain[1].serialNumber(), "3c:16:83:83:59:c4:2a:65:8f:7a:b2:07:10:14:4e:2d:70:9a:3e:23"); if (deferError) { - QCOMPARE(page.loadSpy.count(), 0); + QCOMPARE(page.loadSpy.size(), 0); QCOMPARE(toPlainTextSync(&page), QString()); if (acceptCertificate) @@ -102,9 +102,10 @@ void tst_CertificateError::handleError() page.error.reset(); } - QTRY_COMPARE_WITH_TIMEOUT(page.loadSpy.count(), 1, 30000); + QTRY_COMPARE_WITH_TIMEOUT(page.loadSpy.size(), 1, 30000); QCOMPARE(page.loadSpy.takeFirst().value(0).toBool(), acceptCertificate); QCOMPARE(toPlainTextSync(&page), expectedContent); + QVERIFY(server.stop()); } void tst_CertificateError::fatalError() diff --git a/tests/auto/core/devtools/CMakeLists.txt b/tests/auto/core/devtools/CMakeLists.txt index e9b86e802..efde75240 100644 --- a/tests/auto/core/devtools/CMakeLists.txt +++ b/tests/auto/core/devtools/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause qt_internal_add_test(tst_devtools SOURCES diff --git a/tests/auto/core/devtools/tst_devtools.cpp b/tests/auto/core/devtools/tst_devtools.cpp index 477b4cb20..57a2b83a3 100644 --- a/tests/auto/core/devtools/tst_devtools.cpp +++ b/tests/auto/core/devtools/tst_devtools.cpp @@ -21,7 +21,7 @@ void tst_DevTools::attachAndDestroyPageFirst() QSignalSpy spy(page, &QWebEnginePage::loadFinished); page->load(QUrl("data:text/plain,foobarbaz")); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 12000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 12000); // shouldn't do anything until page is set page->triggerAction(QWebEnginePage::InspectElement); @@ -49,7 +49,7 @@ void tst_DevTools::attachAndDestroyInspectorFirst() QSignalSpy spy(page, &QWebEnginePage::loadFinished); page->setHtml(QStringLiteral("<body><h1>FOO BAR!</h1></body>")); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 12000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 12000); page->triggerAction(QWebEnginePage::InspectElement); diff --git a/tests/auto/core/getdomainandregistry/CMakeLists.txt b/tests/auto/core/getdomainandregistry/CMakeLists.txt new file mode 100644 index 000000000..c2b65f9dc --- /dev/null +++ b/tests/auto/core/getdomainandregistry/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +include(../../util/util.cmake) + +qt_internal_add_test(tst_getdomainandregistry + SOURCES + tst_getdomainandregistry.cpp + LIBRARIES + Qt::WebEngineCore + Test::Util +) diff --git a/tests/auto/core/getdomainandregistry/tst_getdomainandregistry.cpp b/tests/auto/core/getdomainandregistry/tst_getdomainandregistry.cpp new file mode 100644 index 000000000..e9e0bf105 --- /dev/null +++ b/tests/auto/core/getdomainandregistry/tst_getdomainandregistry.cpp @@ -0,0 +1,30 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <QtTest/QtTest> +#include <QtWebEngineCore/qtwebenginecoreglobal.h> + +class tst_GetDomainAndRegistry final : public QObject { + Q_OBJECT + +private Q_SLOTS: + void getDomainAndRegistry(); +}; + +void tst_GetDomainAndRegistry::getDomainAndRegistry() { + QCOMPARE(qWebEngineGetDomainAndRegistry({"http://www.google.com/"}), QString("google.com")); + QCOMPARE(qWebEngineGetDomainAndRegistry({"http://www.google.co.uk/"}), QString("google.co.uk")); + QCOMPARE(qWebEngineGetDomainAndRegistry({"http://127.0.0.1/"}), QString()); + QCOMPARE(qWebEngineGetDomainAndRegistry({"https://qt.io/"}), QString("qt.io")); + QCOMPARE(qWebEngineGetDomainAndRegistry({"https://download.qt.io/"}), QString("qt.io")); + QCOMPARE(qWebEngineGetDomainAndRegistry({"https://foo.fr/"}), QString("foo.fr")); + QCOMPARE(qWebEngineGetDomainAndRegistry({"https://foo.gouv.fr/"}), QString("foo.gouv.fr")); + QCOMPARE(qWebEngineGetDomainAndRegistry({"https://bar.foo.gouv.fr/"}), QString("foo.gouv.fr")); + QCOMPARE(qWebEngineGetDomainAndRegistry({"https://fr.wikipedia.org/wiki/%C3%89l%C3%A9phant"}), QString("wikipedia.org")); + QCOMPARE(qWebEngineGetDomainAndRegistry({"https://foo.günstigbestellen.de/"}), QString("foo.xn--gnstigbestellen-zvb.de")); + QCOMPARE(qWebEngineGetDomainAndRegistry({"https://foo.g\u00fcnstigbestellen.de/"}), QString("foo.xn--gnstigbestellen-zvb.de")); +} + + +QTEST_MAIN(tst_GetDomainAndRegistry) +#include "tst_getdomainandregistry.moc" diff --git a/tests/auto/core/origins/CMakeLists.txt b/tests/auto/core/origins/CMakeLists.txt index 7d0773007..306074994 100644 --- a/tests/auto/core/origins/CMakeLists.txt +++ b/tests/auto/core/origins/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../httpserver/httpserver.cmake) include(../../util/util.cmake) @@ -9,6 +9,7 @@ qt_internal_add_test(tst_origins tst_origins.cpp LIBRARIES Qt::WebEngineCore + Qt::WebEngineWidgets Test::HttpServer Test::Util ) @@ -32,6 +33,8 @@ set(tst_origins_resource_files "resources/viewSource.html" "resources/websocket.html" "resources/websocket2.html" + "resources/red.png" + "resources/fetchApi.html" ) qt_internal_add_resource(tst_origins "tst_origins" diff --git a/tests/auto/core/origins/resources/fetchApi.html b/tests/auto/core/origins/resources/fetchApi.html new file mode 100644 index 000000000..7b54416fd --- /dev/null +++ b/tests/auto/core/origins/resources/fetchApi.html @@ -0,0 +1,14 @@ +<!doctype html> +<html> +<body> + <script type="text/javascript"> + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + const url = urlParams.get('url'); + const f = fetch(url); + if (urlParams.get('printRes') == 'true') { + f.then((r) => r.text()).then((p) => console.log(p)); + } + </script> +</body> +</html> diff --git a/tests/auto/core/origins/resources/link.html b/tests/auto/core/origins/resources/link.html new file mode 100644 index 000000000..297b9b273 --- /dev/null +++ b/tests/auto/core/origins/resources/link.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html> + <head> + <title>Link</title> + </head> + <body> + <a id="link" href="">Link</a> + <script> + const urlParams = new URLSearchParams(window.location.search); + document.getElementById("link").href = urlParams.get('linkLocation'); + </script> + </body> +</html> diff --git a/tests/auto/core/origins/resources/mixedSchemes.html b/tests/auto/core/origins/resources/mixedSchemes.html index 260372a29..3e50c2c3b 100644 --- a/tests/auto/core/origins/resources/mixedSchemes.html +++ b/tests/auto/core/origins/resources/mixedSchemes.html @@ -6,23 +6,34 @@ var result; var canary; - function setIFrameUrl(url) { + function setIFrameUrl(frameUrl,imgUrl) { result = undefined; canary = undefined; - document.getElementById("iframe").setAttribute("src", url); - // Early fire is OK unless the test is expecting cannotLoad. - // If timeout is too short then a false positive is possible. - setTimeout(() => { result = result || "cannotLoad"; }, 1000); + let img = document.createElement('img'); + img.onerror = function() { + console.log("TEST:cannotLoad"); + console.log("TEST:done"); + }; + img.onload = function() { + document.getElementById("iframe").setAttribute("src", frameUrl); + }; + img.src = imgUrl } addEventListener("load", function() { document.getElementById("iframe").addEventListener("load", function() { - if (canary && window[0].canary) - result = "canLoadAndAccess"; - else - result = "canLoadButNotAccess"; + if (canary && window[0].canary) { + console.log("TEST:canLoadAndAccess"); + console.log("TEST:done"); + } else { + console.log("TEST:canLoadButNotAccess"); + console.log("TEST:done"); + } }); }); + window.onerror = function(message, url, line, col, errorObj) { + return true; + }; </script> </head> <body> diff --git a/tests/auto/core/origins/resources/mixedSchemes_frame.html b/tests/auto/core/origins/resources/mixedSchemes_frame.html index 40ace2d2f..9499caa1f 100644 --- a/tests/auto/core/origins/resources/mixedSchemes_frame.html +++ b/tests/auto/core/origins/resources/mixedSchemes_frame.html @@ -3,9 +3,12 @@ <head> <title>Mixed - Frame</title> <script> - console.log('Frame Loaded'); - var canary = true; - parent.canary = true; + try{ + var canary = true; + parent.canary = true; + }catch(exception){ + }; + </script> </head> <body></body> diff --git a/tests/auto/core/origins/resources/red.png b/tests/auto/core/origins/resources/red.png Binary files differnew file mode 100644 index 000000000..5ae85192b --- /dev/null +++ b/tests/auto/core/origins/resources/red.png diff --git a/tests/auto/core/origins/tst_origins.cpp b/tests/auto/core/origins/tst_origins.cpp index 6596b3fa0..81385701f 100644 --- a/tests/auto/core/origins/tst_origins.cpp +++ b/tests/auto/core/origins/tst_origins.cpp @@ -13,6 +13,7 @@ #include <QtWebEngineCore/qwebenginesettings.h> #include <QtWebEngineCore/qwebengineprofile.h> #include <QtWebEngineCore/qwebenginepage.h> +#include <QtWebEngineWidgets/qwebengineview.h> #if defined(WEBSOCKETS) #include <QtWebSockets/qwebsocket.h> @@ -150,7 +151,15 @@ void registerSchemes() scheme.setFlags(QWebEngineUrlScheme::LocalScheme | QWebEngineUrlScheme::CorsEnabled); QWebEngineUrlScheme::registerScheme(scheme); } - + { + QWebEngineUrlScheme scheme("fetchapi-allowed"); + scheme.setFlags(QWebEngineUrlScheme::CorsEnabled | QWebEngineUrlScheme::FetchApiAllowed); + QWebEngineUrlScheme::registerScheme(scheme); + } + { + QWebEngineUrlScheme scheme("fetchapi-not-allowed"); + QWebEngineUrlScheme::registerScheme(scheme); + } } Q_CONSTRUCTOR_FUNCTION(registerSchemes) @@ -262,6 +271,23 @@ public: messages << message; qCDebug(lc) << message; } + + bool logContainsDoneMarker() const { return messages.contains("TEST:done"); } + + QString findResultInLog() const + { + // make sure we do not have some extra logs from blink + for (auto message : messages) { + QStringList s = message.split(':'); + if (s.size() > 1 && s[0] == "TEST") + return s[1]; + } + return QString(); + } + + void clearLog() { messages.clear(); } + +private: QStringList messages; }; @@ -281,6 +307,9 @@ private Q_SLOTS: void subdirWithoutAccess(); void fileAccessRemoteUrl_data(); void fileAccessRemoteUrl(); + void fileAccessLocalUrl_data(); + void fileAccessLocalUrl(); + void mixedSchemes_data(); void mixedSchemes(); void mixedSchemesWithCsp(); void mixedXHR_data(); @@ -305,6 +334,9 @@ private Q_SLOTS: void redirectInterceptorSecure(); void redirectInterceptorFile(); void redirectInterceptorHttp(); + void fetchApiCustomUrl_data(); + void fetchApiCustomUrl(); + void fetchApiHttpUrl(); private: bool verifyLoad(const QUrl &url) @@ -442,8 +474,8 @@ void tst_Origins::jsUrlRelative() // URLs even without an initial slash. QCOMPARE(eval(QSL("new URL('bar', 'qrc:foo').href")), QVariant(QSL("qrc:bar"))); QCOMPARE(eval(QSL("new URL('baz', 'qrc:foo/bar').href")), QVariant(QSL("qrc:foo/baz"))); - QCOMPARE(eval(QSL("new URL('bar', 'qrc://foo').href")), QVariant()); - QCOMPARE(eval(QSL("new URL('bar', 'qrc:///foo').href")), QVariant()); + QCOMPARE(eval(QSL("new URL('bar', 'qrc://foo').href")), QVariant(QSL("qrc://bar"))); + QCOMPARE(eval(QSL("new URL('bar', 'qrc:///foo').href")), QVariant(QSL("qrc:///bar"))); // With a slash it works the same as http except 'foo' is part of the path and not the host. QCOMPARE(eval(QSL("new URL('bar', 'qrc:/foo').href")), QVariant(QSL("qrc:/bar"))); @@ -577,13 +609,22 @@ void tst_Origins::subdirWithoutAccess() void tst_Origins::fileAccessRemoteUrl_data() { QTest::addColumn<bool>("EnableAccess"); - QTest::addRow("enabled") << true; - QTest::addRow("disabled") << false; + QTest::addColumn<bool>("UserGesture"); + QTest::addRow("enabled, XHR") << true << false; + QTest::addRow("enabled, link click") << true << true; + QTest::addRow("disabled, XHR") << false << false; + QTest::addRow("disabled, link click") << false << true; } void tst_Origins::fileAccessRemoteUrl() { QFETCH(bool, EnableAccess); + QFETCH(bool, UserGesture); + + QWebEngineView view; + view.setPage(m_page); + view.resize(800, 600); + view.show(); HttpServer server; server.setResourceDirs({ QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + "/resources" }); @@ -592,11 +633,88 @@ void tst_Origins::fileAccessRemoteUrl() ScopedAttribute sa1(m_page->settings(), QWebEngineSettings::LocalContentCanAccessRemoteUrls, EnableAccess); ScopedAttribute sa2(m_page->settings(), QWebEngineSettings::ErrorPageEnabled, false); - QVERIFY(verifyLoad("file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() - + "/resources/mixedXHR.html")); + if (UserGesture) { + QString remoteUrl(server.url("/link.html").toString()); +#ifdef Q_OS_WIN + QString localUrl("file:///" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + "/resources/link.html?linkLocation=" + remoteUrl); +#else + QString localUrl("file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + "/resources/link.html?linkLocation=" + remoteUrl); +#endif + + QVERIFY(verifyLoad(localUrl)); + + QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, elementCenter(m_page, "link")); + // Succeed independently of EnableAccess == false + QTRY_COMPARE(m_page->url(), remoteUrl); + + // Back/forward navigation is also allowed, however they are not user gesture + m_page->triggerAction(QWebEnginePage::Back); + QTRY_COMPARE(m_page->url(), localUrl); + m_page->triggerAction(QWebEnginePage::Forward); + QTRY_COMPARE(m_page->url(), remoteUrl); + } else { + QVERIFY(verifyLoad("file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + "/resources/mixedXHR.html")); + eval("sendXHR('" + server.url("/mixedXHR.txt").toString() + "')"); + QTRY_COMPARE(eval("result"), (EnableAccess ? QString("ok") : QString("error"))); + } +} + +void tst_Origins::fileAccessLocalUrl_data() +{ + QTest::addColumn<bool>("EnableAccess"); + QTest::addColumn<bool>("UserGesture"); + QTest::addRow("enabled, XHR") << true << false; + QTest::addRow("enabled, link click") << true << true; + QTest::addRow("disabled, XHR") << false << false; + QTest::addRow("disabled, link click") << false << true; +} + +void tst_Origins::fileAccessLocalUrl() +{ + QFETCH(bool, EnableAccess); + QFETCH(bool, UserGesture); + + QWebEngineView view; + view.setPage(m_page); + view.resize(800, 600); + view.show(); + + ScopedAttribute sa1(m_page->settings(), QWebEngineSettings::LocalContentCanAccessFileUrls, EnableAccess); + ScopedAttribute sa2(m_page->settings(), QWebEngineSettings::ErrorPageEnabled, false); + + if (UserGesture) { +#ifdef Q_OS_WIN + QString localUrl1("file:///" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + "/resources/link.html?linkLocation=link.html"); + QString localUrl2("file:///" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + "/resources/link.html"); +#else + QString localUrl1("file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + "/resources/link.html?linkLocation=link.html"); + QString localUrl2("file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + "/resources/link.html"); +#endif - eval("sendXHR('" + server.url("/mixedXHR.txt").toString() + "')"); - QTRY_COMPARE(eval("result"), (EnableAccess ? QString("ok") : QString("error"))); + QVERIFY(verifyLoad(localUrl1)); + QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, elementCenter(m_page, "link")); + // Succeed independently of EnableAccess == false + QTRY_COMPARE(m_page->url(), localUrl2); + + // Back/forward navigation is also allowed, however they are not user gesture + m_page->triggerAction(QWebEnginePage::Back); + QTRY_COMPARE(m_page->url(), localUrl1); + m_page->triggerAction(QWebEnginePage::Forward); + QTRY_COMPARE(m_page->url(), localUrl2); + } else { + QVERIFY(verifyLoad("file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + "/resources/mixedXHR.html")); + eval("sendXHR('file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + "/resources/mixedXHR.txt" + "')"); + QTRY_COMPARE(eval("result"), (EnableAccess ? QString("ok") : QString("error"))); + } } // Load the main page over one scheme with an iframe over another scheme. @@ -607,89 +725,135 @@ void tst_Origins::fileAccessRemoteUrl() // Additionally for unregistered custom schemes and custom schemes without // LocalAccessAllowed it should not be possible to load an iframe over the // file: scheme. -void tst_Origins::mixedSchemes() +void tst_Origins::mixedSchemes_data() { - ScopedAttribute sa(m_page->settings(), QWebEngineSettings::ErrorPageEnabled, false); + QTest::addColumn<QString>("schemeFrom"); + QTest::addColumn<QVariantMap>("testPairs"); - QVERIFY(verifyLoad("file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() - + "/resources/mixedSchemes.html")); - eval("setIFrameUrl('file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() - + "/resources/mixedSchemes_frame.html')"); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess"))); - eval(QSL("setIFrameUrl('qrc:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); - eval(QSL("setIFrameUrl('tst:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("cannotLoad"))); - - QVERIFY(verifyLoad(QSL("qrc:/resources/mixedSchemes.html"))); - eval("setIFrameUrl('file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() - + "/resources/mixedSchemes_frame.html')"); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("cannotLoad"))); - eval(QSL("setIFrameUrl('qrc:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess"))); - eval(QSL("setIFrameUrl('tst:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); + QVariant SLF = QVariant(QSL("canLoadAndAccess")), OK = QVariant(QSL("canLoadButNotAccess")), + ERR = QVariant(QSL("cannotLoad")); + std::vector<std::pair<const char *, std::vector<std::pair<const char *, QVariant>>>> data = { + { "file", + { + { "file", SLF }, + { "qrc", OK }, + { "tst", ERR }, + } }, + { "qrc", + { + { "file", ERR }, + { "qrc", SLF }, + { "tst", OK }, + } }, + { "tst", + { + { "file", ERR }, + { "qrc", OK }, + { "tst", SLF }, + } }, + { "PathSyntax", + { + { "PathSyntax", SLF }, + { "PathSyntax-Local", ERR }, + { "PathSyntax-LocalAccessAllowed", OK }, + { "PathSyntax-NoAccessAllowed", OK }, + } }, + { "PathSyntax-LocalAccessAllowed", + { + { "PathSyntax", OK }, + { "PathSyntax-Local", OK }, + { "PathSyntax-LocalAccessAllowed", SLF }, + { "PathSyntax-NoAccessAllowed", OK }, + } }, + { "PathSyntax-NoAccessAllowed", + { + { "PathSyntax", OK }, + { "PathSyntax-Local", ERR }, + { "PathSyntax-LocalAccessAllowed", OK }, + { "PathSyntax-NoAccessAllowed", OK }, + } }, + { "HostSyntax://a", + { + { "HostSyntax://a", SLF }, + { "HostSyntax://b", OK }, + } }, + { "local-localaccess", + { + { "local-cors", OK }, + { "local-localaccess", SLF }, + { "local", OK }, + } }, + { "local-cors", + { + { "local", OK }, + { "local-cors", SLF }, + } }, + }; - QVERIFY(verifyLoad(QSL("tst:/resources/mixedSchemes.html"))); - eval("setIFrameUrl('file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() - + "/resources/mixedSchemes_frame.html')"); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("cannotLoad"))); - eval(QSL("setIFrameUrl('qrc:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); - eval(QSL("setIFrameUrl('tst:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess"))); + for (auto &&d : data) { + auto schemeFrom = d.first; + QVariantMap testPairs; + for (auto &&destSchemes : d.second) { + auto &&destScheme = destSchemes.first; + testPairs[destScheme] = destSchemes.second; + } + QTest::addRow("%s", schemeFrom) << schemeFrom << testPairs; + } +} - QVERIFY(verifyLoad(QSL("PathSyntax:/resources/mixedSchemes.html"))); - eval(QSL("setIFrameUrl('PathSyntax:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess"))); - eval(QSL("setIFrameUrl('PathSyntax-Local:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("cannotLoad"))); - eval(QSL("setIFrameUrl('PathSyntax-LocalAccessAllowed:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); - eval(QSL("setIFrameUrl('PathSyntax-NoAccessAllowed:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); +static QStringList protocolAndHost(const QString scheme) +{ + static QString srcDir(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath()); + QStringList result; + if (scheme == QSL("file")) { + return QStringList{ scheme, srcDir }; + } + if (scheme.contains(QSL("HostSyntax:"))) { + const QStringList &res = scheme.split(':'); + Q_ASSERT(res.size() == 2); + return res; + } + return QStringList{ scheme, "" }; +} - QVERIFY(verifyLoad(QSL("PathSyntax-LocalAccessAllowed:/resources/mixedSchemes.html"))); - eval(QSL("setIFrameUrl('PathSyntax:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); - eval(QSL("setIFrameUrl('PathSyntax-Local:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); - eval(QSL("setIFrameUrl('PathSyntax-LocalAccessAllowed:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess"))); - eval(QSL("setIFrameUrl('PathSyntax-NoAccessAllowed:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); +void tst_Origins::mixedSchemes() +{ + QFETCH(QString, schemeFrom); + QFETCH(QVariantMap, testPairs); - QVERIFY(verifyLoad(QSL("PathSyntax-NoAccessAllowed:/resources/mixedSchemes.html"))); - eval(QSL("setIFrameUrl('PathSyntax:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); - eval(QSL("setIFrameUrl('PathSyntax-Local:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("cannotLoad"))); - eval(QSL("setIFrameUrl('PathSyntax-LocalAccessAllowed:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); - eval(QSL("setIFrameUrl('PathSyntax-NoAccessAllowed:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); + ScopedAttribute sa(m_page->settings(), QWebEngineSettings::ErrorPageEnabled, false); + QString srcDir(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath()); + QString host; + auto pah = protocolAndHost(schemeFrom); + auto loadUrl = QString("%1:%2/resources/mixedSchemes.html").arg(pah[0]).arg(pah[1]); + QVERIFY(verifyLoad(loadUrl)); - QVERIFY(verifyLoad(QSL("HostSyntax://a/resources/mixedSchemes.html"))); - eval(QSL("setIFrameUrl('HostSyntax://a/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess"))); - eval(QSL("setIFrameUrl('HostSyntax://b/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); + QStringList schemesTo, expected, results; + for (auto it = testPairs.begin(), end = testPairs.end(); it != end; ++it) { - QVERIFY(verifyLoad(QSL("local-localaccess:/resources/mixedSchemes.html"))); - eval("setIFrameUrl('local-cors:/resources/mixedSchemes_frame.html')"); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); - eval(QSL("setIFrameUrl('local-localaccess:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess"))); - eval(QSL("setIFrameUrl('local:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); + auto schemeTo = it.key(); + auto pah = protocolAndHost(schemeTo); + auto expectedResult = it.value().toString(); + auto frameUrl = QString("%1:%2/resources/mixedSchemes_frame.html").arg(pah[0]).arg(pah[1]); + auto imgUrl = QString("%1:%2/resources/red.png").arg(pah[0]).arg(pah[1]); - QVERIFY(verifyLoad(QSL("local-cors:/resources/mixedSchemes.html"))); - eval("setIFrameUrl('local:/resources/mixedSchemes_frame.html')"); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); - eval(QSL("setIFrameUrl('local-cors:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess"))); - eval(QSL("setIFrameUrl('local:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); + eval(QString("setIFrameUrl('%1','%2')").arg(frameUrl).arg(imgUrl)); + + // wait for token in the log + QTRY_VERIFY(m_page->logContainsDoneMarker()); + const QString result = m_page->findResultInLog(); + m_page->clearLog(); + schemesTo.append(schemeTo.rightJustified(20)); + results.append(result.rightJustified(20)); + expected.append(expectedResult.rightJustified(20)); + } + + QVERIFY2(results == expected, + qPrintable(QString("\nFrom '%1' to:\n\tScheme: %2\n\tActual: %3\n\tExpect: %4") + .arg(schemeFrom) + .arg(schemesTo.join(' ')) + .arg(results.join(' ')) + .arg(expected.join(' ')))); } // Like mixedSchemes but adds a Content-Security-Policy: frame-src 'none' header. @@ -1005,12 +1169,17 @@ void tst_Origins::mixedContent() auto setIFrameUrl = [&] (const QString &scheme) { if (scheme == "data") - return QString("setIFrameUrl('data:,<script>var canary = true; parent.canary = true</script>')"); + return QString("setIFrameUrl('data:,<script>var canary = true; parent.canary = " + "true</script>','data:image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAAUA" + "AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/" + "w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==')"); auto frameUrl = QString("%1:%2/resources/mixedSchemes_frame.html").arg(scheme).arg(scheme == "file" ? srcDir : ""); - return QString("setIFrameUrl('%1')").arg(frameUrl); + auto imgUrl = + QString("%1:%2/resources/red.png").arg(scheme).arg(scheme == "file" ? srcDir : ""); + return QString("setIFrameUrl('%1','%2')").arg(frameUrl).arg(imgUrl); }; - m_page->messages.clear(); + m_page->clearLog(); QStringList schemesTo, expected, results; for (auto it = testPairs.begin(), end = testPairs.end(); it != end; ++it) { @@ -1019,15 +1188,10 @@ void tst_Origins::mixedContent() eval(setIFrameUrl(schemeTo)); - QTRY_COMPARE(eval(QSL("result !== undefined")), QVariant(true)); - auto result = eval(QSL("result")).toString(); - // Work-around some combinations missing JS loaded signals: - if (m_page->messages.count() > 0) { - if (m_page->messages[0] == QSL("Frame Loaded") && result == QSL("cannotLoad")) - result = QSL("canLoadButNotAccess"); - m_page->messages.clear(); - } - + // wait for token in the log + QTRY_VERIFY(m_page->logContainsDoneMarker()); + const QString result = m_page->findResultInLog(); + m_page->clearLog(); schemesTo.append(schemeTo.rightJustified(20)); results.append(result.rightJustified(20)); expected.append(expectedResult.rightJustified(20)); @@ -1447,5 +1611,120 @@ void tst_Origins::localMediaBlock() } +class FetchApiHandler : public QWebEngineUrlSchemeHandler +{ + Q_OBJECT +public: + FetchApiHandler(QByteArray schemeName, QObject *parent = nullptr) + : QWebEngineUrlSchemeHandler(parent), m_schemeName(schemeName) + { + } + + void requestStarted(QWebEngineUrlRequestJob *job) override + { + QCOMPARE(job->requestUrl(), QUrl(m_schemeName + ":about")); + fetchWasAllowed = true; + } + + bool fetchWasAllowed = false; + +private: + QByteArray m_schemeName; +}; + +class FetchApiPage : public QWebEnginePage +{ + Q_OBJECT + +signals: + void jsCalled(); + +public: + FetchApiPage(QWebEngineProfile *profile, QObject *parent = nullptr) + : QWebEnginePage(profile, parent) + { + } + +protected: + void javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel, + const QString &message, int, const QString &) override + { + qCritical() << "js:" << message; + emit jsCalled(); + } +}; + +void tst_Origins::fetchApiCustomUrl_data() +{ + QTest::addColumn<QUrl>("url"); + QTest::addColumn<QByteArray>("fetchApiScheme"); + QTest::addColumn<bool>("expectedFetchWasAllowed"); + + QTest::newRow("custom url with fetch allowed flag") + << QUrl("qrc:///resources/fetchApi.html?printRes=false&url=fetchapi-allowed:about") + << QBAL("fetchapi-allowed") << true; + QTest::newRow("custom url without fetch allowed flag") + << QUrl("qrc:///resources/fetchApi.html?printRes=false&url=fetchapi-not-allowed:about") + << QBAL("fetchapi-not-allowed") << false; +} + +void tst_Origins::fetchApiCustomUrl() +{ + QFETCH(QUrl, url); + QFETCH(QByteArray, fetchApiScheme); + QFETCH(bool, expectedFetchWasAllowed); + + QWebEngineProfile profile; + FetchApiHandler handler(fetchApiScheme); + + profile.installUrlSchemeHandler(fetchApiScheme, &handler); + + FetchApiPage page(&profile); + QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); + QSignalSpy jsSpy(&page, SIGNAL(jsCalled())); + + if (fetchApiScheme == "fetchapi-not-allowed") { + QTest::ignoreMessage(QtCriticalMsg, QRegularExpression("Failed to fetch")); + QTest::ignoreMessage( + QtCriticalMsg, + QRegularExpression("Fetch API cannot load fetchapi-not-allowed:about.")); + } + + page.load(url); + QTRY_VERIFY(loadSpy.count() > 0); + QTRY_COMPARE(handler.fetchWasAllowed, expectedFetchWasAllowed); + + if (fetchApiScheme == "fetchapi-not-allowed") { + QTRY_VERIFY(jsSpy.count() > 0); + } +} + +void tst_Origins::fetchApiHttpUrl() +{ + HttpServer httpServer; + QObject::connect(&httpServer, &HttpServer::newRequest, this, [](HttpReqRep *rr) { + rr->setResponseBody(QBAL("Fetch Was Allowed")); + rr->setResponseHeader(QBAL("Access-Control-Allow-Origin"), QBAL("*")); + rr->sendResponse(); + }); + QVERIFY(httpServer.start()); + + QWebEngineProfile profile; + FetchApiPage page(&profile); + + QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); + QSignalSpy jsSpy(&page, SIGNAL(jsCalled())); + + QTest::ignoreMessage(QtCriticalMsg, QRegularExpression("Fetch Was Allowed")); + + const QByteArray fullUrl = QByteArray("qrc:///resources/fetchApi.html?printRes=true&url=") + + httpServer.url("/somepage.html").toEncoded(); + page.load(QUrl(fullUrl)); + + QTRY_VERIFY(loadSpy.count() > 0); + QTRY_VERIFY(jsSpy.count() > 0); + QVERIFY(httpServer.stop()); +} + QTEST_MAIN(tst_Origins) #include "tst_origins.moc" diff --git a/tests/auto/core/qtversion/CMakeLists.txt b/tests/auto/core/qtversion/CMakeLists.txt new file mode 100644 index 000000000..9a5e89266 --- /dev/null +++ b/tests/auto/core/qtversion/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_test(tst_qtversion + SOURCES + tst_qtversion.cpp + LIBRARIES + Qt::WebEngineCore +) + diff --git a/tests/auto/core/qtversion/tst_qtversion.cpp b/tests/auto/core/qtversion/tst_qtversion.cpp new file mode 100644 index 000000000..44c2d4e5c --- /dev/null +++ b/tests/auto/core/qtversion/tst_qtversion.cpp @@ -0,0 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <QtTest/QtTest> +#include <QtWebEngineCore/qwebenginepage.h> + +class tst_QtVersion : public QObject +{ + Q_OBJECT +signals: + void done(); +private Q_SLOTS: + void checkVersion(); +}; + +void tst_QtVersion::checkVersion() +{ + QWebEnginePage page; + QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); + QSignalSpy doneSpy(this, &tst_QtVersion::done); + page.load(QUrl("chrome://qt")); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 12000); + page.toPlainText([this](const QString &result) { + QVERIFY(result.contains(qWebEngineVersion())); + QVERIFY(result.contains(qWebEngineChromiumVersion())); + QVERIFY(result.contains(qWebEngineChromiumSecurityPatchVersion())); + emit done(); + }); + QTRY_VERIFY(doneSpy.size()); +} + +QTEST_MAIN(tst_QtVersion) + +#include "tst_qtversion.moc" diff --git a/tests/auto/core/qwebengineclientcertificatestore/CMakeLists.txt b/tests/auto/core/qwebengineclientcertificatestore/CMakeLists.txt index bef727137..8cee7f630 100644 --- a/tests/auto/core/qwebengineclientcertificatestore/CMakeLists.txt +++ b/tests/auto/core/qwebengineclientcertificatestore/CMakeLists.txt @@ -1,11 +1,16 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause + +include(../../httpserver/httpserver.cmake) +include(../../util/util.cmake) qt_internal_add_test(tst_qwebengineclientcertificatestore SOURCES tst_qwebengineclientcertificatestore.cpp LIBRARIES Qt::WebEngineCore + Test::HttpServer + Test::Util ) set(tst_qwebengineclientcertificatestore_resource_files @@ -13,6 +18,13 @@ set(tst_qwebengineclientcertificatestore_resource_files "resources/certificate1.crt" "resources/privatekey.key" "resources/privatekey1.key" + "resources/server.pem" + "resources/server.key" + "resources/client.pem" + "resources/client.key" + "resources/client2.pem" + "resources/client2.key" + "resources/ca.pem" ) qt_internal_add_resource(tst_qwebengineclientcertificatestore "tst_qwebengineclientcertificatestore" @@ -22,3 +34,47 @@ qt_internal_add_resource(tst_qwebengineclientcertificatestore "tst_qwebenginecli ${tst_qwebengineclientcertificatestore_resource_files} ) +if(LINUX AND NOT CMAKE_CROSSCOMPILING) + + get_filename_component(homePath $ENV{HOME} ABSOLUTE) + + find_program(pk12util_EXECUTABLE NAMES pk12util) + find_program(certutil_EXECUTABLE NAMES certutil) + + if(pk12util_EXECUTABLE AND certutil_EXECUTABLE) + add_custom_command( + DEPENDS resources/client2.p12 + COMMAND test -e "${homePath}/.pki/nssdb" || ${CMAKE_COMMAND} -E make_directory + "${homePath}/.pki/nssdb" + COMMAND test -e "${homePath}/.pki/nssdb/cert9.db" || ${certutil_EXECUTABLE} + -N --empty-password -d sql:${homePath}/.pki/nssdb + COMMAND test -e "${homePath}/.pki/nssdb/cert9.db" && ${pk12util_EXECUTABLE} + -d sql:${homePath}/.pki/nssdb + -i "${CMAKE_CURRENT_LIST_DIR}/resources/client2.p12" + -W "" + COMMAND ${CMAKE_COMMAND} -E touch pk12util.stamp + OUTPUT pk12util.stamp + VERBATIM + USES_TERMINAL + ) + add_custom_target( + add-user-personal-certificate + DEPENDS pk12util.stamp + ) + qt_internal_extend_target(tst_qwebengineclientcertificatestore DEFINES TEST_NSS) + add_dependencies(tst_qwebengineclientcertificatestore add-user-personal-certificate) + endif() + + find_program(certutil_EXECUTABLE NAMES certutil) + + if(certutil_EXECUTABLE) + add_custom_target(remove-user-personal-certificate + COMMAND ${CMAKE_COMMAND} -E remove pk12util.stamp + COMMAND ${certutil_EXECUTABLE} + -d sql:"${homePath}/.pki/nssdb" + -D + -n qwebengineclientcertificatestore + ) + endif() +endif() + diff --git a/tests/auto/core/qwebengineclientcertificatestore/resources/ca.pem b/tests/auto/core/qwebengineclientcertificatestore/resources/ca.pem new file mode 100644 index 000000000..cb62ad62c --- /dev/null +++ b/tests/auto/core/qwebengineclientcertificatestore/resources/ca.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIECzCCAvOgAwIBAgIUdhDW1WgGxF313LYA0JjEQpKbanQwDQYJKoZIhvcNAQEL +BQAwgZQxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJl +cmxpbjEXMBUGA1UECgwOVGhlIFF0IENvbXBhbnkxFDASBgNVBAsMC1F0V2ViRW5n +aW5lMRIwEAYDVQQDDAl3d3cucXQuaW8xIDAeBgkqhkiG9w0BCQEWEXF0d2ViZW5n +aW5lQHF0LmlvMB4XDTIyMTExNjExMDQxNFoXDTMyMTExMzExMDQxNFowgZQxCzAJ +BgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJlcmxpbjEXMBUG +A1UECgwOVGhlIFF0IENvbXBhbnkxFDASBgNVBAsMC1F0V2ViRW5naW5lMRIwEAYD +VQQDDAl3d3cucXQuaW8xIDAeBgkqhkiG9w0BCQEWEXF0d2ViZW5naW5lQHF0Lmlv +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxyNLLwAA+FgNQavVJ19n +gdoy+NKLHQyhzcRFykKSp9aAbpAR6e4ukxwG7mWNBcuR7zv1Zw/JqLFE0gmVztVw +FeQWdw1cvTN/OlVEuM+0ShTDHHsCqRpx7/XJT6ytMKVU8jdZN4Vl1m7MubWv4aPy +0WYYd3zIAicciYgy/RHaRhPTKpPzWIPYhmHsM5w2cebL8I0aZXUkC0OeklJArnp9 +007Fr6SXXK0xQ3RO20n7X193gCfd5U70lug0ks/ZZqxtzPHmzIO1WGAOBura50HR +hxUKAu7qQHzBiW5Qwdn0af4FPLJR/SX8ADKTLCSWlMOo1FLYO5w6D8hB4K6/b9VQ +RwIDAQABo1MwUTAdBgNVHQ4EFgQUXuTuB85/iBgwJpLdOc+8TB0KESIwHwYDVR0j +BBgwFoAUXuTuB85/iBgwJpLdOc+8TB0KESIwDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQsFAAOCAQEAvtucUJa0IECltWv8U6R+LQuZ1Q+ubbmstojO/h8tg6Wf +v6FZ5bH3oboSyGEcytRr6INf4G6znUNAypbodehAEW6/PETdzGM9CJyv2JPJAWzV +rxb1H5VTyiEs8924QOqcNATD+oe7G0vwnDkvprcqaWBA6yvQkWpCXoqMc+F95KnY +8VFt2VQw17l4L4nhaX3Us6hJLMiKV+dLeF0pN+pkCPRP9G5WKgW3mT2U6Gig+rLz +6L7rBbb5KWAttdAbuHCrMa65PgXoVD1P/GteFxUnghDd0PWgUaign8c/DyHGsrbA +uvJqSym0kmQQXptryRaKFsGcCrizdbE6FfrH2iE7vQ== +-----END CERTIFICATE----- diff --git a/tests/auto/core/qwebengineclientcertificatestore/resources/client.key b/tests/auto/core/qwebengineclientcertificatestore/resources/client.key new file mode 100644 index 000000000..21c8e3183 --- /dev/null +++ b/tests/auto/core/qwebengineclientcertificatestore/resources/client.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAqnHbq38y1VprEaV2xXzv2nAPyjqCuIfuick8qETkzEsNWPQi +dsBlLfcyf+15wEMhpRIwILXCrUM7Sb7WCGtg1XC00JZvCh2xPBMSD2fiQyHn4men +Fwh9vVbTf1v7w21ZT/pXQrwlgLgNWYZHE3JrcEAwlThQRIdQfzSE6/QeHfYZoGB9 +WfvbREsOWiUlZze/yrblS9vnAVhYwVurelc7lXyHA0dHmkcZ0HwMxVJZ/vLuCyIw +lNGT/ytnA9p1l8uFkAgTcbWZKoyJAsAZG9faZp46hk8+e3KAyKQ78aoUSbjAqnNQ +tBM3bnHeHanf3ddCxyej+k9PfSIY27a9FZxHpQIDAQABAoIBAFsomA8p8ZsQR9Fh +SJupDXMrmhZTotRkxxxkR4/LgP8OaO4ZbFFM5xBldFndPc+pV9Y8WwczjxIxsgTo +Dvrjyx98rwgcXPjxFniFzpP0wJudB7McMs5r2SwpwuYL4SQNWMYgowjrLbehOGqY +GW16NaIMgq9cNfng0RmnkivMHUtyE5GGdK+C6cyK+fIE+cNtQtHPRKfEnwbE9VHz +3EY/nCXGZvMFyj5uHaU4EeZFCzo19TUqhh8H7b0EA44pBtb5U/CxsH4xphZ7rpjt +iVjMfRSMR4qalQNIs6ZEj57We+M/zca/Qq1yhjW+0NYbZifcYo1Oj6e4lC9YlIgn +kGkcuUECgYEA1j0iVFjgBXS8pJP3jBgmbrbBBTNEUv27yjnJCAQx5TbplJkvBM4/ +qzum1uH2o6uRrFtrYJFiAhDHARtg+70rMeYqZp8WFvzJT5c5s+FOmGQPfFjgrD6e +wfnCwFzS7nohJ8TM2mPGJ88pBv0eBYW6D0f7fvcJmEk8hnGktdLRCrECgYEAy6tU +YFZDzGhbgrG2wWzBvAKVngUNhrYZHMiF1WVN8zZdCm7Z8b1S/NMe0rPA5orhAkSX +8fxlDfKOm+U2fKp43aiN0NDiP0TlGRbypAXe7FSnvDxNHbV+Ie0UbwuiJ4s3vJuc +6cdzgKqAs5/rjPXPdUpM8C7344HV7azgSzHIYTUCgYAtVmCmcuxtmye0uG+BoTa4 +5UnxvMivu2x7PkFRxfl9JWLHBKfTn4YPyZ7kCIu2VT+NtwcBN6MDBuPmUxHyFDVI +6Ql+EBqPoM1FX55hd8O3Mi2oxfI94T6dlCpnpP0qZIQRs28apFSx5gArr3Mj/gnC +5BvP4Z2RMaZyWShfJg8A8QKBgQClZEhswyDjiYtmorJqeMsKxn6BiFDnqFDUUvJ7 +zHx0mR0NL9/Es54Eud059ccccIMwuEs7s17M6MBuUMDik/z647nmbPqNroDs0vnP +wQS6njRoY/+rtIrtOf1x/9x6iE+G1keigNmHDu7c72z1V1hVQzUfhsS+99yl2dF6 +vr6eUQKBgF/OHW1bE3FruZ+53Arcb94N/IKnpH9VWoB3elIzr0w6pLtL4HHhmQ58 +TayEpq6YguUAjTvCBbaHuYuKPHiXCAy5DhtrXvP4YdMNH9X1nHc7jVEbGltVbnQU +bG/p5YfZSrDmsjf8w0z7feFOcovC6vF1YCXc8OHK/LQ6JFJ/gtO1 +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/core/qwebengineclientcertificatestore/resources/client.pem b/tests/auto/core/qwebengineclientcertificatestore/resources/client.pem new file mode 100644 index 000000000..dd1f898f7 --- /dev/null +++ b/tests/auto/core/qwebengineclientcertificatestore/resources/client.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDrzCCApcCFFNQAgGBu5nr81tUMdXXLGkm8Li+MA0GCSqGSIb3DQEBCwUAMIGU +MQswCQYDVQQGEwJERTEPMA0GA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4x +FzAVBgNVBAoMDlRoZSBRdCBDb21wYW55MRQwEgYDVQQLDAtRdFdlYkVuZ2luZTES +MBAGA1UEAwwJd3d3LnF0LmlvMSAwHgYJKoZIhvcNAQkBFhFxdHdlYmVuZ2luZUBx +dC5pbzAeFw0yMjExMTYxMjExMDFaFw0zMjExMTMxMjExMDFaMIGSMQswCQYDVQQG +EwJERTEPMA0GA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xFzAVBgNVBAoM +DlRoZSBRdCBDb21wYW55MRQwEgYDVQQLDAtRdFdlYkVuZ2luZTEVMBMGA1UEAwwM +Y2xpZW50LnF0LmlvMRswGQYJKoZIhvcNAQkBFgxjbGllbnRAcXQuaW8wggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqcdurfzLVWmsRpXbFfO/acA/KOoK4 +h+6JyTyoROTMSw1Y9CJ2wGUt9zJ/7XnAQyGlEjAgtcKtQztJvtYIa2DVcLTQlm8K +HbE8ExIPZ+JDIefiZ6cXCH29VtN/W/vDbVlP+ldCvCWAuA1ZhkcTcmtwQDCVOFBE +h1B/NITr9B4d9hmgYH1Z+9tESw5aJSVnN7/KtuVL2+cBWFjBW6t6VzuVfIcDR0ea +RxnQfAzFUln+8u4LIjCU0ZP/K2cD2nWXy4WQCBNxtZkqjIkCwBkb19pmnjqGTz57 +coDIpDvxqhRJuMCqc1C0Ezducd4dqd/d10LHJ6P6T099Ihjbtr0VnEelAgMBAAEw +DQYJKoZIhvcNAQELBQADggEBALE75ZQxmEXJA16cNAxxmxCKHkaqAE6Ulim1vXNH +jCFfNCDGYn/R28F3BVtMe+bIMoomaTh3h5eOd/9uc2nm8IiT5FUz9epJWPeRG/cl +I+hQ3fvaE7oJ3m3EwfGq1mdqUf1zi+DFjtkimNbn9ZRDocZfpO5VN0u23ptEuk0P +5cH4+Dst0giRMv5W0kXG6QD13H/eVH3jDZCtZa/8T4oxGGskHEa4yDr8s976lVOV +XLI1r7oN4a/KXKow8WN3oHFeKn4QJx86z1uecuZLtT8xjABKSWpZqgsIlmGTGE1a +9W06C+uPVamwn5ND3gnf93YQqn6PwrjlHdrQOTG/vngJLPw= +-----END CERTIFICATE----- diff --git a/tests/auto/core/qwebengineclientcertificatestore/resources/client2.key b/tests/auto/core/qwebengineclientcertificatestore/resources/client2.key new file mode 100644 index 000000000..3c1346519 --- /dev/null +++ b/tests/auto/core/qwebengineclientcertificatestore/resources/client2.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAv0vrzULGwDJBoZgnGXdkMFxCvkTqqQYCE/LlNtStLJfJH7Fo +CgenVFcJ8RIFHdkL7HeFAIZjDLSjIp2Ud41fd+VsaGgB/+j1/UeEN8nkArvYB9ol +OnKGq6CbSrCocrLo2o2X+6eyLtrtLG6RLr8/UiqB2OWNAdnw70S5RCvnbV6phr8z +bgYqPdPSBaedfZk5Kj6yM6XvIKSK6IjgZuo+Z5SyabJqk2VhaBlB7mjCf3Mj4zPD +XvQXsAq0ZNQXQVwKRfJ2I9uAeNAZiQP5i00pBqe2kIJEKnk8qbP4/Jho2Tp8XSBC +jHMn0oWrAZyO9vw3W940qmqmdRftyt+J8DO9xwIDAQABAoIBAGBpXTCYRR88tQNC +cgJNv/r3pNPMXBBP7OAs/QUDbzwYS89jVDIp5VWGgIY1NMr0RyQooKnBEU6oA8hA +b0FJySHeSSLduJRHzyKV1rdfU0Fldt2OPlEUw3bgfSPJoTwdm2n7DuxQemdPA1Xv +a9CJpto8fjDYkJasRtfwZQdMsVjXCfQ/cCzkOkblUDZcc7yTx3uiBKF8Jy8C+0qc +98btotYU88KWoE9A0ucWt/ik68MjYmccO6PYXKerNW2Ijgd1kik35G3TbEWxOFWW +y3zLFtfoD+21SdUgTMzM06owDVfSt/MER4tOxFyUPRuze7BJXrBofGQfuPiGiPuK +f5QZP8ECgYEA+x1PkClsqtRnjrzmRfi3OFez1Kbbzneucg5ssWR+Hd4EUFhhO42q +te1ZYoydy09tEqd002U7e5hob0/o+rVK9jldpZszMCBfVDYCDqdtw5rNI89bL1Uz +8krn6nk3BBx42lgAFU4C1JEaur4r14OOUtoFfRTAwjogQHcDmpyPNjcCgYEAwwSv +FJAKRjw1oOXKlGotoeYEAREVxH9HFnfM5IcVwcwMt+KUFEyrMtXeH1gk7jo+2ev4 +87njQ8hU3VPObCUcnTJHi2a6D9JIY+zA9bKTJjc8drcBathipmwtak14TsX2qe14 +JBIKlC3V0h1FqM3ep76p4dnt7sTmVc7ZOqBR7PECgYEA1HQE94wEkzdnch0hmbuG +kBWrYNPXDgS1w2uuzBqglPZcoflUMkV2U7s+r6EWc4d8WZbxwVRZkgTs/pgWHd66 +UD1SnKUFFsecv6t97BX9SMu0mYJ6vD4S2ABF3Fu3jzPjj596WowI2vz1J19zyj9U +b4ZjtGKVfv4cgU3v76RbidsCgYAx4CvKzX/jMJjimoJx7KnZAxO5Fh6ED60loOQE ++ktlMgN6r/cBLg6GxM23JHrldn4Gi+QyqTLnbf/OTxW28NLdnTNRAqfJThV3gOBk +thQOLQhIsEsrgUXRnE8NJd0EAHsyQGp+hyKvfP13bEcZgfVU311hRrQkYbUq8uj5 +pnDtcQKBgEFIpP7EzdJWrVOUjnjMQloqBhW8KVVtNwI5bmlcsUvVYjfZph016SiF +UTfZss1KkBmQClAVtyZsrKIfObIJ9KJ4hPAzzk+ca1D6XTLsYjxPwtB/U0ewB2Dm +yMxkXpT1kAiJ2Tdr1hZ8OcQhvnGWmrhtz+AkjyLXiYgST7Hubrxt +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/core/qwebengineclientcertificatestore/resources/client2.p12 b/tests/auto/core/qwebengineclientcertificatestore/resources/client2.p12 Binary files differnew file mode 100644 index 000000000..81e7eb624 --- /dev/null +++ b/tests/auto/core/qwebengineclientcertificatestore/resources/client2.p12 diff --git a/tests/auto/core/qwebengineclientcertificatestore/resources/client2.pem b/tests/auto/core/qwebengineclientcertificatestore/resources/client2.pem new file mode 100644 index 000000000..39c0b3f09 --- /dev/null +++ b/tests/auto/core/qwebengineclientcertificatestore/resources/client2.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDsTCCApkCFFNQAgGBu5nr81tUMdXXLGkm8LjBMA0GCSqGSIb3DQEBCwUAMIGU +MQswCQYDVQQGEwJERTEPMA0GA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4x +FzAVBgNVBAoMDlRoZSBRdCBDb21wYW55MRQwEgYDVQQLDAtRdFdlYkVuZ2luZTES +MBAGA1UEAwwJd3d3LnF0LmlvMSAwHgYJKoZIhvcNAQkBFhFxdHdlYmVuZ2luZUBx +dC5pbzAeFw0yMjExMTYxOTIwMzBaFw0zMjExMTMxOTIwMzBaMIGUMQswCQYDVQQG +EwJERTEPMA0GA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xFzAVBgNVBAoM +DlRoZSBRdCBDb21wYW55MRQwEgYDVQQLDAtRdFdlYkVuZ2luZTEWMBQGA1UEAwwN +Y2xpZW50Mi5xdC5pbzEcMBoGCSqGSIb3DQEJARYNY2xpZW50MkBxdC5pbzCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9L681CxsAyQaGYJxl3ZDBcQr5E +6qkGAhPy5TbUrSyXyR+xaAoHp1RXCfESBR3ZC+x3hQCGYwy0oyKdlHeNX3flbGho +Af/o9f1HhDfJ5AK72AfaJTpyhqugm0qwqHKy6NqNl/unsi7a7SxukS6/P1Iqgdjl +jQHZ8O9EuUQr521eqYa/M24GKj3T0gWnnX2ZOSo+sjOl7yCkiuiI4GbqPmeUsmmy +apNlYWgZQe5own9zI+Mzw170F7AKtGTUF0FcCkXydiPbgHjQGYkD+YtNKQantpCC +RCp5PKmz+PyYaNk6fF0gQoxzJ9KFqwGcjvb8N1veNKpqpnUX7crfifAzvccCAwEA +ATANBgkqhkiG9w0BAQsFAAOCAQEAic8F8q1TpP2ufnBRbrBp54Jgddl/zdVb7O3M +AAK67KiEpEr9xPPVcIowfns1ZTIsIB8D4VS4NQGJXBrwvGWL08SpSmi76I1E156x +9Hql0PHXCjqsJTOSEvljIgQ4sp33zs0DTmlyejSSGnG9sw2FtcYAGZNV+ImAhTO2 +DNxw3BnF++ilHsQbiWIKD5z14bOXb77SJrimup0YBzfwBWJO013k8g8lkiRRs5Ng +XYVr3NoTLcIJQ7BTFu4W1Wegxwrw3fQZ98BBlCVh0htrOcLpWKelJeI16MgZA/7T +P4MwvN5tkyjqrcsrDORldR6JKdX8i+GLF49MgRW4QispcZzoYA== +-----END CERTIFICATE----- diff --git a/tests/auto/core/qwebengineclientcertificatestore/resources/server.key b/tests/auto/core/qwebengineclientcertificatestore/resources/server.key new file mode 100644 index 000000000..632cc4d2e --- /dev/null +++ b/tests/auto/core/qwebengineclientcertificatestore/resources/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA5gQoJryenjmvzy4RbqHdNXHK8Gk/8Lto1SwT8+Wbh5EyYRTt +hFdioT1JYcIe3XMwOmx3TjADY1jAXAPfeRcjTkMcnZwF76AXUK2XqBANhaG1wjsi +b7ISGU/U5/Jarm2iwQJ5zjKsNm8pZYqpmKsYAVFMErtfcpdLdSp6BG54SrbItcXh +WHfsUs5cuVEi9nCeugLkDzoPLlj/TeouKWOdzhyvLXkPvPmD4/hD0dULTXpCDZhf +73AuQBWTGsWeUnJQiQhDRwuXWhGRX8qFJQ4rzY8rIbaKhge+BQ6BL+pij2uzHKNQ +j12ZLFZgLihLDJogGp08y9Ud6Ru/3WGoFkY38wIDAQABAoIBABM/TczQA8XhteB0 +Tmkfik8qknzDkeInDIKqCZFjKTyS3dBZ2/YzCcHMSxOvFr4ZIXQCF4mnYuExUAdj +G5QaZ43o98AIikae8tSBcitSDI+eFIOIRz1pfTI5B+vQz93AttnHx0GF4/s6GhCx +JbfsuTmDAAahPz9rgZjwUP2F8PLvaAZqJrXBPY+QLWz0SN2zh6vWAHPbJA0sO/4E +oWUhRPXJDf33YCFxnwtbUBie5313suAfNspODcyH+AxBH2FFh63pe0ZGOhX7XFMJ +yxJqujeZrQdfwFZNPXAPVLJGbd7AIOrVE+O8/bYUB/uuj6pPJBqr+Ob/JhY48pRb +VG2qL4ECgYEA9n3PuL13F9XFcLeergGH7fUcSQeD1T6Z1qaI2Wth0Umfmer/fFZh +IKSCSwEGMTLsalFdlTj8jsSAasjuSorQTeSgHjzvzik1Ll2P6syputjsD1RX/nkl +8L50Pwdeey57Y9dgow7Cw/heGYs6dkXLe9H6qM7eoB8Vrk7/TAFuqNECgYEA7uOl +oKyOxeLn005cenc5enY2IxDhXTaAjTGHE64C0lmicD2OZB7/b+ZIb8M5R7GnCNox +4TxLSRhZYOMO/QcTrnSND5PXbX/HLd3nyQRIN1XtBbg7pJooxP/MQ/Ne5XTTMjCg +qPudkOe0ZgUHEcuH8m/YAFY3DDJC50uiXqYtxYMCgYBHfL+ExbZHfGExyp9Duf/x +PHhCmeJbMzessEnaPLF24FJgcm48YlTzAaMkG5zvIeS9BPIOOCPPSCAyWCn8BnxZ +SuhBPM0TzpG067+0ijzjiswTuhN3Iy2kv6e5K+rz8MwqbamCQOKtsVehMub2rFFS +jNiUosKgT8Oa9SBHq9arMQKBgQCE3EVEnFP3iOAILH/QeLiV/GLVk9DTR7mtTUtj +zZayKLnoFMQ5uOe182x8BCa6UfqlOL0fGKqCZ7Fl6kJuxV3T2+yMKlxZAQTk5JLB +wMjtRbPCR5mcTUS5c87GR/eSRCwlsNfZw775VXSGfOtWoUzlsACBB3IsLVP6UZ1n +aKLyQwKBgC61BvKiyGBEYIchqMI4dSF+zCJbSjNUtjwVobcgC6yERZtX2OeLFCoh +NEf9CcL2Eqb+RzwAD3OV65AiZcrThQNXZ8poBxvwWK8I6E6zB+LX7POAvNu/AV/5 +ANnxwHGGLqi+wTcdMZal2iXkdsrno1Ek/nGMCdA7IVs7l5k7fEpG +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/core/qwebengineclientcertificatestore/resources/server.pem b/tests/auto/core/qwebengineclientcertificatestore/resources/server.pem new file mode 100644 index 000000000..4706fa73e --- /dev/null +++ b/tests/auto/core/qwebengineclientcertificatestore/resources/server.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDrzCCApcCFFNQAgGBu5nr81tUMdXXLGkm8Li/MA0GCSqGSIb3DQEBCwUAMIGU +MQswCQYDVQQGEwJERTEPMA0GA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4x +FzAVBgNVBAoMDlRoZSBRdCBDb21wYW55MRQwEgYDVQQLDAtRdFdlYkVuZ2luZTES +MBAGA1UEAwwJd3d3LnF0LmlvMSAwHgYJKoZIhvcNAQkBFhFxdHdlYmVuZ2luZUBx +dC5pbzAeFw0yMjExMTYxMjExMTRaFw0zMjExMTMxMjExMTRaMIGSMQswCQYDVQQG +EwJERTEPMA0GA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xFzAVBgNVBAoM +DlRoZSBRdCBDb21wYW55MRQwEgYDVQQLDAtRdFdlYkVuZ2luZTEVMBMGA1UEAwwM +c2VydmVyLnF0LmlvMRswGQYJKoZIhvcNAQkBFgxzZXJ2ZXJAcXQuaW8wggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDmBCgmvJ6eOa/PLhFuod01ccrwaT/w +u2jVLBPz5ZuHkTJhFO2EV2KhPUlhwh7dczA6bHdOMANjWMBcA995FyNOQxydnAXv +oBdQrZeoEA2FobXCOyJvshIZT9Tn8lqubaLBAnnOMqw2bylliqmYqxgBUUwSu19y +l0t1KnoEbnhKtsi1xeFYd+xSzly5USL2cJ66AuQPOg8uWP9N6i4pY53OHK8teQ+8 ++YPj+EPR1QtNekINmF/vcC5AFZMaxZ5SclCJCENHC5daEZFfyoUlDivNjyshtoqG +B74FDoEv6mKPa7Mco1CPXZksVmAuKEsMmiAanTzL1R3pG7/dYagWRjfzAgMBAAEw +DQYJKoZIhvcNAQELBQADggEBAHotgaBbqIlG4EqjzSpX8kQnZnGJUsA51dbY3K5C +4tNCd+JquQfPmCIKDHkRsmmEU6pcU+LT8m+toJ8Gx0XG4nrdUIDt0Nlf/QrykbPj +hN8z+aSfP9J5tg4NsT7qMWmqUHOa3BcsgWcC4IwWVkbOMz/XbczEQqdBJMbE0+PC +32ihTKPZBPC2QlIvXyuwupvQtcXgEjw1r2FQeYcmItk3CKbJPE/Rk4/aXSCo4b0F +iXPphh8BJPZVvQ2cLpPaGvcse5qjIhF9ODb2HEK3myMwuJVi7teURy8mPlS23Li/ +8gRCNu/stjMlkic7d3dqV0LwaG8+Df1W2wzxsT7IkxN/Z+o= +-----END CERTIFICATE----- diff --git a/tests/auto/core/qwebengineclientcertificatestore/tst_qwebengineclientcertificatestore.cpp b/tests/auto/core/qwebengineclientcertificatestore/tst_qwebengineclientcertificatestore.cpp index 3ca28b901..7d82a5640 100644 --- a/tests/auto/core/qwebengineclientcertificatestore/tst_qwebengineclientcertificatestore.cpp +++ b/tests/auto/core/qwebengineclientcertificatestore/tst_qwebengineclientcertificatestore.cpp @@ -1,9 +1,14 @@ // Copyright (C) 2019 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include <httpsserver.h> +#include <util.h> #include <QtTest/QtTest> #include <QtWebEngineCore/qwebengineclientcertificatestore.h> +#include <QtWebEngineCore/qwebenginepage.h> #include <QtWebEngineCore/qwebengineprofile.h> +#include <QtWebEngineCore/qwebenginecertificateerror.h> +#include <QtWebEngineCore/qwebenginesettings.h> class tst_QWebEngineClientCertificateStore : public QObject { @@ -14,8 +19,12 @@ public: ~tst_QWebEngineClientCertificateStore(); private Q_SLOTS: + void init(); + void cleanup(); void addAndListCertificates(); void removeAndClearCertificates(); + void clientAuthentication_data(); + void clientAuthentication(); }; tst_QWebEngineClientCertificateStore::tst_QWebEngineClientCertificateStore() @@ -26,6 +35,19 @@ tst_QWebEngineClientCertificateStore::~tst_QWebEngineClientCertificateStore() { } +void tst_QWebEngineClientCertificateStore::init() +{ + QCOMPARE(0, + QWebEngineProfile::defaultProfile()->clientCertificateStore()->certificates().size()); +} + +void tst_QWebEngineClientCertificateStore::cleanup() +{ + QWebEngineProfile::defaultProfile()->clientCertificateStore()->clear(); + QCOMPARE(0, + QWebEngineProfile::defaultProfile()->clientCertificateStore()->certificates().size()); +} + void tst_QWebEngineClientCertificateStore::addAndListCertificates() { // Load QSslCertificate @@ -52,21 +74,93 @@ void tst_QWebEngineClientCertificateStore::addAndListCertificates() QWebEngineProfile::defaultProfile()->clientCertificateStore()->add(cert, sslKey); QWebEngineProfile::defaultProfile()->clientCertificateStore()->add(certSecond, sslKeySecond); - QCOMPARE(2, QWebEngineProfile::defaultProfile()->clientCertificateStore()->certificates().length()); + QCOMPARE(2, QWebEngineProfile::defaultProfile()->clientCertificateStore()->certificates().size()); } void tst_QWebEngineClientCertificateStore::removeAndClearCertificates() { - QCOMPARE(2, QWebEngineProfile::defaultProfile()->clientCertificateStore()->certificates().length()); + addAndListCertificates(); + QCOMPARE(2, QWebEngineProfile::defaultProfile()->clientCertificateStore()->certificates().size()); // Remove one certificate from in-memory store auto list = QWebEngineProfile::defaultProfile()->clientCertificateStore()->certificates(); QWebEngineProfile::defaultProfile()->clientCertificateStore()->remove(list[0]); - QCOMPARE(1, QWebEngineProfile::defaultProfile()->clientCertificateStore()->certificates().length()); + QCOMPARE(1, QWebEngineProfile::defaultProfile()->clientCertificateStore()->certificates().size()); // Remove all certificates in-memory store QWebEngineProfile::defaultProfile()->clientCertificateStore()->clear(); - QCOMPARE(0, QWebEngineProfile::defaultProfile()->clientCertificateStore()->certificates().length()); + QCOMPARE(0, QWebEngineProfile::defaultProfile()->clientCertificateStore()->certificates().size()); +} + +void tst_QWebEngineClientCertificateStore::clientAuthentication_data() +{ + QTest::addColumn<QString>("client_certificate"); + QTest::addColumn<QString>("client_key"); + QTest::addColumn<bool>("in_memory"); + QTest::addColumn<bool>("add_more_in_memory_certificates"); + QTest::newRow("in_memory") << ":/resources/client.pem" + << ":/resources/client.key" << true << false; +#if defined(TEST_NSS) + QTest::newRow("nss") << ":/resources/client2.pem" + << ":/resources/client2.key" << false << false; + QTest::newRow("in_memory + nss") << ":/resources/client2.pem" + << ":/resources/client2.key" << false << true; +#endif +} + +void tst_QWebEngineClientCertificateStore::clientAuthentication() +{ + QFETCH(QString, client_certificate); + QFETCH(QString, client_key); + QFETCH(bool, in_memory); + QFETCH(bool, add_more_in_memory_certificates); + + HttpsServer server(":/resources/server.pem", ":/resources/server.key", ":resources/ca.pem"); + server.setExpectError(false); + QVERIFY(server.start()); + + connect(&server, &HttpsServer::newRequest, [&](HttpReqRep *rr) { + rr->setResponseBody(QByteArrayLiteral("<html><body>TEST</body></html>")); + rr->sendResponse(); + }); + + QFile certFile(client_certificate); + certFile.open(QIODevice::ReadOnly); + const QSslCertificate cert(certFile.readAll(), QSsl::Pem); + + QFile keyFile(client_key); + keyFile.open(QIODevice::ReadOnly); + const QSslKey sslKey(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, ""); + + if (in_memory) + QWebEngineProfile::defaultProfile()->clientCertificateStore()->add(cert, sslKey); + + if (add_more_in_memory_certificates) + addAndListCertificates(); + + QWebEnginePage page; + connect(&page, &QWebEnginePage::certificateError, [](QWebEngineCertificateError e) { + // ca is self signed in this test simply accept the certificate error + e.acceptCertificate(); + }); + connect(&page, &QWebEnginePage::selectClientCertificate, &page, + [&cert](QWebEngineClientCertificateSelection selection) { + QVERIFY(!selection.certificates().isEmpty()); + for (const QSslCertificate &sCert : selection.certificates()) { + if (cert == sCert) { + selection.select(sCert); + return; + } + } + QFAIL("No certificate found."); + }); + QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); + page.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + page.setUrl(server.url()); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size() > 0, true, 20000); + QCOMPARE(loadFinishedSpy.takeFirst().at(0).toBool(), true); + QCOMPARE(toPlainTextSync(&page), QStringLiteral("TEST")); + QVERIFY(server.stop()); } QTEST_MAIN(tst_QWebEngineClientCertificateStore) diff --git a/tests/auto/core/qwebenginecookiestore/CMakeLists.txt b/tests/auto/core/qwebenginecookiestore/CMakeLists.txt index 5273d31aa..cc14940f1 100644 --- a/tests/auto/core/qwebenginecookiestore/CMakeLists.txt +++ b/tests/auto/core/qwebenginecookiestore/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../httpserver/httpserver.cmake) include(../../util/util.cmake) diff --git a/tests/auto/core/qwebenginecookiestore/tst_qwebenginecookiestore.cpp b/tests/auto/core/qwebenginecookiestore/tst_qwebenginecookiestore.cpp index e0fee6b08..3fff2cd45 100644 --- a/tests/auto/core/qwebenginecookiestore/tst_qwebenginecookiestore.cpp +++ b/tests/auto/core/qwebenginecookiestore/tst_qwebenginecookiestore.cpp @@ -34,6 +34,7 @@ private Q_SLOTS: // as it checks storage manipulation without navigation void setAndDeleteCookie(); + void setInvalidCookie(); void cookieSignals(); void batchCookieTasks(); void basicFilter(); @@ -83,22 +84,22 @@ void tst_QWebEngineCookieStore::cookieSignals() page.load(QUrl("qrc:///resources/index.html")); - QWE_TRY_COMPARE(loadSpy.count(), 1); + QWE_TRY_COMPARE(loadSpy.size(), 1); QVariant success = loadSpy.takeFirst().takeFirst(); QVERIFY(success.toBool()); - QWE_TRY_COMPARE(cookieAddedSpy.count(), 2); + QWE_TRY_COMPARE(cookieAddedSpy.size(), 2); // try whether updating a cookie to be expired results in that cookie being removed. QNetworkCookie expiredCookie(QNetworkCookie::parseCookies(QByteArrayLiteral("SessionCookie=delete; expires=Thu, 01-Jan-1970 00:00:00 GMT; path=///resources")).first()); client->setCookie(expiredCookie, QUrl("qrc:///resources/index.html")); - QWE_TRY_COMPARE(cookieRemovedSpy.count(), 1); + QWE_TRY_COMPARE(cookieRemovedSpy.size(), 1); cookieRemovedSpy.clear(); // try removing the other cookie. QNetworkCookie nonSessionCookie(QNetworkCookie::parseCookies(QByteArrayLiteral("CookieWithExpiresField=QtWebEngineCookieTest; path=///resources")).first()); client->deleteCookie(nonSessionCookie, QUrl("qrc:///resources/index.html")); - QWE_TRY_COMPARE(cookieRemovedSpy.count(), 1); + QWE_TRY_COMPARE(cookieRemovedSpy.size(), 1); } void tst_QWebEngineCookieStore::setAndDeleteCookie() @@ -119,33 +120,64 @@ void tst_QWebEngineCookieStore::setAndDeleteCookie() client->loadAllCookies(); // /* FIXME remove 'blank' navigation once loadAllCookies api is fixed page.load(QUrl("about:blank")); - QWE_TRY_COMPARE(loadSpy.count(), 1); + QWE_TRY_COMPARE(loadSpy.size(), 1); // */ // check if pending cookies are set and removed client->setCookie(cookie1); client->setCookie(cookie2); - QWE_TRY_COMPARE(cookieAddedSpy.count(), 2); + QWE_TRY_COMPARE(cookieAddedSpy.size(), 2); client->deleteCookie(cookie1); - QWE_TRY_COMPARE(cookieRemovedSpy.count(), 1); + QWE_TRY_COMPARE(cookieRemovedSpy.size(), 1); page.load(QUrl("qrc:///resources/content.html")); - QWE_TRY_COMPARE(loadSpy.count(), 2); + QWE_TRY_COMPARE(loadSpy.size(), 2); QVariant success = loadSpy.takeFirst().takeFirst(); QVERIFY(success.toBool()); - QWE_TRY_COMPARE(cookieAddedSpy.count(), 2); - QWE_TRY_COMPARE(cookieRemovedSpy.count(), 1); + QWE_TRY_COMPARE(cookieAddedSpy.size(), 2); + QWE_TRY_COMPARE(cookieRemovedSpy.size(), 1); cookieAddedSpy.clear(); cookieRemovedSpy.clear(); client->setCookie(cookie3); - QWE_TRY_COMPARE(cookieAddedSpy.count(), 1); + QWE_TRY_COMPARE(cookieAddedSpy.size(), 1); // updating a cookie with an expired 'expires' field should remove the cookie with the same name client->setCookie(expiredCookie3); client->deleteCookie(cookie2); - QWE_TRY_COMPARE(cookieAddedSpy.count(), 1); - QWE_TRY_COMPARE(cookieRemovedSpy.count(), 2); + QWE_TRY_COMPARE(cookieAddedSpy.size(), 1); + QWE_TRY_COMPARE(cookieRemovedSpy.size(), 2); +} + +void tst_QWebEngineCookieStore::setInvalidCookie() +{ + QWebEnginePage page(m_profile); + QWebEngineCookieStore *client = m_profile->cookieStore(); + + QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); + QSignalSpy cookieAddedSpy(client, SIGNAL(cookieAdded(const QNetworkCookie &))); + QSignalSpy cookieRemovedSpy(client, SIGNAL(cookieRemoved(const QNetworkCookie &))); + + QNetworkCookie goodCookie( + QNetworkCookie::parseCookies( + QByteArrayLiteral("khaos=I9GX8CWI; Domain=.example.com; Path=/docs")) + .first()); + QNetworkCookie badCookie( + QNetworkCookie::parseCookies(QByteArrayLiteral("TestCookie=foo\tbar;")).first()); + + // force to init storage as it's done lazily upon first navigation + client->loadAllCookies(); + // /* FIXME remove 'blank' navigation once loadAllCookies api is fixed + page.load(QUrl("about:blank")); + QWE_TRY_COMPARE(loadSpy.size(), 1); + // */ + + client->setCookie(badCookie); + client->setCookie(goodCookie); + client->deleteCookie(goodCookie); + // by the time the second cookie is removed, only one cookie should have been added + QWE_TRY_COMPARE(cookieRemovedSpy.size(), 1); + QWE_TRY_COMPARE(cookieAddedSpy.size(), 1); } void tst_QWebEngineCookieStore::batchCookieTasks() @@ -164,29 +196,29 @@ void tst_QWebEngineCookieStore::batchCookieTasks() client->loadAllCookies(); // /* FIXME remove 'blank' navigation once loadAllCookies api is fixed page.load(QUrl("about:blank")); - QWE_TRY_COMPARE(loadSpy.count(), 1); + QWE_TRY_COMPARE(loadSpy.size(), 1); // */ client->setCookie(cookie1); client->setCookie(cookie2); - QWE_TRY_COMPARE(cookieAddedSpy.count(), 2); + QWE_TRY_COMPARE(cookieAddedSpy.size(), 2); page.load(QUrl("qrc:///resources/index.html")); - QWE_TRY_COMPARE(loadSpy.count(), 2); + QWE_TRY_COMPARE(loadSpy.size(), 2); QVariant success = loadSpy.takeFirst().takeFirst(); QVERIFY(success.toBool()); - QWE_TRY_COMPARE(cookieAddedSpy.count(), 4); - QWE_TRY_COMPARE(cookieRemovedSpy.count(), 0); + QWE_TRY_COMPARE(cookieAddedSpy.size(), 4); + QWE_TRY_COMPARE(cookieRemovedSpy.size(), 0); cookieAddedSpy.clear(); cookieRemovedSpy.clear(); client->deleteSessionCookies(); - QWE_TRY_COMPARE(cookieRemovedSpy.count(), 3); + QWE_TRY_COMPARE(cookieRemovedSpy.size(), 3); client->deleteAllCookies(); - QWE_TRY_COMPARE(cookieRemovedSpy.count(), 4); + QWE_TRY_COMPARE(cookieRemovedSpy.size(), 4); } void tst_QWebEngineCookieStore::basicFilter() @@ -203,22 +235,22 @@ void tst_QWebEngineCookieStore::basicFilter() page.load(QUrl("qrc:///resources/index.html")); - QWE_TRY_COMPARE(loadSpy.count(), 1); + QWE_TRY_COMPARE(loadSpy.size(), 1); QVERIFY(loadSpy.takeFirst().takeFirst().toBool()); - QWE_TRY_COMPARE(cookieAddedSpy.count(), 2); + QWE_TRY_COMPARE(cookieAddedSpy.size(), 2); QWE_TRY_COMPARE(accessTested.loadAcquire(), 2); // FIXME? client->deleteAllCookies(); - QWE_TRY_COMPARE(cookieRemovedSpy.count(), 2); + QWE_TRY_COMPARE(cookieRemovedSpy.size(), 2); client->setCookieFilter([&](const QWebEngineCookieStore::FilterRequest &){ ++accessTested; return false; }); page.triggerAction(QWebEnginePage::ReloadAndBypassCache); - QWE_TRY_COMPARE(loadSpy.count(), 1); + QWE_TRY_COMPARE(loadSpy.size(), 1); QVERIFY(loadSpy.takeFirst().takeFirst().toBool()); QWE_TRY_COMPARE(accessTested.loadAcquire(), 4); // FIXME? // Test cookies are NOT added: QTest::qWait(100); - QCOMPARE(cookieAddedSpy.count(), 2); + QCOMPARE(cookieAddedSpy.size(), 2); } void tst_QWebEngineCookieStore::basicFilterOverHTTP() @@ -259,25 +291,25 @@ void tst_QWebEngineCookieStore::basicFilterOverHTTP() QUrl firstPartyUrl = httpServer.url("/test.html"); page.load(firstPartyUrl); - QWE_TRY_COMPARE(loadSpy.count(), 1); + QWE_TRY_COMPARE(loadSpy.size(), 1); QVERIFY(loadSpy.takeFirst().takeFirst().toBool()); - QWE_TRY_COMPARE(cookieAddedSpy.count(), 1); + QWE_TRY_COMPARE(cookieAddedSpy.size(), 1); QWE_TRY_COMPARE(accessTested.loadAcquire(), 4); QVERIFY(cookieRequestHeader.isEmpty()); - QWE_TRY_COMPARE(serverSpy.count(), 3); + QWE_TRY_COMPARE(serverSpy.size(), 3); page.triggerAction(QWebEnginePage::Reload); - QWE_TRY_COMPARE(loadSpy.count(), 1); + QWE_TRY_COMPARE(loadSpy.size(), 1); QVERIFY(loadSpy.takeFirst().takeFirst().toBool()); QVERIFY(!cookieRequestHeader.isEmpty()); - QWE_TRY_COMPARE(cookieAddedSpy.count(), 1); + QWE_TRY_COMPARE(cookieAddedSpy.size(), 1); QWE_TRY_COMPARE(accessTested.loadAcquire(), 6); - QWE_TRY_COMPARE(serverSpy.count(), 5); + QWE_TRY_COMPARE(serverSpy.size(), 5); client->deleteAllCookies(); - QWE_TRY_COMPARE(cookieRemovedSpy.count(), 1); + QWE_TRY_COMPARE(cookieRemovedSpy.size(), 1); client->setCookieFilter([&](const QWebEngineCookieStore::FilterRequest &request) { resourceFirstParty.append(qMakePair(request.origin, request.firstPartyUrl)); @@ -285,28 +317,28 @@ void tst_QWebEngineCookieStore::basicFilterOverHTTP() return false; }); page.triggerAction(QWebEnginePage::ReloadAndBypassCache); - QWE_TRY_COMPARE(loadSpy.count(), 1); + QWE_TRY_COMPARE(loadSpy.size(), 1); QVERIFY(loadSpy.takeFirst().takeFirst().toBool()); QVERIFY(cookieRequestHeader.isEmpty()); // Test cookies are NOT added: QTest::qWait(100); - QCOMPARE(cookieAddedSpy.count(), 1); + QCOMPARE(cookieAddedSpy.size(), 1); QWE_TRY_COMPARE(accessTested.loadAcquire(), 9); - QWE_TRY_COMPARE(serverSpy.count(), 7); + QWE_TRY_COMPARE(serverSpy.size(), 7); page.triggerAction(QWebEnginePage::Reload); - QWE_TRY_COMPARE(loadSpy.count(), 1); + QWE_TRY_COMPARE(loadSpy.size(), 1); QVERIFY(loadSpy.takeFirst().takeFirst().toBool()); QVERIFY(cookieRequestHeader.isEmpty()); - QCOMPARE(cookieAddedSpy.count(), 1); + QCOMPARE(cookieAddedSpy.size(), 1); // Wait for last GET /favicon.ico - QWE_TRY_COMPARE(serverSpy.count(), 9); + QWE_TRY_COMPARE(serverSpy.size(), 9); (void) httpServer.stop(); QCOMPARE(resourceFirstParty.size(), accessTested.loadAcquire()); - for (auto &&p : qAsConst(resourceFirstParty)) + for (auto &&p : std::as_const(resourceFirstParty)) QVERIFY2(p.second == firstPartyUrl, qPrintable(QString("Resource [%1] has wrong firstPartyUrl: %2").arg(p.first.toString(), p.second.toString()))); } @@ -323,7 +355,7 @@ void tst_QWebEngineCookieStore::html5featureFilter() page.load(QUrl("qrc:///resources/content.html")); - QWE_TRY_COMPARE(loadSpy.count(), 1); + QWE_TRY_COMPARE(loadSpy.size(), 1); QVERIFY(loadSpy.takeFirst().takeFirst().toBool()); QCOMPARE(accessTested.loadAcquire(), 0); // FIXME? QTest::ignoreMessage(QtCriticalMsg, QRegularExpression(".*Uncaught SecurityError.*sessionStorage.*")); diff --git a/tests/auto/core/qwebengineframe/CMakeLists.txt b/tests/auto/core/qwebengineframe/CMakeLists.txt new file mode 100644 index 000000000..d02b4307d --- /dev/null +++ b/tests/auto/core/qwebengineframe/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +include(../../util/util.cmake) + +qt_internal_add_test(tst_qwebengineframe + SOURCES + tst_qwebengineframe.cpp + LIBRARIES + Qt::WebEngineCore + Qt::WebEngineWidgets + Test::Util +) + +qt_internal_add_resource(tst_qwebengineframe "tst_qwebengineframe" + PREFIX + "/" + FILES + "resources/frameset.html" + "resources/iframes.html" + "resources/nesting-iframe.html" +) diff --git a/tests/auto/core/qwebengineframe/resources/frameset.html b/tests/auto/core/qwebengineframe/resources/frameset.html new file mode 100644 index 000000000..53f5e6638 --- /dev/null +++ b/tests/auto/core/qwebengineframe/resources/frameset.html @@ -0,0 +1,20 @@ +<!doctype html> +<html> + <head><title>Test-title</title></head> + <script> + window.name = 'test-main-frame' + onload = (e) => { + const frames = window.frames; + for (let i = 0; i < frames.length; i++) { + frames[i].name = 'test-subframe' + i; + } + }; + </script> + <frameset cols="50%, 50%"> + <frameset cols="50%, 50%"> + <frame style="border: red dashed 1em;"/> + <frame style="border: green dashed 1em;"/> + </frameset> + <frame style="border: blue solid 1em;"/> + </frameset> +</html> diff --git a/tests/auto/core/qwebengineframe/resources/iframes.html b/tests/auto/core/qwebengineframe/resources/iframes.html new file mode 100644 index 000000000..648acb166 --- /dev/null +++ b/tests/auto/core/qwebengineframe/resources/iframes.html @@ -0,0 +1,17 @@ +<!doctype html> +<html> + <head><title>Test-title</title></head> + <script> + window.name = 'test-main-frame' + onload = (e) => { + const frames = window.frames; + for (let i = 0; i < frames.length; i++) { + frames[i].name = 'test-subframe' + i; + } + }; + </script> + <body> + <iframe name="iframe0-300x200" width="300" height="200"></iframe> + <iframe name="iframe1-350x250" width="350" height="250"></iframe> + </body> +</html> diff --git a/tests/auto/core/qwebengineframe/resources/nesting-iframe.html b/tests/auto/core/qwebengineframe/resources/nesting-iframe.html new file mode 100644 index 000000000..cd784e3dd --- /dev/null +++ b/tests/auto/core/qwebengineframe/resources/nesting-iframe.html @@ -0,0 +1,7 @@ +<!doctype html> +<html> + <head><title>Test-title</title></head> + <body> + <iframe name="iframe2-parent" src="iframes.html"></iframe> + </body> +</html> diff --git a/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp b/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp new file mode 100644 index 000000000..b70a655d3 --- /dev/null +++ b/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp @@ -0,0 +1,180 @@ +/* + Copyright (C) 2024 The Qt Company Ltd. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <util.h> + +#include <QtTest/QtTest> + +#include <QtWebEngineCore/qwebengineframe.h> + +class tst_QWebEngineFrame : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void mainFrame(); + void findFrameByName(); + void isValid(); + void name(); + void htmlName(); + void children(); + void childrenOfInvalidFrame(); + void url(); + void size(); + +private: +}; + +void tst_QWebEngineFrame::mainFrame() +{ + QWebEnginePage page; + QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) }; + page.load(QUrl("qrc:/resources/frameset.html")); + QTRY_COMPARE(loadSpy.size(), 1); + auto frame = page.mainFrame(); + QVERIFY(frame.isValid()); +} + +void tst_QWebEngineFrame::findFrameByName() +{ + QWebEnginePage page; + QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) }; + page.load(QUrl("qrc:/resources/iframes.html")); + QTRY_COMPARE(loadSpy.size(), 1); + auto maybeFrame = page.findFrameByName("test-subframe0"); + QVERIFY(maybeFrame.has_value()); + QCOMPARE(maybeFrame->name(), "test-subframe0"); + QVERIFY(!page.findFrameByName("foobar").has_value()); +} + +void tst_QWebEngineFrame::isValid() +{ + QWebEnginePage page; + QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) }; + page.load(QUrl("qrc:/resources/iframes.html")); + QTRY_COMPARE(loadSpy.size(), 1); + auto firstPageSubframe = page.findFrameByName("test-subframe0"); + QVERIFY(firstPageSubframe && firstPageSubframe->isValid()); + + page.load(QUrl("qrc:/resources/frameset.html")); + QTRY_COMPARE(loadSpy.size(), 2); + QVERIFY(!firstPageSubframe->isValid()); +} + +void tst_QWebEngineFrame::name() +{ + QWebEnginePage page; + QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) }; + page.load(QUrl("qrc:/resources/frameset.html")); + QTRY_COMPARE(loadSpy.size(), 1); + QCOMPARE(page.mainFrame().name(), "test-main-frame"); + auto children = page.mainFrame().children(); + QCOMPARE(children.at(0).name(), "test-subframe0"); + QCOMPARE(children.at(1).name(), "test-subframe1"); + QCOMPARE(children.at(2).name(), "test-subframe2"); + + page.load(QUrl("qrc:/resources/iframes.html")); + QTRY_COMPARE(loadSpy.size(), 2); + QVERIFY(!children.at(0).isValid()); + QCOMPARE(children.at(0).name(), QString()); +} + +void tst_QWebEngineFrame::htmlName() +{ + QWebEnginePage page; + QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) }; + page.load(QUrl("qrc:/resources/iframes.html")); + QTRY_COMPARE(loadSpy.size(), 1); + auto children = page.mainFrame().children(); + QCOMPARE(children.at(0).name(), "test-subframe0"); + QCOMPARE(children.at(0).htmlName(), "iframe0-300x200"); + QCOMPARE(children.at(1).name(), "test-subframe1"); + QCOMPARE(children.at(1).htmlName(), "iframe1-350x250"); + + page.load(QUrl("qrc:/resources/frameset.html")); + QTRY_COMPARE(loadSpy.size(), 2); + QVERIFY(!children.at(0).isValid()); + QCOMPARE(children.at(0).htmlName(), QString()); +} + +void tst_QWebEngineFrame::children() +{ + QWebEnginePage page; + QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) }; + page.load(QUrl("qrc:/resources/frameset.html")); + QTRY_COMPARE(loadSpy.size(), 1); + auto frame = page.mainFrame(); + auto children = frame.children(); + QCOMPARE(children.size(), 3); + for (auto child : children) { + QVERIFY(child.isValid()); + } +} + +void tst_QWebEngineFrame::childrenOfInvalidFrame() +{ + QWebEnginePage page; + QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) }; + page.load(QUrl("qrc:/resources/nesting-iframe.html")); + QTRY_COMPARE(loadSpy.size(), 1); + auto nestedFrame = page.mainFrame().children().at(0); + QCOMPARE(nestedFrame.children().size(), 2); + + page.load(QUrl("qrc:/resources/frameset.html")); + QTRY_COMPARE(loadSpy.size(), 2); + QVERIFY(!nestedFrame.isValid()); + QVERIFY(nestedFrame.children().empty()); +} + +void tst_QWebEngineFrame::url() +{ + QWebEnginePage page; + QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) }; + page.load(QUrl("qrc:/resources/nesting-iframe.html")); + QTRY_COMPARE(loadSpy.size(), 1); + auto children = page.mainFrame().children(); + QCOMPARE(children.at(0).url(), QUrl("qrc:/resources/iframes.html")); + + page.load(QUrl("qrc:/resources/frameset.html")); + QTRY_COMPARE(loadSpy.size(), 2); + QVERIFY(!children.at(0).isValid()); + QCOMPARE(children.at(0).url(), QUrl()); +} + +void tst_QWebEngineFrame::size() +{ + QWebEnginePage page; + QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) }; + page.load(QUrl("qrc:/resources/iframes.html")); + QTRY_COMPARE(loadSpy.size(), 1); + auto frame1 = *page.findFrameByName("test-subframe0"); + auto size1 = frame1.size(); + auto size2 = page.findFrameByName("test-subframe1")->size(); + QCOMPARE(size1, QSizeF(300, 200)); + QCOMPARE(size2, QSizeF(350, 250)); + + page.load(QUrl("qrc:/resources/frameset.html")); + QTRY_COMPARE(loadSpy.size(), 2); + QVERIFY(!frame1.isValid()); + QCOMPARE(frame1.size(), QSizeF()); +} + +QTEST_MAIN(tst_QWebEngineFrame) + +#include "tst_qwebengineframe.moc" diff --git a/tests/auto/core/qwebengineglobalsettings/CMakeLists.txt b/tests/auto/core/qwebengineglobalsettings/CMakeLists.txt new file mode 100644 index 000000000..dd2f28857 --- /dev/null +++ b/tests/auto/core/qwebengineglobalsettings/CMakeLists.txt @@ -0,0 +1,30 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +include(../../httpserver/httpserver.cmake) +include(../../util/util.cmake) + +qt_internal_add_test(tst_qwebengineglobalsettings + SOURCES + tst_qwebengineglobalsettings.cpp + LIBRARIES + Qt::Network + Qt::WebEngineCore + Test::HttpServer + Qt::WebEngineWidgets + Test::Util +) + +# Resources: +set(tst_qwebengineglobalsettings_resource_files + "cert/localhost.crt" + "cert/localhost.key" + "cert/RootCA.pem" +) + +qt_add_resources(tst_qwebengineglobalsettings "tst_qwebengineglobalsettings" + PREFIX + "/" + FILES + ${tst_qwebengineglobalsettings_resource_files} +) diff --git a/tests/auto/core/qwebengineglobalsettings/cert/RootCA.pem b/tests/auto/core/qwebengineglobalsettings/cert/RootCA.pem new file mode 100644 index 000000000..16be384bb --- /dev/null +++ b/tests/auto/core/qwebengineglobalsettings/cert/RootCA.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDTzCCAjegAwIBAgIUNYpmREIW67Fh7WNzCwPL6nnSgRMwDQYJKoZIhvcNAQEL +BQAwNzELMAkGA1UEBhMCREUxKDAmBgNVBAMMH1dlYkVuZ2luZUdsb2JhbFNldHRp +bmdzLVRlc3QtQ0EwHhcNMjMwMzI5MTYwMzMzWhcNMjYwMTE2MTYwMzMzWjA3MQsw +CQYDVQQGEwJERTEoMCYGA1UEAwwfV2ViRW5naW5lR2xvYmFsU2V0dGluZ3MtVGVz +dC1DQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJhZ9DwcdbVBzMyY +/nEqt5KUi74LFwEnS0G2ne8IYco9+Pbkpb8wV5u6n43IsQ2c3u8D/KVtu1Vy3tf2 +G3aKOwhFzaj7GWLE9FweZyMoL6ASOtWEa55myT5zAysVQtHAkePu0smAPP0gVq3E +vjSTwV1W1mVXv4wMwffR8AvNGhKrJIa3L2/uYKGbzEmaCk2kt0vIqfrx8095RlXC +lUcwTMJ6/d/e/DMDtqQ1ypUuz5QYQybIVKwuqkhojT2DXbitv0rE4HLQub8CxOZ+ +9GcQjeAt8Tzrlp1UP6c9OtlsMoo37gJYzb/XDE6OPnk42chQXDxGQjtVRs+60kcT +Dx/YHG0CAwEAAaNTMFEwHQYDVR0OBBYEFP1FK1U9CUHQEp7coaab7IdR18zDMB8G +A1UdIwQYMBaAFP1FK1U9CUHQEp7coaab7IdR18zDMA8GA1UdEwEB/wQFMAMBAf8w +DQYJKoZIhvcNAQELBQADggEBAGTlcxmRsuwBeRW0CsjX/qbdcB0OWkIC857Jn8RU +6yGa7P9i6EQb1O/DEF+Z7dkASx5zfN6LPIrph6J56/mmcNBeqArovWJwxQUTNO9i +1kOU3xoH5n/ya+gdBr3reA90bAMKWXwa6uI3smPJKy+2hOkdDaSBa5KECYWhniH0 +yRxL7YdhQhuCc7Ijf+S6WzeHRwdLkdiV8c2vAGWdunDFuGT2iYVOZ1qbp6O/tmjv +TxWAnvP4+0ykFIlMor0vYWD8xbnq28263VmNh7mrFwkBYnHJiY/nTDwxaL+g6Uji +9n6+VuxUDgfQWX1YRHC4a89zI/Zvnn2z/92To7zRmNd73RQ= +-----END CERTIFICATE----- diff --git a/tests/auto/core/qwebengineglobalsettings/cert/localhost.crt b/tests/auto/core/qwebengineglobalsettings/cert/localhost.crt new file mode 100644 index 000000000..c063bf268 --- /dev/null +++ b/tests/auto/core/qwebengineglobalsettings/cert/localhost.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDtDCCApygAwIBAgIURotPFTfDJxwaqhZsr0IpAahl2EMwDQYJKoZIhvcNAQEL +BQAwNzELMAkGA1UEBhMCREUxKDAmBgNVBAMMH1dlYkVuZ2luZUdsb2JhbFNldHRp +bmdzLVRlc3QtQ0EwHhcNMjMwMzI5MTYwNDI1WhcNMjYwMTE2MTYwNDI1WjB0MQsw +CQYDVQQGEwJERTEPMA0GA1UECAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xKTAn +BgNVBAoMIFFXZWJFbmdpbmVHbG9iYWxTZXR0aW5ncy1FeGFtcGxlMRgwFgYDVQQD +DA9sb2NhbGhvc3QubG9jYWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCY9FX/8YetuJKSFSoYPxMKPvjt2zJ5g13DywqZtbDzLAIASCxn4iad3qgxaoWB +uI0g4ykzrhUa98YHU8fDH4T4Vwhwu72SRYW4+MgT9ohc1oCKBX05b+BWSuTSeuHy +leqdL78bj3bu5TtFs2dJt2t8eA6SNR9lDa5g4v7oA4xVp93gMo2YqZwaLONmxIKY +cI4lcETnHHsvc6+dB2UqWHJEN75UkdC/XnDLM/VbL3/4zxU+9x34nvvfSJwCHVnE +u+zYwrZXkbiDVDovT855phVC/K5skVgBL2miz3eygljuw1tIwnmVix/e/xHZyqMg +Lje/LZN53v5G61Wut6bbdkeVAgMBAAGjezB5MB8GA1UdIwQYMBaAFP1FK1U9CUHQ +Ep7coaab7IdR18zDMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgTwMB8GA1UdEQQYMBaC +CWxvY2FsaG9zdIIJMTI3LjAuMC4xMB0GA1UdDgQWBBQv20ImViFtvMm5gHqTeML6 +pi5BtTANBgkqhkiG9w0BAQsFAAOCAQEAGsL7eOms30+IPdKQZ2pjtX22GfM6EiUs +xsQfsX/Q3bus30B2m9GJ6AVIwVUJimOGiMauDCLjDeXWCMZpihzodExhC0D/X1B+ +FsCLagcjlgfWwekKEo8sUWUZp0DNCyacPtTPxqoS2RA7foEzQhRLViLSvf+UXU8g +jZAwWGB/5V849zcbbNBcWKzRsPvNOqeENWEn1ByGcsWhas2V0KzRcUODuA9UHv1x ++eDlLZYsWV9c1MuL8a1VDEluIR19eR/Gl9axjPZY2oiPvlp2b7I4z1bY+wV2i6vh +NeDCAxAxJ42tXeb86vtnVXfSDbzedDbLv0l2kEhcywVN3MwhsEpmpg== +-----END CERTIFICATE----- diff --git a/tests/auto/core/qwebengineglobalsettings/cert/localhost.key b/tests/auto/core/qwebengineglobalsettings/cert/localhost.key new file mode 100644 index 000000000..49502ab9a --- /dev/null +++ b/tests/auto/core/qwebengineglobalsettings/cert/localhost.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCY9FX/8YetuJKS +FSoYPxMKPvjt2zJ5g13DywqZtbDzLAIASCxn4iad3qgxaoWBuI0g4ykzrhUa98YH +U8fDH4T4Vwhwu72SRYW4+MgT9ohc1oCKBX05b+BWSuTSeuHyleqdL78bj3bu5TtF +s2dJt2t8eA6SNR9lDa5g4v7oA4xVp93gMo2YqZwaLONmxIKYcI4lcETnHHsvc6+d +B2UqWHJEN75UkdC/XnDLM/VbL3/4zxU+9x34nvvfSJwCHVnEu+zYwrZXkbiDVDov +T855phVC/K5skVgBL2miz3eygljuw1tIwnmVix/e/xHZyqMgLje/LZN53v5G61Wu +t6bbdkeVAgMBAAECggEAF7ADX5NivUsv29LORZoDE1ukRoXjX8Ex9MANoLdsM4S1 +vKBwzBfQfjN83cZO7cOMi7LSby//EcGcmAboEXZgq+siof7ZQX1l07snlTvha2tG +1dk6xvnmBscrf9NLCbwg7P33fUevFhlHICjEDr0KtuiK7Sav+YDwaA3Ph1QBWERd +GO3sVlnuGsDpf/0GijlwqEGuDKUePEEANOhXcByh693VmvlKVf9SbrimYeunKW1O +FvvcAiBMzqurhZotb9/juiq+fIMID29OULCCxlZZSySRYw0REpnAAxgWvaSZqyWd +tGosSKEgc4SptPTmC8DzRDDfN/zqvXmkmYnN9o4qMwKBgQC3tVYdEPn3fHX973Df +Ukp55cRpZFuNxjOiV3Qo1aTAKqpbmJ4/x8tUL7rhsmJXSxlW7xdNQ/WIHM1PJlZx +UAIr5eBq1MUVd5OENP8PuVIdAIumHXICB5FioJR/WnXRXLJEbGxSRr5gwaTw3sXd +ObPRQEUOrJtK+W0aeBKePRtIiwKBgQDVJN7A26vy8PMcE4TcDp75vAY/qasZl6ES +oksaHf3c4/jsnr70wRoOXi3fPo/DpWmVFylttMSEnzh3nfnOkDXZD3mQx3sdVw8l +12++t59733hllJZlwqk5OAc990kE1X44UW/gPA+5Vb9kpo6ahpFtqwhDmqa4RjtZ +0R/1H/KUXwKBgGjR7Qq0rwwJVgHIZ2zlNV2MPp+sBZlFaBzPLZZHILQNJBsTX+gg +heHJQiaZdAc+8Hxr+624gxZg6LyqsVQCRNrrVTtfn/x5uBANdSNxqGqn7wafcne5 +/bh6y4BHC0akT4s/Gidv+hyXIRfW5Ksvy2wv8bdHwWvsGdaqgGUNlM21AoGAe9Vc +BbibAh6zYBCHFEL6YiW3i61L1yadUnIwKBBcucVJjk/8qb63ILne9OEoLYcg/Jnk +W/S2aEcJS5Xg2P44CtBO1KrRAI7gIiA0sB2G7zU6gen+J0kdgDzpGDtflQtktdu6 +oBDFIeyLsjKCj4y3WXwQ5RYo3s8PFHPHmWbiTQkCgYBViqAHaAaPJQZG7Q6/Xqhf +rFosC0DeZk5PHrEDbpAnuJbySafGZ4LxY5Oq05+aM8BeR+IuGopciBBDWXoh7msO +N8WItu7WI86lIu7JZRbeju2w59tgj0EA+GyU1udPjTjseP5qpB27X1JEGV6TYBNs +LMmQSgdGWqwteKsc50UrkA== +-----END PRIVATE KEY----- diff --git a/tests/auto/core/qwebengineglobalsettings/tst_qwebengineglobalsettings.cpp b/tests/auto/core/qwebengineglobalsettings/tst_qwebengineglobalsettings.cpp new file mode 100644 index 000000000..5b6b64778 --- /dev/null +++ b/tests/auto/core/qwebengineglobalsettings/tst_qwebengineglobalsettings.cpp @@ -0,0 +1,130 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <QtTest> +#include <widgetutil.h> +#include <QNetworkAccessManager> +#include <QNetworkRequest> +#include <QWebEngineProfile> +#include <QWebEnginePage> +#include <QWebEngineGlobalSettings> +#include <QWebEngineLoadingInfo> + +#include "httpsserver.h" +#include "httpreqrep.h" + +class tst_QWebEngineGlobalSettings : public QObject +{ + Q_OBJECT + +public: + tst_QWebEngineGlobalSettings() { } + ~tst_QWebEngineGlobalSettings() { } + +public Q_SLOTS: + void init() { } + void cleanup() { } + +private Q_SLOTS: + void initTestCase() { } + void cleanupTestCase() { } + void dnsOverHttps_data(); + void dnsOverHttps(); +}; + +Q_LOGGING_CATEGORY(lc, "qt.webengine.tests") + +void tst_QWebEngineGlobalSettings::dnsOverHttps_data() +{ + QTest::addColumn<QWebEngineGlobalSettings::SecureDnsMode>("dnsMode"); + QTest::addColumn<QString>("uriTemplate"); + QTest::addColumn<bool>("isMockDnsServerCalledExpected"); + QTest::addColumn<bool>("isDnsResolutionSuccessExpected"); + QTest::addColumn<bool>("isConfigurationSuccessExpected"); + QTest::newRow("DnsMode::SystemOnly (no DoH server)") + << QWebEngineGlobalSettings::SecureDnsMode::SystemOnly << QStringLiteral("") << false + << true << true; + QTest::newRow("DnsMode::SecureOnly (mock DoH server)") + << QWebEngineGlobalSettings::SecureDnsMode::SecureOnly + << QStringLiteral("https://127.0.0.1:3000/dns-query{?dns}") << true << false << true; + QTest::newRow("DnsMode::SecureOnly (real DoH server)") + << QWebEngineGlobalSettings::SecureDnsMode::SecureOnly + << QStringLiteral("https://dns.google/dns-query{?dns}") << false << true << true; + QTest::newRow("DnsMode::SecureOnly (Empty URI Templates)") + << QWebEngineGlobalSettings::SecureDnsMode::SecureOnly << QStringLiteral("") << false + << false << false; + // Note: In the following test, we can't verify that the DoH server is called first and + // afterwards insecure DNS is tried, because for the DoH server to ever be used when the DNS + // mode is set to DnsMode::WithFallback, Chromium starts an asynchronous DoH server DnsProbe and + // requires that the connection is successful. That is, we'd have to implement a correct + // DNS response, which in turn requires that certificate errors aren't ignored and + // non-self-signed certificates are used for correct encryption. Instead of implementing + // all of that, this test verifies that Chromium tries probing the configured DoH server only. + QTest::newRow("DnsMode::SecureWithFallback (mock DoH server)") + << QWebEngineGlobalSettings::SecureDnsMode::SecureWithFallback + << QStringLiteral("https://127.0.0.1:3000/dns-query{?dns}") << true << true << true; + QTest::newRow("DnsMode::SecureWithFallback (Empty URI Templates)") + << QWebEngineGlobalSettings::SecureDnsMode::SecureWithFallback << QStringLiteral("") + << false << false << false; +} + +void tst_QWebEngineGlobalSettings::dnsOverHttps() +{ + const QUrl url = QStringLiteral("https://google.com/"); + // Verify network access with NAM because the result of loadFinished signal + // is used to verify that the DNS resolution was successful. + QNetworkAccessManager nam; + QSignalSpy namSpy(&nam, &QNetworkAccessManager::finished); + QScopedPointer<QNetworkReply> reply(nam.get(QNetworkRequest(url))); + if (!namSpy.wait(20000) || reply->error() != QNetworkReply::NoError) + QSKIP("Couldn't load page from network, skipping test."); + + QFETCH(QWebEngineGlobalSettings::SecureDnsMode, dnsMode); + QFETCH(QString, uriTemplate); + QFETCH(bool, isMockDnsServerCalledExpected); + QFETCH(bool, isDnsResolutionSuccessExpected); + QFETCH(bool, isConfigurationSuccessExpected); + bool isMockDnsServerCalled = false; + bool isLoadSuccessful = false; + + bool configurationSuccess = + QWebEngineGlobalSettings::setDnsMode({ dnsMode, QStringList{ uriTemplate } }); + QCOMPARE(configurationSuccess, isConfigurationSuccessExpected); + + if (!configurationSuccess) { + // In this case, DNS has invalid configuration, so the DNS change transaction is not + // triggered and the result of the DNS resolution depends on the current DNS mode, which is + // set by the previous run of this function. + return; + } + HttpsServer httpsServer(":/cert/localhost.crt", ":/cert/localhost.key", ":/cert/RootCA.pem", + 3000, this); + QObject::connect(&httpsServer, &HttpsServer::newRequest, this, + [&isMockDnsServerCalled](HttpReqRep *rr) { + QVERIFY(rr->requestPath().contains(QByteArrayLiteral("/dns-query?dns="))); + isMockDnsServerCalled = true; + rr->close(); + }); + QVERIFY(httpsServer.start()); + httpsServer.setExpectError(isMockDnsServerCalledExpected); + httpsServer.setVerifyMode(QSslSocket::PeerVerifyMode::VerifyNone); + + QWebEngineProfile profile; + QWebEnginePage page(&profile); + QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); + + connect(&page, &QWebEnginePage::loadFinished, this, + [&isLoadSuccessful](bool ok) { isLoadSuccessful = ok; }); + + page.load(url); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); + + QTRY_COMPARE(isMockDnsServerCalled, isMockDnsServerCalledExpected); + QCOMPARE(isLoadSuccessful, isDnsResolutionSuccessExpected); + QVERIFY(httpsServer.stop()); +} + +static QByteArrayList params = QByteArrayList() << "--ignore-certificate-errors"; + +W_QTEST_MAIN(tst_QWebEngineGlobalSettings, params) +#include "tst_qwebengineglobalsettings.moc" diff --git a/tests/auto/core/qwebengineloadinginfo/CMakeLists.txt b/tests/auto/core/qwebengineloadinginfo/CMakeLists.txt new file mode 100644 index 000000000..09d9c30f5 --- /dev/null +++ b/tests/auto/core/qwebengineloadinginfo/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_test(tst_qwebengineloadinginfo + SOURCES + tst_qwebengineloadinginfo.cpp + LIBRARIES + Qt::WebEngineCore + Test::HttpServer +) diff --git a/tests/auto/core/qwebengineloadinginfo/tst_qwebengineloadinginfo.cpp b/tests/auto/core/qwebengineloadinginfo/tst_qwebengineloadinginfo.cpp new file mode 100644 index 000000000..ccae7436b --- /dev/null +++ b/tests/auto/core/qwebengineloadinginfo/tst_qwebengineloadinginfo.cpp @@ -0,0 +1,93 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <QtTest/QtTest> +#include <QtWebEngineCore/qwebengineprofile.h> +#include <QtWebEngineCore/qwebenginepage.h> +#include <QtWebEngineCore/qwebengineloadinginfo.h> +#include <QtWebEngineCore/qwebenginehttprequest.h> +#include <QtWebEngineCore/qwebenginesettings.h> + +#include <httpserver.h> +#include <httpreqrep.h> + +typedef QMultiMap<QByteArray, QByteArray> Map; + +class tst_QWebEngineLoadingInfo : public QObject +{ + Q_OBJECT + +public: + tst_QWebEngineLoadingInfo() { } + +public slots: + void loadingInfoChanged(QWebEngineLoadingInfo loadingInfo) + { + const auto responseHeaders = loadingInfo.responseHeaders(); + QFETCH(Map, expected); + + if (loadingInfo.status() == QWebEngineLoadingInfo::LoadSucceededStatus + || loadingInfo.status() == QWebEngineLoadingInfo::LoadFailedStatus) { + if (!expected.empty()) + QCOMPARE(responseHeaders, expected); + } else { + QVERIFY(responseHeaders.size() == 0); + } + } + +private Q_SLOTS: + void responseHeaders_data() + { + QTest::addColumn<int>("responseCode"); + QTest::addColumn<Map>("input"); + QTest::addColumn<Map>("expected"); + + const Map empty; + const Map input { + std::make_pair("header1", "value1"), + std::make_pair("header2", "value2") + }; + const Map expected { + std::make_pair("header1", "value1"), + std::make_pair("header2", "value2"), + std::make_pair("Connection", "close") + }; + + + QTest::newRow("with headers HTTP 200") << 200 << input << expected; + QTest::newRow("with headers HTTP 500") << 500 << input << expected; + QTest::newRow("without headers HTTP 200") << 200 << empty << empty; + QTest::newRow("without headers HTTP 500") << 500 << empty << empty; + } + + void responseHeaders() + { + HttpServer httpServer; + + QFETCH(Map, input); + QFETCH(int, responseCode); + QObject::connect(&httpServer, &HttpServer::newRequest, this, [&](HttpReqRep *rr) { + for (auto it = input.cbegin(); it != input.cend(); ++it) + rr->setResponseHeader(it.key(), it.value()); + + rr->sendResponse(responseCode); + }); + QVERIFY(httpServer.start()); + + QWebEngineProfile profile; + QWebEnginePage page(&profile); + page.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + QObject::connect(&page, &QWebEnginePage::loadingChanged, this, &tst_QWebEngineLoadingInfo::loadingInfoChanged); + + + QWebEngineHttpRequest request(httpServer.url("/somepage.html")); + page.load(request); + + QTRY_VERIFY(spy.count() > 0); + QVERIFY(httpServer.stop()); + } +}; + +QTEST_MAIN(tst_QWebEngineLoadingInfo) +#include "tst_qwebengineloadinginfo.moc" diff --git a/tests/auto/core/qwebenginesettings/CMakeLists.txt b/tests/auto/core/qwebenginesettings/CMakeLists.txt index e2b1c4704..756b99bbb 100644 --- a/tests/auto/core/qwebenginesettings/CMakeLists.txt +++ b/tests/auto/core/qwebenginesettings/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../util/util.cmake) @@ -8,5 +8,6 @@ qt_internal_add_test(tst_qwebenginesettings tst_qwebenginesettings.cpp LIBRARIES Qt::WebEngineCore + Qt::WebEngineWidgets Test::Util ) diff --git a/tests/auto/core/qwebenginesettings/tst_qwebenginesettings.cpp b/tests/auto/core/qwebenginesettings/tst_qwebenginesettings.cpp index 4220f496b..e856dd094 100644 --- a/tests/auto/core/qwebenginesettings/tst_qwebenginesettings.cpp +++ b/tests/auto/core/qwebenginesettings/tst_qwebenginesettings.cpp @@ -27,6 +27,7 @@ #include <QtGui/qclipboard.h> #include <QtGui/qguiapplication.h> +#include <QtWebEngineWidgets/qwebengineview.h> class tst_QWebEngineSettings: public QObject { Q_OBJECT @@ -38,6 +39,10 @@ private Q_SLOTS: void javascriptClipboard_data(); void javascriptClipboard(); void setInAcceptNavigationRequest(); + void disableReadingFromCanvas_data(); + void disableReadingFromCanvas(); + void forceDarkMode(); + void forceDarkModeMultiView(); }; void tst_QWebEngineSettings::resetAttributes() @@ -143,7 +148,7 @@ void tst_QWebEngineSettings::javascriptClipboard() // - return value of queryCommandEnabled and // - return value of execCommand // - comparing the clipboard / input field - QGuiApplication::clipboard()->clear(); + QGuiApplication::clipboard()->setText(QString()); QCOMPARE(evaluateJavaScriptSync(&page, "document.queryCommandEnabled('copy')").toBool(), copyResult); QCOMPARE(evaluateJavaScriptSync(&page, "document.execCommand('copy')").toBool(), copyResult); @@ -193,6 +198,110 @@ void tst_QWebEngineSettings::setInAcceptNavigationRequest() QCOMPARE(toPlainTextSync(&page), QStringLiteral("PASS")); } +void tst_QWebEngineSettings::disableReadingFromCanvas_data() +{ + QTest::addColumn<bool>("disableReadingFromCanvas"); + QTest::addColumn<bool>("result"); + QTest::newRow("disabled") << false << true; + QTest::newRow("enabled") << true << false; +} + +void tst_QWebEngineSettings::disableReadingFromCanvas() +{ + QFETCH(bool, disableReadingFromCanvas); + QFETCH(bool, result); + + QWebEnginePage page; + QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); + page.settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, true); + page.settings()->setAttribute(QWebEngineSettings::ReadingFromCanvasEnabled, + !disableReadingFromCanvas); + page.setHtml("<html><body>" + "<canvas id='myCanvas' width='200' height='40' style='border:1px solid " + "#000000;'></canvas>" + "</body></html>"); + QVERIFY(loadFinishedSpy.wait()); + QCOMPARE(page.settings()->testAttribute(QWebEngineSettings::ReadingFromCanvasEnabled), + !disableReadingFromCanvas); + + const QString jsCode("(function(){" + " var canvas = document.getElementById(\"myCanvas\");" + " var ctx = canvas.getContext(\"2d\");" + " ctx.fillStyle = \"rgb(255,0,255)\";" + " ctx.fillRect(0, 0, 200, 40);" + " try {" + " src = canvas.toDataURL();" + " }" + " catch(err) {" + " src = \"\";" + " }" + " return src.length ? true : false;" + "})();"); + QCOMPARE(evaluateJavaScriptSync(&page, jsCode).toBool(), result); +} + +void tst_QWebEngineSettings::forceDarkMode() +{ + QWebEnginePage page; + QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); + page.settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, true); + + // based on: https://developer.chrome.com/blog/auto-dark-theme/#detecting-auto-dark-theme + page.setHtml("<html><body>" + "<div id=\"detection\", style=\"display: none; background-color: canvas; color-scheme: light\"</div>" + "</body></html>"); + + const QString isAutoDark("(() => {" + " const detectionDiv = document.querySelector('#detection');" + " return getComputedStyle(detectionDiv).backgroundColor != 'rgb(255, 255, 255)';" + "})()"); + + QVERIFY(loadFinishedSpy.wait()); + QTRY_COMPARE(evaluateJavaScriptSync(&page, isAutoDark).toBool(), false); + page.settings()->setAttribute(QWebEngineSettings::ForceDarkMode, true); + QTRY_COMPARE(evaluateJavaScriptSync(&page, isAutoDark).toBool(), true); +} + +void tst_QWebEngineSettings::forceDarkModeMultiView() +{ + QWebEngineView view1; + QWebEngineView view2; + QWebEnginePage *page1 = view1.page(); + QWebEnginePage *page2 = view2.page(); + page1->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, true); + page2->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, true); + view1.resize(300,300); + view2.resize(300,300); + view1.show(); + view2.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view1)); + QVERIFY(QTest::qWaitForWindowExposed(&view2)); + + QSignalSpy loadFinishedSpy(page1, SIGNAL(loadFinished(bool))); + QSignalSpy loadFinishedSpy2(page2, SIGNAL(loadFinished(bool))); + QString html("<html><body>" + "<div id=\"detection\", style=\"display: none; background-color: canvas; color-scheme: light\"</div>" + "</body></html>"); + + const QString isAutoDark("(() => {" + " const detectionDiv = document.querySelector('#detection');" + " return getComputedStyle(detectionDiv).backgroundColor != 'rgb(255, 255, 255)';" + "})()"); + + view1.setHtml(html); + QVERIFY(loadFinishedSpy.wait()); + view2.setHtml(html); + QVERIFY(loadFinishedSpy2.wait()); + + // both views has light color-scheme + QTRY_COMPARE(evaluateJavaScriptSync(page1, isAutoDark).toBool(), false); + QTRY_COMPARE(evaluateJavaScriptSync(page2, isAutoDark).toBool(), false); + view1.settings()->setAttribute(QWebEngineSettings::ForceDarkMode, true); + // dark mode should apply only for view1 + QTRY_COMPARE(evaluateJavaScriptSync(page1, isAutoDark).toBool(), true); + QTRY_COMPARE(evaluateJavaScriptSync(page2, isAutoDark).toBool(), false); +} + QTEST_MAIN(tst_QWebEngineSettings) #include "tst_qwebenginesettings.moc" diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/CMakeLists.txt b/tests/auto/core/qwebengineurlrequestinterceptor/CMakeLists.txt index 0f9238790..c12c0c45c 100644 --- a/tests/auto/core/qwebengineurlrequestinterceptor/CMakeLists.txt +++ b/tests/auto/core/qwebengineurlrequestinterceptor/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../util/util.cmake) include(../../httpserver/httpserver.cmake) @@ -9,12 +9,16 @@ qt_internal_add_test(tst_qwebengineurlrequestinterceptor tst_qwebengineurlrequestinterceptor.cpp LIBRARIES Qt::WebEngineCore + Qt::WebEngineCorePrivate + Qt::CorePrivate Test::HttpServer Test::Util ) set(tst_qwebengineurlrequestinterceptor_resource_files "resources/content.html" + "resources/content2.html" + "resources/content3.html" "resources/favicon.html" "resources/firstparty.html" "resources/fontawesome.woff" @@ -34,6 +38,7 @@ set(tst_qwebengineurlrequestinterceptor_resource_files "resources/style.css" "resources/sw.html" "resources/sw.js" + "resources/postBodyFile.txt" ) qt_internal_add_resource(tst_qwebengineurlrequestinterceptor "tst_qwebengineurlrequestinterceptor" diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/resources/content3.html b/tests/auto/core/qwebengineurlrequestinterceptor/resources/content3.html new file mode 100644 index 000000000..84bf55036 --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestinterceptor/resources/content3.html @@ -0,0 +1,6 @@ +<html> +<head><link rel="icon" href="data:,"></head> +<body> +<a>Simple test page without favicon (meaning no separate request from http server)</a> +</body> +</html> diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/resources/postBodyFile.txt b/tests/auto/core/qwebengineurlrequestinterceptor/resources/postBodyFile.txt new file mode 100644 index 000000000..7729c4e0a --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestinterceptor/resources/postBodyFile.txt @@ -0,0 +1,3 @@ +{ +"test": "1234" +} diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp index 99cf3f244..7cea14c0c 100644 --- a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp +++ b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp @@ -1,13 +1,17 @@ // Copyright (C) 2017 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses + #include <util.h> #include <QtTest/QtTest> #include <QtWebEngineCore/qwebengineurlrequestinfo.h> +#include <QtWebEngineCore/private/qwebengineurlrequestinfo_p.h> #include <QtWebEngineCore/qwebengineurlrequestinterceptor.h> #include <QtWebEngineCore/qwebenginesettings.h> #include <QtWebEngineCore/qwebengineprofile.h> #include <QtWebEngineCore/qwebenginepage.h> +#include <QtWebEngineCore/qwebenginehttprequest.h> #include <httpserver.h> #include <httpreqrep.h> @@ -39,12 +43,18 @@ private Q_SLOTS: void requestInterceptorByResourceType_data(); void requestInterceptorByResourceType(); void firstPartyUrlHttp(); + void headers(); void customHeaders(); void initiator(); void jsServiceWorker(); void replaceInterceptor_data(); void replaceInterceptor(); void replaceOnIntercept(); + void multipleRedirects(); + void postWithBody_data(); + void postWithBody(); + void profilePreventsPageInterception_data(); + void profilePreventsPageInterception(); }; tst_QWebEngineUrlRequestInterceptor::tst_QWebEngineUrlRequestInterceptor() @@ -77,12 +87,14 @@ struct RequestInfo { , firstPartyUrl(info.firstPartyUrl()) , initiator(info.initiator()) , resourceType(info.resourceType()) + , headers(info.httpHeaders()) {} QUrl requestUrl; QUrl firstPartyUrl; QUrl initiator; int resourceType; + QHash<QByteArray, QByteArray> headers; }; static const QUrl kRedirectUrl = QUrl("qrc:///resources/content.html"); @@ -129,7 +141,7 @@ public: // MEMO avoid unintentionally changing request when it is not needed for test logic // since api behavior depends on 'changed' state of the info object - Q_ASSERT(info.changed() == (block || redirect || !headers.empty())); + Q_ASSERT(info.changed() == (block || redirect)); } bool shouldSkipRequest(const RequestInfo &requestInfo) @@ -177,6 +189,29 @@ public: } }; +class TestMultipleRedirectsInterceptor : public QWebEngineUrlRequestInterceptor { +public: + QList<RequestInfo> requestInfos; + QMap<QUrl, QUrl> redirectPairs; + int redirectCount = 0; + void interceptRequest(QWebEngineUrlRequestInfo &info) override + { + QVERIFY(QThread::currentThread() == QCoreApplication::instance()->thread()); + qCDebug(lc) << this << "Type:" << info.resourceType() << info.requestMethod() << "Navigation:" << info.navigationType() + << info.requestUrl() << "Initiator:" << info.initiator(); + auto redirectUrl = redirectPairs.constFind(info.requestUrl()); + if (redirectUrl != redirectPairs.constEnd()) { + info.redirect(redirectUrl.value()); + requestInfos.append(info); + redirectCount++; + } + } + + TestMultipleRedirectsInterceptor() + { + } +}; + class ConsolePage : public QWebEnginePage { Q_OBJECT public: @@ -206,7 +241,7 @@ void tst_QWebEngineUrlRequestInterceptor::interceptRequest() QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); page.load(QUrl("qrc:///resources/index.html")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); QVariant success = loadSpy.takeFirst().takeFirst(); QVERIFY(success.toBool()); loadSpy.clear(); @@ -214,7 +249,7 @@ void tst_QWebEngineUrlRequestInterceptor::interceptRequest() page.runJavaScript("post();", [&ok](const QVariant result){ ok = result; }); QTRY_VERIFY(ok.toBool()); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); success = loadSpy.takeFirst().takeFirst(); // We block non-GET requests, so this should not succeed. QVERIFY(!success.toBool()); @@ -222,22 +257,22 @@ void tst_QWebEngineUrlRequestInterceptor::interceptRequest() interceptor.shouldRedirect = true; page.load(QUrl("qrc:///resources/__placeholder__")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); success = loadSpy.takeFirst().takeFirst(); // The redirection for __placeholder__ should succeed. QVERIFY(success.toBool()); loadSpy.clear(); - QCOMPARE(interceptor.requestInfos.count(), 4); + QCOMPARE(interceptor.requestInfos.size(), 4); // Make sure that registering an observer does not modify the request. TestRequestInterceptor observer(/* intercept */ false); profile.setUrlRequestInterceptor(&observer); page.load(QUrl("qrc:///resources/__placeholder__")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); success = loadSpy.takeFirst().takeFirst(); // Since we do not intercept, loading an invalid path should not succeed. QVERIFY(!success.toBool()); - QCOMPARE(observer.requestInfos.count(), 1); + QCOMPARE(observer.requestInfos.size(), 1); } class LocalhostContentProvider : public QWebEngineUrlRequestInterceptor @@ -270,15 +305,15 @@ void tst_QWebEngineUrlRequestInterceptor::ipv6HostEncoding() QSignalSpy spyLoadFinished(&page, SIGNAL(loadFinished(bool))); page.setHtml("<p>Hi", QUrl::fromEncoded("http://[::1]/index.html")); - QTRY_COMPARE(spyLoadFinished.count(), 1); - QCOMPARE(contentProvider.requestedUrls.count(), 0); + QTRY_COMPARE(spyLoadFinished.size(), 1); + QCOMPARE(contentProvider.requestedUrls.size(), 0); evaluateJavaScriptSync(&page, "var r = new XMLHttpRequest();" "r.open('GET', 'http://[::1]/test.xml', false);" "r.send(null);" ); - QCOMPARE(contentProvider.requestedUrls.count(), 1); + QCOMPARE(contentProvider.requestedUrls.size(), 1); QCOMPARE(contentProvider.requestedUrls.at(0), QUrl::fromEncoded("http://[::1]/test.xml")); } @@ -306,8 +341,8 @@ void tst_QWebEngineUrlRequestInterceptor::requestedUrl() page.setUrl(QUrl("qrc:///resources/__placeholder__")); QVERIFY(spy.wait()); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 20000); - QVERIFY(interceptor.requestInfos.count() >= 1); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 20000); + QVERIFY(interceptor.requestInfos.size() >= 1); QCOMPARE(interceptor.requestInfos.at(0).requestUrl, QUrl("qrc:///resources/content.html")); QCOMPARE(page.requestedUrl(), QUrl("qrc:///resources/__placeholder__")); QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); @@ -315,15 +350,15 @@ void tst_QWebEngineUrlRequestInterceptor::requestedUrl() interceptor.shouldRedirect = false; page.setUrl(QUrl("qrc:/non-existent.html")); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 2, 20000); - QVERIFY(interceptor.requestInfos.count() >= 3); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 2, 20000); + QVERIFY(interceptor.requestInfos.size() >= 3); QCOMPARE(interceptor.requestInfos.at(2).requestUrl, QUrl("qrc:/non-existent.html")); QCOMPARE(page.requestedUrl(), QUrl("qrc:///resources/__placeholder__")); QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); page.setUrl(QUrl("http://abcdef.abcdef")); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 3, 20000); - QVERIFY(interceptor.requestInfos.count() >= 4); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 3, 20000); + QVERIFY(interceptor.requestInfos.size() >= 4); QCOMPARE(interceptor.requestInfos.at(3).requestUrl, QUrl("http://abcdef.abcdef/")); QCOMPARE(page.requestedUrl(), QUrl("qrc:///resources/__placeholder__")); QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); @@ -351,23 +386,23 @@ void tst_QWebEngineUrlRequestInterceptor::setUrlSameUrl() page.setUrl(QUrl("qrc:///resources/__placeholder__")); QVERIFY(spy.wait()); QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); page.setUrl(QUrl("qrc:///resources/__placeholder__")); QVERIFY(spy.wait()); QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); // Now a case without redirect. page.setUrl(QUrl("qrc:///resources/content.html")); QVERIFY(spy.wait()); QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); - QCOMPARE(spy.count(), 3); + QCOMPARE(spy.size(), 3); page.setUrl(QUrl("qrc:///resources/__placeholder__")); QVERIFY(spy.wait()); QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); - QCOMPARE(spy.count(), 4); + QCOMPARE(spy.size(), 4); } void tst_QWebEngineUrlRequestInterceptor::firstPartyUrl() @@ -381,12 +416,12 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrl() page.setUrl(QUrl("qrc:///resources/firstparty.html")); QVERIFY(spy.wait()); - QVERIFY(interceptor.requestInfos.count() >= 2); + QVERIFY(interceptor.requestInfos.size() >= 2); QCOMPARE(interceptor.requestInfos.at(0).requestUrl, QUrl("qrc:///resources/firstparty.html")); QCOMPARE(interceptor.requestInfos.at(1).requestUrl, QUrl("qrc:///resources/content.html")); QCOMPARE(interceptor.requestInfos.at(0).firstPartyUrl, QUrl("qrc:///resources/firstparty.html")); QCOMPARE(interceptor.requestInfos.at(1).firstPartyUrl, QUrl("qrc:///resources/firstparty.html")); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } void tst_QWebEngineUrlRequestInterceptor::firstPartyUrlNestedIframes_data() @@ -419,21 +454,21 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrlNestedIframes() QWebEnginePage page(&profile); QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); page.setUrl(requestUrl); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); - QVERIFY(interceptor.requestInfos.count() >= 1); + QVERIFY(interceptor.requestInfos.size() >= 1); RequestInfo info = interceptor.requestInfos.at(0); QCOMPARE(info.requestUrl, requestUrl); QCOMPARE(info.firstPartyUrl, requestUrl); QCOMPARE(info.resourceType, QWebEngineUrlRequestInfo::ResourceTypeMainFrame); - QVERIFY(interceptor.requestInfos.count() >= 2); + QVERIFY(interceptor.requestInfos.size() >= 2); info = interceptor.requestInfos.at(1); QCOMPARE(info.requestUrl, QUrl(adjustedUrl + "iframe2.html")); QCOMPARE(info.firstPartyUrl, requestUrl); QCOMPARE(info.resourceType, QWebEngineUrlRequestInfo::ResourceTypeSubFrame); - QVERIFY(interceptor.requestInfos.count() >= 3); + QVERIFY(interceptor.requestInfos.size() >= 3); info = interceptor.requestInfos.at(2); QCOMPARE(info.requestUrl, QUrl(adjustedUrl + "iframe3.html")); QCOMPARE(info.firstPartyUrl, requestUrl); @@ -509,11 +544,11 @@ void tst_QWebEngineUrlRequestInterceptor::requestInterceptorByResourceType() QWebEnginePage page(&profile); QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); page.setUrl(firstPartyUrl); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); - QTRY_COMPARE(interceptor.getUrlRequestForType(static_cast<QWebEngineUrlRequestInfo::ResourceType>(resourceType)).count(), 1); + QTRY_COMPARE(interceptor.getUrlRequestForType(static_cast<QWebEngineUrlRequestInfo::ResourceType>(resourceType)).size(), 1); QList<RequestInfo> infos = interceptor.getUrlRequestForType(static_cast<QWebEngineUrlRequestInfo::ResourceType>(resourceType)); - QVERIFY(infos.count() >= 1); + QVERIFY(infos.size() >= 1); QCOMPARE(infos.at(0).requestUrl, requestUrl); QCOMPARE(infos.at(0).firstPartyUrl, firstPartyUrl); QCOMPARE(infos.at(0).resourceType, resourceType); @@ -577,6 +612,40 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrlHttp() QCOMPARE(info.firstPartyUrl, firstPartyUrl); } +void tst_QWebEngineUrlRequestInterceptor::headers() +{ + HttpServer httpServer; + httpServer.setResourceDirs({ QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + "/resources" }); + QVERIFY(httpServer.start()); + QWebEngineProfile profile; + TestRequestInterceptor interceptor(false); + profile.setUrlRequestInterceptor(&interceptor); + + QWebEnginePage page(&profile); + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + + QWebEngineHttpRequest request(httpServer.url("/content.html")); + request.setHeader("X-HEADERNAME", "HEADERVALUE"); + page.load(request); + QVERIFY(spy.wait()); + QVERIFY(interceptor.requestInfos.last().headers.contains("X-HEADERNAME")); + QCOMPARE(interceptor.requestInfos.last().headers.value("X-HEADERNAME"), + QByteArray("HEADERVALUE")); + + bool jsFinished = false; + + page.runJavaScript(R"( +var request = new XMLHttpRequest(); +request.open('GET', 'resource.html', /* async = */ false); +request.setRequestHeader('X-FOO', 'BAR'); +request.send(); +)", + [&](const QVariant &) { jsFinished = true; }); + QTRY_VERIFY(jsFinished); + QVERIFY(interceptor.requestInfos.last().headers.contains("X-FOO")); + QCOMPARE(interceptor.requestInfos.last().headers.value("X-FOO"), QByteArray("BAR")); +} + void tst_QWebEngineUrlRequestInterceptor::customHeaders() { // Create HTTP Server to parse the request. @@ -711,7 +780,7 @@ void tst_QWebEngineUrlRequestInterceptor::jsServiceWorker() // We expect only one message here, because logging of services workers is not exposed in our API. // Note this is very fragile setup , you need fresh profile otherwise install event might not get triggered // and this in turn can lead to incorrect intercepted requests, therefore we should keep this off the record. - QTRY_COMPARE_WITH_TIMEOUT(page->messages.count(), 5, 20000); + QTRY_COMPARE_WITH_TIMEOUT(page->messages.size(), 5, 20000); QCOMPARE(page->levels.at(0), QWebEnginePage::InfoMessageLevel); QCOMPARE(page->messages.at(0),QLatin1String("Service worker installing")); @@ -785,7 +854,7 @@ void tst_QWebEngineUrlRequestInterceptor::replaceInterceptor() }); page.setUrl(server.url("/favicon.html")); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 2, 20000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 2, 20000); QTRY_VERIFY(fetchFinished); QString s; QDebug d(&s); @@ -829,7 +898,7 @@ void tst_QWebEngineUrlRequestInterceptor::replaceOnIntercept() }; page.setUrl(server.url("/favicon.html")); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 20000); QTRY_COMPARE(profileInterceptor.requestInfos.size(), 2); // if interceptor for page was replaced on intercept call in profile then, since request first @@ -850,5 +919,204 @@ void tst_QWebEngineUrlRequestInterceptor::replaceOnIntercept() QCOMPARE(profileInterceptor.requestInfos.size(), pageInterceptor2.requestInfos.size()); } +void tst_QWebEngineUrlRequestInterceptor::multipleRedirects() +{ + HttpServer server; + server.setResourceDirs({ ":/resources" }); + QVERIFY(server.start()); + + TestMultipleRedirectsInterceptor multiInterceptor; + multiInterceptor.redirectPairs.insert(QUrl(server.url("/content.html")), QUrl(server.url("/content2.html"))); + multiInterceptor.redirectPairs.insert(QUrl(server.url("/content2.html")), QUrl(server.url("/content3.html"))); + + QWebEngineProfile profile; + profile.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + profile.setUrlRequestInterceptor(&multiInterceptor); + QWebEnginePage page(&profile); + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + + page.setUrl(server.url("/content.html")); + + QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 20000); + QTRY_COMPARE(multiInterceptor.redirectCount, 2); + QTRY_COMPARE(multiInterceptor.requestInfos.size(), 2); +} + +class TestPostRequestInterceptor : public QWebEngineUrlRequestInterceptor +{ +public: + TestPostRequestInterceptor(QString expected, bool isAppendFile, QObject *parent = nullptr) + : QWebEngineUrlRequestInterceptor(parent) + , m_expected(expected) + , m_isAppendFile(isAppendFile) + {}; + + void interceptRequest(QWebEngineUrlRequestInfo &info) override + { + info.block(true); + isCalled = true; + + QIODevice *requestBodyDevice = info.requestBody(); + + if (m_isAppendFile) { + info.d_ptr->appendFileToResourceRequestBodyForTest(":/resources/postBodyFile.txt"); + } + + requestBodyDevice->open(QIODevice::ReadOnly); + + const QString webKitBoundary = requestBodyDevice->read(40); + QVERIFY(webKitBoundary.contains("------WebKitFormBoundary")); + + const QString fullBodyWithoutBoundaries = (webKitBoundary + requestBodyDevice->readAll()) + .replace(webKitBoundary, "") + .replace("\r", "") + .replace("\n", "") + .replace(" ", ""); + + QCOMPARE(fullBodyWithoutBoundaries, m_expected); + + requestBodyDevice->close(); + } + + bool isCalled = false; + QString m_expected; + bool m_isAppendFile; +}; + +void tst_QWebEngineUrlRequestInterceptor::postWithBody_data() +{ + QTest::addColumn<QString>("input"); + QTest::addColumn<QString>("output"); + QTest::addColumn<bool>("isAppendFile"); + QTest::addRow("FormData append (DataElementByte)") + << "fd.append('userId', 1);" + "fd.append('title',' Test123');" + "fd.append('completed', false);" + << "Content-Disposition:form-data;name=\"userId" + "\"1Content-Disposition:form-data" + ";name=\"title\"Test123Content-Di" + "sposition:form-data;name=\"completed\"f" + "alse--" + << false; + QTest::addRow("FormData blob (DataElementPipe)") + << "const blob1 = new Blob(['blob1thisisablob']," + "{type: 'text/plain'});" + "fd.append('blob1', blob1);" + << "Content-Disposition:form-data;name=\"blob1" + "\";filename=\"blob\"Content-Type:text/plai" + "nblob1thisisablob--" + << false; + QTest::addRow("Append file (DataElementFile)") << "" + << "--{\"test\":\"1234\"}\"1234\"}" << true; + QTest::addRow("All combined") << "fd.append('userId', 1);" + "fd.append('title', 'Test123');" + "fd.append('completed', false);" + "const blob1 = new Blob(['blob1thisisablob']," + "{type: 'text/plain'});" + "const blob2 = new Blob(['blob2thisisanotherblob']," + "{type: 'text/plain'});" + "fd.append('blob1', blob1);" + "fd.append('userId', 2);" + "fd.append('title', 'Test456');" + "fd.append('completed', true);" + "fd.append('blob2', blob2);" + << "Content-Disposition:form-data;name=\"userId\"" + "1Content-Disposition:form-data;na" + "me=\"title\"Test123Content-Disposit" + "ion:form-data;name=\"completed\"false" + "Content-Disposition:form-data;name=\"blob1\";" + "filename=\"blob\"Content-Type:text/plain" + "blob1thisisablobContent-Disposition:form-" + "data;name=\"userId\"2Content-Dispos" + "ition:form-data;name=\"title\"Test456" + "Content-Disposition:form-data;name=\"complete" + "d\"trueContent-Disposition:form-da" + "ta;name=\"blob2\";filename=\"blob\"Content-Ty" + "pe:text/plainblob2thisisanotherblob--" + "{\"test\":\"1234\"}\"1234\"}" + << true; +} + +void tst_QWebEngineUrlRequestInterceptor::postWithBody() +{ + QFETCH(QString, input); + QFETCH(QString, output); + QFETCH(bool, isAppendFile); + + QString script; + script.append("const fd = new FormData();"); + script.append(input); + script.append("fetch('http://127.0.0.1', {method: 'POST',body: fd});"); + + QWebEngineProfile profile; + profile.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + TestPostRequestInterceptor interceptor(output, isAppendFile); + profile.setUrlRequestInterceptor(&interceptor); + QWebEnginePage page(&profile); + bool ok = false; + + page.runJavaScript(script, [&ok](const QVariant) { ok = true; }); + + QTRY_VERIFY(ok); + QVERIFY(interceptor.isCalled); +} + +class PageOrProfileInterceptor : public QWebEngineUrlRequestInterceptor +{ +public: + PageOrProfileInterceptor(const QString &profileAction) + : profileAction(profileAction) + { + } + + void interceptRequest(QWebEngineUrlRequestInfo &info) override + { + if (profileAction == "block") + info.block(true); + else if (profileAction == "redirect") + info.redirect(QUrl("data:text/html,<p>redirected")); + else if (profileAction == "add header") + info.setHttpHeader("Custom-Header", "Value"); + else + QVERIFY(info.httpHeaders().contains("Custom-Header")); + ran = true; + } + + QString profileAction; + bool ran = false; +}; + +void tst_QWebEngineUrlRequestInterceptor::profilePreventsPageInterception_data() +{ + QTest::addColumn<QString>("profileAction"); + QTest::addColumn<bool>("interceptInProfile"); + QTest::addColumn<bool>("interceptInPage"); + QTest::newRow("block") << "block" << true << false; + QTest::newRow("redirect") << "redirect" << true << false; + QTest::newRow("add header") << "add header" << true << true; +} + +void tst_QWebEngineUrlRequestInterceptor::profilePreventsPageInterception() +{ + QFETCH(QString, profileAction); + QFETCH(bool, interceptInProfile); + QFETCH(bool, interceptInPage); + + QWebEngineProfile profile; + PageOrProfileInterceptor profileInterceptor(profileAction); + profile.setUrlRequestInterceptor(&profileInterceptor); + profile.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + + QWebEnginePage page(&profile); + PageOrProfileInterceptor pageInterceptor(""); + page.setUrlRequestInterceptor(&pageInterceptor); + QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); + + page.load(QUrl("qrc:///resources/index.html")); + QTRY_COMPARE(loadSpy.size(), 1); + QCOMPARE(profileInterceptor.ran, interceptInProfile); + QCOMPARE(pageInterceptor.ran, interceptInPage); +} + QTEST_MAIN(tst_QWebEngineUrlRequestInterceptor) #include "tst_qwebengineurlrequestinterceptor.moc" diff --git a/tests/auto/core/qwebengineurlrequestjob/CMakeLists.txt b/tests/auto/core/qwebengineurlrequestjob/CMakeLists.txt new file mode 100644 index 000000000..02b668313 --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestjob/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_test(tst_qwebengineurlrequestjob + SOURCES + tst_qwebengineurlrequestjob.cpp + LIBRARIES + Qt::WebEngineCore +) + +# Resources: +set(tst_qwebengineurlrequestjob_resource_files + "additionalResponseHeadersScript.html" + "requestBodyScript.html" +) + +qt_add_resources(tst_qwebengineurlrequestjob "tst_qwebengineurlrequestjob" + PREFIX + "/" + FILES + ${tst_qwebengineurlrequestjob_resource_files} +) diff --git a/tests/auto/core/qwebengineurlrequestjob/additionalResponseHeadersScript.html b/tests/auto/core/qwebengineurlrequestjob/additionalResponseHeadersScript.html new file mode 100644 index 000000000..a1b8a92d3 --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestjob/additionalResponseHeadersScript.html @@ -0,0 +1,12 @@ +<!doctype html> +<html> +<body> + <script type="text/javascript"> + var request = new XMLHttpRequest(); + request.open('GET', 'additionalresponseheadershandler:about', /* async = */ false); + request.send(); + var headers = request.getAllResponseHeaders(); + console.log("TST_ADDITIONALRESPONSEHEADERS;" + headers); + </script> +</body> +</html> diff --git a/tests/auto/core/qwebengineurlrequestjob/requestBodyScript.html b/tests/auto/core/qwebengineurlrequestjob/requestBodyScript.html new file mode 100644 index 000000000..95b7ff1bf --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestjob/requestBodyScript.html @@ -0,0 +1,12 @@ +<!doctype html> +<html> +<body> + <script type="text/javascript"> + var request = new XMLHttpRequest(); + request.open('POST', 'requestbodyhandler:about', /* async = */ false); + request.setRequestHeader('Content-Type', 'text/plain'); + request.send('reading request body successful'); + console.log("TST_REQUESTBODY;" + request.response); + </script> +</body> +</html> diff --git a/tests/auto/core/qwebengineurlrequestjob/tst_qwebengineurlrequestjob.cpp b/tests/auto/core/qwebengineurlrequestjob/tst_qwebengineurlrequestjob.cpp new file mode 100644 index 000000000..d48da2c44 --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestjob/tst_qwebengineurlrequestjob.cpp @@ -0,0 +1,187 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <QtTest/QtTest> +#include <QtWebEngineCore/qwebengineurlschemehandler.h> +#include <QtWebEngineCore/qwebengineurlscheme.h> +#include <QtWebEngineCore/qwebengineurlrequestjob.h> +#include <QtWebEngineCore/qwebengineprofile.h> +#include <QtWebEngineCore/qwebenginepage.h> + +class CustomPage : public QWebEnginePage +{ + Q_OBJECT + +public: + CustomPage(QWebEngineProfile *profile, QString compareStringPrefix, QString compareStringSuffix, + QObject *parent = nullptr) + : QWebEnginePage(profile, parent) + , m_compareStringPrefix(compareStringPrefix) + , m_compareStringSuffix(compareStringSuffix) + , m_comparedMessageCount(0) + { + } + + int comparedMessageCount() const { return m_comparedMessageCount; } + +signals: + void receivedMessage(); + +protected: + void javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel level, + const QString &message, int lineNumber, + const QString &sourceID) override + { + Q_UNUSED(level); + Q_UNUSED(lineNumber); + Q_UNUSED(sourceID); + + auto splitMessage = message.split(";"); + if (splitMessage[0] == m_compareStringPrefix) { + QCOMPARE(splitMessage[1], m_compareStringSuffix); + m_comparedMessageCount++; + emit receivedMessage(); + } + } + +private: + QString m_compareStringPrefix; + QString m_compareStringSuffix; + int m_comparedMessageCount; +}; + +class AdditionalResponseHeadersHandler : public QWebEngineUrlSchemeHandler +{ + Q_OBJECT +public: + AdditionalResponseHeadersHandler(bool addAdditionalResponseHeaders, QObject *parent = nullptr) + : QWebEngineUrlSchemeHandler(parent) + , m_addAdditionalResponseHeaders(addAdditionalResponseHeaders) + { + } + + void requestStarted(QWebEngineUrlRequestJob *job) override + { + QCOMPARE(job->requestUrl(), QUrl(schemeName + ":about")); + + QMultiMap<QByteArray, QByteArray> additionalResponseHeaders; + if (m_addAdditionalResponseHeaders) { + additionalResponseHeaders.insert(QByteArray::fromStdString("test1"), + QByteArray::fromStdString("test1VALUE")); + additionalResponseHeaders.insert(QByteArray::fromStdString("test2"), + QByteArray::fromStdString("test2VALUE")); + } + job->setAdditionalResponseHeaders(additionalResponseHeaders); + + QFile *file = new QFile(QStringLiteral(":additionalResponseHeadersScript.html"), job); + file->open(QIODevice::ReadOnly); + + job->reply(QByteArrayLiteral("text/html"), file); + } + + static void registerUrlScheme() + { + QWebEngineUrlScheme webUiScheme(schemeName); + webUiScheme.setFlags(QWebEngineUrlScheme::CorsEnabled); + QWebEngineUrlScheme::registerScheme(webUiScheme); + } + + const static inline QByteArray schemeName = + QByteArrayLiteral("additionalresponseheadershandler"); + +private: + bool m_addAdditionalResponseHeaders; +}; + +class RequestBodyHandler : public QWebEngineUrlSchemeHandler +{ + Q_OBJECT +public: + void requestStarted(QWebEngineUrlRequestJob *job) override + { + QCOMPARE(job->requestUrl(), QUrl(schemeName + ":about")); + QCOMPARE(job->requestMethod(), QByteArrayLiteral("POST")); + + QIODevice *requestBodyDevice = job->requestBody(); + requestBodyDevice->open(QIODevice::ReadOnly); + QByteArray requestBody = requestBodyDevice->readAll(); + requestBodyDevice->close(); + + QBuffer *buf = new QBuffer(job); + buf->open(QBuffer::ReadWrite); + buf->write(requestBody); + job->reply(QByteArrayLiteral("text/plain"), buf); + buf->close(); + } + + static void registerUrlScheme() + { + QWebEngineUrlScheme webUiScheme(schemeName); + webUiScheme.setFlags(QWebEngineUrlScheme::CorsEnabled + | QWebEngineUrlScheme::FetchApiAllowed); + QWebEngineUrlScheme::registerScheme(webUiScheme); + } + + const static inline QByteArray schemeName = QByteArrayLiteral("requestbodyhandler"); +}; + +class tst_QWebEngineUrlRequestJob : public QObject +{ + Q_OBJECT + +public: + tst_QWebEngineUrlRequestJob() { } + +private Q_SLOTS: + void initTestCase() + { + AdditionalResponseHeadersHandler::registerUrlScheme(); + RequestBodyHandler::registerUrlScheme(); + } + + void withAdditionalResponseHeaders_data() + { + QTest::addColumn<bool>("withHeaders"); + QTest::addColumn<QString>("expectedHeaders"); + QTest::newRow("headers enabled") + << true << "content-type: text/html\r\ntest1: test1value\r\ntest2: test2value\r\n"; + QTest::newRow("headers disabled") << false << "content-type: text/html\r\n"; + } + + void withAdditionalResponseHeaders() + { + QFETCH(bool, withHeaders); + QFETCH(QString, expectedHeaders); + + QWebEngineProfile profile; + + AdditionalResponseHeadersHandler handler(withHeaders); + profile.installUrlSchemeHandler(AdditionalResponseHeadersHandler::schemeName, &handler); + + CustomPage page(&profile, "TST_ADDITIONALRESPONSEHEADERS", expectedHeaders); + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + + page.load(QUrl("qrc:///additionalResponseHeadersScript.html")); + QVERIFY(spy.wait()); + QCOMPARE(page.comparedMessageCount(), 1); + } + + void requestBody() + { + QWebEngineProfile profile; + + RequestBodyHandler handler; + profile.installUrlSchemeHandler(RequestBodyHandler::schemeName, &handler); + + const QString expected = "reading request body successful"; + CustomPage page(&profile, "TST_REQUESTBODY", expected); + QSignalSpy spy(&page, SIGNAL(receivedMessage())); + + page.load(QUrl("qrc:///requestBodyScript.html")); + QVERIFY(spy.wait()); + QCOMPARE(page.comparedMessageCount(), 1); + } +}; + +QTEST_MAIN(tst_QWebEngineUrlRequestJob) +#include "tst_qwebengineurlrequestjob.moc" diff --git a/tests/auto/core/webenginedriver/CMakeLists.txt b/tests/auto/core/webenginedriver/CMakeLists.txt new file mode 100644 index 000000000..c8cf8b3ab --- /dev/null +++ b/tests/auto/core/webenginedriver/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_webenginedriver LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +add_subdirectory(test) +add_subdirectory(browser) + +add_dependencies(tst_webenginedriver + testbrowser +) diff --git a/tests/auto/core/webenginedriver/browser/CMakeLists.txt b/tests/auto/core/webenginedriver/browser/CMakeLists.txt new file mode 100644 index 000000000..25e162e7b --- /dev/null +++ b/tests/auto/core/webenginedriver/browser/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_executable(testbrowser + SOURCES + main.cpp + LIBRARIES + Qt::Core + Qt::Gui + Qt::WebEngineWidgets + OUTPUT_DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR}/.. +) diff --git a/tests/auto/core/webenginedriver/browser/main.cpp b/tests/auto/core/webenginedriver/browser/main.cpp new file mode 100644 index 000000000..4b8f3513f --- /dev/null +++ b/tests/auto/core/webenginedriver/browser/main.cpp @@ -0,0 +1,21 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWebEngineCore/qwebenginepage.h> +#include <QtWebEngineWidgets/qwebengineview.h> +#include <QtWidgets/qapplication.h> + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + QWebEngineView view; + QObject::connect(view.page(), &QWebEnginePage::windowCloseRequested, &app, &QApplication::quit); + QObject::connect(&app, &QApplication::aboutToQuit, + []() { fprintf(stderr, "Test browser is about to quit.\n"); }); + + view.resize(100, 100); + view.show(); + + return app.exec(); +} diff --git a/tests/auto/core/webenginedriver/resources/input.html b/tests/auto/core/webenginedriver/resources/input.html new file mode 100644 index 000000000..c21458350 --- /dev/null +++ b/tests/auto/core/webenginedriver/resources/input.html @@ -0,0 +1,5 @@ +<html> +<body> + <input type="text" id="text_input"> +</body> +</html> diff --git a/tests/auto/core/webenginedriver/test/CMakeLists.txt b/tests/auto/core/webenginedriver/test/CMakeLists.txt new file mode 100644 index 000000000..041bf955b --- /dev/null +++ b/tests/auto/core/webenginedriver/test/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +include(../../../util/util.cmake) + +qt_internal_add_test(tst_webenginedriver + SOURCES + ../tst_webenginedriver.cpp + LIBRARIES + Qt::Network + Qt::WebEngineCore + Qt::WebEngineWidgets + Test::Util + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." +) + +set(tst_webenginedriver_resource_files + "../resources/input.html" +) + +qt_internal_add_resource(tst_webenginedriver "tst_webenginedriver" + PREFIX + "/" + FILES + ${tst_webenginedriver_resource_files} +) diff --git a/tests/auto/core/webenginedriver/tst_webenginedriver.cpp b/tests/auto/core/webenginedriver/tst_webenginedriver.cpp new file mode 100644 index 000000000..cd3098b25 --- /dev/null +++ b/tests/auto/core/webenginedriver/tst_webenginedriver.cpp @@ -0,0 +1,631 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <QtTest/QtTest> + +#include <QtCore/qjsondocument.h> +#include <QtCore/qlibraryinfo.h> +#include <QtCore/qobject.h> +#include <QtCore/qprocess.h> +#include <QtGui/qimage.h> +#include <QtNetwork/qnetworkaccessmanager.h> +#include <QtNetwork/qnetworkreply.h> +#include <QtNetwork/qnetworkrequest.h> +#include <QtWebEngineCore/qtwebenginecoreglobal.h> +#include <QtWebEngineCore/qwebenginepage.h> +#include <QtWebEngineCore/qwebengineprofile.h> +#include <QtWebEngineWidgets/qwebengineview.h> + +#include <widgetutil.h> + +#define REMOTE_DEBUGGING_PORT 12345 +#define WEBENGINEDRIVER_PORT 9515 + +class DriverServer : public QObject +{ + Q_OBJECT + +public: + DriverServer(QProcessEnvironment processEnvironment = {}, QStringList processArguments = {}) + : m_serverURL(QUrl( + QLatin1String("http://localhost:%1").arg(QString::number(WEBENGINEDRIVER_PORT)))) + { + QString driverPath = QLibraryInfo::path(QLibraryInfo::LibraryExecutablesPath) + + QLatin1String("/webenginedriver"); +#if defined(Q_OS_WIN) + driverPath += QLatin1String(".exe"); +#endif + m_process.setProgram(driverPath); + + if (processArguments.isEmpty()) + processArguments + << QLatin1String("--port=%1").arg(QString::number(WEBENGINEDRIVER_PORT)); + m_process.setArguments(processArguments); + + if (!processEnvironment.isEmpty()) + m_process.setProcessEnvironment(processEnvironment); + + connect(&m_process, &QProcess::errorOccurred, [this](QProcess::ProcessError error) { + qWarning() << "WebEngineDriver error occurred:" << error; + dumpConsoleMessages(); + }); + + connect(&m_process, &QProcess::readyReadStandardError, [this]() { + QProcess::ProcessChannel tmp = m_process.readChannel(); + m_process.setReadChannel(QProcess::StandardError); + while (m_process.canReadLine()) { + QString line = QString::fromUtf8(m_process.readLine()); + if (line.endsWith(QLatin1Char('\n'))) + line.truncate(line.size() - 1); + m_stderr << line; + } + m_process.setReadChannel(tmp); + }); + + connect(&m_process, &QProcess::readyReadStandardOutput, [this]() { + QProcess::ProcessChannel tmp = m_process.readChannel(); + m_process.setReadChannel(QProcess::StandardOutput); + while (m_process.canReadLine()) { + QString line = QString::fromUtf8(m_process.readLine()); + if (line.endsWith(QLatin1Char('\n'))) + line.truncate(line.size() - 1); + m_stdout << line; + } + m_process.setReadChannel(tmp); + }); + } + + ~DriverServer() + { + if (m_process.state() != QProcess::Running) + return; + + if (!m_sessionId.isEmpty()) + deleteSession(); + + shutdown(); + } + + bool start() + { + if (!QFileInfo::exists(m_process.program())) { + qWarning() << "WebEngineDriver executable not found:" << m_process.program(); + return false; + } + + connect(&m_process, &QProcess::finished, + [this](int exitCode, QProcess::ExitStatus exitStatus) { + qWarning().nospace() + << "WebEngineDriver exited unexpectedly (exitCode: " << exitCode + << " exitStatus: " << exitStatus << "):"; + dumpConsoleMessages(); + }); + + m_process.start(); + + bool started = m_process.waitForStarted(); + if (!started) { + qWarning() << "Failed to start WebEngineDriver:" << m_process.errorString(); + return false; + } + + bool ready = QTest::qWaitFor( + [this]() { + if (m_process.state() != QProcess::Running) + return true; + + for (QString line : m_stdout) { + if (line.contains( + QLatin1String("WebEngineDriver was started successfully."))) + return true; + } + + return false; + }, + 10000); + + if (ready && m_process.state() != QProcess::Running) { + // Warning is already reported by handler of QProcess::finished() signal. + return false; + } + + if (!ready) { + if (m_stdout.empty()) + qWarning("Starting WebEngineDriver timed out."); + else + qWarning("Something went wrong while starting WebEngineDriver:"); + + dumpConsoleMessages(); + return false; + } + + return true; + } + + bool shutdown() + { + // Do not warn about unexpected exit. + disconnect(&m_process, &QProcess::finished, nullptr, nullptr); + + bool sent = sendCommand(QLatin1String("/shutdown")); + + bool finished = (m_process.state() == QProcess::NotRunning) || m_process.waitForFinished(); + if (!finished || !sent) + qWarning() << "Failed to properly shutdown WebEngineDriver:" << m_process.errorString(); + + return finished; + } + + bool sendCommand(QString command, const QJsonDocument ¶ms = {}, + QJsonDocument *result = nullptr) + { + if (command.contains(QLatin1String(":sessionId"))) { + if (m_sessionId.isEmpty()) { + qWarning("Unable to execute session command without session."); + return false; + } + + QStringList commandList = command.split(QLatin1Char('/')); + for (int i = 0; i < commandList.size(); ++i) { + if (commandList[i] == QLatin1String(":sessionId")) { + commandList[i] = m_sessionId; + break; + } + } + + command = commandList.join(QLatin1Char('/')); + } + + QNetworkReply::NetworkError replyError = QNetworkReply::NoError; + QString replyString; + + connect( + &m_qnam, &QNetworkAccessManager::finished, this, + [&replyError, &replyString](QNetworkReply *reply) { + replyError = reply->error(); + replyString = QString::fromUtf8(reply->readAll()); + }, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection)); + + QNetworkRequest request; + QUrl requestURL = m_serverURL; + + requestURL.setPath(command); + request.setUrl(requestURL); + + if (params.isEmpty()) { + m_qnam.get(request); + } else { + request.setHeader(QNetworkRequest::ContentTypeHeader, + QVariant::fromValue(QStringLiteral("application/json"))); + m_qnam.post(request, params.toJson(QJsonDocument::Compact)); + } + + bool ready = QTest::qWaitFor( + [&replyError, &replyString]() { + return replyError != QNetworkReply::NoError || !replyString.isEmpty(); + }, + 10000); + + if (!ready) { + qWarning() << "Command" << command << "timed out."; + dumpConsoleMessages(); + return false; + } + + if (replyError != QNetworkReply::NoError) { + qWarning() << "Network error:" << replyError; + if (!replyString.isEmpty()) { + QJsonDocument errorReply = QJsonDocument::fromJson(replyString.toLatin1()); + if (!errorReply.isNull()) { + QString error = errorReply["value"]["error"].toString(); + QString message = errorReply["value"]["message"].toString(); + if (!error.isEmpty() || message.isEmpty()) { + qWarning() << "error:" << error; + qWarning() << "message:" << message; + return false; + } + } + + qWarning() << replyString; + return false; + } + + dumpConsoleMessages(); + return false; + } + + if (result) { + if (replyString.isEmpty()) { + qWarning("Network reply is empty."); + return false; + } + + QJsonParseError jsonError; + *result = QJsonDocument::fromJson(replyString.toLatin1(), &jsonError); + + if (jsonError.error != QJsonParseError::NoError) { + qWarning() << "Unable to parse reply:" << jsonError.errorString(); + return false; + } + } + + return true; + } + + bool createSession(QJsonObject chromeOptions = {}, QString *sessionId = nullptr) + { + if (!m_sessionId.isEmpty()) { + qWarning("A session already exists."); + return false; + } + + QJsonObject root; + + if (chromeOptions.isEmpty()) { + // Connect to the test by default. + chromeOptions.insert( + QLatin1String("debuggerAddress"), + QLatin1String("localhost:%1").arg(QString::number(REMOTE_DEBUGGING_PORT))); + chromeOptions.insert(QLatin1String("w3c"), true); + } + + QJsonObject alwaysMatch; + alwaysMatch.insert(QLatin1String("goog:chromeOptions"), chromeOptions); + + QJsonObject seOptions; + seOptions.insert(QLatin1String("loggingPrefs"), QJsonObject()); + alwaysMatch.insert(QLatin1String("se:options"), seOptions); + + QJsonObject capabilities; + capabilities.insert(QLatin1String("alwaysMatch"), alwaysMatch); + root.insert(QLatin1String("capabilities"), capabilities); + + QJsonDocument params; + params.setObject(root); + + QJsonDocument sessionReply; + bool sent = sendCommand(QLatin1String("/session"), params, &sessionReply); + if (sent) { + m_sessionId = sessionReply["value"]["sessionId"].toString(); + if (sessionId) + *sessionId = m_sessionId; + } + + return sent; + } + + bool deleteSession() + { + if (m_sessionId.isEmpty()) { + qWarning("There is no active session."); + return false; + } + + QNetworkReply::NetworkError replyError = QNetworkReply::NoError; + QString replyString; + + connect( + &m_qnam, &QNetworkAccessManager::finished, this, + [&replyError, &replyString](QNetworkReply *reply) { + replyError = reply->error(); + replyString = QString::fromUtf8(reply->readAll()); + }, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection)); + + QNetworkRequest request; + QUrl requestURL = m_serverURL; + requestURL.setPath(QLatin1String("/session/%1").arg(m_sessionId)); + request.setUrl(requestURL); + m_qnam.deleteResource(request); + + bool ready = QTest::qWaitFor( + [&replyError, &replyString]() { + return replyError != QNetworkReply::NoError || !replyString.isEmpty(); + }, + 10000); + + if (!ready) { + qWarning("Deleting session timed out."); + return false; + } + + if (replyError != QNetworkReply::NoError) { + qWarning() << "Network error:" << replyError; + return false; + } + + return true; + } + + QStringList stderrLines() const { return m_stderr; } + + bool waitForMessageOnStderr(const QRegularExpression &re, QString *message = nullptr) const + { + return QTest::qWaitFor( + [this, &re, message]() { + for (QString line : m_stderr) { + if (line.contains(re)) { + if (message) + *message = line; + return true; + } + } + + return false; + }, + 10000); + } + +private: + void dumpConsoleMessages() + { + auto dumpLines = [](QStringList *lines) { + if (lines->empty()) + return; + + for (QString line : *lines) + qWarning() << qPrintable(line); + + lines->clear(); + }; + + dumpLines(&m_stdout); + dumpLines(&m_stderr); + } + + QProcess m_process; + QStringList m_stdout; + QStringList m_stderr; + + QUrl m_serverURL; + QString m_sessionId; + + QNetworkAccessManager m_qnam; +}; + +class tst_WebEngineDriver : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void status(); + void startBrowser(); + void navigate(); + void typeElement(); + void executeScript(); + void screenshot(); +}; + +void tst_WebEngineDriver::status() +{ + DriverServer driverServer; + QVERIFY(driverServer.start()); + + QJsonDocument statusReply; + QVERIFY(driverServer.sendCommand(QLatin1String("/status"), {}, &statusReply)); + + QString versionString = statusReply["value"]["build"]["version"].toString(); + QCOMPARE(qWebEngineChromiumVersion(), versionString.split(QLatin1Char(' '))[0]); + + bool ready = statusReply["value"]["ready"].toBool(); + QVERIFY(ready); +} + +void tst_WebEngineDriver::startBrowser() +{ + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(QLatin1String("QTWEBENGINE_REMOTE_DEBUGGING"), + QString::number(REMOTE_DEBUGGING_PORT)); + + QStringList args; + args << QLatin1String("--port=%1").arg(QString::number(WEBENGINEDRIVER_PORT)); + args << QLatin1String("--log-level=ALL"); + + DriverServer driverServer(env, args); + QVERIFY(driverServer.start()); + + QString testBrowserPath = + QCoreApplication::applicationDirPath() + QLatin1String("/testbrowser"); +#if defined(Q_OS_WIN) + testBrowserPath += QLatin1String(".exe"); +#endif + + QJsonArray chromeArgs; + chromeArgs.append(QLatin1String("enable-logging=stderr")); + chromeArgs.append(QLatin1String("v=1")); + // To force graceful shutdown. + chromeArgs.append(QLatin1String("log-net-log")); + + QJsonObject chromeOptions; + chromeOptions.insert(QLatin1String("binary"), testBrowserPath); + chromeOptions.insert(QLatin1String("args"), chromeArgs); + chromeOptions.insert(QLatin1String("w3c"), true); + QString sessionId; + QVERIFY(driverServer.createSession(chromeOptions, &sessionId)); + + // Test if the browser is started. + QVERIFY(driverServer.waitForMessageOnStderr(QRegularExpression( + QLatin1String("^DevTools listening on ws://127.0.0.1:%1/devtools/browser/") + .arg(QString::number(REMOTE_DEBUGGING_PORT))))); + QVERIFY(driverServer.waitForMessageOnStderr(QRegularExpression( + QLatin1String("^Remote debugging server started successfully. " + "Try pointing a Chromium-based browser to http://127.0.0.1:%1") + .arg(QString::number(REMOTE_DEBUGGING_PORT))))); + + // Check custom chromeArgs via logging. + QVERIFY(driverServer.waitForMessageOnStderr(QRegularExpression(QLatin1String("VERBOSE1")))); + + QJsonDocument statusReply; + QVERIFY(driverServer.sendCommand(QLatin1String("/status"), {}, &statusReply)); + bool ready = statusReply["value"]["ready"].toBool(); + QVERIFY(ready); + + QVERIFY(driverServer.deleteSession()); + + // Test if the browser is stopped. + QVERIFY(driverServer.waitForMessageOnStderr( + QRegularExpression(QLatin1String("Test browser is about to quit.")))); + QVERIFY(driverServer.waitForMessageOnStderr( + QRegularExpression(QLatin1String("\\[%1\\] RESPONSE Quit").arg(sessionId)))); +} + +void tst_WebEngineDriver::navigate() +{ + DriverServer driverServer; + QVERIFY(driverServer.start()); + + QWebEnginePage page; + QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); + + page.load(QUrl(QLatin1String("about:blank"))); + QTRY_COMPARE(loadSpy.size(), 1); + + QVERIFY(driverServer.createSession()); + + QUrl url = QUrl(QLatin1String("qrc:///resources/input.html")); + QString paramsString = QLatin1String("{\"url\": \"%1\"}").arg(url.toString()); + QJsonDocument params = QJsonDocument::fromJson(paramsString.toUtf8()); + QVERIFY(driverServer.sendCommand(QLatin1String("/session/:sessionId/url"), params)); + QTRY_COMPARE(loadSpy.size(), 2); + QVERIFY(loadSpy.at(1).at(0).toBool()); + QCOMPARE(url, page.url()); + + QVERIFY(driverServer.deleteSession()); +} + +void tst_WebEngineDriver::typeElement() +{ + DriverServer driverServer; + QVERIFY(driverServer.start()); + + QWebEngineView view; + view.resize(300, 100); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QUrl url = QUrl(QLatin1String("qrc:///resources/input.html")); + QSignalSpy loadSpy(view.page(), &QWebEnginePage::loadFinished); + view.load(url); + QTRY_COMPARE(loadSpy.size(), 1); + + QVERIFY(driverServer.createSession()); + + // Find <input id="text_input"> element and extract its id from the reply. + QString textInputId; + { + QString paramsString = QLatin1String( + "{\"using\": \"css selector\", \"value\": \"[id=\\\"text_input\\\"]\"}"); + QJsonDocument params = QJsonDocument::fromJson(paramsString.toUtf8()); + QJsonDocument elementReply; + QVERIFY(driverServer.sendCommand(QLatin1String("/session/:sessionId/element"), params, + &elementReply)); + + QVERIFY(!elementReply.isEmpty()); + QJsonObject value = elementReply["value"].toObject(); + QVERIFY(!value.isEmpty()); + QStringList keys = value.keys(); + QCOMPARE(keys.size(), 1); + textInputId = value[keys[0]].toString(); + QVERIFY(!textInputId.isEmpty()); + } + + // Type text into the input field. + QString inputText = QLatin1String("WebEngineDriver"); + { + QString command = QLatin1String("/session/:sessionId/element/%1/value").arg(textInputId); + + QJsonObject root; + root.insert(QLatin1String("text"), inputText); + QJsonArray value; + for (QChar ch : inputText) { + value.append(QJsonValue(ch)); + } + root.insert(QLatin1String("value"), value); + root.insert(QLatin1String("id"), textInputId); + QJsonDocument params; + params.setObject(root); + + QVERIFY(driverServer.sendCommand(command, params)); + + QTRY_COMPARE( + evaluateJavaScriptSync(view.page(), "document.getElementById('text_input').value") + .toString(), + inputText); + } + + QVERIFY(driverServer.deleteSession()); +} + +void tst_WebEngineDriver::executeScript() +{ + DriverServer driverServer; + QVERIFY(driverServer.start()); + + QUrl url = QUrl(QLatin1String("qrc:///resources/input.html")); + QWebEnginePage page; + QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); + + page.load(url); + QTRY_COMPARE(loadSpy.size(), 1); + QCOMPARE(page.title(), url.toString()); + QSignalSpy titleSpy(&page, &QWebEnginePage::titleChanged); + + QString newTitle = QLatin1String("WebEngineDriver Test Page"); + QString script = QLatin1String("document.title = '%1';" + "return document.title;") + .arg(newTitle); + + QVERIFY(driverServer.createSession()); + QString paramsString = QLatin1String("{\"script\": \"%1\", \"args\": []}").arg(script); + QJsonDocument params = QJsonDocument::fromJson(paramsString.toUtf8()); + QJsonDocument executeReply; + QVERIFY(driverServer.sendCommand(QLatin1String("/session/:sessionId/execute/sync"), params, + &executeReply)); + + QTRY_COMPARE(titleSpy.size(), 1); + QCOMPARE(executeReply["value"].toString(), newTitle); + QCOMPARE(page.title(), newTitle); + + QVERIFY(driverServer.deleteSession()); +} + +void tst_WebEngineDriver::screenshot() +{ + DriverServer driverServer; + QVERIFY(driverServer.start()); + + QWebEngineView view; + view.resize(300, 100); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QSignalSpy loadSpy(view.page(), &QWebEnginePage::loadFinished); + + view.setHtml(QLatin1String("<html><head><style>" + "html {background-color:red;}" + "</style></head><body></body></html>")); + QTRY_COMPARE(loadSpy.size(), 1); + + QVERIFY(driverServer.createSession()); + QJsonDocument screenshotReply; + QVERIFY(driverServer.sendCommand(QLatin1String("/session/:sessionId/screenshot"), {}, + &screenshotReply)); + + QByteArray base64 = screenshotReply["value"].toString().toLocal8Bit(); + QVERIFY(!base64.isEmpty()); + QImage screenshot; + screenshot.loadFromData(QByteArray::fromBase64(base64)); + QVERIFY(!screenshot.isNull()); + QCOMPARE(screenshot.pixel(screenshot.width() / 2, screenshot.height() / 2), 0xFFFF0000); + + QVERIFY(driverServer.deleteSession()); +} + +#define STRINGIFY_LITERAL(x) #x +#define STRINGIFY_EXPANDED(x) STRINGIFY_LITERAL(x) +static QByteArrayList params = QByteArrayList() + << "--remote-debugging-port=" STRINGIFY_EXPANDED(REMOTE_DEBUGGING_PORT); +W_QTEST_MAIN(tst_WebEngineDriver, params) + +#include "tst_webenginedriver.moc" diff --git a/tests/auto/httpserver/CMakeLists.txt b/tests/auto/httpserver/CMakeLists.txt index 848131f50..0a1f881b9 100644 --- a/tests/auto/httpserver/CMakeLists.txt +++ b/tests/auto/httpserver/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 3.18) project(minimal LANGUAGES CXX) diff --git a/tests/auto/httpserver/httpreqrep.cpp b/tests/auto/httpserver/httpreqrep.cpp index 34e148678..8b338ce4e 100644 --- a/tests/auto/httpserver/httpreqrep.cpp +++ b/tests/auto/httpserver/httpreqrep.cpp @@ -57,6 +57,11 @@ QByteArray HttpReqRep::requestHeader(const QByteArray &key) const return {}; } +bool HttpReqRep::hasRequestHeader(const QByteArray &key) const +{ + return m_requestHeaders.find(key.toLower()) != m_requestHeaders.end(); +} + void HttpReqRep::handleReadyRead() { while (m_socket->canReadLine()) { diff --git a/tests/auto/httpserver/httpreqrep.h b/tests/auto/httpserver/httpreqrep.h index a8482536d..774c08eb1 100644 --- a/tests/auto/httpserver/httpreqrep.h +++ b/tests/auto/httpserver/httpreqrep.h @@ -25,6 +25,7 @@ public: QByteArray requestMethod() const { return m_requestMethod; } QByteArray requestPath() const { return m_requestPath; } QByteArray requestHeader(const QByteArray &key) const; + bool hasRequestHeader(const QByteArray &key) const; // Response parameters (can be set until sendResponse()/close()). diff --git a/tests/auto/httpserver/httpserver.cmake b/tests/auto/httpserver/httpserver.cmake index cacbb2d91..f98434e1a 100644 --- a/tests/auto/httpserver/httpserver.cmake +++ b/tests/auto/httpserver/httpserver.cmake @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause if (NOT TARGET Test::HttpServer) diff --git a/tests/auto/httpserver/httpserver.cpp b/tests/auto/httpserver/httpserver.cpp index 6dd64ab88..e08af77e7 100644 --- a/tests/auto/httpserver/httpserver.cpp +++ b/tests/auto/httpserver/httpserver.cpp @@ -24,7 +24,8 @@ HttpServer::HttpServer(QTcpServer *tcpServer, const QString &protocol, { m_url.setHost(hostAddress.toString()); m_url.setScheme(protocol); - connect(tcpServer, &QTcpServer::newConnection, this, &HttpServer::handleNewConnection); + connect(tcpServer, &QTcpServer::pendingConnectionAvailable, this, + &HttpServer::handleNewConnection); } HttpServer::~HttpServer() @@ -79,7 +80,7 @@ void HttpServer::handleNewConnection() // if request wasn't handled or purposely ignored for default behavior // then try to serve htmls from resources dirs if set if (rr->requestMethod() == "GET") { - for (auto &&dir : qAsConst(m_dirs)) { + for (auto &&dir : std::as_const(m_dirs)) { QFile f(dir + rr->requestPath()); if (f.exists()) { if (f.open(QFile::ReadOnly)) { diff --git a/tests/auto/httpserver/httpserver.h b/tests/auto/httpserver/httpserver.h index 270b6265f..201eef4c6 100644 --- a/tests/auto/httpserver/httpserver.h +++ b/tests/auto/httpserver/httpserver.h @@ -59,6 +59,8 @@ public: Q_INVOKABLE void setHostDomain(const QString &host) { m_url.setHost(host); } + Q_INVOKABLE QTcpServer *getTcpServer() const { return m_tcpServer; } + Q_SIGNALS: // Emitted after a HTTP request has been successfully parsed. void newRequest(HttpReqRep *reqRep); diff --git a/tests/auto/httpserver/httpsserver.h b/tests/auto/httpserver/httpsserver.h index d064c1416..d029851aa 100644 --- a/tests/auto/httpserver/httpsserver.h +++ b/tests/auto/httpserver/httpsserver.h @@ -7,54 +7,67 @@ #include "httpserver.h" #include <QDebug> -#include <QFile> -#include <QSslKey> -#include <QSslSocket> -#include <QSslConfiguration> -#include <QTcpServer> +#include <QtCore/qfile.h> +#include <QtNetwork/qsslkey.h> +#include <QtNetwork/qsslsocket.h> +#include <QtNetwork/qsslconfiguration.h> +#include <QtNetwork/qsslserver.h> -struct SslTcpServer : QTcpServer +static QSslServer *createServer(const QString &certificateFileName, const QString &keyFileName, + const QString &ca) { - SslTcpServer(const QString &certPath, const QString &keyPath) { - sslconf.setLocalCertificateChain(QSslCertificate::fromPath(certPath)); - sslconf.setPrivateKey(readKey(keyPath)); - } - - void incomingConnection(qintptr d) override { - auto socket = new QSslSocket(this); - socket->setSslConfiguration(sslconf); + QSslConfiguration configuration(QSslConfiguration::defaultConfiguration()); - if (!socket->setSocketDescriptor(d)) { - qWarning() << "Failed to setup ssl socket!"; - delete socket; - return; + QFile keyFile(keyFileName); + if (keyFile.open(QIODevice::ReadOnly)) { + QSslKey key(keyFile.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); + if (!key.isNull()) { + configuration.setPrivateKey(key); + } else { + qCritical() << "Could not parse key: " << keyFileName; } + } else { + qCritical() << "Could not find key: " << keyFileName; + } - connect(socket, QOverload<QSslSocket::SocketError>::of(&QSslSocket::errorOccurred), - [] (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(); + QList<QSslCertificate> localCerts = QSslCertificate::fromPath(certificateFileName); + if (!localCerts.isEmpty()) { + configuration.setLocalCertificateChain(localCerts); + } else { + qCritical() << "Could not find certificate: " << certificateFileName; } - QSslKey readKey(const QString &path) const { - QFile file(path); - file.open(QIODevice::ReadOnly); - return QSslKey(file.readAll(), QSsl::Rsa, QSsl::Pem); + if (!ca.isEmpty()) { + QList<QSslCertificate> caCerts = QSslCertificate::fromPath(ca); + if (!caCerts.isEmpty()) { + configuration.addCaCertificates(caCerts); + configuration.setPeerVerifyMode(QSslSocket::VerifyPeer); + } else { + qCritical() << "Could not find certificate: " << certificateFileName; + } } - QSslConfiguration sslconf; -}; + QSslServer *server = new QSslServer(); + server->setSslConfiguration(configuration); + return server; +} struct HttpsServer : HttpServer { - HttpsServer(const QString &certPath, const QString &keyPath, QObject *parent = nullptr) - : HttpServer(new SslTcpServer(certPath, keyPath), "https", QHostAddress::LocalHost, 0, + HttpsServer(const QString &certPath, const QString &keyPath, const QString &ca, + quint16 port = 0, QObject *parent = nullptr) + : HttpServer(createServer(certPath, keyPath, ca), "https", QHostAddress::LocalHost, port, parent) { } + + void setVerifyMode(const QSslSocket::PeerVerifyMode verifyMode) + { + QSslServer *server = static_cast<QSslServer *>(getTcpServer()); + QSslConfiguration config = server->sslConfiguration(); + config.setPeerVerifyMode(verifyMode); + server->setSslConfiguration(config); + } }; #endif diff --git a/tests/auto/pdf/CMakeLists.txt b/tests/auto/pdf/CMakeLists.txt index c482c623a..205bd24d0 100644 --- a/tests/auto/pdf/CMakeLists.txt +++ b/tests/auto/pdf/CMakeLists.txt @@ -1,9 +1,12 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause add_subdirectory(qpdfbookmarkmodel) -#add_subdirectory(qpdfpagenavigator) +if (TARGET Qt::PdfWidgets) + add_subdirectory(qpdfpagenavigator) +endif() add_subdirectory(qpdfpagerenderer) +add_subdirectory(qpdfsearchmodel) if(TARGET Qt::PrintSupport) add_subdirectory(qpdfdocument) endif() diff --git a/tests/auto/pdf/qpdfbookmarkmodel/CMakeLists.txt b/tests/auto/pdf/qpdfbookmarkmodel/CMakeLists.txt index 0627ce953..729bc9138 100644 --- a/tests/auto/pdf/qpdfbookmarkmodel/CMakeLists.txt +++ b/tests/auto/pdf/qpdfbookmarkmodel/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause qt_internal_add_test(tst_qpdfbookmarkmodel SOURCES @@ -8,5 +8,8 @@ qt_internal_add_test(tst_qpdfbookmarkmodel Qt::Gui Qt::Network Qt::Pdf + TESTDATA + pdf-sample.bookmarks.pdf + pdf-sample.bookmarks_pages.pdf ) diff --git a/tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp b/tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp index e3b778396..a1804e179 100644 --- a/tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp +++ b/tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp @@ -63,8 +63,8 @@ void tst_QPdfBookmarkModel::setEmptyDocumentAndLoad() QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks.pdf")), QPdfDocument::Error::None); - QCOMPARE(modelAboutToBeResetSpy.count(), 1); - QCOMPARE(modelResetSpy.count(), 1); + QCOMPARE(modelAboutToBeResetSpy.size(), 1); + QCOMPARE(modelResetSpy.size(), 1); QCOMPARE(model.rowCount(), 3); } @@ -81,8 +81,8 @@ void tst_QPdfBookmarkModel::setLoadedDocument() model.setDocument(&document); - QCOMPARE(modelAboutToBeResetSpy.count(), 1); - QCOMPARE(modelResetSpy.count(), 1); + QCOMPARE(modelAboutToBeResetSpy.size(), 1); + QCOMPARE(modelResetSpy.size(), 1); QCOMPARE(model.rowCount(), 3); } @@ -102,8 +102,8 @@ void tst_QPdfBookmarkModel::unloadDocument() document.close(); - QCOMPARE(modelAboutToBeResetSpy.count(), 1); - QCOMPARE(modelResetSpy.count(), 1); + QCOMPARE(modelAboutToBeResetSpy.size(), 1); + QCOMPARE(modelResetSpy.size(), 1); QCOMPARE(model.rowCount(), 0); } diff --git a/tests/auto/pdf/qpdfdocument/CMakeLists.txt b/tests/auto/pdf/qpdfdocument/CMakeLists.txt index c683b4919..b8300ef27 100644 --- a/tests/auto/pdf/qpdfdocument/CMakeLists.txt +++ b/tests/auto/pdf/qpdfdocument/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause qt_internal_add_test(tst_qpdfdocument SOURCES @@ -9,4 +9,10 @@ qt_internal_add_test(tst_qpdfdocument Qt::Network Qt::PrintSupport Qt::Pdf + TESTDATA + pdf-sample.protected.pdf + pdf-sample.metadata.pdf + rotated_text.pdf + tagged_mcr_multipage.pdf + test.pdf ) diff --git a/tests/auto/pdf/qpdfdocument/rotated_text.pdf b/tests/auto/pdf/qpdfdocument/rotated_text.pdf new file mode 100644 index 000000000..d6d8db84e --- /dev/null +++ b/tests/auto/pdf/qpdfdocument/rotated_text.pdf @@ -0,0 +1,70 @@ +%PDF-1.7 +% ò¤ô +1 0 obj << + /Type /Catalog + /Pages 2 0 R +>> +endobj +2 0 obj << + /Type /Pages + /MediaBox [ 0 0 200 200 ] + /Count 1 + /Kids [ 3 0 R ] +>> +endobj +3 0 obj << + /Type /Page + /Parent 2 0 R + /Resources << + /Font << + /F1 4 0 R + >> + >> + /Contents 5 0 R +>> +endobj +4 0 obj << + /Type /Font + /Subtype /Type1 + /BaseFont /Times-Roman +>> +endobj +5 0 obj << + /Length 406 +>> +stream +BT +0 0 Td +/F1 12 Tf +0.70710678118 -0.70710678118 0.70710678118 0.70710678118 100 100 Tm +(Hello,) Tj +0 0 Td +/F1 12 Tf +-0.70710678118 -0.70710678118 0.70710678118 -0.70710678118 100 100 Tm +( world!\r\n) Tj +0 0 Td +/F1 12 Tf +-0.70710678118 0.70710678118 -0.70710678118 -0.70710678118 100 100 Tm +(Goodbye,) Tj +0 0 Td +/F1 12 Tf +0.70710678118 0.70710678118 -0.70710678118 0.70710678118 100 100 Tm +( world!) Tj +ET +endstream +endobj +xref +0 6 +0000000000 65535 f +0000000015 00000 n +0000000068 00000 n +0000000161 00000 n +0000000287 00000 n +0000000365 00000 n +trailer << + /Root 1 0 R + /Size 6 +>> +startxref +823 +%%EOF diff --git a/tests/auto/pdf/qpdfdocument/tagged_mcr_multipage.pdf b/tests/auto/pdf/qpdfdocument/tagged_mcr_multipage.pdf new file mode 100644 index 000000000..fcc5fafda --- /dev/null +++ b/tests/auto/pdf/qpdfdocument/tagged_mcr_multipage.pdf @@ -0,0 +1,136 @@ +%PDF-1.7 +% ò¤ô +1 0 obj << + /Type /Catalog + /MarkInfo << + /Type /MarkInfo + /Marked true + >> + /Pages 2 0 R + /StructTreeRoot 8 0 R +>> +endobj +2 0 obj << + /Type /Pages + /CropBox [ 10.8197 8.459 605.705 801.639 ] + /MediaBox [ 0.0 0.0 616.721 809.902 ] + /Count 2 + /Kids [ + 4 0 R + 6 0 R + ] +>> +endobj +3 0 obj << + /Type /Font + /Subtype /Type1 + /BaseFont /Times-Roman +>> +endobj +4 0 obj << + /Type /Page + /Tabs /S + /Parent 2 0 R + /StructParents 0 + /Contents 5 0 R + /Resources << + /ProcSet [/PDF /Text] + /Font << + /F1 3 0 R + >> + >> +>> +endobj +5 0 obj << + /Length 83 +>> +stream +BT +/Document <</MCID 0 >>BDC +0 i +/F1 1 Tf +12 0 0 12 43.073 771.625 Tm +(1)Tj +EMC +ET +endstream +endobj +6 0 obj << + /Type /Page + /Tabs /S + /Parent 2 0 R + /StructParents 1 + /Contents 7 0 R + /Resources << + /ProcSet [/PDF /Text] + /Font << + /F1 3 0 R + >> + >> +>> +endobj +7 0 obj << + /Length 83 +>> +stream +BT +/Document <</MCID 0 >>BDC +0 i +/F1 1 Tf +12 0 0 12 43.073 771.625 Tm +(2)Tj +EMC +ET +endstream +endobj +8 0 obj << + /Type /StructTreeRoot + /K 10 0 R + /ParentTree 9 0 R + /ParentTreeNextKey 2 +>> +endobj +9 0 obj << + /Nums [ + 0 + [10 0 R] + 1 + [10 0 R] + ] +>> +endobj +10 0 obj << + /T () + /S /Document + /P 8 0 R + /Pg 4 0 R + /K [ + 0 + << + /MCID 0 + /Pg 6 0 R + /Type /MCR + >> + ] +>> +%endobj +xref +0 11 +0000000000 65535 f +0000000015 00000 n +0000000149 00000 n +0000000315 00000 n +0000000393 00000 n +0000000575 00000 n +0000000709 00000 n +0000000891 00000 n +0000001025 00000 n +0000001125 00000 n +0000001198 00000 n +trailer << + /Root 1 0 R + /Size 11 +>> +startxref +1345 +%%EOF diff --git a/tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp b/tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp index a481ee57c..d222bff0c 100644 --- a/tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp +++ b/tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp @@ -7,7 +7,9 @@ #include <QPainter> #include <QPdfDocument> #include <QPrinter> +#include <QDateTime> #include <QTemporaryFile> +#include <QTimeZone> #include <QNetworkAccessManager> #include <QNetworkRequest> #include <QNetworkReply> @@ -34,6 +36,10 @@ private slots: void passwordClearedOnClose(); void metaData(); void pageLabels(); + void getSelection_data(); + void getSelection(); + void getSelectionAtIndex_data(); + void getSelectionAtIndex(); private: void consistencyCheck(QPdfDocument &doc) const; @@ -54,7 +60,7 @@ struct TemporaryPdf: public QTemporaryFile }; -TemporaryPdf::TemporaryPdf() +TemporaryPdf::TemporaryPdf():QTemporaryFile(QStringLiteral("qpdfdocument")) { open(); pageLayout = QPageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF()); @@ -86,7 +92,7 @@ void tst_QPdfDocument::pageCount() QCOMPARE(doc.pageCount(), 0); QCOMPARE(doc.load(tempPdf.fileName()), QPdfDocument::Error::None); QCOMPARE(doc.pageCount(), 2); - QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy.size(), 1); QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount()); QCOMPARE(doc.pagePointSize(0).toSize(), tempPdf.pageLayout.fullRectPoints().size()); @@ -99,12 +105,12 @@ void tst_QPdfDocument::loadFromIODevice() QSignalSpy statusChangedSpy(&doc, SIGNAL(statusChanged(QPdfDocument::Status))); QSignalSpy pageCountChangedSpy(&doc, SIGNAL(pageCountChanged(int))); doc.load(&tempPdf); - QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy.size(), 2); QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Loading); QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Ready); QCOMPARE(doc.error(), QPdfDocument::Error::None); QCOMPARE(doc.pageCount(), 2); - QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy.size(), 1); QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount()); consistencyCheck(doc); @@ -136,11 +142,11 @@ void tst_QPdfDocument::loadAsync() doc.load(reply.data()); - QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy.size(), 2); QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Loading); QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Ready); QCOMPARE(doc.pageCount(), 2); - QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy.size(), 1); QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount()); consistencyCheck(doc); @@ -153,13 +159,13 @@ void tst_QPdfDocument::password() QCOMPARE(doc.pageCount(), 0); QCOMPARE(doc.load(QFINDTESTDATA("pdf-sample.protected.pdf")), QPdfDocument::Error::IncorrectPassword); - QCOMPARE(passwordChangedSpy.count(), 0); + QCOMPARE(passwordChangedSpy.size(), 0); doc.setPassword(QStringLiteral("WrongPassword")); - QCOMPARE(passwordChangedSpy.count(), 1); + QCOMPARE(passwordChangedSpy.size(), 1); QCOMPARE(doc.load(QFINDTESTDATA("pdf-sample.protected.pdf")), QPdfDocument::Error::IncorrectPassword); QCOMPARE(doc.status(), QPdfDocument::Status::Error); doc.setPassword(QStringLiteral("Qt")); - QCOMPARE(passwordChangedSpy.count(), 2); + QCOMPARE(passwordChangedSpy.size(), 2); QCOMPARE(doc.load(QFINDTESTDATA("pdf-sample.protected.pdf")), QPdfDocument::Error::None); QCOMPARE(doc.pageCount(), 1); } @@ -174,10 +180,10 @@ void tst_QPdfDocument::close() doc.load(&tempPdf); - QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy.size(), 2); QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Loading); QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Ready); - QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy.size(), 1); QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount()); statusChangedSpy.clear(); @@ -188,11 +194,11 @@ void tst_QPdfDocument::close() return; doc.close(); - QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy.size(), 2); QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Unloading); QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Null); QCOMPARE(doc.pageCount(), 0); - QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy.size(), 1); QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount()); } @@ -205,30 +211,30 @@ void tst_QPdfDocument::loadAfterClose() QSignalSpy pageCountChangedSpy(&doc, SIGNAL(pageCountChanged(int))); doc.load(&tempPdf); - QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy.size(), 2); QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Loading); QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Ready); - QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy.size(), 1); QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount()); statusChangedSpy.clear(); pageCountChangedSpy.clear(); doc.close(); - QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy.size(), 2); QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Unloading); QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Null); - QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy.size(), 1); QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount()); statusChangedSpy.clear(); pageCountChangedSpy.clear(); doc.load(&tempPdf); - QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy.size(), 2); QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Loading); QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Ready); QCOMPARE(doc.error(), QPdfDocument::Error::None); QCOMPARE(doc.pageCount(), 2); - QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy.size(), 1); QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount()); consistencyCheck(doc); @@ -249,10 +255,10 @@ void tst_QPdfDocument::closeOnDestroy() delete doc; - QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy.size(), 2); QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Unloading); QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Null); - QCOMPARE(pageCountChangedSpy.count(), 1); + QCOMPARE(pageCountChangedSpy.size(), 1); QCOMPARE(pageCountChangedSpy[0][0].toInt(), 0); } @@ -267,8 +273,8 @@ void tst_QPdfDocument::closeOnDestroy() delete doc; - QCOMPARE(statusChangedSpy.count(), 0); - QCOMPARE(pageCountChangedSpy.count(), 0); + QCOMPARE(statusChangedSpy.size(), 0); + QCOMPARE(pageCountChangedSpy.size(), 0); } } @@ -283,7 +289,7 @@ void tst_QPdfDocument::status() // open existing document doc.load(&tempPdf); - QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy.size(), 2); QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Loading); QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Ready); statusChangedSpy.clear(); @@ -293,7 +299,7 @@ void tst_QPdfDocument::status() // close document doc.close(); - QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy.size(), 2); QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Unloading); QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Null); statusChangedSpy.clear(); @@ -302,7 +308,7 @@ void tst_QPdfDocument::status() // try to open non-existing document doc.load(QFINDTESTDATA("does-not-exist.pdf")); - QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy.size(), 2); QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Loading); QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Error); QCOMPARE(doc.status(), QPdfDocument::Status::Error); @@ -320,13 +326,13 @@ void tst_QPdfDocument::status() stopWatch.start(); forever { QCoreApplication::instance()->processEvents(); - if (statusChangedSpy.count() == 2) + if (statusChangedSpy.size() == 2) break; if (stopWatch.elapsed() >= 30000) break; } - QCOMPARE(statusChangedSpy.count(), 2); + QCOMPARE(statusChangedSpy.size(), 2); QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Loading); QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Status::Error); statusChangedSpy.clear(); @@ -340,17 +346,17 @@ void tst_QPdfDocument::passwordClearedOnClose() QSignalSpy passwordChangedSpy(&doc, SIGNAL(passwordChanged())); doc.setPassword(QStringLiteral("Qt")); - QCOMPARE(passwordChangedSpy.count(), 1); + QCOMPARE(passwordChangedSpy.size(), 1); QCOMPARE(doc.load(QFINDTESTDATA("pdf-sample.protected.pdf")), QPdfDocument::Error::None); passwordChangedSpy.clear(); doc.close(); // password is cleared on close - QCOMPARE(passwordChangedSpy.count(), 1); + QCOMPARE(passwordChangedSpy.size(), 1); passwordChangedSpy.clear(); doc.load(&tempPdf); doc.close(); // signal is not emitted if password didn't change - QCOMPARE(passwordChangedSpy.count(), 0); + QCOMPARE(passwordChangedSpy.size(), 0); } void tst_QPdfDocument::metaData() @@ -376,8 +382,10 @@ void tst_QPdfDocument::metaData() QCOMPARE(doc.metaData(QPdfDocument::MetaDataField::Keywords).toString(), QString::fromLatin1("meta data keywords")); QCOMPARE(doc.metaData(QPdfDocument::MetaDataField::Producer).toString(), QString::fromLatin1("LibreOffice 5.1")); QCOMPARE(doc.metaData(QPdfDocument::MetaDataField::Creator).toString(), QString::fromLatin1("Writer")); - QCOMPARE(doc.metaData(QPdfDocument::MetaDataField::CreationDate).toDateTime(), QDateTime(QDate(2016, 8, 7), QTime(7, 3, 6), Qt::UTC)); - QCOMPARE(doc.metaData(QPdfDocument::MetaDataField::ModificationDate).toDateTime(), QDateTime(QDate(2016, 8, 8), QTime(8, 3, 6), Qt::UTC)); + QCOMPARE(doc.metaData(QPdfDocument::MetaDataField::CreationDate).toDateTime(), + QDateTime(QDate(2016, 8, 7), QTime(7, 3, 6), QTimeZone::UTC)); + QCOMPARE(doc.metaData(QPdfDocument::MetaDataField::ModificationDate).toDateTime(), + QDateTime(QDate(2016, 8, 8), QTime(8, 3, 6), QTimeZone::UTC)); } void tst_QPdfDocument::pageLabels() @@ -390,6 +398,86 @@ void tst_QPdfDocument::pageLabels() QCOMPARE(doc.pageLabel(2), "i"); // i of the tiger! } +void tst_QPdfDocument::getSelection_data() +{ + QTest::addColumn<QString>("pdfPath"); + QTest::addColumn<int>("page"); + QTest::addColumn<QPointF>("start"); + QTest::addColumn<QPointF>("end"); + QTest::addColumn<QString>("expectedText"); + QTest::addColumn<int>("expectedStartIndex"); + QTest::addColumn<int>("expectedEndIndex"); + QTest::addColumn<QRect>("expectedBounds"); + QTest::addColumn<int>("expectedPolygonCount"); + + QTest::newRow("raid") << QFINDTESTDATA("test.pdf") + << 1 << QPointF(316.4, 206) << QPointF(339, 201) + << "raid" << 80 << 84 << QRect(316, 201, 21, 12) << 1; + QTest::newRow("rotated text") << QFINDTESTDATA("rotated_text.pdf") + << 0 << QPointF(102, 94) << QPointF(125, 73) + << "world!" << 25 << 31 << QRect(98, 70, 26, 28) << 1; +} + +void tst_QPdfDocument::getSelection() +{ + QFETCH(QString, pdfPath); + QFETCH(int, page); + QFETCH(QPointF, start); + QFETCH(QPointF, end); + QFETCH(QString, expectedText); + QFETCH(int, expectedStartIndex); + QFETCH(int, expectedEndIndex); + QFETCH(QRect, expectedBounds); + QFETCH(int, expectedPolygonCount); + + QPdfDocument doc; + QCOMPARE(doc.load(pdfPath), QPdfDocument::Error::None); + + QPdfSelection sel = doc.getSelection(page, start, end); + QCOMPARE(sel.text(), expectedText); + QCOMPARE(sel.startIndex(), expectedStartIndex); + QCOMPARE(sel.endIndex(), expectedEndIndex); + QCOMPARE(sel.boundingRectangle().toRect(), expectedBounds); + QCOMPARE(sel.bounds().size(), expectedPolygonCount); +} + +void tst_QPdfDocument::getSelectionAtIndex_data() +{ + QTest::addColumn<QString>("pdfPath"); + QTest::addColumn<int>("page"); + QTest::addColumn<int>("start"); + QTest::addColumn<int>("maxLen"); + QTest::addColumn<QString>("expectedText"); + QTest::addColumn<QRect>("expectedBounds"); + QTest::addColumn<int>("expectedPolygonCount"); + + QTest::newRow("raid") << QFINDTESTDATA("test.pdf") + << 1 << 80 << 4 << "raid" << QRect(316, 201, 21, 12) << 1; + QTest::newRow("rotated text") << QFINDTESTDATA("rotated_text.pdf") + << 0 << 7 << 6 << "world!" << QRect(76, 102, 26, 28) << 1; + QTest::newRow("displaced text") << QFINDTESTDATA("tagged_mcr_multipage.pdf") + << 0 << 0 << 10 << "1" << QRect(34, 22, 3, 8) << 1; +} + +void tst_QPdfDocument::getSelectionAtIndex() +{ + QFETCH(QString, pdfPath); + QFETCH(int, page); + QFETCH(int, start); + QFETCH(int, maxLen); + QFETCH(QString, expectedText); + QFETCH(QRect, expectedBounds); + QFETCH(int, expectedPolygonCount); + + QPdfDocument doc; + QCOMPARE(doc.load(pdfPath), QPdfDocument::Error::None); + + QPdfSelection sel = doc.getSelectionAtIndex(page, start, maxLen); + QCOMPARE(sel.text(), expectedText); + QCOMPARE(sel.boundingRectangle().toRect(), expectedBounds); + QCOMPARE(sel.bounds().size(), expectedPolygonCount); +} + QTEST_MAIN(tst_QPdfDocument) #include "tst_qpdfdocument.moc" diff --git a/tests/auto/pdf/qpdfpagenavigator/CMakeLists.txt b/tests/auto/pdf/qpdfpagenavigator/CMakeLists.txt new file mode 100644 index 000000000..3e8b8f416 --- /dev/null +++ b/tests/auto/pdf/qpdfpagenavigator/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_test(tst_qpdfpagenavigator.cpp + SOURCES + tst_qpdfpagenavigator.cpp + LIBRARIES + Qt::Gui + Qt::Network + Qt::Pdf + Qt::PdfWidgets + TESTDATA + pdf-sample.bookmarks_pages.pdf +) diff --git a/tests/auto/pdf/qpdfpagenavigator/pdf-sample.bookmarks_pages.pdf b/tests/auto/pdf/qpdfpagenavigator/pdf-sample.bookmarks_pages.pdf Binary files differnew file mode 100644 index 000000000..c4e1aa36e --- /dev/null +++ b/tests/auto/pdf/qpdfpagenavigator/pdf-sample.bookmarks_pages.pdf diff --git a/tests/auto/pdf/qpdfpagenavigator/tst_qpdfpagenavigator.cpp b/tests/auto/pdf/qpdfpagenavigator/tst_qpdfpagenavigator.cpp new file mode 100644 index 000000000..327a9f36a --- /dev/null +++ b/tests/auto/pdf/qpdfpagenavigator/tst_qpdfpagenavigator.cpp @@ -0,0 +1,70 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + + +#include <QtTest/QtTest> + +#include <QPdfDocument> +#include <QPdfView> +#include <QPdfPageNavigator> + +class tst_QPdfPageNavigator: public QObject +{ + Q_OBJECT + +private slots: + void offScreenSignals(); +}; + +void tst_QPdfPageNavigator::offScreenSignals() +{ + QPdfDocument document; + QPdfView pdfView; + QPdfPageNavigator *navigator = pdfView.pageNavigator(); + + QSignalSpy currentPageChanged(navigator, &QPdfPageNavigator::currentPageChanged); + QSignalSpy currentLocationChanged(navigator, &QPdfPageNavigator::currentLocationChanged); + QSignalSpy backAvailableChanged(navigator, &QPdfPageNavigator::backAvailableChanged); + QSignalSpy forwardAvailableChanged(navigator, &QPdfPageNavigator::forwardAvailableChanged); + QSignalSpy jumped(navigator, &QPdfPageNavigator::jumped); + + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks_pages.pdf")), QPdfDocument::Error::None); + QVERIFY2(document.pageCount() == 3, "Test document has changed! 3 pages expected."); + pdfView.setDocument(&document); + + // Start with a clean history + QCOMPARE(forwardAvailableChanged.count(), 0); + QCOMPARE(backAvailableChanged.count(), 0); + + navigator->jump(3, QPoint()); + QCOMPARE(forwardAvailableChanged.count(), 0); + QCOMPARE(backAvailableChanged.count(), 1); + QCOMPARE(currentPageChanged.count(), 1); + QCOMPARE(currentLocationChanged.count(), 0); + QCOMPARE(jumped.count(), 1); + + navigator->jump(1, QPoint()); + QCOMPARE(forwardAvailableChanged.count(), 0); + QCOMPARE(backAvailableChanged.count(), 1); + QCOMPARE(currentPageChanged.count(), 2); + QCOMPARE(currentLocationChanged.count(), 0); + QCOMPARE(jumped.count(), 2); + + navigator->back(); + QCOMPARE(forwardAvailableChanged.count(), 1); + QCOMPARE(backAvailableChanged.count(), 1); + QCOMPARE(currentPageChanged.count(), 3); + QCOMPARE(currentLocationChanged.count(), 0); + QCOMPARE(jumped.count(), 3); + + navigator->forward(); + QCOMPARE(forwardAvailableChanged.count(), 2); + QCOMPARE(backAvailableChanged.count(), 1); + QCOMPARE(currentPageChanged.count(), 4); + QCOMPARE(currentLocationChanged.count(), 0); + QCOMPARE(jumped.count(), 4); +} + +QTEST_MAIN(tst_QPdfPageNavigator) + +#include "tst_qpdfpagenavigator.moc" diff --git a/tests/auto/pdf/qpdfpagerenderer/CMakeLists.txt b/tests/auto/pdf/qpdfpagerenderer/CMakeLists.txt index 0659c3228..53a68fe59 100644 --- a/tests/auto/pdf/qpdfpagerenderer/CMakeLists.txt +++ b/tests/auto/pdf/qpdfpagerenderer/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause qt_internal_add_test(tst_qpdfpagerenderer SOURCES @@ -8,5 +8,7 @@ qt_internal_add_test(tst_qpdfpagerenderer Qt::Gui Qt::Network Qt::Pdf + TESTDATA + pdf-sample.pagerenderer.pdf ) diff --git a/tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp b/tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp index a958123c7..39d32df0b 100644 --- a/tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp +++ b/tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp @@ -64,7 +64,7 @@ void tst_QPdfPageRenderer::withLoadedDocumentSingleThreaded() const quint64 requestId = pageRenderer.requestPage(0, imageSize); QCOMPARE(requestId, quint64(1)); - QTRY_COMPARE(pageRenderedSpy.count(), 1); + QTRY_COMPARE(pageRenderedSpy.size(), 1); QCOMPARE(pageRenderedSpy[0][0].toInt(), 0); QCOMPARE(pageRenderedSpy[0][1].toSize(), imageSize); QCOMPARE(pageRenderedSpy[0][2].value<QImage>().size(), imageSize); @@ -87,7 +87,7 @@ void tst_QPdfPageRenderer::withLoadedDocumentMultiThreaded() const quint64 requestId = pageRenderer.requestPage(0, imageSize); QCOMPARE(requestId, quint64(1)); - QTRY_COMPARE(pageRenderedSpy.count(), 1); + QTRY_COMPARE(pageRenderedSpy.size(), 1); QCOMPARE(pageRenderedSpy[0][0].toInt(), 0); QCOMPARE(pageRenderedSpy[0][1].toSize(), imageSize); QCOMPARE(pageRenderedSpy[0][2].value<QImage>().size(), imageSize); @@ -108,7 +108,7 @@ void tst_QPdfPageRenderer::switchingRenderMode() const QSize imageSize(100, 100); const quint64 firstRequestId = pageRenderer.requestPage(0, imageSize); - QTRY_COMPARE(pageRenderedSpy.count(), 1); + QTRY_COMPARE(pageRenderedSpy.size(), 1); QCOMPARE(pageRenderedSpy[0][0].toInt(), 0); QCOMPARE(pageRenderedSpy[0][1].toSize(), imageSize); QCOMPARE(pageRenderedSpy[0][2].value<QImage>().size(), imageSize); @@ -124,7 +124,7 @@ void tst_QPdfPageRenderer::switchingRenderMode() const quint64 secondRequestId = pageRenderer.requestPage(0, imageSize); QVERIFY(firstRequestId != secondRequestId); - QTRY_COMPARE(pageRenderedSpy.count(), 1); + QTRY_COMPARE(pageRenderedSpy.size(), 1); QCOMPARE(pageRenderedSpy[0][0].toInt(), 0); QCOMPARE(pageRenderedSpy[0][1].toSize(), imageSize); QCOMPARE(pageRenderedSpy[0][2].value<QImage>(), image); @@ -138,7 +138,7 @@ void tst_QPdfPageRenderer::switchingRenderMode() const quint64 thirdRequestId = pageRenderer.requestPage(0, imageSize); - QTRY_COMPARE(pageRenderedSpy.count(), 1); + QTRY_COMPARE(pageRenderedSpy.size(), 1); QCOMPARE(pageRenderedSpy[0][0].toInt(), 0); QCOMPARE(pageRenderedSpy[0][1].toSize(), imageSize); QCOMPARE(pageRenderedSpy[0][2].value<QImage>(), image); diff --git a/tests/auto/pdf/qpdfsearchmodel/CMakeLists.txt b/tests/auto/pdf/qpdfsearchmodel/CMakeLists.txt index 8f7ec01e2..668d1ea36 100644 --- a/tests/auto/pdf/qpdfsearchmodel/CMakeLists.txt +++ b/tests/auto/pdf/qpdfsearchmodel/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause qt_internal_add_test(tst_qpdfsearchmodel SOURCES @@ -8,4 +8,8 @@ qt_internal_add_test(tst_qpdfsearchmodel Qt::Gui Qt::Network Qt::Pdf + TESTDATA + rotated_text.pdf + tagged_mcr_multipage.pdf + test.pdf ) diff --git a/tests/auto/pdf/qpdfsearchmodel/rotated_text.pdf b/tests/auto/pdf/qpdfsearchmodel/rotated_text.pdf new file mode 100644 index 000000000..d6d8db84e --- /dev/null +++ b/tests/auto/pdf/qpdfsearchmodel/rotated_text.pdf @@ -0,0 +1,70 @@ +%PDF-1.7 +% ò¤ô +1 0 obj << + /Type /Catalog + /Pages 2 0 R +>> +endobj +2 0 obj << + /Type /Pages + /MediaBox [ 0 0 200 200 ] + /Count 1 + /Kids [ 3 0 R ] +>> +endobj +3 0 obj << + /Type /Page + /Parent 2 0 R + /Resources << + /Font << + /F1 4 0 R + >> + >> + /Contents 5 0 R +>> +endobj +4 0 obj << + /Type /Font + /Subtype /Type1 + /BaseFont /Times-Roman +>> +endobj +5 0 obj << + /Length 406 +>> +stream +BT +0 0 Td +/F1 12 Tf +0.70710678118 -0.70710678118 0.70710678118 0.70710678118 100 100 Tm +(Hello,) Tj +0 0 Td +/F1 12 Tf +-0.70710678118 -0.70710678118 0.70710678118 -0.70710678118 100 100 Tm +( world!\r\n) Tj +0 0 Td +/F1 12 Tf +-0.70710678118 0.70710678118 -0.70710678118 -0.70710678118 100 100 Tm +(Goodbye,) Tj +0 0 Td +/F1 12 Tf +0.70710678118 0.70710678118 -0.70710678118 0.70710678118 100 100 Tm +( world!) Tj +ET +endstream +endobj +xref +0 6 +0000000000 65535 f +0000000015 00000 n +0000000068 00000 n +0000000161 00000 n +0000000287 00000 n +0000000365 00000 n +trailer << + /Root 1 0 R + /Size 6 +>> +startxref +823 +%%EOF diff --git a/tests/auto/pdf/qpdfsearchmodel/tagged_mcr_multipage.pdf b/tests/auto/pdf/qpdfsearchmodel/tagged_mcr_multipage.pdf new file mode 100644 index 000000000..fcc5fafda --- /dev/null +++ b/tests/auto/pdf/qpdfsearchmodel/tagged_mcr_multipage.pdf @@ -0,0 +1,136 @@ +%PDF-1.7 +% ò¤ô +1 0 obj << + /Type /Catalog + /MarkInfo << + /Type /MarkInfo + /Marked true + >> + /Pages 2 0 R + /StructTreeRoot 8 0 R +>> +endobj +2 0 obj << + /Type /Pages + /CropBox [ 10.8197 8.459 605.705 801.639 ] + /MediaBox [ 0.0 0.0 616.721 809.902 ] + /Count 2 + /Kids [ + 4 0 R + 6 0 R + ] +>> +endobj +3 0 obj << + /Type /Font + /Subtype /Type1 + /BaseFont /Times-Roman +>> +endobj +4 0 obj << + /Type /Page + /Tabs /S + /Parent 2 0 R + /StructParents 0 + /Contents 5 0 R + /Resources << + /ProcSet [/PDF /Text] + /Font << + /F1 3 0 R + >> + >> +>> +endobj +5 0 obj << + /Length 83 +>> +stream +BT +/Document <</MCID 0 >>BDC +0 i +/F1 1 Tf +12 0 0 12 43.073 771.625 Tm +(1)Tj +EMC +ET +endstream +endobj +6 0 obj << + /Type /Page + /Tabs /S + /Parent 2 0 R + /StructParents 1 + /Contents 7 0 R + /Resources << + /ProcSet [/PDF /Text] + /Font << + /F1 3 0 R + >> + >> +>> +endobj +7 0 obj << + /Length 83 +>> +stream +BT +/Document <</MCID 0 >>BDC +0 i +/F1 1 Tf +12 0 0 12 43.073 771.625 Tm +(2)Tj +EMC +ET +endstream +endobj +8 0 obj << + /Type /StructTreeRoot + /K 10 0 R + /ParentTree 9 0 R + /ParentTreeNextKey 2 +>> +endobj +9 0 obj << + /Nums [ + 0 + [10 0 R] + 1 + [10 0 R] + ] +>> +endobj +10 0 obj << + /T () + /S /Document + /P 8 0 R + /Pg 4 0 R + /K [ + 0 + << + /MCID 0 + /Pg 6 0 R + /Type /MCR + >> + ] +>> +%endobj +xref +0 11 +0000000000 65535 f +0000000015 00000 n +0000000149 00000 n +0000000315 00000 n +0000000393 00000 n +0000000575 00000 n +0000000709 00000 n +0000000891 00000 n +0000001025 00000 n +0000001125 00000 n +0000001198 00000 n +trailer << + /Root 1 0 R + /Size 11 +>> +startxref +1345 +%%EOF diff --git a/tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp b/tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp index 0bdb9296b..cf71b148e 100644 --- a/tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp +++ b/tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp @@ -7,6 +7,8 @@ #include <QPdfDocument> #include <QPdfSearchModel> +Q_LOGGING_CATEGORY(lcTests, "qt.pdf.tests") + class tst_QPdfSearchModel: public QObject { Q_OBJECT @@ -15,20 +17,51 @@ public: tst_QPdfSearchModel() {} private slots: + void findText_data(); void findText(); }; +void tst_QPdfSearchModel::findText_data() +{ + QTest::addColumn<QString>("pdfPath"); + QTest::addColumn<QString>("searchString"); + QTest::addColumn<int>("expectedMatchCount"); + QTest::addColumn<int>("matchIndexToCheck"); + QTest::addColumn<int>("expectedRectangleCount"); + QTest::addColumn<int>("rectIndexToCheck"); + QTest::addColumn<QRect>("expectedMatchBounds"); + + QTest::newRow("the search for ai") << QFINDTESTDATA("test.pdf") + << "ai" << 3 << 0 << 1 << 0 << QRect(321, 202, 9, 11); + QTest::newRow("rotated text") << QFINDTESTDATA("rotated_text.pdf") + << "world!" << 2 << 0 << 1 << 0 << QRect(76, 102, 26, 28); + QTest::newRow("displaced text") << QFINDTESTDATA("tagged_mcr_multipage.pdf") + << "1" << 1 << 0 << 1 << 0 << QRect(34, 22, 3, 8); +} + void tst_QPdfSearchModel::findText() { + QFETCH(QString, pdfPath); + QFETCH(QString, searchString); + QFETCH(int, expectedMatchCount); + QFETCH(int, matchIndexToCheck); + QFETCH(int, expectedRectangleCount); + QFETCH(int, rectIndexToCheck); + QFETCH(QRect, expectedMatchBounds); + QPdfDocument document; - QCOMPARE(document.load(QFINDTESTDATA("test.pdf")), QPdfDocument::NoError); + QCOMPARE(document.load(pdfPath), QPdfDocument::Error::None); QPdfSearchModel model; model.setDocument(&document); - QList<QRectF> matches = model.matches(1, "ai"); + model.setSearchString(searchString); - qDebug() << matches; - QCOMPARE(matches.count(), 3); + QTRY_COMPARE(model.count(), expectedMatchCount); // wait for the timer + QPdfLink match = model.resultAtIndex(matchIndexToCheck); + qCDebug(lcTests) << match; + QList<QRectF> rects = match.rectangles(); + QCOMPARE(rects.size(), expectedRectangleCount); + QCOMPARE(rects.at(rectIndexToCheck).toRect(), expectedMatchBounds); } QTEST_MAIN(tst_QPdfSearchModel) diff --git a/tests/auto/pdfquick/CMakeLists.txt b/tests/auto/pdfquick/CMakeLists.txt new file mode 100644 index 000000000..e6a3a460c --- /dev/null +++ b/tests/auto/pdfquick/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(multipageview) +add_subdirectory(pdfpageimage) diff --git a/tests/auto/pdfquick/multipageview/BLACKLIST b/tests/auto/pdfquick/multipageview/BLACKLIST new file mode 100644 index 000000000..9012902f6 --- /dev/null +++ b/tests/auto/pdfquick/multipageview/BLACKLIST @@ -0,0 +1,7 @@ +# QTBUG-111306 +[navigation:click links and go back, twice] +android + +# QTBUG-111306 +[navigation:click two links in series and then go back] +android diff --git a/tests/auto/pdfquick/multipageview/CMakeLists.txt b/tests/auto/pdfquick/multipageview/CMakeLists.txt new file mode 100644 index 000000000..50f7d7d8f --- /dev/null +++ b/tests/auto/pdfquick/multipageview/CMakeLists.txt @@ -0,0 +1,30 @@ +# Collect test data +file(GLOB_RECURSE test_data_glob + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + data/*) +list(APPEND test_data ${test_data_glob}) + +qt_internal_add_test(tst_multipageview + SOURCES + tst_multipageview.cpp + ../shared/util.cpp ../shared/util.h + LIBRARIES + Qt::Gui + Qt::Quick + Qt::PdfQuickPrivate + TESTDATA ${test_data} +) + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_multipageview CONDITION ANDROID OR IOS + DEFINES + QT_QMLTEST_DATADIR=":/data" +) + +qt_internal_extend_target(tst_multipageview CONDITION NOT ANDROID AND NOT IOS + DEFINES + QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" +) + diff --git a/tests/auto/pdfquick/multipageview/data/bookmarksAndLinks.pdf b/tests/auto/pdfquick/multipageview/data/bookmarksAndLinks.pdf new file mode 100644 index 000000000..aa0b99039 --- /dev/null +++ b/tests/auto/pdfquick/multipageview/data/bookmarksAndLinks.pdf @@ -0,0 +1,317 @@ +%PDF-1.7
+
+1 0 obj
+<<
+ /Type /Catalog
+ /PageMode /FullScreen
+ /Outlines 6 0 R
+ /Pages 2 0 R
+ /Names 50 0 R
+ /PageLabels 23 0 R
+ /ViewerPreferences<</NonFullScreenPageMode (UseThumbs)>>
+>>
+endobj
+
+50 0 obj
+<<
+ /Dests <</Names [ (ToTest2) [4 0 R /XYZ 300 300 1] (ToTest3) [5 0 R /XYZ 290 10 0.5] (ToTest1) [3 0 R /XYZ 600 800 1] ]>>
+>>
+endobj
+
+23 0 obj
+<<
+ /Nums [0 <</S /D /P(test )>> 3 <</S /A >> 4<</S /R/St >> 5<</S /r/St >> ]
+ /Limits [0 5]
+>>
+endobj
+
+2 0 obj
+<<
+ /Type /Pages
+ /Kids [3 0 R 4 0 R 5 0 R]
+ /Count 3
+>>
+endobj
+
+3 0 obj
+<<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 10 600 800]
+ /Annots [24 0 R 25 0 R]
+ /Contents 16 0 R
+ /Resources <<
+ /Font <</F1 18 0 R>>
+ >>
+>>
+endobj
+
+24 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest2)
+ /A << /Type /Action
+ /S /GoTo
+ /D [5 0 R /FitR ¨C4 399 199 533]
+ >>
+ /Rect [10 690 150 720]
+
+>>
+endobj
+
+25 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest3)
+ /Rect [10 630 150 650]
+>>
+endobj
+
+
+16 0 obj
+<< /Length 0 >>
+ stream
+ BT
+ /F1 72 Tf
+ 200 200 TD
+ 0 0 1 RG
+ 5 Tr
+ (Test_1) Tj
+ 0 800 m
+ 600 0 l S
+ /F1 30 Tf
+ 0 1 0 RG
+ 1 Tr
+ -190 490 TD
+ (GO Test_2) Tj
+ 0 -50 TD
+ 5 w
+ 2 Tr
+ 1 0 0 RG
+ (GO Test_3) Tj
+ ET
+ endstream
+endobj
+
+
+endobj
+
+18 0 obj
+<<
+ /Type /Font
+ /Subtype /Type1
+ /Name /F1
+ /BaseFont /Helvetica
+>>
+endobj
+
+4 0 obj
+<<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [10 0 500 700]
+ /Annots [60 0 R]
+ /Contents 19 0 R
+ /Resources <<
+ /Font <</F2 20 0 R>>
+ >>
+>>
+endobj
+
+19 0 obj
+<< /Length 0 >>
+stream
+BT
+ 1 -0.7 0 1 30 100 cm
+ /F2 50 Tf
+ 10 50 TD
+ (TEST_2) Tj
+
+ 1 0.7 0 1 -30 -100 cm
+ /F2 25 Tf
+ 1 0 1 RG
+ 7 w
+ 100 60 TD
+
+ (GO Test_1) Tj
+ 100 100 140 40 re S f
+ET
+endstream
+endobj
+
+20 0 obj
+<<
+ /Type /Font
+ /Subtype /TrueType
+ /Name /F2
+ /BaseFont /NewYork , Bold
+ /FirstChar 0
+ /LastChar 255
+ /Widths 23 0 R
+ /FontDescriptor 7 0 R
+ /Encoding /MacRomanEncoding
+>>
+endobj
+
+60 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest1)
+ /Rect [110 110 230 150]
+>>
+endobj
+
+5 0 obj
+<<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [-10 -10 400 600]
+ /Annots [61 0 R]
+ /Contents 21 0 R
+ /Resources << /Font <</F3 22 0 R>> >>
+>>
+endobj
+
+21 0 obj
+<< /Length 0 >>
+stream
+BT
+ /F3 30 Tf
+ 290 10 TD
+ (TEST_3) Tj
+ -50 90 TD
+ (GO Test_2)Tj
+ET
+endstream
+endobj
+
+22 0 obj
+<<
+ /Type /Font
+ /Subtype /Type1
+ /Name /F3
+ /BaseFont /Courier-Bold
+>>
+endobj
+
+61 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest2)
+ /Rect [240 90 400 130]
+>>
+
+6 0 obj
+<<
+ /Type /Outlines
+ /First 7 0 R
+ /Last 11 0 R
+ /Count 4 0 R
+>>
+endobj
+
+7 0 obj
+<<
+ /Title (First)
+ /Parent 6 0 R
+ /Next 8 0 R
+ /C [1 0 0]
+ /Dest [ 3 0 R /XYZ 600 800 0.5 ]
+>>
+endobj
+
+8 0 obj
+<<
+ /Title (Second)
+ /Parent 6 0 R
+ /Prev 7 0 R
+ /Next 9 0 R
+ /C [0 1 0]
+ % /Dest [ 4 0 R /XYZ 500 700 null ]
+/Dest (ToTest2)
+>>
+endobj
+
+9 0 obj
+<<
+ /Title (Third)
+ /Parent 6 0 R
+ /Prev 8 0 R
+ /Next 10 0 R
+ /C [0 0 1]
+ /Dest [ 5 0 R /XYZ 400 600 0.8 ]
+>>
+endobj
+
+10 0 obj
+<<
+ /Title (Fourth)
+ /Parent 6 0 R
+ /Prev 9 0 R
+ /Next 11 0 R
+>>
+endobj
+
+11 0 obj
+<<
+ /Title (Fivth)
+ /Parent 6 0 R
+ /Prev 10 0 R
+ /First 12 0 R
+ /Last 15 0 R
+ /Count 4
+>>
+endobj
+
+12 0 obj
+<<
+ /Title (Fivth_1)
+ /Parent 11 0 R
+ /Next 13 0 R
+>>
+endobj
+
+13 0 obj
+<<
+ /Title (Fivth_2)
+ /Parent 11 0 R
+ /Prev 12 0 R
+ /Next 14 0 R
+>>
+endobj
+
+14 0 obj
+<<
+ /Title (Fivth_3)
+ /Parent 11 0 R
+ /Prev 13 0 R
+ /Next 15 0 R
+>>
+endobj
+
+15 0 obj
+<<
+ /Title (Fivth_4)
+ /Parent 11 0 R
+ /Prev 14 0 R
+>>
+endobj
+
+
+
+
+xref
+0000000000 65536 f
+
+trailer
+<<
+ /Size 0
+ /Root 1 0 R
+>>
+startxref
+0
+%%EOF
diff --git a/tests/auto/pdfquick/multipageview/data/jumpOnDocumentReady.qml b/tests/auto/pdfquick/multipageview/data/jumpOnDocumentReady.qml new file mode 100644 index 000000000..ce74f5ed8 --- /dev/null +++ b/tests/auto/pdfquick/multipageview/data/jumpOnDocumentReady.qml @@ -0,0 +1,18 @@ +import QtQuick +import QtQuick.Pdf + +PdfMultiPageView { + id: view + width: 480 + height: 480 + + document: PdfDocument { + id: document + onStatusChanged: { + if(status === PdfDocument.Ready) + view.goToPage(2) + } + } + + Component.onCompleted: document.source = "bookmarksAndLinks.pdf" +} diff --git a/tests/auto/pdfquick/multipageview/data/multiPageView.qml b/tests/auto/pdfquick/multipageview/data/multiPageView.qml new file mode 100644 index 000000000..bf88180ce --- /dev/null +++ b/tests/auto/pdfquick/multipageview/data/multiPageView.qml @@ -0,0 +1,8 @@ +import QtQuick +import QtQuick.Pdf + +PdfMultiPageView { + width: 480; height: 480 + property alias source: document.source + document: PdfDocument { id: document } +} diff --git a/tests/auto/pdfquick/multipageview/data/multiPageViewWithFeedback.qml b/tests/auto/pdfquick/multipageview/data/multiPageViewWithFeedback.qml new file mode 100644 index 000000000..93a556c97 --- /dev/null +++ b/tests/auto/pdfquick/multipageview/data/multiPageViewWithFeedback.qml @@ -0,0 +1,18 @@ +import QtQuick +import QtQuick.Pdf + +PdfMultiPageView { + id: view + property point hoverPos: hover.point.position + width: 640; height: 480 + document: PdfDocument { } + + // mouse hover feedback for test development + Rectangle { + width: 200 + height: hoverPosLabel.implicitHeight + 12 + color: "beige" + Text { id: hoverPosLabel; x: 6; y: 6; text: view.hoverPos.x + ", " + view.hoverPos.y } + } + HoverHandler { id: hover } +} diff --git a/tests/auto/pdfquick/multipageview/data/pdf-sample.protected.pdf b/tests/auto/pdfquick/multipageview/data/pdf-sample.protected.pdf Binary files differnew file mode 100644 index 000000000..d76fdd1a6 --- /dev/null +++ b/tests/auto/pdfquick/multipageview/data/pdf-sample.protected.pdf diff --git a/tests/auto/pdfquick/multipageview/data/qpdfwriter.pdf b/tests/auto/pdfquick/multipageview/data/qpdfwriter.pdf Binary files differnew file mode 100644 index 000000000..4abc76f6d --- /dev/null +++ b/tests/auto/pdfquick/multipageview/data/qpdfwriter.pdf diff --git a/tests/auto/pdfquick/multipageview/tst_multipageview.cpp b/tests/auto/pdfquick/multipageview/tst_multipageview.cpp new file mode 100644 index 000000000..c5e0b30db --- /dev/null +++ b/tests/auto/pdfquick/multipageview/tst_multipageview.cpp @@ -0,0 +1,446 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QSignalSpy> +#include <QTest> +#include <QtCore/QLoggingCategory> +#include <QtGui/QClipboard> +#include <QtGui/QPointingDevice> +#include <QtGui/QStyleHints> +#include <QtQuick/QQuickView> +#include <QtPdfQuick/private/qquickpdflinkmodel_p.h> +#include <QtPdfQuick/private/qquickpdfsearchmodel_p.h> +#include <QtPdfQuick/private/qquickpdfpageimage_p.h> +#include "../shared/util.h" + +using namespace Qt::StringLiterals; + +Q_LOGGING_CATEGORY(lcTests, "qt.pdf.tests") + +class tst_MultiPageView : public QQuickDataTest +{ + Q_OBJECT + +private Q_SLOTS: + void internalLink_data(); + void internalLink(); + void navigation_data(); + void navigation(); + void password(); + void selectionAndClipboard(); + void search(); + void pinchDragPinch(); + void jumpOnDocumentReady(); + +public: + enum NavigationAction { + Back, + Forward, + GotoPage, + GotoLocation, + ClickLink + }; + Q_ENUM(NavigationAction) + + struct NavigationCommand { + NavigationAction action; + int index; + QPointF location; + qreal zoom; + QPointF expectedContentPos; + int expectedCurrentPage; + }; + +private: + QScopedPointer<QPointingDevice> touchscreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice()); +}; + +void tst_MultiPageView::internalLink_data() +{ + QTest::addColumn<int>("linkIndex"); + QTest::addColumn<int>("expectedPage"); + QTest::addColumn<qreal>("expectedZoom"); + QTest::addColumn<QPoint>("expectedScroll"); + + QTest::newRow("first link") << 0 << 1 << qreal(1) << QPoint(134, 1286); + // TODO fails because it zooms out, and the view leaves gaps between pages currently +// QTest::newRow("second link") << 1 << 2 << qreal(0.5) << QPoint(0, 717); +} + +void tst_MultiPageView::internalLink() +{ + QFETCH(int, linkIndex); + QFETCH(int, expectedPage); + QFETCH(qreal, expectedZoom); + QFETCH(QPoint, expectedScroll); + + QQuickView window; + QVERIFY(showView(window, testFileUrl("multiPageView.qml"))); + QQuickItem *pdfView = window.rootObject(); + QVERIFY(pdfView); + pdfView->setProperty("source", testFileUrl("bookmarksAndLinks.pdf")); + QTRY_COMPARE(pdfView->property("currentPageRenderingStatus").toInt(), QQuickPdfPageImage::Ready); + + QQuickItem *table = static_cast<QQuickItem *>(findFirstChild(pdfView, "QQuickTableView")); + QVERIFY(table); + QQuickItem *firstPage = tableViewItemAtCell(table, 0, 0); + QVERIFY(firstPage); + QQuickPdfLinkModel *linkModel = firstPage->findChild<QQuickPdfLinkModel*>(); + QVERIFY(linkModel); + QQuickItem *repeater = qobject_cast<QQuickItem *>(linkModel->parent()); + QVERIFY(repeater); + QVERIFY(repeater->property("count").toInt() > linkIndex); + + QCOMPARE(pdfView->property("backEnabled").toBool(), false); + QCOMPARE(pdfView->property("forwardEnabled").toBool(), false); + + // get the PdfLinkDelegate instance, which has a TapHandler declared inside + QQuickItem *linkDelegate = repeaterItemAt(repeater, linkIndex); + QVERIFY(linkDelegate); + const auto modelIdx = linkModel->index(linkIndex); + const int linkPage = linkModel->data(modelIdx, int(QPdfLinkModel::Role::Page)).toInt(); + QVERIFY(linkPage >= 0); + const QPointF linkLocation = linkModel->data(modelIdx, int(QPdfLinkModel::Role::Location)).toPointF(); + const qreal linkZoom = linkModel->data(modelIdx, int(QPdfLinkModel::Role::Zoom)).toReal(); + + // click on it, and check whether it went to the right place + const auto point = linkDelegate->position().toPoint() + QPoint(15, 15); + QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, point); + QTRY_COMPARE(tableViewContentPos(table).y(), expectedScroll.y()); + const auto linkScrollPos = tableViewContentPos(table); + qCDebug(lcTests, "clicked link @ %d, %d and expected scrolling to %d, %d; actually scrolled to %d, %d", + point.x(), point.y(), expectedScroll.x(), expectedScroll.y(), linkScrollPos.x(), linkScrollPos.y()); + QVERIFY(qAbs(linkScrollPos.x() - expectedScroll.x()) < 15); + QTRY_COMPARE(pdfView->property("currentPageRenderingStatus").toInt(), QQuickPdfPageImage::Ready); + QCOMPARE(pdfView->property("currentPage").toInt(), linkPage); + QCOMPARE(linkPage, expectedPage); + QCOMPARE(pdfView->property("renderScale").toReal(), linkZoom); + QCOMPARE(linkZoom, expectedZoom); + qCDebug(lcTests, "link %d goes to page %d location {%lf,%lf} zoom %lf scroll to {%lf,%lf}", + linkIndex, linkPage, linkLocation.x(), linkLocation.y(), linkZoom, + table->property("contentX").toReal(), table->property("contentY").toReal()); + + // check that we can go back to where we came from + QCOMPARE(pdfView->property("backEnabled").toBool(), true); + QCOMPARE(pdfView->property("forwardEnabled").toBool(), false); + QVERIFY(QMetaObject::invokeMethod(pdfView, "back")); + QTRY_COMPARE(tableViewContentPos(table), QPoint(0, 0)); + QCOMPARE(pdfView->property("currentPage").toInt(), 0); + QCOMPARE(pdfView->property("renderScale").toReal(), qreal(1)); + + // and then forward again + QCOMPARE(pdfView->property("backEnabled").toBool(), false); + QCOMPARE(pdfView->property("forwardEnabled").toBool(), true); + QVERIFY(QMetaObject::invokeMethod(pdfView, "forward")); + QTRY_COMPARE(tableViewContentPos(table), linkScrollPos); + QCOMPARE(pdfView->property("currentPage").toInt(), linkPage); + QCOMPARE(pdfView->property("renderScale").toReal(), linkZoom); +} + +void tst_MultiPageView::navigation_data() +{ + QTest::addColumn<QList<NavigationCommand>>("actions"); + const int totalPageSpacing = 832; // 826 points + 6 px (rowSpacing) + + QList<NavigationCommand> actions; + actions << NavigationCommand {NavigationAction::GotoPage, 2, {}, 0, {0, 1664}, 2} + << NavigationCommand {NavigationAction::GotoPage, 3, {}, 0, {0, 2496}, 3} + << NavigationCommand {NavigationAction::Back, 0, {}, 0, {0, 1664}, 2} + << NavigationCommand {NavigationAction::Back, 0, {}, 0, {0, 0}, 0}; + QTest::newRow("goto and back") << actions; + + actions.clear(); + actions // first link is "More..." going to page 0, location 8, 740 + << NavigationCommand {NavigationAction::ClickLink, 0, {465, 65}, 0, {0, 740}, 0} + << NavigationCommand {NavigationAction::Back, 0, {}, 0, {0, 0}, 0} + // link "setPdfVersion()" going to page 3, location 8, 295 + << NavigationCommand {NavigationAction::ClickLink, 0, {255, 455}, 0, {0, totalPageSpacing * 3 + 295}, 3} + << NavigationCommand {NavigationAction::Back, 0, {}, 0, {0, 0}, 0}; + QTest::newRow("click links and go back, twice") << actions; + + actions.clear(); + actions // first link is "More..." going to page 0, location 8, 740 + << NavigationCommand {NavigationAction::ClickLink, 0, {465, 65}, 0, {0, 740}, 0} + // link "newPage()" going to page 1, location 8, 290 + << NavigationCommand {NavigationAction::ClickLink, 0, {480, 40}, 0, {0, totalPageSpacing + 290}, 1} // fails, goes back to page 0 + << NavigationCommand {NavigationAction::Back, 0, {}, 0, {8, 740}, 0} + << NavigationCommand {NavigationAction::Back, 0, {}, 0, {0, 0}, 0}; + QTest::newRow("click two links in series and then go back") << actions; +} + +void tst_MultiPageView::navigation() +{ + QFETCH(QList<NavigationCommand>, actions); + + QQuickView window; + window.setColor(Qt::gray); + window.setSource(testFileUrl("multiPageViewWithFeedback.qml")); + QTRY_COMPARE(window.status(), QQuickView::Ready); + QQuickItem *pdfView = window.rootObject(); + QVERIFY(pdfView); + QObject *doc = pdfView->property("document").value<QObject *>(); + QVERIFY(doc); + doc->setProperty("source", testFileUrl("qpdfwriter.pdf")); + QQuickItem *table = static_cast<QQuickItem *>(findFirstChild(pdfView, "QQuickTableView")); + QVERIFY(table); + // Expect that contentY == destination y after a jump, for ease of comparison. + // 0.01 is close enough to 0 that we can compare int positions accurately, + // but nonzero so that QRectF::isValid() is true in tableView.positionViewAtCell() + table->setProperty("jumpLocationMargin", QPointF(0.01, 0.01)); + + window.show(); + window.requestActivate(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + QTRY_COMPARE(table->property("contentHeight").toInt(), 3322); + QCOMPARE(table->property("contentY").toInt(), 0); + + for (const NavigationCommand &nav : actions) { + switch (nav.action) { + case NavigationAction::Back: + QVERIFY(QMetaObject::invokeMethod(pdfView, "back")); + QCOMPARE(pdfView->property("forwardEnabled").toBool(), true); + break; + case NavigationAction::Forward: + QVERIFY(QMetaObject::invokeMethod(pdfView, "forward")); + QCOMPARE(pdfView->property("backEnabled").toBool(), true); + break; + case NavigationAction::GotoPage: + QVERIFY(QMetaObject::invokeMethod(pdfView, "goToPage", + Q_ARG(QVariant, QVariant(nav.index)))); + QCOMPARE(pdfView->property("backEnabled").toBool(), true); + break; + case NavigationAction::GotoLocation: + QVERIFY(QMetaObject::invokeMethod(pdfView, "goToLocation", + Q_ARG(QVariant, QVariant(nav.index)), + Q_ARG(QVariant, QVariant(nav.location)), + Q_ARG(QVariant, QVariant(nav.zoom)) )); + break; + case NavigationAction::ClickLink: + // Link delegates don't exist until page rendering is done + QTRY_VERIFY(pdfView->property("currentPageRenderingStatus").toInt() == 1); // QQuickImage::Status::Ready + QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, nav.location.toPoint()); + // Wait for the destination page to be rendered + QTRY_VERIFY(pdfView->property("currentPageRenderingStatus").toInt() == 1); // QQuickImage::Status::Ready + break; + } + qCDebug(lcTests) << "action" << nav.action << "index" << nav.index + << "contentX,Y" << table->property("contentX").toInt() << table->property("contentY").toInt() + << "expected" << nav.expectedContentPos; + QTRY_COMPARE(table->property("contentY").toInt(), nav.expectedContentPos.y()); + // some minor side-to-side scrolling happens, in practice + QVERIFY(qAbs(table->property("contentX").toInt() - nav.expectedContentPos.x()) < 10); + QCOMPARE(pdfView->property("currentPage").toInt(), nav.expectedCurrentPage); + } + + QCOMPARE(pdfView->property("backEnabled").toBool(), false); +} + +void tst_MultiPageView::password() +{ + QQuickView window; + QVERIFY(showView(window, testFileUrl("multiPageView.qml"))); + QQuickItem *pdfView = window.rootObject(); + QVERIFY(pdfView); + QQuickPdfDocument *doc = pdfView->property("document").value<QQuickPdfDocument*>(); + QVERIFY(doc); + QPdfDocument *cppDoc = static_cast<QPdfDocument *>(qmlExtendedObject(doc)); + QVERIFY(cppDoc); + QSignalSpy passwordRequiredSpy(doc, SIGNAL(passwordRequired())); + // actually QPdfDocument::passwordRequired, but QML_EXTENDED gives us this signal virtually in QQuickPdfDocument + QVERIFY(passwordRequiredSpy.isValid()); + QSignalSpy passwordChangedSpy(doc, SIGNAL(passwordChanged())); + // actually QPdfDocument::passwordChanged, but QML_EXTENDED gives us this signal virtually in QQuickPdfDocument + QVERIFY(passwordChangedSpy.isValid()); + QSignalSpy statusChangedSpy(doc, SIGNAL(statusChanged(QPdfDocument::Status))); + // actually QPdfDocument::statusChanged, but QML_EXTENDED gives us this signal virtually in QQuickPdfDocument + QVERIFY(statusChangedSpy.isValid()); + QSignalSpy pageCountChangedSpy(doc, SIGNAL(pageCountChanged(int))); + // QPdfDocument::pageCountChanged(int), but QML_EXTENDED gives us this signal virtually in QQuickPdfDocument + QVERIFY(pageCountChangedSpy.isValid()); + QSignalSpy extPageCountChangedSpy(cppDoc, &QPdfDocument::pageCountChanged); + // actual QPdfDocument::pageCountChanged(int), for comparison with the illusory QQuickPdfDocument::pageCountChanged + QVERIFY(extPageCountChangedSpy.isValid()); + + QVERIFY(pdfView->setProperty("source", testFileUrl(u"pdf-sample.protected.pdf"_s))); + + QTRY_COMPARE(passwordRequiredSpy.size(), 1); + qCDebug(lcTests) << "error while awaiting password" << doc->error() + << "passwordRequired count" << passwordRequiredSpy.size() + << "statusChanged count" << statusChangedSpy.size(); + QCOMPARE(doc->property("status").toInt(), int(QPdfDocument::Status::Error)); + QCOMPARE(pageCountChangedSpy.size(), 0); + QCOMPARE(extPageCountChangedSpy.size(), 0); + QCOMPARE(statusChangedSpy.size(), 2); // Loading and then Error + statusChangedSpy.clear(); + QVERIFY(doc->setProperty("password", u"Qt"_s)); + QCOMPARE(passwordChangedSpy.size(), 1); + QTRY_COMPARE(doc->property("status").toInt(), int(QPdfDocument::Status::Ready)); + qCDebug(lcTests) << "after setPassword" << doc->error() + << "passwordChanged count" << passwordChangedSpy.size() + << "statusChanged count" << statusChangedSpy.size() + << "pageCountChanged count" << pageCountChangedSpy.size(); + QCOMPARE(statusChangedSpy.size(), 2); // Loading and then Ready + QCOMPARE(pageCountChangedSpy.size(), 1); + QCOMPARE(extPageCountChangedSpy.size(), pageCountChangedSpy.size()); +} + +void tst_MultiPageView::selectionAndClipboard() +{ + QQuickView window; + QVERIFY(showView(window, testFileUrl("multiPageView.qml"))); + QQuickItem *pdfView = window.rootObject(); + QVERIFY(pdfView); + QQuickPdfDocument *doc = pdfView->property("document").value<QQuickPdfDocument*>(); + QVERIFY(doc); + QVERIFY(doc->setProperty("password", u"Qt"_s)); + QVERIFY(pdfView->setProperty("source", testFileUrl((u"pdf-sample.protected.pdf"_s)))); + QTRY_COMPARE(pdfView->property("currentPageRenderingStatus").toInt(), QQuickPdfPageImage::Ready); + + QVERIFY(QMetaObject::invokeMethod(pdfView, "selectAll")); + QString sel = pdfView->property("selectedText").toString(); + QCOMPARE(sel.size(), 1073); + +#if QT_CONFIG(clipboard) + QClipboard *clip = qApp->clipboard(); + if (clip->supportsSelection()) + QCOMPARE(clip->text(QClipboard::Selection), sel); + QVERIFY(QMetaObject::invokeMethod(pdfView, "copySelectionToClipboard")); + QCOMPARE(clip->text(QClipboard::Clipboard), sel); +#endif // clipboard +} + +void tst_MultiPageView::search() +{ + QQuickView window; + QVERIFY(showView(window, testFileUrl("multiPageView.qml"))); + window.setResizeMode(QQuickView::SizeRootObjectToView); + window.resize(200, 200); + QQuickItem *pdfView = window.rootObject(); + QVERIFY(pdfView); + QTRY_COMPARE(pdfView->width(), 200); + QQuickPdfDocument *doc = pdfView->property("document").value<QQuickPdfDocument*>(); + QVERIFY(doc); + QVERIFY(doc->setProperty("password", u"Qt"_s)); + QVERIFY(pdfView->setProperty("source", testFileUrl(u"pdf-sample.protected.pdf"_s))); + QTRY_COMPARE(pdfView->property("currentPageRenderingStatus").toInt(), QQuickPdfPageImage::Ready); + QPdfSearchModel *searchModel = pdfView->property("searchModel").value<QPdfSearchModel*>(); + QVERIFY(searchModel); + QQuickItem *table = static_cast<QQuickItem *>(findFirstChild(pdfView, "QQuickTableView")); + QVERIFY(table); + QQuickItem *firstPage = tableViewItemAtCell(table, 0, 0); + QVERIFY(firstPage); + QObject *multiline = findFirstChild(firstPage, "QQuickPathMultiline"); + QVERIFY(multiline); + + pdfView->setProperty("searchString", u"PDF"_s); + QTRY_COMPARE(searchModel->rowCount(QModelIndex()), 7); // occurrences of the word "PDF" in this file + const int count = searchModel->rowCount(QModelIndex()); + QList<QList<QPointF>> resultOutlines = multiline->property("paths").value<QList<QList<QPointF>>>(); + QCOMPARE(resultOutlines.size(), 7); + QPoint contentPos = tableViewContentPos(table); + int movements = 0; + for (int i = 0; i < count; ++i) { + // only one page, so IndexOnPage data is the same as overall index + QCOMPARE(i, searchModel->data(searchModel->index(i), int(QPdfSearchModel::Role::IndexOnPage)).toInt()); + QCOMPARE(resultOutlines.at(i).size(), 5); // 5-point polygon is a rectangle (including drawing back to the start, to close it) + QCOMPARE(resultOutlines.at(i).first(), searchModel->data(searchModel->index(i), int(QPdfSearchModel::Role::Location)).toPointF()); + + QVERIFY(QMetaObject::invokeMethod(pdfView, "searchForward")); + QTest::qWait(500); // animation time; but it doesn't always need to move + // TODO maybe: if movement starts, wait for it to stop somehow? + qCDebug(lcTests) << i << resultOutlines.at(i) << "scrolled to" << tableViewContentPos(table); + if (tableViewContentPos(table) != contentPos) + ++movements; + contentPos = tableViewContentPos(table); + } + qCDebug(lcTests) << "total movements" << movements; + QVERIFY(movements > 4); +} + +void tst_MultiPageView::pinchDragPinch() +{ + qputenv("QML_NO_TOUCH_COMPRESSION", "1"); + QQuickView window; + QVERIFY(showView(window, testFileUrl("multiPageView.qml"))); + QQuickItem *pdfView = window.rootObject(); + QVERIFY(pdfView); + pdfView->setProperty("source", testFileUrl("bookmarksAndLinks.pdf")); + QTRY_COMPARE(pdfView->property("currentPageRenderingStatus").toInt(), QQuickPdfPageImage::Ready); + QQuickItem *table = static_cast<QQuickItem *>(findFirstChild(pdfView, "QQuickTableView")); + QVERIFY(table); + QQuickItem *firstPage = tableViewItemAtCell(table, 0, 0); + QVERIFY(firstPage); + QQuickItem *paper = firstPage->childAt(10, 10); + QVERIFY(paper); + QQuickPdfPageImage *image = firstPage->findChild<QQuickPdfPageImage *>(); + QVERIFY(image); + + auto pinch = [&window, paper, this]() { + const int threshold = QGuiApplication::styleHints()->startDragDistance(); + const int movement = 100; + QCOMPARE_GT(movement, threshold); + const qreal initialScale = paper->scale(); + QPoint p0(100, 200); + QPoint p1(200, 200); + QTest::QTouchEventSequence seq = QTest::touchEvent(&window, touchscreen.get()); + seq.press(0, p0, &window).commit(); + seq.stationary(0).press(1, p1, &window).commit(); + p1.setX(p1.x() + movement); + QSignalSpy frameSwappedSpy(&window, &QQuickWindow::frameSwapped); + seq.stationary(0).move(1, p1, &window).commit(); + // after a frame is rendered, the PinchHandler ought to be active + // (but verifying it would require private API) + QTRY_VERIFY(frameSwappedSpy.size() > 0); + QTRY_COMPARE(paper->scale(), initialScale); + + for (int i = 1; i <= 2; ++i) { + p1.setX(p1.x() + movement); + seq.stationary(0).move(1, p1, &window).commit(); + QTRY_COMPARE(paper->scale(), initialScale + i * 0.5); + } + seq.release(0, p0, &window).release(1, p1, &window).commit(); + }; + + auto drag = [&window, table, this]() { + const int movement = 100; + QPoint p0(200, 200); + QTest::QTouchEventSequence seq = QTest::touchEvent(&window, touchscreen.get()); + seq.press(0, p0, &window).commit(); + p0.setY(p0.y() + movement); + seq.move(0, p0, &window).commit(); + p0.setY(p0.y() + movement); + seq.move(0, p0, &window).commit(); + seq.release(0, p0, &window).commit(); + QTRY_COMPARE(table->property("moving"), false); + }; + + pinch(); + qCDebug(lcTests) << "new scale" << pdfView->property("renderScale").toReal(); + QTRY_COMPARE(pdfView->property("renderScale").toReal(), 2); + + drag(); + QCOMPARE(pdfView->property("renderScale").toReal(), 2); + + pinch(); + qCDebug(lcTests) << "new scale" << pdfView->property("renderScale").toReal(); + QTRY_COMPARE(pdfView->property("renderScale").toReal(), 4); + + // wait for rendering to be done before we exit: if we delete the document + // prematurely, QPdfIOHandler might access a dangling pointer + QTRY_COMPARE(image->status(), QQuickPdfPageImage::Ready); +} + +void tst_MultiPageView::jumpOnDocumentReady() // QTBUG-119416 +{ + QQuickView window; + QVERIFY(showView(window, testFileUrl("jumpOnDocumentReady.qml"))); + QQuickItem *pdfView = window.rootObject(); + QVERIFY(pdfView); + + // QML calls view.goToPage(2): verify that it eventually happens + QTRY_COMPARE(pdfView->property("currentPage").toInt(), 2); +} + +QTEST_MAIN(tst_MultiPageView) +#include "tst_multipageview.moc" diff --git a/tests/auto/pdfquick/pdfpageimage/CMakeLists.txt b/tests/auto/pdfquick/pdfpageimage/CMakeLists.txt new file mode 100644 index 000000000..da67d8721 --- /dev/null +++ b/tests/auto/pdfquick/pdfpageimage/CMakeLists.txt @@ -0,0 +1,30 @@ +# Collect test data +file(GLOB_RECURSE test_data_glob + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + data/*) +list(APPEND test_data ${test_data_glob}) + +qt_internal_add_test(tst_pdfpageimage + SOURCES + tst_pdfpageimage.cpp + ../shared/util.cpp ../shared/util.h + LIBRARIES + Qt::Gui + Qt::Quick + Qt::PdfQuickPrivate + TESTDATA ${test_data} +) + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_pdfpageimage CONDITION ANDROID OR IOS + DEFINES + QT_QMLTEST_DATADIR=":/data" +) + +qt_internal_extend_target(tst_pdfpageimage CONDITION NOT ANDROID AND NOT IOS + DEFINES + QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" +) + diff --git a/tests/auto/pdfquick/pdfpageimage/data/bookmarksAndLinks.pdf b/tests/auto/pdfquick/pdfpageimage/data/bookmarksAndLinks.pdf new file mode 100644 index 000000000..aa0b99039 --- /dev/null +++ b/tests/auto/pdfquick/pdfpageimage/data/bookmarksAndLinks.pdf @@ -0,0 +1,317 @@ +%PDF-1.7
+
+1 0 obj
+<<
+ /Type /Catalog
+ /PageMode /FullScreen
+ /Outlines 6 0 R
+ /Pages 2 0 R
+ /Names 50 0 R
+ /PageLabels 23 0 R
+ /ViewerPreferences<</NonFullScreenPageMode (UseThumbs)>>
+>>
+endobj
+
+50 0 obj
+<<
+ /Dests <</Names [ (ToTest2) [4 0 R /XYZ 300 300 1] (ToTest3) [5 0 R /XYZ 290 10 0.5] (ToTest1) [3 0 R /XYZ 600 800 1] ]>>
+>>
+endobj
+
+23 0 obj
+<<
+ /Nums [0 <</S /D /P(test )>> 3 <</S /A >> 4<</S /R/St >> 5<</S /r/St >> ]
+ /Limits [0 5]
+>>
+endobj
+
+2 0 obj
+<<
+ /Type /Pages
+ /Kids [3 0 R 4 0 R 5 0 R]
+ /Count 3
+>>
+endobj
+
+3 0 obj
+<<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 10 600 800]
+ /Annots [24 0 R 25 0 R]
+ /Contents 16 0 R
+ /Resources <<
+ /Font <</F1 18 0 R>>
+ >>
+>>
+endobj
+
+24 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest2)
+ /A << /Type /Action
+ /S /GoTo
+ /D [5 0 R /FitR ¨C4 399 199 533]
+ >>
+ /Rect [10 690 150 720]
+
+>>
+endobj
+
+25 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest3)
+ /Rect [10 630 150 650]
+>>
+endobj
+
+
+16 0 obj
+<< /Length 0 >>
+ stream
+ BT
+ /F1 72 Tf
+ 200 200 TD
+ 0 0 1 RG
+ 5 Tr
+ (Test_1) Tj
+ 0 800 m
+ 600 0 l S
+ /F1 30 Tf
+ 0 1 0 RG
+ 1 Tr
+ -190 490 TD
+ (GO Test_2) Tj
+ 0 -50 TD
+ 5 w
+ 2 Tr
+ 1 0 0 RG
+ (GO Test_3) Tj
+ ET
+ endstream
+endobj
+
+
+endobj
+
+18 0 obj
+<<
+ /Type /Font
+ /Subtype /Type1
+ /Name /F1
+ /BaseFont /Helvetica
+>>
+endobj
+
+4 0 obj
+<<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [10 0 500 700]
+ /Annots [60 0 R]
+ /Contents 19 0 R
+ /Resources <<
+ /Font <</F2 20 0 R>>
+ >>
+>>
+endobj
+
+19 0 obj
+<< /Length 0 >>
+stream
+BT
+ 1 -0.7 0 1 30 100 cm
+ /F2 50 Tf
+ 10 50 TD
+ (TEST_2) Tj
+
+ 1 0.7 0 1 -30 -100 cm
+ /F2 25 Tf
+ 1 0 1 RG
+ 7 w
+ 100 60 TD
+
+ (GO Test_1) Tj
+ 100 100 140 40 re S f
+ET
+endstream
+endobj
+
+20 0 obj
+<<
+ /Type /Font
+ /Subtype /TrueType
+ /Name /F2
+ /BaseFont /NewYork , Bold
+ /FirstChar 0
+ /LastChar 255
+ /Widths 23 0 R
+ /FontDescriptor 7 0 R
+ /Encoding /MacRomanEncoding
+>>
+endobj
+
+60 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest1)
+ /Rect [110 110 230 150]
+>>
+endobj
+
+5 0 obj
+<<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [-10 -10 400 600]
+ /Annots [61 0 R]
+ /Contents 21 0 R
+ /Resources << /Font <</F3 22 0 R>> >>
+>>
+endobj
+
+21 0 obj
+<< /Length 0 >>
+stream
+BT
+ /F3 30 Tf
+ 290 10 TD
+ (TEST_3) Tj
+ -50 90 TD
+ (GO Test_2)Tj
+ET
+endstream
+endobj
+
+22 0 obj
+<<
+ /Type /Font
+ /Subtype /Type1
+ /Name /F3
+ /BaseFont /Courier-Bold
+>>
+endobj
+
+61 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest2)
+ /Rect [240 90 400 130]
+>>
+
+6 0 obj
+<<
+ /Type /Outlines
+ /First 7 0 R
+ /Last 11 0 R
+ /Count 4 0 R
+>>
+endobj
+
+7 0 obj
+<<
+ /Title (First)
+ /Parent 6 0 R
+ /Next 8 0 R
+ /C [1 0 0]
+ /Dest [ 3 0 R /XYZ 600 800 0.5 ]
+>>
+endobj
+
+8 0 obj
+<<
+ /Title (Second)
+ /Parent 6 0 R
+ /Prev 7 0 R
+ /Next 9 0 R
+ /C [0 1 0]
+ % /Dest [ 4 0 R /XYZ 500 700 null ]
+/Dest (ToTest2)
+>>
+endobj
+
+9 0 obj
+<<
+ /Title (Third)
+ /Parent 6 0 R
+ /Prev 8 0 R
+ /Next 10 0 R
+ /C [0 0 1]
+ /Dest [ 5 0 R /XYZ 400 600 0.8 ]
+>>
+endobj
+
+10 0 obj
+<<
+ /Title (Fourth)
+ /Parent 6 0 R
+ /Prev 9 0 R
+ /Next 11 0 R
+>>
+endobj
+
+11 0 obj
+<<
+ /Title (Fivth)
+ /Parent 6 0 R
+ /Prev 10 0 R
+ /First 12 0 R
+ /Last 15 0 R
+ /Count 4
+>>
+endobj
+
+12 0 obj
+<<
+ /Title (Fivth_1)
+ /Parent 11 0 R
+ /Next 13 0 R
+>>
+endobj
+
+13 0 obj
+<<
+ /Title (Fivth_2)
+ /Parent 11 0 R
+ /Prev 12 0 R
+ /Next 14 0 R
+>>
+endobj
+
+14 0 obj
+<<
+ /Title (Fivth_3)
+ /Parent 11 0 R
+ /Prev 13 0 R
+ /Next 15 0 R
+>>
+endobj
+
+15 0 obj
+<<
+ /Title (Fivth_4)
+ /Parent 11 0 R
+ /Prev 14 0 R
+>>
+endobj
+
+
+
+
+xref
+0000000000 65536 f
+
+trailer
+<<
+ /Size 0
+ /Root 1 0 R
+>>
+startxref
+0
+%%EOF
diff --git a/tests/auto/pdfquick/pdfpageimage/data/pdfPageImage.qml b/tests/auto/pdfquick/pdfpageimage/data/pdfPageImage.qml new file mode 100644 index 000000000..a268bf14b --- /dev/null +++ b/tests/auto/pdfquick/pdfpageimage/data/pdfPageImage.qml @@ -0,0 +1,16 @@ +import QtQuick +import QtQuick.Pdf + +Item { + width: 320 + height: 320 + + PdfDocument { + id: doc + source: "bookmarksAndLinks.pdf" + } + + PdfPageImage { + anchors.centerIn: parent + } +} diff --git a/tests/auto/pdfquick/pdfpageimage/tst_pdfpageimage.cpp b/tests/auto/pdfquick/pdfpageimage/tst_pdfpageimage.cpp new file mode 100644 index 000000000..d2c9c8709 --- /dev/null +++ b/tests/auto/pdfquick/pdfpageimage/tst_pdfpageimage.cpp @@ -0,0 +1,131 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QRegularExpression> +#include <QSignalSpy> +#include <QTest> +#include <QtQuick/QQuickView> +#include <QtPdfQuick/private/qquickpdfdocument_p.h> +#include <QtPdfQuick/private/qquickpdfpageimage_p.h> +#include "../shared/util.h" + +using namespace Qt::StringLiterals; + +Q_LOGGING_CATEGORY(lcTests, "qt.pdf.tests") + +// #define DEBUG_WRITE_OUTPUT + +class tst_PdfPageImage : public QQuickDataTest +{ + Q_OBJECT + +private Q_SLOTS: + void settableProperties_data(); + void settableProperties(); + +public: + enum Property { + Source = 0x01, + Document = 0x02, + SourceSize = 0x04, + SourceClipRect = 0x08, + MipMap = 0x10, + AutoTransform = 0x20, + Asynchronous = 0x40, + NoCache = 0x80, + Mirror = 0x100, + MirrorVertically = 0x200, + ColorSpace = 0x400, + }; + Q_DECLARE_FLAGS(Properties, Property) + Q_FLAG(Properties) + +private: +#ifdef DEBUG_WRITE_OUTPUT + QTemporaryDir m_tmpDir; +#endif +}; + +void tst_PdfPageImage::settableProperties_data() +{ + QTest::addColumn<tst_PdfPageImage::Properties>("toSet"); + QTest::addColumn<QSize>("expectedSize"); + QTest::addColumn<QRegularExpression>("expectedWarning"); + + const QRegularExpression NoWarning; + const qreal dpr = qGuiApp->devicePixelRatio(); + + QTest::newRow("source") << Properties(Source) << (QSizeF(600, 790) * dpr).toSize() + << QRegularExpression("document property not set: falling back to inefficient loading"); // QTBUG-104767 + QTest::newRow("document") << Properties(Document) << QSize(600, 790) << NoWarning; + QTest::newRow("source and document") << Properties(Source | Document) << QSize(600, 790) + << QRegularExpression("document and source properties in conflict"); + QTest::newRow("document and sourceSize") << Properties(Document | SourceSize) << QSize(100, 100) << NoWarning; + QTest::newRow("document and sourceClipRect") << Properties(Document | SourceClipRect) << QSize(100, 100) << NoWarning; + QTest::newRow("document and autoTransform") << Properties(Document | AutoTransform) << QSize(600, 790) << NoWarning; + QTest::newRow("document and async") << Properties(Document | Asynchronous) << QSize(600, 790) << NoWarning; + QTest::newRow("document and nocache") << Properties(Document | NoCache) << QSize(600, 790) << NoWarning; + QTest::newRow("document and mirror") << Properties(Document | Mirror) << QSize(600, 790) << NoWarning; + QTest::newRow("document and mirrorVertically") << Properties(Document | MirrorVertically) << QSize(600, 790) << NoWarning; + QTest::newRow("document and colorSpace") << Properties(Document | ColorSpace) << QSize(600, 790) << NoWarning; +} + +void tst_PdfPageImage::settableProperties() +{ + QFETCH(tst_PdfPageImage::Properties, toSet); + QFETCH(QSize, expectedSize); + QFETCH(QRegularExpression, expectedWarning); + + QQuickView window; + if (!expectedWarning.pattern().isEmpty()) + QTest::ignoreMessage(QtWarningMsg, expectedWarning); + QVERIFY(showView(window, testFileUrl("pdfPageImage.qml"))); + QQuickPdfPageImage *pdfImage = window.rootObject()->findChild<QQuickPdfPageImage *>(); + QVERIFY(pdfImage); + QQuickPdfDocument *doc = window.rootObject()->findChild<QQuickPdfDocument *>(); + QVERIFY(doc); + if (toSet.testFlag(Document)) + pdfImage->setDocument(doc); + if (toSet.testFlag(Source)) + pdfImage->setSource(doc->source()); + if (toSet.testFlag(SourceSize)) + pdfImage->setSourceSize({100, 100}); + if (toSet.testFlag(SourceClipRect)) + pdfImage->setSourceClipRect({100, 100, 100, 100}); + if (toSet.testFlag(MipMap)) + pdfImage->setMipmap(true); + if (toSet.testFlag(AutoTransform)) + pdfImage->setAutoTransform(true); + if (toSet.testFlag(Asynchronous)) { + QCOMPARE(pdfImage->asynchronous(), false); + // test the opposite of the default + pdfImage->setAsynchronous(true); + } + if (toSet.testFlag(NoCache)) { + QCOMPARE(pdfImage->cache(), true); + // test the opposite of the default + pdfImage->setCache(false); + } + if (toSet.testFlag(Mirror)) { + QCOMPARE(pdfImage->mirror(), false); + pdfImage->setMirror(true); + } + if (toSet.testFlag(MirrorVertically)) { + QCOMPARE(pdfImage->mirrorVertically(), false); + pdfImage->setMirrorVertically(true); + } + if (toSet.testFlag(ColorSpace)) + pdfImage->setColorSpace(QColorSpace::ProPhotoRgb); + QTRY_COMPARE(pdfImage->status(), QQuickPdfPageImage::Ready); + const QImage img = pdfImage->image(); + QCOMPARE(img.size(), expectedSize); +#ifdef DEBUG_WRITE_OUTPUT + m_tmpDir.setAutoRemove(false); + const auto path = m_tmpDir.filePath(QString::fromLocal8Bit(QTest::currentDataTag()) + ".png"); + qCDebug(lcTests) << "saving to" << path; + img.save(path); +#endif +} + +QTEST_MAIN(tst_PdfPageImage) +#include "tst_pdfpageimage.moc" diff --git a/tests/auto/pdfquick/shared/util.cpp b/tests/auto/pdfquick/shared/util.cpp new file mode 100644 index 000000000..c540ebfa6 --- /dev/null +++ b/tests/auto/pdfquick/shared/util.cpp @@ -0,0 +1,110 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "util.h" +#include <QtQuick/QQuickItem> + +QQuickDataTest::QQuickDataTest() : + m_initialized(false), +#ifdef QT_TESTCASE_BUILDDIR + m_dataDirectory(QTest::qFindTestData("data", QT_QMLTEST_DATADIR, 0, QT_TESTCASE_BUILDDIR)), +#else + m_dataDirectory(QTest::qFindTestData("data", QT_QMLTEST_DATADIR, 0)), +#endif + + m_dataDirectoryUrl(m_dataDirectory.startsWith(QLatin1Char(':')) + ? QUrl(QLatin1String("qrc") + m_dataDirectory) + : QUrl::fromLocalFile(m_dataDirectory + QLatin1Char('/'))) +{ +} + +QQuickDataTest::~QQuickDataTest() +{ +} + +void QQuickDataTest::initTestCase() +{ + QVERIFY2(!m_dataDirectory.isEmpty(), "'data' directory not found"); + m_directory = QFileInfo(m_dataDirectory).absolutePath(); + if (m_dataDirectoryUrl.scheme() != QLatin1String("qrc")) + QVERIFY2(QDir::setCurrent(m_directory), qPrintable(QLatin1String("Could not chdir to ") + m_directory)); + + if (QGuiApplication::platformName() == QLatin1String("offscreen") + || QGuiApplication::platformName() == QLatin1String("minimal")) + { + QSKIP("Skipping visual tests due to running with offscreen/minimal"); + } + + m_initialized = true; +} + +void QQuickDataTest::cleanupTestCase() +{ + m_initialized = false; +} + +QString QQuickDataTest::testFile(const QString &fileName) const +{ + if (m_directory.isEmpty()) + qFatal("QQuickDataTest::initTestCase() not called."); + QString result = m_dataDirectory; + result += QLatin1Char('/'); + result += fileName; + return result; +} + +QObject *QQuickDataTest::findFirstChild(QObject *parent, const char *className) +{ + const auto children = parent->findChildren<QObject*>(); + for (QObject *child : children) { + if (child->inherits(className)) + return child; + } + return nullptr; +} + +bool QQuickDataTest::showView(QQuickView &view, const QUrl &url) +{ + view.setSource(url); + while (view.status() == QQuickView::Loading) + QTest::qWait(10); + if (view.status() != QQuickView::Ready) + return false; + const QRect screenGeometry = view.screen()->availableGeometry(); + const QSize size = view.size(); + const QPoint offset = QPoint(size.width() / 2, size.height() / 2); + view.setFramePosition(screenGeometry.center() - offset); +#if QT_CONFIG(cursor) // Get the cursor out of the way. + QCursor::setPos(view.geometry().topRight() + QPoint(100, 100)); +#endif + view.show(); + if (!QTest::qWaitForWindowExposed(&view)) + return false; + if (!view.rootObject()) + return false; + return true; +} + +QQuickItem *QQuickDataTest::repeaterItemAt(QQuickItem *repeater, int i) +{ + static const QMetaMethod itemAtMethod = repeater->metaObject()->method( + repeater->metaObject()->indexOfMethod("itemAt(int)")); + QQuickItem *ret = nullptr; + itemAtMethod.invoke(repeater, Qt::DirectConnection, Q_RETURN_ARG(QQuickItem*, ret), Q_ARG(int, i)); + return ret; +} + +QQuickItem *QQuickDataTest::tableViewItemAtCell(QQuickItem *table, int col, int row) +{ + static const QMetaMethod itemAtCellMethod = table->metaObject()->method( + table->metaObject()->indexOfMethod("itemAtCell(int,int)")); + QQuickItem *ret = nullptr; + itemAtCellMethod.invoke(table, Qt::DirectConnection, + Q_RETURN_ARG(QQuickItem*, ret), Q_ARG(int, col), Q_ARG(int, row)); + return ret; +} + +QPoint QQuickDataTest::tableViewContentPos(QQuickItem *table) +{ + return QPoint(table->property("contentX").toInt(), table->property("contentY").toInt()); +} diff --git a/tests/auto/pdfquick/shared/util.h b/tests/auto/pdfquick/shared/util.h new file mode 100644 index 000000000..9ceb711af --- /dev/null +++ b/tests/auto/pdfquick/shared/util.h @@ -0,0 +1,58 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QUICK_VISUAL_TEST_UTIL_H +#define QUICK_VISUAL_TEST_UTIL_H + +#include <QtCore/QUrl> +#include <QtQuick/QQuickView> +#include <QtTest/QTest> + +/*! \internal + Base class for tests with data that are located in a "data" subfolder. +*/ +class QQuickDataTest : public QObject +{ + Q_OBJECT +public: + QQuickDataTest(); + ~QQuickDataTest(); + + bool initialized() const { return m_initialized; } + + bool showView(QQuickView &view, const QUrl &url); + + QString testFile(const QString &fileName) const; + inline QString testFile(const char *fileName) const + { return testFile(QLatin1String(fileName)); } + inline QUrl testFileUrl(const QString &fileName) const + { + const QString fn = testFile(fileName); + return fn.startsWith(QLatin1Char(':')) + ? QUrl(QLatin1String("qrc") + fn) + : QUrl::fromLocalFile(fn); + } + inline QUrl testFileUrl(const char *fileName) const + { return testFileUrl(QLatin1String(fileName)); } + + inline QString dataDirectory() const { return m_dataDirectory; } + inline QUrl dataDirectoryUrl() const { return m_dataDirectoryUrl; } + inline QString directory() const { return m_directory; } + + QObject *findFirstChild(QObject *parent, const char *className); + QQuickItem *repeaterItemAt(QQuickItem *repeater, int i); + QQuickItem *tableViewItemAtCell(QQuickItem *table, int col, int row); + QPoint tableViewContentPos(QQuickItem *table); + +public slots: + virtual void initTestCase(); + virtual void cleanupTestCase(); + +private: + bool m_initialized; + QString m_dataDirectory; + QUrl m_dataDirectoryUrl; + QString m_directory; +}; + +#endif diff --git a/tests/auto/quick/CMakeLists.txt b/tests/auto/quick/CMakeLists.txt index 2470d253c..d2cf7c3b3 100644 --- a/tests/auto/quick/CMakeLists.txt +++ b/tests/auto/quick/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause add_subdirectory(dialogs) add_subdirectory(publicapi) diff --git a/tests/auto/quick/dialogs/CMakeLists.txt b/tests/auto/quick/dialogs/CMakeLists.txt index 1c7488355..4d8dc853b 100644 --- a/tests/auto/quick/dialogs/CMakeLists.txt +++ b/tests/auto/quick/dialogs/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../httpserver/httpserver.cmake) include(../../util/util.cmake) diff --git a/tests/auto/quick/dialogs/tst_dialogs.cpp b/tests/auto/quick/dialogs/tst_dialogs.cpp index 086587cf4..2b861efa6 100644 --- a/tests/auto/quick/dialogs/tst_dialogs.cpp +++ b/tests/auto/quick/dialogs/tst_dialogs.cpp @@ -71,7 +71,7 @@ void tst_Dialogs::createDialog(const QLatin1String &dialog, bool &ok) m_listener->runJavaScript(trigger.arg(dialog)); QTRY_VERIFY(m_listener->ready()); QTest::mouseClick(m_window, Qt::LeftButton); - QTRY_COMPARE(dialogSpy.count(), 1); + QTRY_COMPARE(dialogSpy.size(), 1); ok = true; } @@ -96,7 +96,7 @@ void tst_Dialogs::contextMenuRequested() QTRY_COMPARE_WITH_TIMEOUT(m_listener->ready(), true, 20000); QSignalSpy dialogSpy(m_listener, &TestHandler::requestChanged); QTest::mouseClick(m_window, Qt::RightButton); - QTRY_COMPARE(dialogSpy.count(), 1); + QTRY_COMPARE(dialogSpy.size(), 1); auto dialog = qobject_cast<QWebEngineContextMenuRequest *>(m_listener->request()); QVERIFY2(dialog, "Incorrect dialog requested"); } @@ -153,7 +153,7 @@ void tst_Dialogs::authenticationDialogRequested() QSignalSpy dialogSpy(m_listener, &TestHandler::requestChanged); m_listener->load(url); - QTRY_COMPARE(dialogSpy.count(), 1); + QTRY_COMPARE(dialogSpy.size(), 1); auto *dialog = qobject_cast<QQuickWebEngineAuthenticationDialogRequest*>(m_listener->request()); QVERIFY2(dialog, "Incorrect dialog requested"); dialog->dialogReject(); @@ -197,7 +197,7 @@ void tst_Dialogs::javaScriptDialogRequested() QSignalSpy dialogSpy(m_listener, &TestHandler::requestChanged); m_listener->runJavaScript(script); - QTRY_COMPARE(dialogSpy.count(), 1); + QTRY_COMPARE(dialogSpy.size(), 1); auto *dialog = qobject_cast<QQuickWebEngineJavaScriptDialogRequest*>(m_listener->request()); QVERIFY2(dialog, "Incorrect dialog requested"); dialog->dialogReject(); diff --git a/tests/auto/quick/inspectorserver/CMakeLists.txt b/tests/auto/quick/inspectorserver/CMakeLists.txt index a86d12b1f..d890581b8 100644 --- a/tests/auto/quick/inspectorserver/CMakeLists.txt +++ b/tests/auto/quick/inspectorserver/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause qt_internal_add_test(tst_inspectorserver SOURCES diff --git a/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp b/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp index 6c22c5d33..a9638bee4 100644 --- a/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp +++ b/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp @@ -9,6 +9,7 @@ #include <QtTest/QtTest> #include <QQuickWebEngineProfile> #include <QtWebEngineQuick/private/qquickwebengineview_p.h> +#include <QWebEnginePage> #define INSPECTOR_SERVER_PORT "23654" static const QUrl s_inspectorServerHttpBaseUrl("http://localhost:" INSPECTOR_SERVER_PORT); @@ -22,6 +23,7 @@ private Q_SLOTS: void init(); void cleanup(); + void testDevToolsId(); void testPageList(); void testRemoteDebuggingMessage(); void openRemoteDebuggingSession(); @@ -36,6 +38,7 @@ private: tst_InspectorServer::tst_InspectorServer() { + qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--remote-allow-origins=*"); qputenv("QTWEBENGINE_REMOTE_DEBUGGING", INSPECTOR_SERVER_PORT); QtWebEngineQuick::initialize(); QQuickWebEngineProfile::defaultProfile()->setOffTheRecord(true); @@ -78,7 +81,7 @@ inline QQuickWebEngineView* tst_InspectorServer::webView() const QJsonArray tst_InspectorServer::fetchPageList() const { QNetworkAccessManager qnam; - QSignalSpy spy(&qnam, &QNetworkAccessManager::finished);; + QSignalSpy spy(&qnam, &QNetworkAccessManager::finished); QNetworkRequest request(s_inspectorServerHttpBaseUrl.resolved(QUrl("json/list"))); QScopedPointer<QNetworkReply> reply(qnam.get(request)); spy.wait(); @@ -90,6 +93,21 @@ QJsonArray tst_InspectorServer::fetchPageList() const return QJsonDocument::fromJson(reply->readAll()).array(); } +void tst_InspectorServer::testDevToolsId() +{ + const QUrl testPageUrl = QUrl::fromLocalFile(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + QLatin1String("/html/basic_page.html")); + QSignalSpy loadSpy(webView(), SIGNAL(loadingChanged(QWebEngineLoadingInfo))); + webView()->setUrl(testPageUrl); + QTRY_VERIFY_WITH_TIMEOUT(loadSpy.size() && !webView()->isLoading(), 10000); + + // Our page should be the only one in the list. + QJsonArray pageList = fetchPageList(); + QCOMPARE(pageList.size(), 1); + QCOMPARE(testPageUrl.toString(), pageList.at(0).toObject().value("url").toString()); + QCOMPARE(webView()->devToolsId(), pageList.at(0).toObject().value("id").toString()); +} + void tst_InspectorServer::testPageList() { const QUrl testPageUrl = QUrl::fromLocalFile(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() @@ -136,7 +154,7 @@ void tst_InspectorServer::testRemoteDebuggingMessage() .arg(pageList.at(0).toObject().value("webSocketDebuggerUrl").toString()) .arg(jsExpression)); - QTRY_COMPARE(webSocketQueryWebView->title(), jsExpressionResult); + QTRY_COMPARE_WITH_TIMEOUT(webSocketQueryWebView->title(), jsExpressionResult, 10000); } void tst_InspectorServer::openRemoteDebuggingSession() @@ -160,7 +178,7 @@ void tst_InspectorServer::openRemoteDebuggingSession() // - The page list didn't return a valid inspector URL // - Or the front-end couldn't be loaded through the inspector HTTP server // - Or the web socket connection couldn't be established between the front-end and the page through the inspector server - QTRY_VERIFY_WITH_TIMEOUT(inspectorWebView->title().startsWith("DevTools -"), 30000); + QTRY_VERIFY_WITH_TIMEOUT(inspectorWebView->title().startsWith("DevTools -"), 60000); } QTEST_MAIN(tst_InspectorServer) diff --git a/tests/auto/quick/publicapi/CMakeLists.txt b/tests/auto/quick/publicapi/CMakeLists.txt index aee7d551a..e345a076a 100644 --- a/tests/auto/quick/publicapi/CMakeLists.txt +++ b/tests/auto/quick/publicapi/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause qt_internal_add_test(tst_publicapi SOURCES diff --git a/tests/auto/quick/publicapi/tst_publicapi.cpp b/tests/auto/quick/publicapi/tst_publicapi.cpp index 75c7cd4f7..b98e2a942 100644 --- a/tests/auto/quick/publicapi/tst_publicapi.cpp +++ b/tests/auto/quick/publicapi/tst_publicapi.cpp @@ -10,6 +10,7 @@ #include <QtTest/QtTest> #include <QtWebEngineQuick/QQuickWebEngineProfile> #include <QtWebEngineCore/QWebEngineCertificateError> +#include <QtWebEngineCore/QWebEngineDesktopMediaRequest> #include <QtWebEngineCore/QWebEngineFileSystemAccessRequest> #include <QtWebEngineCore/QWebEngineFindTextResult> #include <QtWebEngineCore/QWebEngineFullScreenRequest> @@ -23,6 +24,7 @@ #include <QtWebEngineCore/QWebEngineDownloadRequest> #include <QtWebEngineCore/QWebEngineScript> #include <QtWebEngineCore/QWebEngineLoadingInfo> +#include <QtWebEngineCore/QWebEngineWebAuthUxRequest> #include <private/qquickwebengineview_p.h> #include <private/qquickwebengineaction_p.h> #include <private/qquickwebengineclientcertificateselection_p.h> @@ -61,18 +63,24 @@ static const QList<const QMetaObject *> typesToCheck = QList<const QMetaObject * << &QQuickWebEngineTooltipRequest::staticMetaObject << &QWebEngineContextMenuRequest::staticMetaObject << &QWebEngineCertificateError::staticMetaObject + << &QWebEngineDesktopMediaRequest::staticMetaObject << &QWebEngineFileSystemAccessRequest::staticMetaObject << &QWebEngineFindTextResult::staticMetaObject << &QWebEngineLoadingInfo::staticMetaObject + << &QAbstractListModel::staticMetaObject << &QWebEngineNavigationRequest::staticMetaObject << &QWebEngineNewWindowRequest::staticMetaObject << &QWebEngineNotification::staticMetaObject << &QWebEngineQuotaRequest::staticMetaObject << &QWebEngineRegisterProtocolHandlerRequest::staticMetaObject << &QQuickWebEngineTouchSelectionMenuRequest::staticMetaObject + << &QWebEngineWebAuthUxRequest::staticMetaObject + << &QWebEngineWebAuthPinRequest::staticMetaObject ; -static QList<QMetaEnum> knownEnumNames = QList<QMetaEnum>(); +static QList<QMetaEnum> knownEnumNames = QList<QMetaEnum>() + << QWebEngineDownloadRequest::staticMetaObject.enumerator(QWebEngineDownloadRequest::staticMetaObject.indexOfEnumerator("SavePageFormat")) + ; static const QStringList hardcodedTypes = QStringList() << "QJSValue" @@ -84,7 +92,8 @@ static const QStringList hardcodedTypes = QStringList() << "QWebEngineCookieStore*" << "Qt::LayoutDirection" << "QQuickWebEngineScriptCollection*" - << "QQmlComponent*"; + << "QQmlComponent*" + << "QMultiMap<QByteArray,QByteArray>"; static const QStringList expectedAPI = QStringList() << "QQuickWebEngineAction.text --> QString" @@ -261,6 +270,11 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineTooltipRequest.text --> QString" << "QQuickWebEngineTooltipRequest.type --> QQuickWebEngineTooltipRequest::RequestType" << "QQuickWebEngineTooltipRequest.accepted --> bool" + << "QWebEngineDesktopMediaRequest.screensModel --> QAbstractListModel*" + << "QWebEngineDesktopMediaRequest.windowsModel --> QAbstractListModel*" + << "QWebEngineDesktopMediaRequest.selectScreen(QModelIndex) --> void" + << "QWebEngineDesktopMediaRequest.selectWindow(QModelIndex) --> void" + << "QWebEngineDesktopMediaRequest.cancel() --> void" << "QWebEngineFullScreenRequest.accept() --> void" << "QWebEngineFullScreenRequest.origin --> QUrl" << "QWebEngineFullScreenRequest.reject() --> void" @@ -293,6 +307,7 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineJavaScriptDialogRequest.title --> QString" << "QQuickWebEngineJavaScriptDialogRequest.type --> QQuickWebEngineJavaScriptDialogRequest::DialogType" << "QWebEngineLoadingInfo.errorCode --> int" + << "QWebEngineLoadingInfo.responseHeaders --> QMultiMap<QByteArray,QByteArray>" << "QWebEngineLoadingInfo.errorDomain --> QWebEngineLoadingInfo::ErrorDomain" << "QWebEngineLoadingInfo.errorString --> QString" << "QWebEngineLoadingInfo.status --> QWebEngineLoadingInfo::LoadStatus" @@ -313,6 +328,7 @@ static const QStringList expectedAPI = QStringList() << "QWebEngineNavigationRequest.action --> QWebEngineNavigationRequest::NavigationRequestAction" << "QWebEngineNavigationRequest.actionChanged() --> void" << "QWebEngineNavigationRequest.isMainFrame --> bool" + << "QWebEngineNavigationRequest.hasFormData --> bool" << "QWebEngineNavigationRequest.navigationType --> QWebEngineNavigationRequest::NavigationType" << "QWebEngineNavigationRequest.url --> QUrl" << "QWebEngineNavigationRequest.AcceptRequest --> NavigationRequestAction" @@ -344,6 +360,7 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineProfile.cachePath --> QString" << "QQuickWebEngineProfile.cachePathChanged() --> void" << "QQuickWebEngineProfile.clearHttpCache() --> void" + << "QQuickWebEngineProfile.clearHttpCacheCompleted() --> void" << "QQuickWebEngineProfile.downloadFinished(QQuickWebEngineDownloadRequest*) --> void" << "QQuickWebEngineProfile.downloadRequested(QQuickWebEngineDownloadRequest*) --> void" << "QQuickWebEngineProfile.downloadPath --> QString" @@ -363,6 +380,8 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineProfile.persistentCookiesPolicyChanged() --> void" << "QQuickWebEngineProfile.persistentStoragePath --> QString" << "QQuickWebEngineProfile.persistentStoragePathChanged() --> void" + << "QQuickWebEngineProfile.isPushServiceEnabled --> bool" + << "QQuickWebEngineProfile.pushServiceEnabledChanged() --> void" << "QQuickWebEngineProfile.spellCheckEnabled --> bool" << "QQuickWebEngineProfile.spellCheckEnabledChanged() --> void" << "QQuickWebEngineProfile.spellCheckLanguages --> QStringList" @@ -391,6 +410,10 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineSettings.dnsPrefetchEnabledChanged() --> void" << "QQuickWebEngineSettings.errorPageEnabled --> bool" << "QQuickWebEngineSettings.errorPageEnabledChanged() --> void" + << "QQuickWebEngineSettings.forceDarkMode --> bool" + << "QQuickWebEngineSettings.forceDarkModeChanged() --> void" + << "QQuickWebEngineSettings.scrollAnimatorEnabled --> bool" + << "QQuickWebEngineSettings.scrollAnimatorEnabledChanged() --> void" << "QQuickWebEngineSettings.focusOnNavigationEnabled --> bool" << "QQuickWebEngineSettings.focusOnNavigationEnabledChanged() --> void" << "QQuickWebEngineSettings.fullScreenSupportEnabled --> bool" @@ -437,6 +460,8 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineSettings.webGLEnabledChanged() --> void" << "QQuickWebEngineSettings.webRTCPublicInterfacesOnly --> bool" << "QQuickWebEngineSettings.webRTCPublicInterfacesOnlyChanged() --> void" + << "QQuickWebEngineSettings.readingFromCanvasEnabled --> bool" + << "QQuickWebEngineSettings.readingFromCanvasEnabledChanged() --> void" << "QQuickWebEngineSingleton.defaultProfile --> QQuickWebEngineProfile*" << "QQuickWebEngineSingleton.settings --> QQuickWebEngineSettings*" << "QQuickWebEngineSingleton.script() --> QWebEngineScript" @@ -461,7 +486,6 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.action(WebAction) --> QQuickWebEngineAction*" << "QQuickWebEngineView.A0 --> PrintedPageSizeId" << "QQuickWebEngineView.A1 --> PrintedPageSizeId" - << "QQuickWebEngineView.A10 --> PrintedPageSizeId" << "QQuickWebEngineView.A2 --> PrintedPageSizeId" << "QQuickWebEngineView.A3 --> PrintedPageSizeId" << "QQuickWebEngineView.A3Extra --> PrintedPageSizeId" @@ -475,6 +499,7 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.A7 --> PrintedPageSizeId" << "QQuickWebEngineView.A8 --> PrintedPageSizeId" << "QQuickWebEngineView.A9 --> PrintedPageSizeId" + << "QQuickWebEngineView.A10 --> PrintedPageSizeId" << "QQuickWebEngineView.AbnormalTerminationStatus --> RenderProcessTerminationStatus" << "QQuickWebEngineView.AlignCenter --> WebAction" << "QQuickWebEngineView.AlignJustified --> WebAction" @@ -492,7 +517,6 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.ArchE --> PrintedPageSizeId" << "QQuickWebEngineView.B0 --> PrintedPageSizeId" << "QQuickWebEngineView.B1 --> PrintedPageSizeId" - << "QQuickWebEngineView.B10 --> PrintedPageSizeId" << "QQuickWebEngineView.B2 --> PrintedPageSizeId" << "QQuickWebEngineView.B3 --> PrintedPageSizeId" << "QQuickWebEngineView.B4 --> PrintedPageSizeId" @@ -502,9 +526,13 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.B7 --> PrintedPageSizeId" << "QQuickWebEngineView.B8 --> PrintedPageSizeId" << "QQuickWebEngineView.B9 --> PrintedPageSizeId" + << "QQuickWebEngineView.B10 --> PrintedPageSizeId" << "QQuickWebEngineView.Back --> WebAction" << "QQuickWebEngineView.C5E --> PrintedPageSizeId" << "QQuickWebEngineView.CertificateErrorDomain --> ErrorDomain" + << "QQuickWebEngineView.ChangeTextDirectionLTR --> WebAction" + << "QQuickWebEngineView.ChangeTextDirectionRTL --> WebAction" + << "QQuickWebEngineView.ClipboardReadWrite --> Feature" << "QQuickWebEngineView.Comm10E --> PrintedPageSizeId" << "QQuickWebEngineView.ConnectionErrorDomain --> ErrorDomain" << "QQuickWebEngineView.Copy --> WebAction" @@ -617,11 +645,10 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.LoadStartedStatus --> LoadStatus" << "QQuickWebEngineView.LoadStoppedStatus --> LoadStatus" << "QQuickWebEngineView.LoadSucceededStatus --> LoadStatus" + << "QQuickWebEngineView.LocalFontsAccess --> Feature" << "QQuickWebEngineView.MediaAudioCapture --> Feature" << "QQuickWebEngineView.MediaAudioVideoCapture --> Feature" << "QQuickWebEngineView.MediaVideoCapture --> Feature" - << "QQuickWebEngineView.NPageSize --> PrintedPageSizeId" - << "QQuickWebEngineView.NPaperSize --> PrintedPageSizeId" << "QQuickWebEngineView.NoErrorDomain --> ErrorDomain" << "QQuickWebEngineView.Notifications --> Feature" << "QQuickWebEngineView.NoWebAction --> WebAction" @@ -661,6 +688,7 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.ToggleUnderline --> WebAction" << "QQuickWebEngineView.Undo --> WebAction" << "QQuickWebEngineView.Unselect --> WebAction" + << "QQuickWebEngineView.OpenLinkInNewBackgroundTab --> WebAction" << "QQuickWebEngineView.ViewSource --> WebAction" << "QQuickWebEngineView.WarningMessageLevel --> JavaScriptConsoleMessageLevel" << "QQuickWebEngineView.WebActionCount --> WebAction" @@ -680,9 +708,11 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.contentsSize --> QSizeF" << "QQuickWebEngineView.contentsSizeChanged(QSizeF) --> void" << "QQuickWebEngineView.contextMenuRequested(QWebEngineContextMenuRequest*) --> void" + << "QQuickWebEngineView.desktopMediaRequested(QWebEngineDesktopMediaRequest) --> void" + << "QQuickWebEngineView.devToolsId --> QString" << "QQuickWebEngineView.devToolsView --> QQuickWebEngineView*" << "QQuickWebEngineView.devToolsViewChanged() --> void" - << "QQuickWebEngineView.featurePermissionRequested(QUrl,Feature) --> void" + << "QQuickWebEngineView.featurePermissionRequested(QUrl,QQuickWebEngineView::Feature) --> void" << "QQuickWebEngineView.fileDialogRequested(QQuickWebEngineFileDialogRequest*) --> void" << "QQuickWebEngineView.fileSystemAccessRequested(QWebEngineFileSystemAccessRequest) --> void" << "QQuickWebEngineView.findText(QString) --> void" @@ -695,7 +725,7 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.goBack() --> void" << "QQuickWebEngineView.goBackOrForward(int) --> void" << "QQuickWebEngineView.goForward() --> void" - << "QQuickWebEngineView.grantFeaturePermission(QUrl,Feature,bool) --> void" + << "QQuickWebEngineView.grantFeaturePermission(QUrl,QQuickWebEngineView::Feature,bool) --> void" << "QQuickWebEngineView.history --> QWebEngineHistory*" << "QQuickWebEngineView.icon --> QUrl" << "QQuickWebEngineView.iconChanged() --> void" @@ -703,10 +733,10 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.inspectedViewChanged() --> void" << "QQuickWebEngineView.isFullScreen --> bool" << "QQuickWebEngineView.isFullScreenChanged() --> void" - << "QQuickWebEngineView.javaScriptConsoleMessage(JavaScriptConsoleMessageLevel,QString,int,QString) --> void" + << "QQuickWebEngineView.javaScriptConsoleMessage(QQuickWebEngineView::JavaScriptConsoleMessageLevel,QString,int,QString) --> void" << "QQuickWebEngineView.javaScriptDialogRequested(QQuickWebEngineJavaScriptDialogRequest*) --> void" << "QQuickWebEngineView.lifecycleState --> QQuickWebEngineView::LifecycleState" - << "QQuickWebEngineView.lifecycleStateChanged(LifecycleState) --> void" + << "QQuickWebEngineView.lifecycleStateChanged(QQuickWebEngineView::LifecycleState) --> void" << "QQuickWebEngineView.linkHovered(QUrl) --> void" << "QQuickWebEngineView.loadHtml(QString) --> void" << "QQuickWebEngineView.loadHtml(QString,QUrl) --> void" @@ -745,11 +775,11 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.renderProcessPid --> qlonglong" << "QQuickWebEngineView.renderProcessPidChanged(qlonglong) --> void" << "QQuickWebEngineView.recommendedState --> QQuickWebEngineView::LifecycleState" - << "QQuickWebEngineView.recommendedStateChanged(LifecycleState) --> void" + << "QQuickWebEngineView.recommendedStateChanged(QQuickWebEngineView::LifecycleState) --> void" << "QQuickWebEngineView.registerProtocolHandlerRequested(QWebEngineRegisterProtocolHandlerRequest) --> void" << "QQuickWebEngineView.reload() --> void" << "QQuickWebEngineView.reloadAndBypassCache() --> void" - << "QQuickWebEngineView.renderProcessTerminated(RenderProcessTerminationStatus,int) --> void" + << "QQuickWebEngineView.renderProcessTerminated(QQuickWebEngineView::RenderProcessTerminationStatus,int) --> void" << "QQuickWebEngineView.replaceMisspelledWord(QString) --> void" << "QQuickWebEngineView.runJavaScript(QString) --> void" << "QQuickWebEngineView.runJavaScript(QString,QJSValue) --> void" @@ -781,6 +811,8 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.zoomFactor --> double" << "QQuickWebEngineView.zoomFactorChanged(double) --> void" << "QQuickWebEngineView.acceptAsNewWindow(QWebEngineNewWindowRequest*) --> void" + << "QQuickWebEngineView.save(QString) --> void" + << "QQuickWebEngineView.save(QString,QWebEngineDownloadRequest::SavePageFormat) --> void" << "QWebEngineQuotaRequest.accept() --> void" << "QWebEngineQuotaRequest.origin --> QUrl" << "QWebEngineQuotaRequest.reject() --> void" @@ -799,6 +831,55 @@ static const QStringList expectedAPI = QStringList() << "QWebEngineNotification.click() --> void" << "QWebEngineNotification.close() --> void" << "QWebEngineNotification.closed() --> void" + << "QQuickWebEngineView.webAuthUxRequested(QWebEngineWebAuthUxRequest*) --> void" + << "QWebEngineWebAuthUxRequest.WebAuthUxState.NotStarted --> WebAuthUxState" + << "QWebEngineWebAuthUxRequest.WebAuthUxState.SelectAccount --> WebAuthUxState" + << "QWebEngineWebAuthUxRequest.WebAuthUxState.CollectPin --> WebAuthUxState" + << "QWebEngineWebAuthUxRequest.WebAuthUxState.FinishTokenCollection --> WebAuthUxState" + << "QWebEngineWebAuthUxRequest.WebAuthUxState.RequestFailed --> WebAuthUxState" + << "QWebEngineWebAuthUxRequest.WebAuthUxState.Cancelled --> WebAuthUxState" + << "QWebEngineWebAuthUxRequest.WebAuthUxState.Completed --> WebAuthUxState" + << "QWebEngineWebAuthUxRequest.PinEntryReason.Set --> PinEntryReason" + << "QWebEngineWebAuthUxRequest.PinEntryReason.Change --> PinEntryReason" + << "QWebEngineWebAuthUxRequest.PinEntryReason.Challenge --> PinEntryReason" + << "QWebEngineWebAuthUxRequest.PinEntryError.NoError --> PinEntryError" + << "QWebEngineWebAuthUxRequest.PinEntryError.InternalUvLocked --> PinEntryError" + << "QWebEngineWebAuthUxRequest.PinEntryError.WrongPin --> PinEntryError" + << "QWebEngineWebAuthUxRequest.PinEntryError.TooShort --> PinEntryError" + << "QWebEngineWebAuthUxRequest.PinEntryError.InvalidCharacters --> PinEntryError" + << "QWebEngineWebAuthUxRequest.PinEntryError.SameAsCurrentPin --> PinEntryError" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.Timeout --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.KeyNotRegistered --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.KeyAlreadyRegistered --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.SoftPinBlock --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.HardPinBlock --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.AuthenticatorRemovedDuringPinEntry --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.AuthenticatorMissingResidentKeys --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.AuthenticatorMissingUserVerification --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.AuthenticatorMissingLargeBlob --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.NoCommonAlgorithms --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.StorageFull --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.UserConsentDenied --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.WinUserCancelled --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.userNames --> QStringList" + << "QWebEngineWebAuthUxRequest.state --> QWebEngineWebAuthUxRequest::WebAuthUxState" + << "QWebEngineWebAuthUxRequest.relyingPartyId --> QString" + << "QWebEngineWebAuthUxRequest.pinRequest --> QWebEngineWebAuthPinRequest" + << "QWebEngineWebAuthUxRequest.requestFailureReason --> QWebEngineWebAuthUxRequest::RequestFailureReason" + << "QWebEngineWebAuthUxRequest.stateChanged(QWebEngineWebAuthUxRequest::WebAuthUxState) --> void" + << "QWebEngineWebAuthUxRequest.cancel() --> void" + << "QWebEngineWebAuthUxRequest.retry() --> void" + << "QWebEngineWebAuthUxRequest.setSelectedAccount(QString) --> void" + << "QWebEngineWebAuthUxRequest.setPin(QString) --> void" + << "QWebEngineWebAuthPinRequest.reason --> QWebEngineWebAuthUxRequest::PinEntryReason" + << "QWebEngineWebAuthPinRequest.error --> QWebEngineWebAuthUxRequest::PinEntryError" + << "QWebEngineWebAuthPinRequest.minPinLength --> int" + << "QWebEngineWebAuthPinRequest.remainingAttempts --> int" + << "QQuickWebEngineSettings.AllowImageAnimation --> ImageAnimationPolicy" + << "QQuickWebEngineSettings.AnimateImageOnce --> ImageAnimationPolicy" + << "QQuickWebEngineSettings.DisallowImageAnimation --> ImageAnimationPolicy" + << "QQuickWebEngineSettings.imageAnimationPolicy --> QQuickWebEngineSettings::ImageAnimationPolicy" + << "QQuickWebEngineSettings.imageAnimationPolicyChanged() --> void" ; static bool isCheckedEnum(QMetaType t) @@ -892,12 +973,12 @@ void tst_publicapi::publicAPI() // Uncomment to print the actual API. // QStringList sortedAPI(actualAPI); // std::sort(sortedAPI.begin(), sortedAPI.end()); - // for (const QString &actual : qAsConst(sortedAPI)) + // for (const QString &actual : std::as_const(sortedAPI)) // printf(" << \"%s\"\n", qPrintable(actual)); bool apiMatch = true; // Make sure that nothing slips in the public API unintentionally. - for (const QString &actual : qAsConst(actualAPI)) { + for (const QString &actual : std::as_const(actualAPI)) { if (!expectedAPI.contains(actual)) { qWarning("Expected list is not up-to-date: %ls", qUtf16Printable(actual)); apiMatch = false; diff --git a/tests/auto/quick/qmltests/BLACKLIST b/tests/auto/quick/qmltests/BLACKLIST index 2f8fa5e66..fc8f9f0d8 100644 --- a/tests/auto/quick/qmltests/BLACKLIST +++ b/tests/auto/quick/qmltests/BLACKLIST @@ -9,3 +9,4 @@ macos [CertificateError::test_error] * + diff --git a/tests/auto/quick/qmltests/CMakeLists.txt b/tests/auto/quick/qmltests/CMakeLists.txt index 3e520ee78..daae6d60d 100644 --- a/tests/auto/quick/qmltests/CMakeLists.txt +++ b/tests/auto/quick/qmltests/CMakeLists.txt @@ -1,5 +1,5 @@ -# Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause include(../../httpserver/httpserver.cmake) include(../../util/util.cmake) @@ -25,15 +25,17 @@ set(testList tst_datalist.qml tst_desktopBehaviorLoadHtml.qml tst_download.qml + tst_dragHandlerUnderView.qml tst_favicon.qml tst_faviconDatabase.qml tst_filePicker.qml + tst_filesystem.qml tst_findText.qml tst_focusOnNavigation.qml tst_fullScreenRequest.qml - tst_geopermission.qml tst_getUserMedia.qml tst_inputMethod.qml + tst_inputTextDirection.qml tst_javaScriptDialogs.qml tst_keyboardEvents.qml tst_keyboardModifierMapping.qml @@ -58,6 +60,7 @@ set(testList tst_userScripts.qml tst_userScriptCollection.qml tst_viewSource.qml + tst_save.qml ) if(QT_FEATURE_webengine_webchannel) @@ -68,6 +71,10 @@ if(QT_FEATURE_ssl) list(APPEND testList tst_certificateError.qml) endif() +if (NOT APPLE) + list(APPEND testList tst_geopermission.qml) +endif() + set(content "") foreach(test ${testList}) set(contents "${contents}${CMAKE_CURRENT_LIST_DIR}/data/${test}\n") diff --git a/tests/auto/quick/qmltests/data/TestWebEngineView.qml b/tests/auto/quick/qmltests/data/TestWebEngineView.qml index 7f8c271d0..415985471 100644 --- a/tests/auto/quick/qmltests/data/TestWebEngineView.qml +++ b/tests/auto/quick/qmltests/data/TestWebEngineView.qml @@ -40,7 +40,7 @@ WebEngineView { } function _waitFor(predicate, timeout) { if (timeout === undefined) - timeout = 12000; + timeout = 30000; var i = 0 while (i < timeout && !predicate()) { testResult.wait(50) diff --git a/tests/auto/quick/qmltests/data/filesystemapi.html b/tests/auto/quick/qmltests/data/filesystemapi.html new file mode 100644 index 000000000..ab1a33e4d --- /dev/null +++ b/tests/auto/quick/qmltests/data/filesystemapi.html @@ -0,0 +1,66 @@ +<html> +<head> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<title> Failed to Upload </title> +</script> +</head> + +<body> +<button>Request File Picker</button> +<script> + async function handleDirectoryEntry( dirHandle, out ) { + for await (const entry of dirHandle.values()) { + if (entry.kind === "file"){ + const file = await entry.getFile(); + out[ file.name ] = file; + } + if (entry.kind === "directory") { + const newHandle = await dirHandle.getDirectoryHandle( entry.name, { create: false } ); + const newOut = out[ entry.name ] = {}; + await handleDirectoryEntry( newHandle, newOut ); + } + } + } + const button = document.querySelector('button'); + button.addEventListener('click', async function() { + switch(window.dialogType) { + case "savePicker": + const saveFileHandle = await window.showSaveFilePicker(); + const writable = await saveFileHandle.createWritable(); + await writable.write(new Blob(['TEST_CONTENT'])); + await writable.close(); + console.log("TEST:DONE") + break; + case "filePicker": + let [openFileHandle] = await window.showOpenFilePicker(); + const options = {}; + options.mode = 'readwrite' + await openFileHandle.requestPermission(options) + const file = await openFileHandle.getFile(); + const contents = await file.text(); + console.log("TEST:" + contents) + console.log("TEST:DONE") + break; + case "directoryPicker": + console.log("start") + const dirHandle = await window.showDirectoryPicker(); + for await (const entry of dirHandle.values()) { + if (entry.kind === "file"){ + continue + } + if (entry.kind === "directory") { + console.log("TEST:" + entry.name) + } + } + console.log("TEST:DONE") + break; + default: + } + }); + window.onload = function() { + window.dialogType = window.location.href.split('=')[1]; + document.querySelector('button').focus() + } +</script> +</body> +</html> diff --git a/tests/auto/quick/qmltests/data/test4.html b/tests/auto/quick/qmltests/data/test4.html index cf5708994..82830668a 100644 --- a/tests/auto/quick/qmltests/data/test4.html +++ b/tests/auto/quick/qmltests/data/test4.html @@ -9,7 +9,6 @@ font-size: 50px; } </style> - <meta name="viewport" content="initial-scale=2.0"/> </head> <body> <button onclick="scrollWin()" id="scroll">Click me to scroll!</button><br><br> diff --git a/tests/auto/quick/qmltests/data/tst_action.qml b/tests/auto/quick/qmltests/data/tst_action.qml index abe5f71b0..9e49c2dbf 100644 --- a/tests/auto/quick/qmltests/data/tst_action.qml +++ b/tests/auto/quick/qmltests/data/tst_action.qml @@ -65,7 +65,9 @@ TestWebEngineView { { webAction: WebEngineView.Indent, text: "&Indent", iconName: "", enabled: true }, { webAction: WebEngineView.Outdent, text: "&Outdent", iconName: "", enabled: true }, { webAction: WebEngineView.InsertOrderedList, text: "Insert &Ordered List", iconName: "", enabled: true }, - { webAction: WebEngineView.InsertUnorderedList, text: "Insert &Unordered List", iconName: "", enabled: true } + { webAction: WebEngineView.InsertUnorderedList, text: "Insert &Unordered List", iconName: "", enabled: true }, + { webAction: WebEngineView.ChangeTextDirectionLTR, text: "Change text direction left to right", iconName: "", enabled: true }, + { webAction: WebEngineView.ChangeTextDirectionRTL, text: "Change text direction right to left", iconName: "", enabled: true } ]; } diff --git a/tests/auto/quick/qmltests/data/tst_dragHandlerUnderView.qml b/tests/auto/quick/qmltests/data/tst_dragHandlerUnderView.qml new file mode 100644 index 000000000..c22bd44c2 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_dragHandlerUnderView.qml @@ -0,0 +1,67 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtTest +import QtWebEngine + +Item { + id: parentItem + width: 400 + height: 300 + + Rectangle { + id: draggableDownUnder + color: "wheat" + width: 350 + height: 250 + + DragHandler { id: dragHandler } + } + + TestWebEngineView { + id: webEngineView + width: 300 + height: 250 + + property var testUrl: Qt.resolvedUrl("test4.html") + + SignalSpy { + id: scrollPositionSpy + target: webEngineView + signalName: "onScrollPositionChanged" + } + + SignalSpy { + id: dragActiveSpy + target: dragHandler + signalName: "activeChanged" + } + + TestCase { + id: testCase + name: "KeepMouseGrabDuringScrolling" + when: windowShown + + function test_scroll() { + webEngineView.url = Qt.resolvedUrl("test4.html"); + verify(webEngineView.waitForLoadSucceeded()); + + mousePress(webEngineView, 295, 20); + mouseMove(webEngineView, 295, 200); + mouseRelease(webEngineView, 295, 200); + + // WebEngineView scrolled if the scrollbar was visible. + // But on macOS, the scrollbar is hidden, so text gets selected. + tryVerify(function() { + return (scrollPositionSpy.count === 1 && webEngineView.scrollPosition.y > 100) + || webEngineView.getTextSelection().length > 0; + }); + + // DragHandler didn't take over and drag + compare(dragActiveSpy.count, 0); + compare(draggableDownUnder.y, 0); + } + } + } +} diff --git a/tests/auto/quick/qmltests/data/tst_faviconDatabase.qml b/tests/auto/quick/qmltests/data/tst_faviconDatabase.qml index 774708af0..284390619 100644 --- a/tests/auto/quick/qmltests/data/tst_faviconDatabase.qml +++ b/tests/auto/quick/qmltests/data/tst_faviconDatabase.qml @@ -85,6 +85,9 @@ TestWebEngineView { function test_iconDatabase(row) { + if (Screen.devicePixelRatio !== 1.0) + skip("This test is not supported on High DPI screens."); + webEngineView.profile = row.profile; compare(iconChangedSpy.count, 0); @@ -129,6 +132,9 @@ TestWebEngineView { function test_iconDatabaseMultiView() { + if (Screen.devicePixelRatio !== 1.0) + skip("This test is not supported on High DPI screens."); + var pixel; var faviconImage = Qt.createQmlObject(" diff --git a/tests/auto/quick/qmltests/data/tst_filePicker.qml b/tests/auto/quick/qmltests/data/tst_filePicker.qml index 2404efd2d..a7b59b2e9 100644 --- a/tests/auto/quick/qmltests/data/tst_filePicker.qml +++ b/tests/auto/quick/qmltests/data/tst_filePicker.qml @@ -11,7 +11,6 @@ TestWebEngineView { id: webEngineView width: 400 height: 300 - property var titleChanges: [] function driveLetter() { if (Qt.platform.os !== "windows") @@ -30,8 +29,6 @@ TestWebEngineView { signalName: "renderProcessTerminated" } - onTitleChanged: { titleChanges.push(webEngineView.title) } - TestCase { id: testCase name: "WebEngineViewSingleFileUpload" @@ -44,7 +41,6 @@ TestWebEngineView { FilePickerParams.nameFilters = [] titleSpy.clear() terminationSpy.clear() - titleChanges = [] } function cleanup() { @@ -87,10 +83,10 @@ TestWebEngineView { keyClick(Qt.Key_Enter); // Focus is on the button. Open FileDialog. tryCompare(FilePickerParams, "filePickerOpened", true); + tryCompare(webEngineView, "title", row.expected); webEngineView.url = Qt.resolvedUrl("about:blank"); verify(webEngineView.waitForLoadSucceeded()); tryCompare(webEngineView, "title", "about:blank"); - compare(titleChanges[titleChanges.length-2], row.expected); // Custom dialog @@ -98,7 +94,7 @@ TestWebEngineView { function acceptedFileHandler(request) { request.accepted = true; - request.dialogAccept(row.input); + request.dialogAccept([row.input]); finished = true; } @@ -108,10 +104,10 @@ TestWebEngineView { keyClick(Qt.Key_Enter); // Focus is on the button. Open FileDialog. tryVerify(function() { return finished; }); + tryCompare(webEngineView, "title", row.expected); webEngineView.url = Qt.resolvedUrl("about:blank"); verify(webEngineView.waitForLoadSucceeded()); tryCompare(webEngineView, "title", "about:blank"); - compare(titleChanges[titleChanges.length-2], row.expected); webEngineView.fileDialogRequested.disconnect(acceptedFileHandler); } @@ -146,7 +142,7 @@ TestWebEngineView { FilePickerParams.selectedFilesUrl.push(Qt.resolvedUrl("../data")) keyClick(Qt.Key_Enter) // Focus is on the button. Open FileDialog. - tryCompare(FilePickerParams, "filePickerOpened", true) + tryCompare(FilePickerParams, "directoryPickerOpened", true) // Check that the title is a file list (eg. "test1.html,test2.html") tryVerify(function() { return webEngineView.title.match("^([^,]+,)+[^,]+$"); }) @@ -242,10 +238,10 @@ TestWebEngineView { keyClick(Qt.Key_Enter); // Focus is on the button. Open FileDialog. tryCompare(FilePickerParams, "filePickerOpened", true); + tryCompare(webEngineView, "title", row.expected); webEngineView.url = Qt.resolvedUrl("about:blank"); verify(webEngineView.waitForLoadSucceeded()); tryCompare(webEngineView, "title", "about:blank"); - compare(titleChanges[titleChanges.length-2], row.expected); // Custom dialog @@ -253,7 +249,7 @@ TestWebEngineView { function acceptedFileHandler(request) { request.accepted = true; - request.dialogAccept(row.input); + request.dialogAccept([row.input]); finished = true; } @@ -263,10 +259,10 @@ TestWebEngineView { keyClick(Qt.Key_Enter); // Focus is on the button. Open FileDialog. tryVerify(function() { return finished; }); + tryCompare(webEngineView, "title", row.expected); webEngineView.url = Qt.resolvedUrl("about:blank"); verify(webEngineView.waitForLoadSucceeded()); tryCompare(webEngineView, "title", "about:blank"); - compare(titleChanges[titleChanges.length-2], row.expected); webEngineView.fileDialogRequested.disconnect(acceptedFileHandler); } diff --git a/tests/auto/quick/qmltests/data/tst_filesystem.qml b/tests/auto/quick/qmltests/data/tst_filesystem.qml new file mode 100644 index 000000000..fa0da4457 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_filesystem.qml @@ -0,0 +1,124 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtTest +import QtWebEngine +import Test.util +import "../../qmltests/data" +import "../mock-delegates/TestParams" + + +TestWebEngineView { + id: webEngineView + width: 400 + height: 300 + property var logs: [] + property bool accessRequested: false + property url file: tempDir.pathUrl('file.txt') + + onJavaScriptConsoleMessage: function(level, message, lineNumber, source) { + var pair = message.split(':'); + if (pair.length == 2 && pair[0] == "TEST") + logs.push(pair[1]); + } + + TempDir { id: tempDir } + + TestCase { + id: testCase + name: "FileSystemAPI" + when: windowShown + + function init() { + clearLog() + FilePickerParams.filePickerOpened = false + FilePickerParams.selectFiles = false + FilePickerParams.selectedFilesUrl = [] + FilePickerParams.nameFilters = [] + accessRequested = false; + } + + function cleanup() { + clearLog() + } + + function clearLog() { + logs = [] + } + + function logContainsDoneMarker() { + if (logs.indexOf("DONE") > -1) + return true + else + return false + } + + function result() { + return logs[0] + } + + function fileAccessRequest(request) { + testCase.verify(!accessRequested) + accessRequested = true + testCase.verify(request.filePath == file) + testCase.verify(request.accessFlags == WebEngineFileSystemAccessRequest.Write | WebEngineFileSystemAccessRequest.Read) + request.accept() + } + + function directoryAccessRequest(request) { + testCase.verify(!accessRequested) + accessRequested = true + testCase.verify(request.filePath == tempDir.pathUrl()) + testCase.verify(request.accessFlags == WebEngineFileSystemAccessRequest.Read) + request.accept() + } + + function test_saveFile() { + webEngineView.fileSystemAccessRequested.connect(fileAccessRequest); + webEngineView.url = Qt.resolvedUrl("filesystemapi.html?dialog=savePicker"); + verify(webEngineView.waitForLoadSucceeded()); + FilePickerParams.selectFiles = true; + FilePickerParams.selectedFilesUrl.push(file); + keyClick(Qt.Key_Enter); // Open SaveDialog. + tryCompare(FilePickerParams, "filePickerOpened", true); + tryVerify(logContainsDoneMarker,2000) + // write access for save dialogs is automatically granted + verify(!accessRequested) + webEngineView.fileSystemAccessRequested.disconnect(fileAccessRequest); + } + + function test_openFile() { + // first save the file before open + test_saveFile() + init() + webEngineView.fileSystemAccessRequested.connect(fileAccessRequest); + webEngineView.url = Qt.resolvedUrl("filesystemapi.html?dialog=filePicker"); + verify(webEngineView.waitForLoadSucceeded()); + FilePickerParams.selectFiles = true; + FilePickerParams.selectedFilesUrl.push(file); + keyClick(Qt.Key_Enter); // Open FileDialog. + tryCompare(FilePickerParams, "filePickerOpened", true); + tryVerify(logContainsDoneMarker,2000) + verify(logs.indexOf("TEST_CONTENT") > -1) + verify(accessRequested) + webEngineView.fileSystemAccessRequested.disconnect(fileAccessRequest); + } + + function test_selectDirectory() { + tempDir.createDirectory("TEST_DIR") + webEngineView.fileSystemAccessRequested.connect(directoryAccessRequest); + webEngineView.url = Qt.resolvedUrl("filesystemapi.html?dialog=directoryPicker"); + verify(webEngineView.waitForLoadSucceeded()) + FilePickerParams.selectFiles = true; + FilePickerParams.selectedFilesUrl.push(tempDir.pathUrl()); + keyClick(Qt.Key_Enter); // Open showDirectoryDialog. + tryCompare(FilePickerParams, "directoryPickerOpened", true); + tryVerify(logContainsDoneMarker,2000) + verify(logs.indexOf("TEST_DIR") > -1) + verify(accessRequested) + webEngineView.fileSystemAccessRequested.disconnect(directoryAccessRequest); + } + + } +} diff --git a/tests/auto/quick/qmltests/data/tst_findText.qml b/tests/auto/quick/qmltests/data/tst_findText.qml index 392ce5dca..597cff73e 100644 --- a/tests/auto/quick/qmltests/data/tst_findText.qml +++ b/tests/auto/quick/qmltests/data/tst_findText.qml @@ -206,8 +206,7 @@ TestWebEngineView { var listItemText = ''; for (var i = 0; i < 100000; ++i) - listItemText += "bla "; - listItemText = listItemText.trim(); + listItemText += "bla"; webEngineView.loadHtml( "<html><body>" + diff --git a/tests/auto/quick/qmltests/data/tst_inputTextDirection.qml b/tests/auto/quick/qmltests/data/tst_inputTextDirection.qml new file mode 100644 index 000000000..2141db4c8 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_inputTextDirection.qml @@ -0,0 +1,43 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtTest +import QtWebEngine + +TestWebEngineView { + id: webEngineView + width: 400 + height: 400 + + TestCase { + id: testCase + name: "WebEngineInputTextDirection" + when: windowShown + + function getInputTextDirection(element) { + var dir; + runJavaScript("document.getElementById('" + element + "').dir", function(result) { + dir = result; + }); + tryVerify(function() { return dir != undefined; }); + return dir; + } + + function test_changeInputTextDirection() { + webEngineView.loadHtml("<html><body><input type='text' id='textfield' value='some text'></body></html>"); + verify(webEngineView.waitForLoadSucceeded()); + setFocusToElement("textfield"); + + var rtlAction = webEngineView.action(WebEngineView.ChangeTextDirectionRTL); + verify(rtlAction); + rtlAction.trigger(); + compare(getInputTextDirection("textfield"), "rtl"); + + var ltrAction = webEngineView.action(WebEngineView.ChangeTextDirectionLTR); + verify(ltrAction); + ltrAction.trigger(); + compare(getInputTextDirection("textfield"), "ltr"); + } + } +} diff --git a/tests/auto/quick/qmltests/data/tst_newViewRequest.qml b/tests/auto/quick/qmltests/data/tst_newViewRequest.qml index a47862565..68350d107 100644 --- a/tests/auto/quick/qmltests/data/tst_newViewRequest.qml +++ b/tests/auto/quick/qmltests/data/tst_newViewRequest.qml @@ -98,11 +98,10 @@ TestWebEngineView { if (viewType === "dialog") { tryVerify(dialog.webEngineView.loadSucceeded) - compare(dialog.webEngineView.url, ""); + compare(dialog.webEngineView.url, Qt.url("about:blank")); dialog.destroy(); } - // https://chromium-review.googlesource.com/c/chromium/src/+/1300395 - compare(newViewRequest.requestedUrl, 'about:blank#blocked'); + compare(newViewRequest.requestedUrl, 'about:blank'); newViewRequestedSpy.clear(); // Open a page in a new dialog diff --git a/tests/auto/quick/qmltests/data/tst_runJavaScript.qml b/tests/auto/quick/qmltests/data/tst_runJavaScript.qml index 22e427ad4..f16cd9c41 100644 --- a/tests/auto/quick/qmltests/data/tst_runJavaScript.qml +++ b/tests/auto/quick/qmltests/data/tst_runJavaScript.qml @@ -34,8 +34,7 @@ TestWebEngineView { compare(result, testTitle2); callbackCalled = true; }); - wait(100); - verify(callbackCalled); + tryVerify(function() { return callbackCalled; }); } } } diff --git a/tests/auto/quick/qmltests/data/tst_save.qml b/tests/auto/quick/qmltests/data/tst_save.qml new file mode 100644 index 000000000..3289dbd8b --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_save.qml @@ -0,0 +1,185 @@ +import QtQuick +import QtTest +import QtWebEngine +import Test.util + +TestWebEngineView { + id: webEngineView + width: 200 + height: 200 + profile: testSaveProfile + + property url downloadUrl: "" + property int totalBytes: 0 + property int receivedBytes: 0 + property string downloadDir: "" + property string downloadFileName: "" + property bool isSavePageDownload: false + property var downloadState: [] + property int savePageFormat: WebEngineDownloadRequest.MimeHtmlSaveFormat; + property bool autoCancel: false + + TempDir { + id: tempDir + } + + SignalSpy { + id: downLoadRequestedSpy + target: testSaveProfile + signalName: "downloadRequested" + } + + SignalSpy { + id: downloadFinishedSpy + target: testSaveProfile + signalName: "downloadFinished" + } + + WebEngineProfile { + id: testSaveProfile + + onDownloadRequested: function(download) { + downloadState.push(download.state) + downloadUrl = download.url + savePageFormat = download.savePageFormat + downloadDir = download.downloadDirectory; + downloadFileName = download.downloadFileName + isSavePageDownload = download.isSavePageDownload + + if (autoCancel) + download.cancel() + } + onDownloadFinished: function(download) { + receivedBytes = download.receivedBytes + totalBytes = download.totalBytes + downloadState.push(download.state) + } + } + + TestCase { + name: "WebEngineViewSave" + + function verifyData() { + var isDataValid = false + webEngineView.runJavaScript("(function() {" + + "var title = document.title.toString();" + + "var body = document.body.innerText;" + + " return title === \"Test page 1\" && body.includes(\"Hello.\")" + + "})();", function(result) { + isDataValid = result; + }); + tryVerify(function() { return isDataValid }); + return isDataValid; + } + + function init() { + downLoadRequestedSpy.clear() + downloadFinishedSpy.clear() + totalBytes = 0 + receivedBytes = 0 + downloadDir = "" + downloadFileName = "" + isSavePageDownload = false + downloadState = [] + downloadUrl = "" + autoCancel = false + } + + function test_savePage_data() { + return [ + { tag: "SingleHtmlSaveFormat", savePageFormat: WebEngineDownloadRequest.SingleHtmlSaveFormat }, + { tag: "CompleteHtmlSaveFormat", savePageFormat: WebEngineDownloadRequest.CompleteHtmlSaveFormat }, + { tag: "MimeHtmlSaveFormat", savePageFormat: WebEngineDownloadRequest.MimeHtmlSaveFormat }, + ]; + } + + function test_savePage(row) { + var saveFormat = row.savePageFormat + + var fileDir = tempDir.path() + var fileName = "saved_page.html" + var filePath = fileDir + "/"+ fileName + + // load data to view + webEngineView.url = Qt.resolvedUrl("test1.html") + verify(webEngineView.waitForLoadSucceeded()) + verify(verifyData()) + + webEngineView.save(filePath, saveFormat) + downLoadRequestedSpy.wait() + compare(downLoadRequestedSpy.count, 1) + compare(downloadUrl, webEngineView.url) + compare(savePageFormat, saveFormat) + compare(downloadDir, fileDir) + compare(downloadFileName, fileName) + compare(isSavePageDownload, true) + compare(downloadState[0], WebEngineDownloadRequest.DownloadInProgress) + downloadFinishedSpy.wait() + compare(downloadFinishedSpy.count, 1) + compare(totalBytes, receivedBytes) + compare(downloadState[1], WebEngineDownloadRequest.DownloadCompleted) + + // load some other data + webEngineView.url = Qt.resolvedUrl("about:blank") + verify(webEngineView.waitForLoadSucceeded()) + + // load save file to view + webEngineView.url = Qt.resolvedUrl(filePath) + verify(webEngineView.waitForLoadSucceeded()) + verify(verifyData()) + } + + function test_saveImage_data() { + return [ + { tag: "Auto accept", autoCancel: false }, + { tag: "Cancel", autoCancel: true }, + ]; + } + + function test_saveImage(row) { + autoCancel = row.autoCancel + + var fileDir = tempDir.path() + var fileName = "favicon.png" + var filePath = fileDir + "/"+ fileName + + // Load an image + webEngineView.url = Qt.resolvedUrl("icons/favicon.png") + verify(webEngineView.waitForLoadSucceeded()) + + webEngineView.save(filePath) + downLoadRequestedSpy.wait() + compare(downLoadRequestedSpy.count, 1) + compare(downloadUrl, webEngineView.url) + compare(downloadDir, fileDir) + compare(downloadFileName, fileName) + compare(isSavePageDownload, true) + compare(downloadState[0], WebEngineDownloadRequest.DownloadInProgress) + downloadFinishedSpy.wait() + compare(downloadFinishedSpy.count, 1) + if (autoCancel) { + compare(receivedBytes, 0) + compare(downloadState[1], WebEngineDownloadRequest.DownloadCancelled) + } else { + compare(totalBytes, receivedBytes) + compare(downloadState[1], WebEngineDownloadRequest.DownloadCompleted) + } + } + + function test_saveWebAction() { + // Load an image + webEngineView.url = Qt.resolvedUrl("icons/favicon.png") + verify(webEngineView.waitForLoadSucceeded()) + + // Saving without specifying path shouldn't be auto accepted + webEngineView.triggerWebAction(WebEngineView.SavePage) + downLoadRequestedSpy.wait() + compare(downLoadRequestedSpy.count, 1) + compare(downloadUrl, webEngineView.url) + compare(isSavePageDownload, true) + // The initial download request starts from DownloadRequested state, + // which means it wasn't automatically accepted. + compare(downloadState[0], WebEngineDownloadRequest.DownloadRequested) + } + } +} diff --git a/tests/auto/quick/qmltests/data/tst_scrollPosition.qml b/tests/auto/quick/qmltests/data/tst_scrollPosition.qml index e9c72ab7d..cc7d15e4c 100644 --- a/tests/auto/quick/qmltests/data/tst_scrollPosition.qml +++ b/tests/auto/quick/qmltests/data/tst_scrollPosition.qml @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick -import QtQuick.Window import QtTest import QtWebEngine @@ -36,7 +35,7 @@ TestWebEngineView { tryCompare(scrollPositionSpy, "count", 1); compare(webEngineView.scrollPosition.x, 0); - compare(webEngineView.scrollPosition.y, 600 * Screen.devicePixelRatio); + compare(webEngineView.scrollPosition.y, 600); } function test_scrollPositionAfterReload() { @@ -49,13 +48,13 @@ TestWebEngineView { // Wait for proper scroll position change otherwise we cannot expect // the new y position after reload. tryCompare(webEngineView.scrollPosition, "x", 0); - tryCompare(webEngineView.scrollPosition, "y", 600 * Screen.devicePixelRatio); + tryCompare(webEngineView.scrollPosition, "y", 600); webEngineView.reload(); verify(webEngineView.waitForLoadSucceeded()); tryCompare(webEngineView.scrollPosition, "x", 0); - tryCompare(webEngineView.scrollPosition, "y", 600 * Screen.devicePixelRatio); + tryCompare(webEngineView.scrollPosition, "y", 600); } } } diff --git a/tests/auto/quick/qmltests/data/tst_settings.qml b/tests/auto/quick/qmltests/data/tst_settings.qml index 11b2321e0..f47674aa7 100644 --- a/tests/auto/quick/qmltests/data/tst_settings.qml +++ b/tests/auto/quick/qmltests/data/tst_settings.qml @@ -78,6 +78,68 @@ TestWebEngineView { webEngineView2.destroy(); } + + function test_disableReadingFromCanvas_data() { + return [ + { tag: 'disabled', disableReadingFromCanvas: false, result: true }, + { tag: 'enabled', disableReadingFromCanvas: true, result: false }, + ] + } + + function test_disableReadingFromCanvas(data) { + webEngineView.settings.readingFromCanvasEnabled = !data.disableReadingFromCanvas; + webEngineView.loadHtml("<html><body>" + + "<canvas id='myCanvas' width='200' height='40' style='border:1px solid #000000;'></canvas>" + + "</body></html>"); + verify(webEngineView.waitForLoadSucceeded()); + verify(webEngineView.settings.readingFromCanvasEnabled === !data.disableReadingFromCanvas ) + + var jsCode = "(function(){" + + " var canvas = document.getElementById(\"myCanvas\");" + + " var ctx = canvas.getContext(\"2d\");" + + " ctx.fillStyle = \"rgb(255,0,255)\";" + + " ctx.fillRect(0, 0, 200, 40);" + + " try {" + + " src = canvas.toDataURL();" + + " }" + + " catch(err) {" + + " src = \"\";" + + " }" + + " return src.length ? true : false;" + + "})();"; + + var isDataRead = false; + runJavaScript(jsCode, function(result) { + isDataRead = result + }); + tryVerify(function() { return isDataRead === data.result }); + } + + function test_forceDarkMode() { + // based on: https://developer.chrome.com/blog/auto-dark-theme/#detecting-auto-dark-theme + webEngineView.loadHtml("<html><body>" + + "<div id=\"detection\", style=\"display: none; background-color: canvas; color-scheme: light\"</div>" + + "</body></html>"); + const script = "(() => {" + + " const detectionDiv = document.querySelector('#detection');" + + " return getComputedStyle(detectionDiv).backgroundColor != 'rgb(255, 255, 255)';" + + "})()"; + verify(webEngineView.waitForLoadSucceeded()); + + var isAutoDark = true; + runJavaScript(script, result => isAutoDark = result); + tryVerify(() => {return !isAutoDark}); + + webEngineView.settings.forceDarkMode = true; + verify(webEngineView.settings.forceDarkMode == true) + + isAutoDark = false; + // the page is not updated immediately + tryVerify(function() { + runJavaScript(script, result => isAutoDark = result); + return isAutoDark; + }); + } } } diff --git a/tests/auto/quick/qmltests/data/tst_viewSource.qml b/tests/auto/quick/qmltests/data/tst_viewSource.qml index 44a88daab..d4449f7de 100644 --- a/tests/auto/quick/qmltests/data/tst_viewSource.qml +++ b/tests/auto/quick/qmltests/data/tst_viewSource.qml @@ -122,7 +122,7 @@ TestWebEngineView { WebEngine.settings.errorPageEnabled = true webEngineView.url = row.userInputUrl; - tryCompare(loadSpy, 'count', 2); + tryCompare(loadSpy, 'count', 2, 12000); let load = loadSpy.signalArguments[1][0] let expectedStatus = row.loadSucceed ? WebEngineView.LoadSucceededStatus : WebEngineView.LoadFailedStatus compare(load.status, expectedStatus); diff --git a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/DirectoryPicker.qml b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/DirectoryPicker.qml new file mode 100644 index 000000000..71da28843 --- /dev/null +++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/DirectoryPicker.qml @@ -0,0 +1,18 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import "../../TestParams" + +QtObject { + signal folderSelected(var folder) + signal rejected() + + function open() { + FilePickerParams.directoryPickerOpened = true; + if (FilePickerParams.selectFiles) + folderSelected(FilePickerParams.selectedFilesUrl); + else + rejected(); + } +} diff --git a/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml b/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml index 4a1ffeb02..67d67dc40 100644 --- a/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml +++ b/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml @@ -8,5 +8,6 @@ QtObject { property var selectedFilesUrl: []; property bool selectFiles: false; property bool filePickerOpened: false; + property bool directoryPickerOpened: false; property var nameFilters: []; } diff --git a/tests/auto/quick/qmltests/tst_qmltests.cpp b/tests/auto/quick/qmltests/tst_qmltests.cpp index 5018c7e78..9e928157e 100644 --- a/tests/auto/quick/qmltests/tst_qmltests.cpp +++ b/tests/auto/quick/qmltests/tst_qmltests.cpp @@ -105,11 +105,18 @@ public: return tempDir.isValid() ? tempDir.path() : QString(); } + Q_INVOKABLE QUrl pathUrl(const QString &filename = QString()) + { + Q_ASSERT(tempDir.isValid()); + return filename.isEmpty() ? QUrl::fromLocalFile(tempDir.path()) + : QUrl::fromLocalFile(tempDir.filePath(filename)); + } + Q_INVOKABLE void removeRecursive(const QString dirname) { QDir dir(dirname); QFileInfoList entries(dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)); - for (int i = 0; i < entries.count(); ++i) { + for (int i = 0; i < entries.size(); ++i) { if (entries[i].isDir()) removeRecursive(entries[i].filePath()); else @@ -118,6 +125,8 @@ public: QDir().rmdir(dirname); } + Q_INVOKABLE void createDirectory(const QString dirname) { QDir(tempDir.path()).mkdir(dirname); } + private: QTemporaryDir tempDir; }; @@ -253,8 +262,9 @@ int main(int argc, char **argv) #if QT_CONFIG(ssl) qmlRegisterSingletonType<HttpsServer>( - "Test.Shared", 1, 0, "HttpsServer", - [&](QQmlEngine *, QJSEngine *) { return new HttpsServer(":/resources/server.pem",":/resources/server.key"); }); + "Test.Shared", 1, 0, "HttpsServer", [&](QQmlEngine *, QJSEngine *) { + return new HttpsServer(":/resources/server.pem", ":/resources/server.key", ""); + }); #endif Setup setup; int i = quick_test_main_with_setup( diff --git a/tests/auto/quick/qquickwebenginedefaultsurfaceformat/CMakeLists.txt b/tests/auto/quick/qquickwebenginedefaultsurfaceformat/CMakeLists.txt index 9fda5d4a5..9856ed513 100644 --- a/tests/auto/quick/qquickwebenginedefaultsurfaceformat/CMakeLists.txt +++ b/tests/auto/quick/qquickwebenginedefaultsurfaceformat/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../util/util.cmake) diff --git a/tests/auto/quick/qquickwebengineview/CMakeLists.txt b/tests/auto/quick/qquickwebengineview/CMakeLists.txt index 8e06ba60b..307ea36c9 100644 --- a/tests/auto/quick/qquickwebengineview/CMakeLists.txt +++ b/tests/auto/quick/qquickwebengineview/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../util/util.cmake) diff --git a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp index 5b5003846..dbfa1cb33 100644 --- a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp +++ b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp @@ -1,8 +1,11 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses + #include "testwindow.h" #include "quickutil.h" +#include "util.h" #include <QScopedPointer> #include <QtCore/qelapsedtimer.h> @@ -16,6 +19,7 @@ #include <QtGui/private/qinputmethod_p.h> #include <QtWebEngineQuick/private/qquickwebenginescriptcollection_p.h> #include <QtWebEngineQuick/private/qquickwebenginesettings_p.h> +#include <QtWebEngineQuick/private/qquickwebenginedownloadrequest_p.h> #include <QtWebEngineQuick/private/qquickwebengineview_p.h> #include <QtWebEngineCore/private/qtwebenginecore-config_p.h> #include <qpa/qplatforminputcontext.h> @@ -69,9 +73,13 @@ private Q_SLOTS: void javascriptClipboard_data(); void javascriptClipboard(); void setProfile(); - void focusChild(); +#if QT_CONFIG(accessibility) void focusChild_data(); + void focusChild(); +#endif void htmlSelectPopup(); + void savePage_data(); + void savePage(); private: inline QQuickWebEngineView *newWebEngineView(); @@ -423,10 +431,10 @@ void tst_QQuickWebEngineView::transparentWebEngineViews() for (int i = 0; i < image.width(); i++) for (int j = 0; j < image.height(); j++) colors.insert(image.pixel(i, j)); - return colors.count() > 1; + return colors.size() > 1; }); - QVERIFY(colors.count() > 1); + QVERIFY(colors.size() > 1); QVERIFY(colors.contains(qRgb(0, 0, 0))); // black QVERIFY(colors.contains(qRgb(255, 0, 0))); // red for (auto color : colors) { @@ -599,12 +607,12 @@ void tst_QQuickWebEngineView::inputContextQueryInput() " <input type='text' id='input1' />" "</body></html>"); QVERIFY(waitForLoadSucceeded(view)); - QCOMPARE(testContext.infos.count(), 0); + QCOMPARE(testContext.infos.size(), 0); // Set focus on an input field. QPoint textInputCenter = elementCenter(view, "input1"); QTest::mouseClick(view->window(), Qt::LeftButton, {}, textInputCenter); - QTRY_COMPARE(testContext.infos.count(), 2); + QTRY_COMPARE(testContext.infos.size(), 2); QCOMPARE(evaluateJavaScriptSync(view, "document.activeElement.id").toString(), QStringLiteral("input1")); foreach (const InputMethodInfo &info, testContext.infos) { QCOMPARE(info.cursorPosition, 0); @@ -616,7 +624,7 @@ void tst_QQuickWebEngineView::inputContextQueryInput() // Change content of an input field from JavaScript. evaluateJavaScriptSync(view, "document.getElementById('input1').value='QtWebEngine';"); - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 11); QCOMPARE(testContext.infos[0].anchorPosition, 11); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine")); @@ -625,7 +633,7 @@ void tst_QQuickWebEngineView::inputContextQueryInput() // Change content of an input field by key press. QTest::keyClick(view->window(), Qt::Key_Exclam); - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 12); QCOMPARE(testContext.infos[0].anchorPosition, 12); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!")); @@ -634,7 +642,7 @@ void tst_QQuickWebEngineView::inputContextQueryInput() // Change cursor position. QTest::keyClick(view->window(), Qt::Key_Left); - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 11); QCOMPARE(testContext.infos[0].anchorPosition, 11); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!")); @@ -649,7 +657,7 @@ void tst_QQuickWebEngineView::inputContextQueryInput() QInputMethodEvent event("", attributes); QGuiApplication::sendEvent(qApp->focusObject(), &event); } - QTRY_COMPARE(testContext.infos.count(), 2); + QTRY_COMPARE(testContext.infos.size(), 2); // As a first step, Chromium moves the cursor to the start of the selection. // We don't filter this in QtWebEngine because we don't know yet if this is part of a selection. @@ -673,7 +681,7 @@ void tst_QQuickWebEngineView::inputContextQueryInput() QInputMethodEvent event("", attributes); QGuiApplication::sendEvent(qApp->focusObject(), &event); } - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 0); QCOMPARE(testContext.infos[0].anchorPosition, 0); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!")); @@ -686,9 +694,9 @@ void tst_QQuickWebEngineView::inputContextQueryInput() QInputMethodEvent event("123", attributes); QGuiApplication::sendEvent(qApp->focusObject(), &event); } - QTRY_COMPARE(testContext.infos.count(), 1); - QCOMPARE(testContext.infos[0].cursorPosition, 3); - QCOMPARE(testContext.infos[0].anchorPosition, 3); + QTRY_COMPARE(testContext.infos.size(), 1); + QCOMPARE(testContext.infos[0].cursorPosition, 0); + QCOMPARE(testContext.infos[0].anchorPosition, 0); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!")); QCOMPARE(testContext.infos[0].selectedText, QStringLiteral("")); QCOMPARE(evaluateJavaScriptSync(view, "document.getElementById('input1').value").toString(), QStringLiteral("123QtWebEngine!")); @@ -700,7 +708,7 @@ void tst_QQuickWebEngineView::inputContextQueryInput() QInputMethodEvent event("", attributes); QGuiApplication::sendEvent(qApp->focusObject(), &event); } - QTRY_COMPARE(testContext.infos.count(), 2); + QTRY_COMPARE(testContext.infos.size(), 2); foreach (const InputMethodInfo &info, testContext.infos) { QCOMPARE(info.cursorPosition, 0); QCOMPARE(info.anchorPosition, 0); @@ -717,7 +725,7 @@ void tst_QQuickWebEngineView::inputContextQueryInput() event.setCommitString(QStringLiteral("123"), 0, 0); QGuiApplication::sendEvent(qApp->focusObject(), &event); } - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 3); QCOMPARE(testContext.infos[0].anchorPosition, 3); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("123QtWebEngine!")); @@ -727,7 +735,7 @@ void tst_QQuickWebEngineView::inputContextQueryInput() // Focus out. QTest::keyPress(view->window(), Qt::Key_Tab); - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QTRY_COMPARE(evaluateJavaScriptSync(view, "document.activeElement.id").toString(), QStringLiteral("")); testContext.infos.clear(); } @@ -838,7 +846,7 @@ void tst_QQuickWebEngineView::printToPdf() QSignalSpy savePdfSpy(view, SIGNAL(pdfPrintingFinished(const QString&, bool))); QString path = tempDir.path() + "/print_success.pdf"; view->printToPdf(path, QQuickWebEngineView::A4, QQuickWebEngineView::Portrait); - QTRY_VERIFY2(savePdfSpy.count() == 1, "Printing to PDF file failed without signal"); + QTRY_VERIFY2(savePdfSpy.size() == 1, "Printing to PDF file failed without signal"); QList<QVariant> successArguments = savePdfSpy.takeFirst(); QVERIFY2(successArguments.at(0).toString() == path, "File path for first saved PDF does not match arguments"); QVERIFY2(successArguments.at(1).toBool() == true, "Printing to PDF file failed though it should succeed"); @@ -849,7 +857,7 @@ void tst_QQuickWebEngineView::printToPdf() path = tempDir.path() + "/print_|fail.pdf"; #endif // #if !defined(Q_OS_WIN) view->printToPdf(path, QQuickWebEngineView::A4, QQuickWebEngineView::Portrait); - QTRY_VERIFY2(savePdfSpy.count() == 1, "Printing to PDF file failed without signal"); + QTRY_VERIFY2(savePdfSpy.size() == 1, "Printing to PDF file failed without signal"); QList<QVariant> failedArguments = savePdfSpy.takeFirst(); QVERIFY2(failedArguments.at(0).toString() == path, "File path for second saved PDF does not match arguments"); QVERIFY2(failedArguments.at(1).toBool() == false, "Printing to PDF file succeeded though it should fail"); @@ -1117,7 +1125,7 @@ void tst_QQuickWebEngineView::javascriptClipboard() // - return value of queryCommandEnabled and // - return value of execCommand // - comparing the clipboard / input field - QGuiApplication::clipboard()->clear(); + QGuiApplication::clipboard()->setText(QString()); QCOMPARE(evaluateJavaScriptSync(view, "document.queryCommandEnabled('copy')").toBool(), copyResult); QCOMPARE(evaluateJavaScriptSync(view, "document.execCommand('copy')").toBool(), copyResult); @@ -1144,9 +1152,9 @@ void tst_QQuickWebEngineView::javascriptClipboard() "if (result.state == 'prompt') accessPrompt = true;" "})")); - QTRY_COMPARE(evaluateJavaScriptSync(view, "accessGranted").toBool(), copyResult); - QTRY_COMPARE(evaluateJavaScriptSync(view, "accessDenied").toBool(), !javascriptCanAccessClipboard); - QTRY_COMPARE(evaluateJavaScriptSync(view, "accessPrompt").toBool(), false); + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessGranted").toBool(), javascriptCanAccessClipboard && javascriptCanPaste); + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessDenied").toBool(), false); + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessPrompt").toBool(), !javascriptCanAccessClipboard || !javascriptCanPaste); evaluateJavaScriptSync(view, QStringLiteral( @@ -1160,9 +1168,9 @@ void tst_QQuickWebEngineView::javascriptClipboard() "if (result.state == 'prompt') accessPrompt = true;" "})")); - QTRY_COMPARE(evaluateJavaScriptSync(view, "accessGranted").toBool(), pasteResult); - QTRY_COMPARE(evaluateJavaScriptSync(view, "accessDenied").toBool(), !javascriptCanAccessClipboard || !javascriptCanPaste); - QTRY_COMPARE(evaluateJavaScriptSync(view, "accessPrompt").toBool(), false); + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessGranted").toBool(), javascriptCanAccessClipboard && javascriptCanPaste); + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessDenied").toBool(), false); + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessPrompt").toBool(), !javascriptCanAccessClipboard || !javascriptCanPaste); } void tst_QQuickWebEngineView::setProfile() { @@ -1180,6 +1188,7 @@ void tst_QQuickWebEngineView::setProfile() { QTRY_COMPARE(webEngineView()->url() ,urlFromTestPath("html/basic_page2.html")); } +#if QT_CONFIG(accessibility) void tst_QQuickWebEngineView::focusChild_data() { QTest::addColumn<QString>("interfaceName"); @@ -1242,6 +1251,7 @@ void tst_QQuickWebEngineView::focusChild() // <html> -> <body> -> <input> QCOMPARE(traverseToWebDocumentAccessibleInterface(iface)->child(0)->child(0), iface->focusChild()); } +#endif // QT_CONFIG(accessibility) void tst_QQuickWebEngineView::htmlSelectPopup() { @@ -1270,8 +1280,92 @@ void tst_QQuickWebEngineView::htmlSelectPopup() QCOMPARE(evaluateJavaScriptSync(&view, "document.getElementById('select').value").toString(), QStringLiteral("O2")); } +void tst_QQuickWebEngineView::savePage_data() +{ + QTest::addColumn<QWebEngineDownloadRequest::SavePageFormat>("savePageFormat"); + + QTest::newRow("SingleHtmlSaveFormat") << QWebEngineDownloadRequest::SingleHtmlSaveFormat; + QTest::newRow("CompleteHtmlSaveFormat") << QWebEngineDownloadRequest::CompleteHtmlSaveFormat; + QTest::newRow("MimeHtmlSaveFormat") << QWebEngineDownloadRequest::MimeHtmlSaveFormat; +} + +void tst_QQuickWebEngineView::savePage() +{ + QFETCH(QWebEngineDownloadRequest::SavePageFormat, savePageFormat); + + QTemporaryDir tempDir(QDir::tempPath() + "/tst_QQuickWebEngineView-XXXXXX"); + QVERIFY(tempDir.isValid()); + const QString filePath = tempDir.path() + "/saved_page.html"; + + QQuickWebEngineView *view = webEngineView(); + int acceptedCount = 0; + int finishedCount = 0; + ScopedConnection sc1 = connect( + view->profile(), &QQuickWebEngineProfile::downloadRequested, + [&](QQuickWebEngineDownloadRequest *downloadRequest) { + QCOMPARE(downloadRequest->state(), + QQuickWebEngineDownloadRequest::DownloadInProgress); + QCOMPARE(downloadRequest->isSavePageDownload(), true); + QCOMPARE(downloadRequest->savePageFormat(), savePageFormat); + QCOMPARE(QDir(downloadRequest->downloadDirectory()) + .filePath(downloadRequest->downloadFileName()), + filePath); + QCOMPARE(downloadRequest->url(), view->url()); + + connect(downloadRequest, &QQuickWebEngineDownloadRequest::isFinishedChanged, + [&, downloadRequest]() { + QCOMPARE(downloadRequest->state(), + QQuickWebEngineDownloadRequest::DownloadCompleted); + QCOMPARE(downloadRequest->isSavePageDownload(), true); + QCOMPARE(downloadRequest->isFinished(), true); + QCOMPARE(downloadRequest->savePageFormat(), savePageFormat); + QCOMPARE(downloadRequest->totalBytes(), + downloadRequest->receivedBytes()); + QVERIFY(downloadRequest->receivedBytes() > 0); + QCOMPARE(QDir(downloadRequest->downloadDirectory()) + .filePath(downloadRequest->downloadFileName()), + filePath); + QCOMPARE(downloadRequest->url(), view->url()); + finishedCount++; + }); + acceptedCount++; + }); + + const QString originalData = QStringLiteral("Basic page"); + view->setUrl(urlFromTestPath("html/basic_page.html")); + QVERIFY(waitForLoadSucceeded(view)); + QCOMPARE(evaluateJavaScriptSync(view, "document.getElementsByTagName('h1')[0].innerText") + .toString(), + originalData); + + // Save the loaded page as HTML. + view->save(filePath, savePageFormat); + QTRY_COMPARE(acceptedCount, 1); + QTRY_COMPARE(finishedCount, 1); + QFile file(filePath); + QVERIFY(file.exists()); + + // Load something else. + view->setUrl(urlFromTestPath("html/basic_page2.html")); + QVERIFY(waitForLoadSucceeded(view)); + QVERIFY(evaluateJavaScriptSync(view, "document.getElementsByTagName('h1')[0].innerText") + .toString() + != originalData); + + // Load the saved page and compare the contents. + view->setUrl(QUrl::fromLocalFile(filePath)); + QVERIFY(waitForLoadSucceeded(view)); + QCOMPARE(evaluateJavaScriptSync(view, "document.getElementsByTagName('h1')[0].innerText") + .toString(), + originalData); +} + +#if QT_CONFIG(accessibility) static QByteArrayList params = QByteArrayList() << "--force-renderer-accessibility"; +#else +static QByteArrayList params; +#endif W_QTEST_MAIN(tst_QQuickWebEngineView, params) #include "tst_qquickwebengineview.moc" diff --git a/tests/auto/quick/qquickwebengineviewgraphics/CMakeLists.txt b/tests/auto/quick/qquickwebengineviewgraphics/CMakeLists.txt index 70e404ca0..f22408d15 100644 --- a/tests/auto/quick/qquickwebengineviewgraphics/CMakeLists.txt +++ b/tests/auto/quick/qquickwebengineviewgraphics/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../util/util.cmake) qt_internal_add_test(tst_qquickwebengineviewgraphics diff --git a/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp b/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp index 71c3a1cf2..3644ac481 100644 --- a/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp +++ b/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp @@ -124,6 +124,7 @@ void tst_QQuickWebEngineViewGraphics::reparentToOtherWindow() m_view->rootObject()->setParentItem(window.contentItem()); window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); verifyGreenSquare(&window); } diff --git a/tests/auto/quick/qtbug-70248/CMakeLists.txt b/tests/auto/quick/qtbug-70248/CMakeLists.txt index 3551d9564..b177c5309 100644 --- a/tests/auto/quick/qtbug-70248/CMakeLists.txt +++ b/tests/auto/quick/qtbug-70248/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause qt_internal_add_test(tst_qtbug-70248 SOURCES diff --git a/tests/auto/quick/uidelegates/CMakeLists.txt b/tests/auto/quick/uidelegates/CMakeLists.txt index c31e14398..bdf041e04 100644 --- a/tests/auto/quick/uidelegates/CMakeLists.txt +++ b/tests/auto/quick/uidelegates/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../httpserver/httpserver.cmake) include(../../util/util.cmake) diff --git a/tests/auto/util/CMakeLists.txt b/tests/auto/util/CMakeLists.txt index d7eb0d65d..0af0e5032 100644 --- a/tests/auto/util/CMakeLists.txt +++ b/tests/auto/util/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 3.18) project(minimal LANGUAGES CXX) diff --git a/tests/auto/util/quickutil.h b/tests/auto/util/quickutil.h index f7e08f842..687cb94dc 100644 --- a/tests/auto/util/quickutil.h +++ b/tests/auto/util/quickutil.h @@ -113,7 +113,7 @@ inline QPoint elementCenter(QQuickWebEngineView *view, const QString &id) "})()"); QVariantList rectList = evaluateJavaScriptSync(view, jsCode).toList(); - if (rectList.count() != 2) { + if (rectList.size() != 2) { qWarning("elementCenter failed."); return QPoint(); } diff --git a/tests/auto/util/util.cmake b/tests/auto/util/util.cmake index e0cc8d70e..e5142d0b2 100644 --- a/tests/auto/util/util.cmake +++ b/tests/auto/util/util.cmake @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause if (NOT TARGET Test::Util) add_library(qtestutil INTERFACE) diff --git a/tests/auto/util/util.h b/tests/auto/util/util.h index 455dff20c..5533eed80 100644 --- a/tests/auto/util/util.h +++ b/tests/auto/util/util.h @@ -43,7 +43,7 @@ public: bool ensureSignalEmitted() { - bool result = count() > 0; + bool result = size() > 0; if (!result) result = wait(); clear(); @@ -162,8 +162,8 @@ static inline QRect elementGeometry(QWebEnginePage *page, const QString &id) "})()"); QVariantList coords = evaluateJavaScriptSync(page, jsCode).toList(); - if (coords.count() != 4) { - qWarning("elementGeometry faield."); + if (coords.size() != 4) { + qWarning("elementGeometry failed."); return QRect(); } diff --git a/tests/auto/widgets/CMakeLists.txt b/tests/auto/widgets/CMakeLists.txt index 7ede8e7b1..9246be68a 100644 --- a/tests/auto/widgets/CMakeLists.txt +++ b/tests/auto/widgets/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause add_subdirectory(defaultsurfaceformat) add_subdirectory(qwebenginepage) @@ -16,6 +16,7 @@ add_subdirectory(qwebenginehistory) add_subdirectory(qwebenginescript) if(LINUX) add_subdirectory(offscreen) + add_subdirectory(qtbug_110287) endif() if(NOT MACOS) add_subdirectory(touchinput) diff --git a/tests/auto/widgets/accessibility/BLACKLIST b/tests/auto/widgets/accessibility/BLACKLIST index 41d3635d2..6fdb0dd58 100644 --- a/tests/auto/widgets/accessibility/BLACKLIST +++ b/tests/auto/widgets/accessibility/BLACKLIST @@ -1,2 +1,5 @@ +[roles:ax::mojom::Role::kSvgRoot] +b2qt + [focusChild] * diff --git a/tests/auto/widgets/accessibility/CMakeLists.txt b/tests/auto/widgets/accessibility/CMakeLists.txt index 1d638e337..f6a08c9d3 100644 --- a/tests/auto/widgets/accessibility/CMakeLists.txt +++ b/tests/auto/widgets/accessibility/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../util/util.cmake) @@ -8,5 +8,6 @@ qt_internal_add_test(tst_webengine_accessibility tst_accessibility.cpp LIBRARIES Qt::WebEngineWidgets + Qt::WebEngineCorePrivate Test::Util ) diff --git a/tests/auto/widgets/accessibility/tst_accessibility.cpp b/tests/auto/widgets/accessibility/tst_accessibility.cpp index be1257b82..1579b61e2 100644 --- a/tests/auto/widgets/accessibility/tst_accessibility.cpp +++ b/tests/auto/widgets/accessibility/tst_accessibility.cpp @@ -17,6 +17,7 @@ Boston, MA 02110-1301, USA. */ +#include <QtWebEngineCore/private/qtwebenginecore-config_p.h> #include <qtest.h> #include <widgetutil.h> @@ -50,6 +51,7 @@ private Q_SLOTS: void roles(); void objectName(); void crossTreeParent(); + void tableCellInterface(); }; // This will be called before the first test function is executed. @@ -475,7 +477,7 @@ void tst_Accessibility::roles_data() QTest::newRow("ax::mojom::Role::kNote") << QString("<div role='note'>a</div>") << 0 << QAccessible::Note; //QTest::newRow("ax::mojom::Role::kPane"); // No mapping to ARIA role QTest::newRow("ax::mojom::Role::kParagraph") << QString("<p>a</p>") << 0 << QAccessible::Paragraph; - QTest::newRow("ax::mojom::Role::kPopUpButton") << QString("<select><option>a</option></select>") << 1 << QAccessible::ComboBox; + QTest::newRow("ax::mojom::Role::kPopUpButton") << QString("<select><option>a</option></select>") << 1 << QAccessible::PopupMenu; QTest::newRow("ax::mojom::Role::kPre") << QString("<pre>a</pre>") << 0 << QAccessible::Section; //QTest::newRow("ax::mojom::Role::kPresentational") << QString("<div role='presentation'>a</div>") << 0 << QAccessible::NoRole; // FIXME: Aria role 'presentation' should work QTest::newRow("ax::mojom::Role::kProgressIndicator") << QString("<div role='progressbar' aria-valuenow='77' aria-valuemin='22' aria-valuemax='99'></div>") << 0 << QAccessible::ProgressBar; @@ -529,10 +531,10 @@ void tst_Accessibility::roles() QFETCH(QAccessible::Role, role); QWebEngineView webView; + QSignalSpy spyFinished(&webView, &QWebEngineView::loadFinished); webView.setHtml("<html><body>" + html + "</body></html>"); webView.show(); - QSignalSpy spyFinished(&webView, &QWebEngineView::loadFinished); - QVERIFY(spyFinished.wait()); + QTRY_COMPARE_WITH_TIMEOUT(spyFinished.size(), 1, 20000); QAccessibleInterface *view = QAccessible::queryAccessibleInterface(&webView); @@ -542,7 +544,7 @@ void tst_Accessibility::roles() return; } - QTRY_COMPARE(view->child(0)->childCount(), 1); + QTRY_COMPARE_WITH_TIMEOUT(view->child(0)->childCount(), 1, 20000); QAccessibleInterface *document = view->child(0); QAccessibleInterface *element = document->child(0); @@ -605,9 +607,65 @@ void tst_Accessibility::crossTreeParent() QCOMPARE(p->object()->objectName(), QStringLiteral("my_id")); } +void tst_Accessibility::tableCellInterface() +{ + QWebEngineView webView; + webView.resize(400, 400); + webView.show(); + QVERIFY(QTest::qWaitForWindowExposed(&webView)); + + QSignalSpy spyFinished(&webView, &QWebEngineView::loadFinished); + webView.setHtml(QLatin1String( + "<html><body>" + " <ul>" + " <li><a href='#link1' id='link1'>Link in ListItem</a></li>" + " </ul>" + "" + " <div role='rowgroup'>" + " <div role='row'>" + " <span role='cell'><a href='#link2' id='link2'>Link in Cell</a></span>" + " </div>" + " </div>" + "</body></html>")); + QTRY_COMPARE(spyFinished.size(), 1); + + QAccessibleInterface *view = QAccessible::queryAccessibleInterface(&webView); + QAccessibleInterface *document = view->child(0); + QTRY_COMPARE(document->childCount(), 2); + + // ListItem without Table parent. + { + QAccessibleInterface *list = document->child(0); + QAccessibleInterface *listItem = list->child(0); + QVERIFY(!listItem->tableCellInterface()); + + // Should not crash. + QPoint linkCenter = elementCenter(webView.page(), QLatin1String("link1")); + QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, {}, linkCenter); + QTRY_COMPARE(webView.url().fragment(), QLatin1String("link1")); + } + + // Cell without Table parent. + { + QAccessibleInterface *rowgroup = document->child(1); + QAccessibleInterface *row = rowgroup->child(0); + QAccessibleInterface *cell = row->child(0); + QVERIFY(!cell->tableCellInterface()); + + // Should not crash. + QPoint linkCenter = elementCenter(webView.page(), QLatin1String("link2")); + QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, {}, linkCenter); + QTRY_COMPARE(webView.url().fragment(), QLatin1String("link2")); + } +} + static QByteArrayList params = QByteArrayList() << "--force-renderer-accessibility" - << "--enable-features=AccessibilityExposeARIAAnnotations"; + << "--enable-features=AccessibilityExposeARIAAnnotations" +#if QT_CONFIG(webengine_embedded_build) + << "--disable-features=TimedHTMLParserBudget" +#endif + ; W_QTEST_MAIN(tst_Accessibility, params) #include "tst_accessibility.moc" diff --git a/tests/auto/widgets/defaultsurfaceformat/CMakeLists.txt b/tests/auto/widgets/defaultsurfaceformat/CMakeLists.txt index 10419296b..d95c1355b 100644 --- a/tests/auto/widgets/defaultsurfaceformat/CMakeLists.txt +++ b/tests/auto/widgets/defaultsurfaceformat/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../util/util.cmake) diff --git a/tests/auto/widgets/favicon/CMakeLists.txt b/tests/auto/widgets/favicon/CMakeLists.txt index 585bdf0cf..0deae6a37 100644 --- a/tests/auto/widgets/favicon/CMakeLists.txt +++ b/tests/auto/widgets/favicon/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../util/util.cmake) diff --git a/tests/auto/widgets/favicon/tst_favicon.cpp b/tests/auto/widgets/favicon/tst_favicon.cpp index dc1e9f096..c70aa1182 100644 --- a/tests/auto/widgets/favicon/tst_favicon.cpp +++ b/tests/auto/widgets/favicon/tst_favicon.cpp @@ -88,9 +88,9 @@ void tst_Favicon::faviconLoad() + QLatin1String("/resources/favicon-single.html")); m_page->load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); - QTRY_COMPARE(iconUrlChangedSpy.count(), 1); - QTRY_COMPARE(iconChangedSpy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); + QTRY_COMPARE(iconUrlChangedSpy.size(), 1); + QTRY_COMPARE(iconChangedSpy.size(), 1); QUrl iconUrl = iconUrlChangedSpy.at(0).at(0).toString(); QCOMPARE(iconUrl, m_page->iconUrl()); @@ -101,7 +101,7 @@ void tst_Favicon::faviconLoad() const QIcon &icon = m_page->icon(); QVERIFY(!icon.isNull()); - QCOMPARE(icon.availableSizes().count(), 2); + QCOMPARE(icon.availableSizes().size(), 2); QVERIFY(icon.availableSizes().contains(QSize(16, 16))); QVERIFY(icon.availableSizes().contains(QSize(32, 32))); } @@ -115,9 +115,9 @@ void tst_Favicon::faviconLoadFromResources() QUrl url("qrc:/resources/favicon-single.html"); m_page->load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); - QTRY_COMPARE(iconUrlChangedSpy.count(), 1); - QTRY_COMPARE(iconChangedSpy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); + QTRY_COMPARE(iconUrlChangedSpy.size(), 1); + QTRY_COMPARE(iconChangedSpy.size(), 1); QUrl iconUrl = iconUrlChangedSpy.at(0).at(0).toString(); QCOMPARE(iconUrl, m_page->iconUrl()); @@ -126,7 +126,7 @@ void tst_Favicon::faviconLoadFromResources() const QIcon &icon = m_page->icon(); QVERIFY(!icon.isNull()); - QCOMPARE(icon.availableSizes().count(), 2); + QCOMPARE(icon.availableSizes().size(), 2); QVERIFY(icon.availableSizes().contains(QSize(16, 16))); QVERIFY(icon.availableSizes().contains(QSize(32, 32))); } @@ -150,9 +150,9 @@ void tst_Favicon::faviconLoadEncodedUrl() QUrl url(urlString + QLatin1String("?favicon=load should work with#whitespace!")); m_page->load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); - QTRY_COMPARE(iconUrlChangedSpy.count(), 1); - QTRY_COMPARE(iconChangedSpy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); + QTRY_COMPARE(iconUrlChangedSpy.size(), 1); + QTRY_COMPARE(iconChangedSpy.size(), 1); QUrl iconUrl = iconUrlChangedSpy.at(0).at(0).toString(); QCOMPARE(m_page->iconUrl(), iconUrl); @@ -163,7 +163,7 @@ void tst_Favicon::faviconLoadEncodedUrl() const QIcon &icon = m_page->icon(); QVERIFY(!icon.isNull()); - QCOMPARE(icon.availableSizes().count(), 2); + QCOMPARE(icon.availableSizes().size(), 2); QVERIFY(icon.availableSizes().contains(QSize(16, 16))); QVERIFY(icon.availableSizes().contains(QSize(32, 32))); } @@ -175,27 +175,27 @@ void tst_Favicon::faviconLoadAfterHistoryNavigation() QSignalSpy iconChangedSpy(m_page, SIGNAL(iconChanged(QIcon))); m_page->load(QUrl("qrc:/resources/favicon-single.html")); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); - QTRY_COMPARE(iconUrlChangedSpy.count(), 1); - QTRY_COMPARE(iconChangedSpy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); + QTRY_COMPARE(iconUrlChangedSpy.size(), 1); + QTRY_COMPARE(iconChangedSpy.size(), 1); QCOMPARE(m_page->iconUrl(), QUrl("qrc:/resources/icons/qt32.ico")); m_page->load(QUrl("qrc:/resources/favicon-multi.html")); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 2, 30000); - QTRY_COMPARE(iconUrlChangedSpy.count(), 3); - QTRY_COMPARE(iconChangedSpy.count(), 3); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 2, 30000); + QTRY_COMPARE(iconUrlChangedSpy.size(), 3); + QTRY_COMPARE(iconChangedSpy.size(), 3); QCOMPARE(m_page->iconUrl(), QUrl("qrc:/resources/icons/qtmulti.ico")); m_page->triggerAction(QWebEnginePage::Back); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 3, 30000); - QTRY_COMPARE(iconUrlChangedSpy.count(), 5); - QTRY_COMPARE(iconChangedSpy.count(), 5); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 3, 30000); + QTRY_COMPARE(iconUrlChangedSpy.size(), 5); + QTRY_COMPARE(iconChangedSpy.size(), 5); QCOMPARE(m_page->iconUrl(), QUrl("qrc:/resources/icons/qt32.ico")); m_page->triggerAction(QWebEnginePage::Forward); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 4, 30000); - QTRY_COMPARE(iconUrlChangedSpy.count(), 7); - QTRY_COMPARE(iconChangedSpy.count(), 7); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 4, 30000); + QTRY_COMPARE(iconUrlChangedSpy.size(), 7); + QTRY_COMPARE(iconChangedSpy.size(), 7); QCOMPARE(m_page->iconUrl(), QUrl("qrc:/resources/icons/qtmulti.ico")); } @@ -208,9 +208,9 @@ void tst_Favicon::faviconLoadPushState() QUrl url("qrc:/resources/favicon-single.html"); m_page->load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); - QTRY_COMPARE(iconUrlChangedSpy.count(), 1); - QTRY_COMPARE(iconChangedSpy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); + QTRY_COMPARE(iconUrlChangedSpy.size(), 1); + QTRY_COMPARE(iconChangedSpy.size(), 1); QUrl iconUrl = iconUrlChangedSpy.at(0).at(0).toString(); QCOMPARE(iconUrl, m_page->iconUrl()); @@ -229,8 +229,8 @@ void tst_Favicon::faviconLoadPushState() QTRY_COMPARE(m_page->history()->count(), 2); // Favicon change is not expected. - QCOMPARE(iconUrlChangedSpy.count(), 0); - QCOMPARE(iconChangedSpy.count(), 0); + QCOMPARE(iconUrlChangedSpy.size(), 0); + QCOMPARE(iconChangedSpy.size(), 0); QCOMPARE(m_page->iconUrl(), QUrl("qrc:/resources/icons/qt32.ico")); } @@ -251,9 +251,9 @@ void tst_Favicon::noFavicon() + QLatin1String("/resources/test1.html")); m_page->load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); - QCOMPARE(iconUrlChangedSpy.count(), 0); - QCOMPARE(iconChangedSpy.count(), 0); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); + QCOMPARE(iconUrlChangedSpy.size(), 0); + QCOMPARE(iconChangedSpy.size(), 0); QVERIFY(m_page->iconUrl().isEmpty()); QVERIFY(m_page->icon().isNull()); @@ -268,9 +268,9 @@ void tst_Favicon::aboutBlank() QUrl url("about:blank"); m_page->load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); - QCOMPARE(iconUrlChangedSpy.count(), 0); - QCOMPARE(iconChangedSpy.count(), 0); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); + QCOMPARE(iconUrlChangedSpy.size(), 0); + QCOMPARE(iconChangedSpy.size(), 0); QVERIFY(m_page->iconUrl().isEmpty()); QVERIFY(m_page->icon().isNull()); @@ -293,9 +293,9 @@ void tst_Favicon::unavailableFavicon() + QLatin1String("/resources/favicon-unavailable.html")); m_page->load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); - QCOMPARE(iconUrlChangedSpy.count(), 0); - QCOMPARE(iconChangedSpy.count(), 0); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); + QCOMPARE(iconUrlChangedSpy.size(), 0); + QCOMPARE(iconChangedSpy.size(), 0); QVERIFY(m_page->iconUrl().isEmpty()); QVERIFY(m_page->icon().isNull()); @@ -312,9 +312,9 @@ void tst_Favicon::errorPageEnabled() QUrl url("http://url.invalid"); m_page->load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); - QCOMPARE(iconUrlChangedSpy.count(), 0); - QCOMPARE(iconChangedSpy.count(), 0); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); + QCOMPARE(iconUrlChangedSpy.size(), 0); + QCOMPARE(iconChangedSpy.size(), 0); QVERIFY(m_page->iconUrl().isEmpty()); QVERIFY(m_page->icon().isNull()); @@ -331,9 +331,9 @@ void tst_Favicon::errorPageDisabled() QUrl url("http://url.invalid"); m_page->load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); - QCOMPARE(iconUrlChangedSpy.count(), 0); - QCOMPARE(iconChangedSpy.count(), 0); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); + QCOMPARE(iconUrlChangedSpy.size(), 0); + QCOMPARE(iconChangedSpy.size(), 0); QVERIFY(m_page->iconUrl().isEmpty()); QVERIFY(m_page->icon().isNull()); @@ -356,9 +356,9 @@ void tst_Favicon::touchIcon() + QLatin1String("/resources/favicon-touch.html")); m_page->load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); - QCOMPARE(iconUrlChangedSpy.count(), 0); - QCOMPARE(iconChangedSpy.count(), 0); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); + QCOMPARE(iconUrlChangedSpy.size(), 0); + QCOMPARE(iconChangedSpy.size(), 0); QVERIFY(m_page->iconUrl().isEmpty()); QVERIFY(m_page->icon().isNull()); @@ -387,9 +387,9 @@ void tst_Favicon::multiIcon() m_page->settings()->setAttribute(QWebEngineSettings::TouchIconsEnabled, false); m_page->load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); - QTRY_COMPARE(iconUrlChangedSpy.count(), 1); - QTRY_COMPARE(iconChangedSpy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); + QTRY_COMPARE(iconUrlChangedSpy.size(), 1); + QTRY_COMPARE(iconChangedSpy.size(), 1); iconUrl = iconUrlChangedSpy.at(0).at(0).toString(); QCOMPARE(m_page->iconUrl(), iconUrl); @@ -399,14 +399,14 @@ void tst_Favicon::multiIcon() icon = m_page->icon(); QVERIFY(!icon.isNull()); - QCOMPARE(icon.availableSizes().count(), 2); + QCOMPARE(icon.availableSizes().size(), 2); QVERIFY(icon.availableSizes().contains(QSize(16, 16))); QVERIFY(icon.availableSizes().contains(QSize(32, 32))); // Reset loadFinishedSpy.clear(); m_page->load(QUrl("about:blank")); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); iconUrlChangedSpy.clear(); iconChangedSpy.clear(); loadFinishedSpy.clear(); @@ -416,9 +416,9 @@ void tst_Favicon::multiIcon() m_page->settings()->setAttribute(QWebEngineSettings::TouchIconsEnabled, true); m_page->load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); - QTRY_COMPARE(iconUrlChangedSpy.count(), 1); - QTRY_COMPARE(iconChangedSpy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); + QTRY_COMPARE(iconUrlChangedSpy.size(), 1); + QTRY_COMPARE(iconChangedSpy.size(), 1); iconUrl = iconUrlChangedSpy.at(0).at(0).toString(); QCOMPARE(m_page->iconUrl(), iconUrl); @@ -428,7 +428,7 @@ void tst_Favicon::multiIcon() icon = m_page->icon(); QVERIFY(!icon.isNull()); - QCOMPARE(icon.availableSizes().count(), 1); + QCOMPARE(icon.availableSizes().size(), 1); QVERIFY(icon.availableSizes().contains(QSize(64, 64))); } @@ -454,9 +454,9 @@ void tst_Favicon::downloadIconsDisabled() m_page->load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); - QCOMPARE(iconUrlChangedSpy.count(), 0); - QCOMPARE(iconChangedSpy.count(), 0); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); + QCOMPARE(iconUrlChangedSpy.size(), 0); + QCOMPARE(iconChangedSpy.size(), 0); QVERIFY(m_page->iconUrl().isEmpty()); QVERIFY(m_page->icon().isNull()); @@ -491,9 +491,9 @@ void tst_Favicon::downloadTouchIconsEnabled() m_page->load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); - QTRY_COMPARE(iconUrlChangedSpy.count(), 1); - QTRY_COMPARE(iconChangedSpy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); + QTRY_COMPARE(iconUrlChangedSpy.size(), 1); + QTRY_COMPARE(iconChangedSpy.size(), 1); const QUrl &iconUrl = iconUrlChangedSpy.at(0).at(0).toString(); QCOMPARE(m_page->iconUrl(), iconUrl); @@ -502,7 +502,7 @@ void tst_Favicon::downloadTouchIconsEnabled() const QIcon &icon = m_page->icon(); QVERIFY(!icon.isNull()); - QCOMPARE(icon.availableSizes().count(), 1); + QCOMPARE(icon.availableSizes().size(), 1); QCOMPARE(icon.availableSizes().first(), expectedIconSize); } @@ -524,9 +524,9 @@ void tst_Favicon::dynamicFavicon() "<link rel='icon' type='image/png' " "href='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII='/>" "</html>"); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); - QTRY_COMPARE(iconUrlChangedSpy.count(), 1); - QTRY_COMPARE(iconChangedSpy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); + QTRY_COMPARE(iconUrlChangedSpy.size(), 1); + QTRY_COMPARE(iconChangedSpy.size(), 1); QCOMPARE(m_page->icon().pixmap(1, 1).toImage().pixelColor(0, 0), QColor(Qt::black)); @@ -535,7 +535,7 @@ void tst_Favicon::dynamicFavicon() evaluateJavaScriptSync( m_page, "document.getElementsByTagName('link')[0].href = 'data:image/png;base64," + colors[color] + "';"); - QTRY_COMPARE(iconChangedSpy.count(), 1); + QTRY_COMPARE(iconChangedSpy.size(), 1); QTRY_COMPARE(m_page->iconUrl().toString(), QString("data:image/png;base64," + colors[color])); QCOMPARE(m_page->icon().pixmap(1, 1).toImage().pixelColor(0, 0), QColor(color)); @@ -555,13 +555,13 @@ void tst_Favicon::touchIconWithSameURL() "<link rel='icon' type='image/png' href='" + icon + "'/>" "<link rel='apple-touch-icon' type='image/png' href='" + icon + "'/>" "</html>"); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); // The default favicon has to be loaded even if its URL is also set as a touch icon while touch // icons are disabled. - QTRY_COMPARE(iconUrlChangedSpy.count(), 1); + QTRY_COMPARE(iconUrlChangedSpy.size(), 1); QCOMPARE(m_page->iconUrl().toString(), icon); - QTRY_COMPARE(iconChangedSpy.count(), 1); + QTRY_COMPARE(iconChangedSpy.size(), 1); loadFinishedSpy.clear(); iconUrlChangedSpy.clear(); @@ -570,13 +570,13 @@ void tst_Favicon::touchIconWithSameURL() m_page->setHtml("<html>" "<link rel='apple-touch-icon' type='image/png' href='" + icon + "'/>" "</html>"); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); // This page only has a touch icon. With disabled touch icons we don't expect any icon to be // shown even if the same icon was loaded previously. - QTRY_COMPARE(iconUrlChangedSpy.count(), 1); + QTRY_COMPARE(iconUrlChangedSpy.size(), 1); QVERIFY(m_page->iconUrl().toString().isEmpty()); - QTRY_COMPARE(iconChangedSpy.count(), 1); + QTRY_COMPARE(iconChangedSpy.size(), 1); } void tst_Favicon::iconDatabaseOTR() @@ -592,9 +592,9 @@ void tst_Favicon::iconDatabaseOTR() page->load(QUrl("qrc:/resources/favicon-misc.html")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); - QTRY_COMPARE(iconUrlChangedSpy.count(), 1); - QTRY_COMPARE(iconChangedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + QTRY_COMPARE(iconUrlChangedSpy.size(), 1); + QTRY_COMPARE(iconChangedSpy.size(), 1); { bool iconRequestDone = false; @@ -647,15 +647,15 @@ void tst_Favicon::requestIconForIconURL() page->load(QUrl("qrc:/resources/favicon-misc.html")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); - QTRY_COMPARE(iconUrlChangedSpy.count(), 1); - QTRY_COMPARE(iconChangedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + QTRY_COMPARE(iconUrlChangedSpy.size(), 1); + QTRY_COMPARE(iconChangedSpy.size(), 1); page->load(QUrl("about:blank")); - QTRY_COMPARE(loadFinishedSpy.count(), 2); - QTRY_COMPARE(iconUrlChangedSpy.count(), 2); - QTRY_COMPARE(iconChangedSpy.count(), 2); + QTRY_COMPARE(loadFinishedSpy.size(), 2); + QTRY_COMPARE(iconUrlChangedSpy.size(), 2); + QTRY_COMPARE(iconChangedSpy.size(), 2); QVERIFY(page->icon().isNull()); QVERIFY(page->iconUrl().isEmpty()); @@ -717,15 +717,15 @@ void tst_Favicon::requestIconForPageURL() page->load(QUrl("qrc:/resources/favicon-misc.html")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); - QTRY_COMPARE(iconUrlChangedSpy.count(), 1); - QTRY_COMPARE(iconChangedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + QTRY_COMPARE(iconUrlChangedSpy.size(), 1); + QTRY_COMPARE(iconChangedSpy.size(), 1); page->load(QUrl("about:blank")); - QTRY_COMPARE(loadFinishedSpy.count(), 2); - QTRY_COMPARE(iconUrlChangedSpy.count(), 2); - QTRY_COMPARE(iconChangedSpy.count(), 2); + QTRY_COMPARE(loadFinishedSpy.size(), 2); + QTRY_COMPARE(iconUrlChangedSpy.size(), 2); + QTRY_COMPARE(iconChangedSpy.size(), 2); QVERIFY(page->icon().isNull()); QVERIFY(page->iconUrl().isEmpty()); @@ -770,15 +770,15 @@ void tst_Favicon::desiredSize() page->load(QUrl("qrc:/resources/favicon-multi.html")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); - QTRY_COMPARE(iconUrlChangedSpy.count(), 1); - QTRY_COMPARE(iconChangedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + QTRY_COMPARE(iconUrlChangedSpy.size(), 1); + QTRY_COMPARE(iconChangedSpy.size(), 1); page->load(QUrl("about:blank")); - QTRY_COMPARE(loadFinishedSpy.count(), 2); - QTRY_COMPARE(iconUrlChangedSpy.count(), 2); - QTRY_COMPARE(iconChangedSpy.count(), 2); + QTRY_COMPARE(loadFinishedSpy.size(), 2); + QTRY_COMPARE(iconUrlChangedSpy.size(), 2); + QTRY_COMPARE(iconChangedSpy.size(), 2); QVERIFY(page->icon().isNull()); QVERIFY(page->iconUrl().isEmpty()); } @@ -813,15 +813,15 @@ void tst_Favicon::desiredSize() page->load(QUrl("qrc:/resources/favicon-multi.html")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); - QTRY_COMPARE(iconUrlChangedSpy.count(), 1); - QTRY_COMPARE(iconChangedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + QTRY_COMPARE(iconUrlChangedSpy.size(), 1); + QTRY_COMPARE(iconChangedSpy.size(), 1); page->load(QUrl("about:blank")); - QTRY_COMPARE(loadFinishedSpy.count(), 2); - QTRY_COMPARE(iconUrlChangedSpy.count(), 2); - QTRY_COMPARE(iconChangedSpy.count(), 2); + QTRY_COMPARE(loadFinishedSpy.size(), 2); + QTRY_COMPARE(iconUrlChangedSpy.size(), 2); + QTRY_COMPARE(iconChangedSpy.size(), 2); QVERIFY(page->icon().isNull()); QVERIFY(page->iconUrl().isEmpty()); } diff --git a/tests/auto/widgets/loadsignals/CMakeLists.txt b/tests/auto/widgets/loadsignals/CMakeLists.txt index 454cf1584..bbd0387d9 100644 --- a/tests/auto/widgets/loadsignals/CMakeLists.txt +++ b/tests/auto/widgets/loadsignals/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../httpserver/httpserver.cmake) include(../../util/util.cmake) diff --git a/tests/auto/widgets/loadsignals/tst_loadsignals.cpp b/tests/auto/widgets/loadsignals/tst_loadsignals.cpp index 28dd5f938..6140b3766 100644 --- a/tests/auto/widgets/loadsignals/tst_loadsignals.cpp +++ b/tests/auto/widgets/loadsignals/tst_loadsignals.cpp @@ -111,7 +111,7 @@ void tst_LoadSignals::init() if (!view.url().isEmpty()) { loadFinishedSpy.clear(); view.load(QUrl("about:blank")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); } resetSpies(); page.reset(); @@ -277,7 +277,7 @@ void tst_LoadSignals::monotonicity() QVERIFY(server.start()); view.load(server.url("/loadprogress/main.html")); - QTRY_COMPARE(loadFinishedSpy.size(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 10000); QVERIFY(loadFinishedSpy[0][0].toBool()); QVERIFY(page.loadProgress.size() >= 3); @@ -421,11 +421,11 @@ void tst_LoadSignals::loadFinishedAfterNotFoundError() ? server->url("/not-found-page.html") : QUrl(rfcInvalid ? "http://some.invalid" : "http://non.existent/url"); view.load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 20000); QVERIFY(!loadFinishedSpy.at(0).at(0).toBool()); QCOMPARE(toPlainTextSync(view.page()), QString()); - QCOMPARE(loadFinishedSpy.count(), 1); - QCOMPARE(loadStartedSpy.count(), 1); + QCOMPARE(loadFinishedSpy.size(), 1); + QCOMPARE(loadStartedSpy.size(), 1); QVERIFY(std::is_sorted(page.loadProgress.begin(), page.loadProgress.end())); page.loadProgress.clear(); @@ -447,13 +447,13 @@ void tst_LoadSignals::loadFinishedAfterNotFoundError() ? server->url("/another-missing-one.html") : QUrl(rfcInvalid ? "http://some.other.invalid" : "http://another.non.existent/url"); view.load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 2, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 2, 20000); QVERIFY(!loadFinishedSpy.at(1).at(0).toBool()); - QCOMPARE(loadStartedSpy.count(), 2); + QCOMPARE(loadStartedSpy.size(), 2); QEXPECT_FAIL("", "No more loads (like separate load for error pages) are expected", Continue); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 3, 1000); - QCOMPARE(loadStartedSpy.count(), 2); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 3, 1000); + QCOMPARE(loadStartedSpy.size(), 2); QVERIFY(std::is_sorted(page.loadProgress.begin(), page.loadProgress.end())); { auto &&loadStart = page.loadingInfos[2], &&loadFinish = page.loadingInfos[3]; @@ -488,7 +488,7 @@ void tst_LoadSignals::errorPageTriggered() HttpServer server; connect(&server, &HttpServer::newRequest, [] (HttpReqRep *rr) { QList<QByteArray> parts = rr->requestPath().split('/'); - if (parts.length() != 3) { + if (parts.size() != 3) { // For example, /favicon.ico rr->sendResponse(404); return; diff --git a/tests/auto/widgets/offscreen/CMakeLists.txt b/tests/auto/widgets/offscreen/CMakeLists.txt index 3e7816746..756e53c43 100644 --- a/tests/auto/widgets/offscreen/CMakeLists.txt +++ b/tests/auto/widgets/offscreen/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause qt_internal_add_test(tst_offscreen SOURCES diff --git a/tests/auto/widgets/offscreen/tst_offscreen.cpp b/tests/auto/widgets/offscreen/tst_offscreen.cpp index 9553a0394..553dc653b 100644 --- a/tests/auto/widgets/offscreen/tst_offscreen.cpp +++ b/tests/auto/widgets/offscreen/tst_offscreen.cpp @@ -26,7 +26,7 @@ void tst_OffScreen::offscreen() page.load(QUrl("qrc:/test.html")); view.show(); QTRY_COMPARE(view.isVisible(), true); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count() > 0, true, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size() > 0, true, 20000); QCOMPARE(loadFinishedSpy.takeFirst().at(0).toBool(), true); } diff --git a/tests/auto/widgets/printing/CMakeLists.txt b/tests/auto/widgets/printing/CMakeLists.txt index 65ca1ec87..baa3cf747 100644 --- a/tests/auto/widgets/printing/CMakeLists.txt +++ b/tests/auto/widgets/printing/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../util/util.cmake) diff --git a/tests/auto/widgets/printing/tst_printing.cpp b/tests/auto/widgets/printing/tst_printing.cpp index 2e04bc03c..605fb57b5 100644 --- a/tests/auto/widgets/printing/tst_printing.cpp +++ b/tests/auto/widgets/printing/tst_printing.cpp @@ -3,6 +3,7 @@ #include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h> #include <QtWebEngineCore/qtwebenginecore-config.h> +#include <QWebEngineSettings> #include <QWebEngineView> #include <QTemporaryDir> #include <QTest> @@ -22,7 +23,9 @@ private slots: void printRequest(); #if QT_CONFIG(webengine_system_poppler) void printToPdfPoppler(); + void printFromPdfViewer(); #endif + void interruptPrinting(); }; void tst_Printing::printToPdfBasic() @@ -32,13 +35,13 @@ void tst_Printing::printToPdfBasic() QWebEngineView view; QSignalSpy spy(&view, &QWebEngineView::loadFinished); view.load(QUrl("qrc:///resources/basic_printing_page.html")); - QTRY_VERIFY(spy.count() == 1); + QTRY_VERIFY(spy.size() == 1); QSignalSpy savePdfSpy(view.page(), &QWebEnginePage::pdfPrintingFinished); QPageLayout layout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF(0.0, 0.0, 0.0, 0.0)); QString path = tempDir.path() + "/print_1_success.pdf"; view.page()->printToPdf(path, layout); - QTRY_VERIFY2(savePdfSpy.count() == 1, "Printing to PDF file failed without signal"); + QTRY_VERIFY2(savePdfSpy.size() == 1, "Printing to PDF file failed without signal"); QList<QVariant> successArguments = savePdfSpy.takeFirst(); QVERIFY2(successArguments.at(0).toString() == path, "File path for first saved PDF does not match arguments"); @@ -50,7 +53,7 @@ void tst_Printing::printToPdfBasic() path = tempDir.path() + "/print_|2_failed.pdf"; #endif view.page()->printToPdf(path, QPageLayout()); - QTRY_VERIFY2(savePdfSpy.count() == 1, "Printing to PDF file failed without signal"); + QTRY_VERIFY2(savePdfSpy.size() == 1, "Printing to PDF file failed without signal"); QList<QVariant> failedArguments = savePdfSpy.takeFirst(); QVERIFY2(failedArguments.at(0).toString() == path, "File path for second saved PDF does not match arguments"); @@ -58,11 +61,11 @@ void tst_Printing::printToPdfBasic() CallbackSpy<QByteArray> successfulSpy; view.page()->printToPdf(successfulSpy.ref(), layout); - QVERIFY(successfulSpy.waitForResult().length() > 0); + QVERIFY(successfulSpy.waitForResult().size() > 0); CallbackSpy<QByteArray> failedInvalidLayoutSpy; view.page()->printToPdf(failedInvalidLayoutSpy.ref(), QPageLayout()); - QCOMPARE(failedInvalidLayoutSpy.waitForResult().length(), 0); + QCOMPARE(failedInvalidLayoutSpy.waitForResult().size(), 0); } void tst_Printing::printRequest() @@ -76,14 +79,14 @@ void tst_Printing::printRequest() CallbackSpy<QByteArray> resultSpy; view.load(QUrl("qrc:///resources/basic_printing_page.html")); - QTRY_VERIFY(loadFinishedSpy.count() == 1); + QTRY_VERIFY(loadFinishedSpy.size() == 1); view.page()->runJavaScript("window.print()"); - QTRY_VERIFY(printRequestedSpy.count() == 1); - QVERIFY(printRequestedSpy2.count() == 1); + QTRY_VERIFY(printRequestedSpy.size() == 1); + QVERIFY(printRequestedSpy2.size() == 1); //check if printing still works view.printToPdf(resultSpy.ref(), layout); const QByteArray data = resultSpy.waitForResult(); - QVERIFY(data.length() > 0); + QVERIFY(data.size() > 0); } #if QT_CONFIG(webengine_system_poppler) @@ -115,8 +118,65 @@ void tst_Printing::printToPdfPoppler() QVERIFY2(pdfPage->search(ustring::from_latin1("Hello Paper World"), rect, page::search_from_top, case_sensitive ), "Could not find text"); } + +void tst_Printing::printFromPdfViewer() +{ + using namespace poppler; + + QWebEngineView view; + view.page()->settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true); + view.page()->settings()->setAttribute(QWebEngineSettings::PdfViewerEnabled, true); + + // Load a basic HTML + QSignalSpy spy(&view, &QWebEngineView::loadFinished); + view.load(QUrl("qrc:///resources/basic_printing_page.html")); + QTRY_COMPARE(spy.size(), 1); + + // Create a PDF + QTemporaryDir tempDir(QDir::tempPath() + "/tst_printing-XXXXXX"); + QVERIFY(tempDir.isValid()); + QString path = tempDir.path() + "/basic_page.pdf"; + QSignalSpy savePdfSpy(view.page(), &QWebEnginePage::pdfPrintingFinished); + view.page()->printToPdf(path); + QTRY_COMPARE(savePdfSpy.size(), 1); + + // Open the new file with the PDF viewer plugin + view.load(QUrl("file://" + path)); + QTRY_COMPARE(spy.size(), 2); + + // Print from the plugin + // loadFinished signal is not reliable when loading a PDF file, because it has multiple phases. + // Workaround: Try to print it a couple of times until the result matches the expected. + CallbackSpy<QByteArray> resultSpy; + bool ok = QTest::qWaitFor([&]() -> bool { + view.printToPdf(resultSpy.ref()); + QByteArray data = resultSpy.waitForResult(); + + // Check if the result contains text from the original basic HTML + // This catches all the typical issues: empty result or printing the WebUI without PDF content. + QScopedPointer<document> pdf(document::load_from_raw_data(data.constData(), data.length())); + QScopedPointer<page> pdfPage(pdf->create_page(0)); + rectf rect; + return pdfPage->search(ustring::from_latin1("Hello Paper World"), rect, page::search_from_top, + case_sensitive); + }, 10000); + QVERIFY(ok); +} #endif +void tst_Printing::interruptPrinting() +{ + QWebEngineView view; + QSignalSpy spy(&view, &QWebEngineView::loadFinished); + view.load(QUrl("qrc:///resources/basic_printing_page.html")); + QTRY_VERIFY(spy.size() == 1); + + QTemporaryDir tempDir(QDir::tempPath() + "/tst_qwebengineview-XXXXXX"); + QVERIFY(tempDir.isValid()); + view.page()->printToPdf(tempDir.path() + "/file.pdf"); + // Navigation stop interrupts print job, preferably do this without crash/assert + view.page()->triggerAction(QWebEnginePage::Stop); +} QTEST_MAIN(tst_Printing) #include "tst_printing.moc" diff --git a/tests/auto/widgets/proxy/CMakeLists.txt b/tests/auto/widgets/proxy/CMakeLists.txt index 0955cee2e..95dc903ed 100644 --- a/tests/auto/widgets/proxy/CMakeLists.txt +++ b/tests/auto/widgets/proxy/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../httpserver/httpserver.cmake) diff --git a/tests/auto/widgets/proxy/tst_proxy.cpp b/tests/auto/widgets/proxy/tst_proxy.cpp index 961d29303..3dc72618c 100644 --- a/tests/auto/widgets/proxy/tst_proxy.cpp +++ b/tests/auto/widgets/proxy/tst_proxy.cpp @@ -8,7 +8,7 @@ #include <QWebEnginePage> #include <QWebEngineView> #include <QWebEngineUrlRequestInterceptor> - +#include <QWebEngineLoadingInfo> struct Interceptor : public QWebEngineUrlRequestInterceptor { @@ -28,6 +28,7 @@ public: private slots: void proxyAuthentication(); void forwardCookie(); + void invalidHostName(); }; @@ -49,7 +50,7 @@ void tst_Proxy::proxyAuthentication() QWebEnginePage page; QSignalSpy successSpy(&server, &ProxyServer::authenticationSuccess); page.load(QUrl("http://www.qt.io")); - QTRY_VERIFY2(successSpy.count() > 0, "Could not get authentication token"); + QTRY_VERIFY2(successSpy.size() > 0, "Could not get authentication token"); } void tst_Proxy::forwardCookie() @@ -69,7 +70,20 @@ void tst_Proxy::forwardCookie() page.setUrlRequestInterceptor(&interceptor); QSignalSpy cookieSpy(&server, &ProxyServer::cookieMatch); page.load(QUrl("http://www.qt.io")); - QTRY_VERIFY2(cookieSpy.count() > 0, "Could not get cookie"); + QTRY_VERIFY2(cookieSpy.size() > 0, "Could not get cookie"); +} + +// Crash test ( https://bugreports.qt.io/browse/QTBUG-113992 ) +void tst_Proxy::invalidHostName() +{ + QNetworkProxy proxy; + proxy.setType(QNetworkProxy::HttpProxy); + proxy.setHostName("999.0.0.0"); + QNetworkProxy::setApplicationProxy(proxy); + QWebEnginePage page; + QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); + page.load(QUrl("http://www.qt.io")); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); } #include "tst_proxy.moc" diff --git a/tests/auto/widgets/proxypac/CMakeLists.txt b/tests/auto/widgets/proxypac/CMakeLists.txt index a261f26bc..f27160cb6 100644 --- a/tests/auto/widgets/proxypac/CMakeLists.txt +++ b/tests/auto/widgets/proxypac/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../httpserver/httpserver.cmake) diff --git a/tests/auto/widgets/proxypac/tst_proxypac.cpp b/tests/auto/widgets/proxypac/tst_proxypac.cpp index d372f77fa..43ccbf028 100644 --- a/tests/auto/widgets/proxypac/tst_proxypac.cpp +++ b/tests/auto/widgets/proxypac/tst_proxypac.cpp @@ -41,16 +41,16 @@ void tst_ProxyPac::proxypac() const bool v8_proxy_resolver_enabled = !fromEnv.contains("--single-process"); page.load(QUrl("http://test.proxy1.com")); - QTRY_COMPARE(proxySpy1.count() >= 1, v8_proxy_resolver_enabled); - QVERIFY(proxySpy2.count() == 0); + QTRY_COMPARE(proxySpy1.size() >= 1, v8_proxy_resolver_enabled); + QVERIFY(proxySpy2.size() == 0); page.load(QUrl("http://test.proxy2.com")); - QTRY_COMPARE(proxySpy2.count() >= 1, v8_proxy_resolver_enabled); + QTRY_COMPARE(proxySpy2.size() >= 1, v8_proxy_resolver_enabled); // check for crash QSignalSpy spyFinished(&page, &QWebEnginePage::loadFinished); page.load(QUrl("https://contribute.qt-project.org")); - QTRY_VERIFY_WITH_TIMEOUT(!spyFinished.isEmpty(), 100000); + QTRY_VERIFY_WITH_TIMEOUT(!spyFinished.isEmpty(), 200000); } diff --git a/tests/auto/widgets/qtbug_110287/CMakeLists.txt b/tests/auto/widgets/qtbug_110287/CMakeLists.txt new file mode 100644 index 000000000..ac7926dc0 --- /dev/null +++ b/tests/auto/widgets/qtbug_110287/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_test(tst_qtbug_110287 + SOURCES + tst_qtbug_110287.cpp + LIBRARIES + Qt::Network + Qt::WebEngineWidgets +) +target_link_options(tst_qtbug_110287 PRIVATE "-Wl,--as-needed") diff --git a/tests/auto/widgets/qtbug_110287/tst_qtbug_110287.cpp b/tests/auto/widgets/qtbug_110287/tst_qtbug_110287.cpp new file mode 100644 index 000000000..9453ae9b8 --- /dev/null +++ b/tests/auto/widgets/qtbug_110287/tst_qtbug_110287.cpp @@ -0,0 +1,41 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <QNetworkAccessManager> +#include <QNetworkRequest> +#include <QSignalSpy> +#include <QTest> +#include <QWebEngineView> + +class tst_qtbug_110287 : public QObject +{ + Q_OBJECT +public: + tst_qtbug_110287() { } + +private slots: + void getAddrInfo(); +}; + +void tst_qtbug_110287::getAddrInfo() +{ + QNetworkAccessManager nam; + QSignalSpy namSpy(&nam, &QNetworkAccessManager::finished); + + QString address("http://www.example.com"); + QScopedPointer<QNetworkReply> reply(nam.get(QNetworkRequest(address))); + + if (!namSpy.wait(25000) || reply->error() != QNetworkReply::NoError) + QSKIP("Couldn't load page from network, skipping test."); + + QWebEngineView view; + QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); + + // load() will trigger system DNS resolution that uses getaddrinfo() + view.load(QUrl(address)); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size() > 0, true, 30000); + QTRY_COMPARE(loadFinishedSpy[0][0].toBool(), true); +} + +#include "tst_qtbug_110287.moc" +QTEST_MAIN(tst_qtbug_110287) diff --git a/tests/auto/widgets/qwebenginedownloadrequest/CMakeLists.txt b/tests/auto/widgets/qwebenginedownloadrequest/CMakeLists.txt index a230f5b13..5b76909b1 100644 --- a/tests/auto/widgets/qwebenginedownloadrequest/CMakeLists.txt +++ b/tests/auto/widgets/qwebenginedownloadrequest/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../httpserver/httpserver.cmake) include(../../util/util.cmake) diff --git a/tests/auto/widgets/qwebenginedownloadrequest/tst_qwebenginedownloadrequest.cpp b/tests/auto/widgets/qwebenginedownloadrequest/tst_qwebenginedownloadrequest.cpp index 4e9a9fecd..c81a27b3a 100644 --- a/tests/auto/widgets/qwebenginedownloadrequest/tst_qwebenginedownloadrequest.cpp +++ b/tests/auto/widgets/qwebenginedownloadrequest/tst_qwebenginedownloadrequest.cpp @@ -108,8 +108,8 @@ void tst_QWebEngineDownloadRequest::cleanup() for (QWebEngineDownloadRequest *item : m_finishedDownloads) { item->deleteLater(); } - QTRY_COMPARE(m_requestedDownloads.count(), 0); - QCOMPARE(m_finishedDownloads.count(), 0); + QTRY_COMPARE(m_requestedDownloads.size(), 0); + QCOMPARE(m_finishedDownloads.size(), 0); QVERIFY(m_server->stop()); // Set download path to default. m_profile->setDownloadPath(""); @@ -128,15 +128,18 @@ void tst_QWebEngineDownloadRequest::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, {}, {}); + auto event1 = + new QContextMenuEvent(QContextMenuEvent::Mouse, linkPos, m_view->mapToGlobal(linkPos)); + auto event2 = new QMouseEvent(QEvent::MouseButtonPress, linkPos, m_view->mapToGlobal(linkPos), + Qt::RightButton, {}, {}); + auto event3 = new QMouseEvent(QEvent::MouseButtonRelease, linkPos, m_view->mapToGlobal(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); + QTRY_COMPARE(menuSpy.size(), 1); m_page->triggerAction(QWebEnginePage::DownloadLinkToDisk); } @@ -412,7 +415,7 @@ void tst_QWebEngineDownloadRequest::downloadLink() ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadRequest *item) { QCOMPARE(item->state(), QWebEngineDownloadRequest::DownloadRequested); QCOMPARE(item->isFinished(), false); - QCOMPARE(item->totalBytes(), -1); + QCOMPARE(item->totalBytes(), fileContents.size()); QCOMPARE(item->receivedBytes(), 0); QCOMPARE(item->interruptReason(), QWebEngineDownloadRequest::NoReason); QCOMPARE(item->isSavePageDownload(), false); @@ -450,7 +453,7 @@ void tst_QWebEngineDownloadRequest::downloadLink() // attribute or not. QSignalSpy loadSpy(m_page, &QWebEnginePage::loadFinished); m_view->load(m_server->url()); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0).toBool(), true); QCOMPARE(indexRequestCount, 1); @@ -458,7 +461,7 @@ void tst_QWebEngineDownloadRequest::downloadLink() // If file is expected to be displayed and not downloaded then end test if (fileAction == FileIsDisplayed) { - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0).toBool(), true); QCOMPARE(acceptedCount, 0); return; @@ -523,7 +526,7 @@ void tst_QWebEngineDownloadRequest::downloadTwoLinks() ScopedConnection sc2 = connect(m_profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadRequest *item) { QCOMPARE(item->state(), QWebEngineDownloadRequest::DownloadRequested); QCOMPARE(item->isFinished(), false); - QCOMPARE(item->totalBytes(), -1); + QCOMPARE(item->totalBytes(), 5); // strlen("fileN") QCOMPARE(item->receivedBytes(), 0); QCOMPARE(item->interruptReason(), QWebEngineDownloadRequest::NoReason); QCOMPARE(item->savePageFormat(), QWebEngineDownloadRequest::UnknownSaveFormat); @@ -548,7 +551,7 @@ void tst_QWebEngineDownloadRequest::downloadTwoLinks() QSignalSpy loadSpy(m_page, &QWebEnginePage::loadFinished); m_view->load(m_server->url()); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0).toBool(), true); // Trigger downloads @@ -640,7 +643,7 @@ void tst_QWebEngineDownloadRequest::downloadPage() // Load some HTML QSignalSpy loadSpy(m_page, &QWebEnginePage::loadFinished); m_page->load(m_server->url()); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0).toBool(), true); QCOMPARE(indexRequestCount, 1); @@ -685,8 +688,8 @@ void tst_QWebEngineDownloadRequest::downloadViaSetUrl() 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); + QTRY_COMPARE(loadSpy.size(), 1); + QTRY_COMPARE(urlSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0).toBool(), true); QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), indexUrl); @@ -696,9 +699,9 @@ void tst_QWebEngineDownloadRequest::downloadViaSetUrl() for (int i = 0; i != 3; ++i) { m_page->setUrl(fileUrl); QCOMPARE(m_page->url(), fileUrl); - QTRY_COMPARE(loadSpy.count(), 1); - QTRY_COMPARE(urlSpy.count(), 2); - QTRY_COMPARE(downloadUrls.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); + QTRY_COMPARE(urlSpy.size(), 2); + QTRY_COMPARE(downloadUrls.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0).toBool(), false); QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), fileUrl); QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), indexUrl); @@ -1129,21 +1132,21 @@ void tst_QWebEngineDownloadRequest::downloadToDirectoryWithFileName() const QString &originalFileName = item->downloadFileName(); item->setDownloadDirectory(downloadDirectory); QCOMPARE(item->downloadDirectory(), downloadDirectory); - QCOMPARE(directorySpy.count(), 1); + QCOMPARE(directorySpy.size(), 1); isUniquifiedFileName = (originalFileName != item->downloadFileName()); - QCOMPARE(fileNameSpy.count(), isUniquifiedFileName ? 1 : 0); + QCOMPARE(fileNameSpy.size(), isUniquifiedFileName ? 1 : 0); } if (!downloadFileName.isEmpty()) { item->setDownloadFileName(downloadFileName); QCOMPARE(item->downloadFileName(), downloadFileName); - QCOMPARE(fileNameSpy.count(), isUniquifiedFileName ? 2 : 1); + QCOMPARE(fileNameSpy.size(), isUniquifiedFileName ? 2 : 1); } if (!downloadDirectory.isEmpty() && !setDirectoryFirst) { item->setDownloadDirectory(downloadDirectory); QCOMPARE(item->downloadDirectory(), downloadDirectory); - QCOMPARE(directorySpy.count(), 1); + QCOMPARE(directorySpy.size(), 1); } item->accept(); @@ -1271,7 +1274,7 @@ void tst_QWebEngineDownloadRequest::downloadDataUrls() QSignalSpy loadSpy(m_page, &QWebEnginePage::loadFinished); m_view->load(m_server->url()); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0).toBool(), true); // Trigger download diff --git a/tests/auto/widgets/qwebenginehistory/CMakeLists.txt b/tests/auto/widgets/qwebenginehistory/CMakeLists.txt index 1a1bd19a3..e277a7326 100644 --- a/tests/auto/widgets/qwebenginehistory/CMakeLists.txt +++ b/tests/auto/widgets/qwebenginehistory/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../util/util.cmake) diff --git a/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp b/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp index f67c2e03d..ad66e972c 100644 --- a/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp +++ b/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp @@ -39,7 +39,7 @@ protected : { loadFinishedSpy->clear(); page->load(QUrl("qrc:/resources/page" + QString::number(nr) + ".html")); - QTRY_COMPARE(loadFinishedSpy->count(), 1); + QTRY_COMPARE(loadFinishedSpy->size(), 1); loadFinishedSpy->clear(); } @@ -150,8 +150,8 @@ void tst_QWebEngineHistory::back() for (int i = histsize;i > 1;i--) { QTRY_COMPARE(toPlainTextSync(page), QString("page") + QString::number(i)); hist->back(); - QTRY_COMPARE(loadFinishedSpy->count(), histsize-i+1); - QTRY_COMPARE(titleChangedSpy.count(), histsize-i+1); + QTRY_COMPARE(loadFinishedSpy->size(), histsize-i+1); + QTRY_COMPARE(titleChangedSpy.size(), histsize-i+1); } //try one more time (too many). crash test hist->back(); @@ -168,15 +168,15 @@ void tst_QWebEngineHistory::forward() while (hist->canGoBack()) { hist->back(); histBackCount++; - QTRY_COMPARE(loadFinishedSpy->count(), histBackCount); + QTRY_COMPARE(loadFinishedSpy->size(), histBackCount); } QSignalSpy titleChangedSpy(page, SIGNAL(titleChanged(const QString&))); for (int i = 1;i < histsize;i++) { QTRY_COMPARE(toPlainTextSync(page), QString("page") + QString::number(i)); hist->forward(); - QTRY_COMPARE(loadFinishedSpy->count(), i+histBackCount); - QTRY_COMPARE(titleChangedSpy.count(), i); + QTRY_COMPARE(loadFinishedSpy->size(), i+histBackCount); + QTRY_COMPARE(titleChangedSpy.size(), i); } //try one more time (too many). crash test hist->forward(); @@ -205,15 +205,15 @@ void tst_QWebEngineHistory::goToItem() QWebEngineHistoryItem current = hist->currentItem(); hist->back(); - QTRY_COMPARE(loadFinishedSpy->count(), 1); + QTRY_COMPARE(loadFinishedSpy->size(), 1); hist->back(); - QTRY_COMPARE(loadFinishedSpy->count(), 2); + QTRY_COMPARE(loadFinishedSpy->size(), 2); QVERIFY(hist->currentItem().title() != current.title()); hist->goToItem(current); - QTRY_COMPARE(loadFinishedSpy->count(), 2); + QTRY_COMPARE(loadFinishedSpy->size(), 2); QTRY_COMPARE(hist->currentItem().title(), current.title()); } @@ -225,7 +225,7 @@ void tst_QWebEngineHistory::items() { QList<QWebEngineHistoryItem> items = hist->items(); //check count - QTRY_COMPARE(histsize, items.count()); + QTRY_COMPARE(histsize, items.size()); //check order for (int i = 1;i <= histsize;i++) { @@ -236,10 +236,10 @@ void tst_QWebEngineHistory::items() void tst_QWebEngineHistory::backForwardItems() { hist->back(); - QTRY_COMPARE(loadFinishedSpy->count(), 1); + QTRY_COMPARE(loadFinishedSpy->size(), 1); hist->back(); - QTRY_COMPARE(loadFinishedSpy->count(), 2); + QTRY_COMPARE(loadFinishedSpy->size(), 2); QTRY_COMPARE(hist->items().size(), 5); QTRY_COMPARE(hist->backItems(100).size(), 2); @@ -297,9 +297,9 @@ void tst_QWebEngineHistory::serialize_2() hist->back(); QTRY_VERIFY(evaluateJavaScriptSync(page, "location.hash").toString().isEmpty()); hist->back(); - QTRY_COMPARE(loadFinishedSpy->count(), 1); + QTRY_COMPARE(loadFinishedSpy->size(), 1); hist->back(); - QTRY_COMPARE(loadFinishedSpy->count(), 2); + QTRY_COMPARE(loadFinishedSpy->size(), 2); //check if current index was changed (make sure that it is not last item) QVERIFY(hist->currentItemIndex() != initialCurrentIndex); //save current index @@ -310,18 +310,18 @@ void tst_QWebEngineHistory::serialize_2() load >> *hist; QVERIFY(load.status() == QDataStream::Ok); // Restoring the history will trigger a load. - QTRY_COMPARE(loadFinishedSpy->count(), 3); + QTRY_COMPARE(loadFinishedSpy->size(), 3); //check current index QTRY_COMPARE(hist->currentItemIndex(), oldCurrentIndex); hist->forward(); - QTRY_COMPARE(loadFinishedSpy->count(), 4); + QTRY_COMPARE(loadFinishedSpy->size(), 4); hist->forward(); - QTRY_COMPARE(loadFinishedSpy->count(), 5); + QTRY_COMPARE(loadFinishedSpy->size(), 5); hist->forward(); // In-page navigation, the last url was the page5.html - QTRY_COMPARE(loadFinishedSpy->count(), 5); + QTRY_COMPARE(loadFinishedSpy->size(), 5); QTRY_COMPARE(hist->currentItemIndex(), initialCurrentIndex); } @@ -429,7 +429,7 @@ void tst_QWebEngineHistory::saveAndRestore_crash_4() QSignalSpy loadFinishedSpy2(page2.data(), SIGNAL(loadFinished(bool))); QDataStream load(&buffer, QIODevice::ReadOnly); load >> *page2->history(); - QTRY_COMPARE(loadFinishedSpy2.count(), 1); + QTRY_COMPARE(loadFinishedSpy2.size(), 1); } void tst_QWebEngineHistory::saveAndRestore_InternalPage() @@ -468,7 +468,7 @@ void tst_QWebEngineHistory::popPushState() QWebEnginePage page; QSignalSpy spyLoadFinished(&page, SIGNAL(loadFinished(bool))); page.setHtml("<html><body>long live Qt!</body></html>"); - QTRY_COMPARE(spyLoadFinished.count(), 1); + QTRY_COMPARE(spyLoadFinished.size(), 1); evaluateJavaScriptSync(&page, script); } @@ -487,9 +487,9 @@ void tst_QWebEngineHistory::clear() QWebEnginePage page2(this); QWebEngineHistory* hist2 = page2.history(); - QVERIFY(hist2->count() == 0); + QCOMPARE(hist2->count(), 0); hist2->clear(); - QVERIFY(hist2->count() == 0); // Do not change anything. + QCOMPARE(hist2->count(), 0); // Do not change anything. } void tst_QWebEngineHistory::historyItemFromDeletedPage() diff --git a/tests/auto/widgets/qwebenginepage/BLACKLIST b/tests/auto/widgets/qwebenginepage/BLACKLIST index 7eb97b3bb..52def48d1 100644 --- a/tests/auto/widgets/qwebenginepage/BLACKLIST +++ b/tests/auto/widgets/qwebenginepage/BLACKLIST @@ -5,11 +5,11 @@ osx windows macos # Can't move cursor (QTBUG-76312) -[acceptNavigationRequestNavigationType] -b2qt arm - [comboBoxPopupPositionAfterMove] macos [comboBoxPopupPositionAfterChildMove] macos + +[backgroundColor] +macos diff --git a/tests/auto/widgets/qwebenginepage/CMakeLists.txt b/tests/auto/widgets/qwebenginepage/CMakeLists.txt index ef270f2bb..f63d6211c 100644 --- a/tests/auto/widgets/qwebenginepage/CMakeLists.txt +++ b/tests/auto/widgets/qwebenginepage/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../httpserver/httpserver.cmake) include(../../util/util.cmake) @@ -8,7 +8,9 @@ qt_internal_add_test(tst_qwebenginepage SOURCES tst_qwebenginepage.cpp LIBRARIES + Qt::CorePrivate Qt::NetworkPrivate + Qt::WebEngineCorePrivate Qt::WebEngineWidgets Test::HttpServer Test::Util @@ -22,6 +24,7 @@ set(tst_qwebenginepage_resource_files "resources/content.html" "resources/dynamicFrame.html" "resources/foo.txt" + "resources/fontaccess.html" "resources/frame_a.html" "resources/frame_c.html" "resources/framedindex.html" diff --git a/tests/auto/widgets/qwebenginepage/resources/fontaccess.html b/tests/auto/widgets/qwebenginepage/resources/fontaccess.html new file mode 100644 index 000000000..1a0fe8af9 --- /dev/null +++ b/tests/auto/widgets/qwebenginepage/resources/fontaccess.html @@ -0,0 +1,14 @@ +<html> +<body onkeypress='onKeyPress()'> +<a>This is test content</a> +<script> +var done = false; +var fonts; +var activated = false; +function onKeyPress() { + activated = true; + window.queryLocalFonts().then(f => { fonts = f; done = true; }); +} +</script> +</body> +</html> diff --git a/tests/auto/widgets/qwebenginepage/resources/reload.html b/tests/auto/widgets/qwebenginepage/resources/reload.html index d9c33dfcd..062d06807 100644 --- a/tests/auto/widgets/qwebenginepage/resources/reload.html +++ b/tests/auto/widgets/qwebenginepage/resources/reload.html @@ -1,6 +1,6 @@ <html> <head> -<meta http-equiv="refresh" content="2"> +<meta http-equiv="refresh" content="2;url=qrc:///resources/content.html"> </head> <body> This is test content diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index 1fdf69307..f1d64776b 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -1,5 +1,5 @@ /* - Copyright (C) 2016 The Qt Company Ltd. + Copyright (C) 2023 The Qt Company Ltd. Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in> Copyright (C) 2010 Holger Hans Peter Freyther @@ -20,7 +20,9 @@ */ #include <widgetutil.h> +#include <QtNetwork/private/qtnetworkglobal_p.h> #include <QtWebEngineCore/qtwebenginecore-config.h> +#include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h> #include <QByteArray> #include <QClipboard> #include <QDir> @@ -34,6 +36,7 @@ #include <QPaintEngine> #include <QPushButton> #include <QScreen> +#include <QWheelEvent> #if defined(QT_STATEMACHINE_LIB) # include <QStateMachine> #endif @@ -47,8 +50,9 @@ #include <qnetworkcookiejar.h> #include <qnetworkreply.h> #include <qnetworkrequest.h> -#include <QtNetwork/private/qtnetwork-config_p.h> +#include <qwebengineclienthints.h> #include <qwebenginedownloadrequest.h> +#include <qwebenginedesktopmediarequest.h> #include <qwebenginefilesystemaccessrequest.h> #include <qwebenginefindtextresult.h> #include <qwebenginefullscreenrequest.h> @@ -63,18 +67,21 @@ #include <qwebenginescript.h> #include <qwebenginescriptcollection.h> #include <qwebenginesettings.h> +#include <qwebengineurlrequestinterceptor.h> #include <qwebengineurlrequestjob.h> #include <qwebengineurlscheme.h> #include <qwebengineurlschemehandler.h> #include <qwebengineview.h> #include <qimagewriter.h> #include <QColorSpace> +#include <QQuickRenderControl> +#include <QQuickWindow> static void removeRecursive(const QString& dirname) { QDir dir(dirname); QFileInfoList entries(dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)); - for (int i = 0; i < entries.count(); ++i) + for (int i = 0; i < entries.size(); ++i) if (entries[i].isDir()) removeRecursive(entries[i].filePath()); else @@ -110,10 +117,13 @@ private Q_SLOTS: void comboBoxPopupPositionAfterChildMove_data(); void comboBoxPopupPositionAfterChildMove(); void acceptNavigationRequest(); + void acceptNavigationRequestWithFormData(); void acceptNavigationRequestNavigationType(); void acceptNavigationRequestRelativeToNothing(); +#ifndef Q_OS_MACOS void geolocationRequestJS_data(); void geolocationRequestJS(); +#endif void loadFinished(); void actionStates(); void pasteImage(); @@ -162,7 +172,8 @@ private Q_SLOTS: void runJavaScriptDisabled(); void runJavaScriptFromSlot(); void fullScreenRequested(); - void quotaRequested(); + void requestQuota_data(); + void requestQuota(); // Tests from tst_QWebEngineFrame @@ -224,7 +235,13 @@ private Q_SLOTS: void notificationPermission_data(); void notificationPermission(); void sendNotification(); + void clipboardReadWritePermissionInitialState_data(); + void clipboardReadWritePermissionInitialState(); + void clipboardReadWritePermission_data(); + void clipboardReadWritePermission(); void contentsSize(); + void localFontAccessPermission_data(); + void localFontAccessPermission(); void setLifecycleState(); void setVisible(); @@ -260,15 +277,24 @@ private Q_SLOTS: void fileSystemAccessDialog(); void localToRemoteNavigation(); + void clientHints_data(); + void clientHints(); + void childFrameInput(); + void openLinkInNewPageWithWebWindowType_data(); + void openLinkInNewPageWithWebWindowType(); + void keepInterceptorAfterNewWindowRequested(); + void chooseDesktopMedia(); private: - static QPoint elementCenter(QWebEnginePage *page, const QString &id); static bool isFalseJavaScriptResult(QWebEnginePage *page, const QString &javaScript); static bool isTrueJavaScriptResult(QWebEnginePage *page, const QString &javaScript); static bool isEmptyListJavaScriptResult(QWebEnginePage *page, const QString &javaScript); QWebEngineView* m_view; QWebEnginePage* m_page; + QScopedPointer<QPointingDevice> s_touchDevice = + QScopedPointer<QPointingDevice>(QTest::createTouchDevice()); + QString tmpDirPath() const { static QString tmpd = QDir::tempPath() + "/tst_qwebenginepage-" @@ -276,17 +302,24 @@ private: return tmpd; } - QScopedPointer<QPointingDevice> s_touchDevice; - void makeClick(QWindow *window, bool withTouch = false, const QPoint &p = QPoint()) { + void makeClick(const QPointer<QWindow> window, bool withTouch = false, + const QPoint &p = QPoint()) + { + QVERIFY2(window, "window is gone"); if (!withTouch) { QTest::mouseClick(window, Qt::LeftButton, Qt::KeyboardModifiers(), p); } else { - if (!s_touchDevice) - s_touchDevice.reset(QTest::createTouchDevice()); QTest::touchEvent(window, s_touchDevice.get()).press(1, p); QTest::touchEvent(window, s_touchDevice.get()).release(1, p); } }; + + void makeScroll(QWidget *target, QPointF pos, QPoint globalPos, QPoint angleDelta) + { + QWheelEvent ev(pos, globalPos, QPoint(0, 0), angleDelta, Qt::NoButton, Qt::NoModifier, + Qt::NoScrollPhase, false); + QGuiApplication::sendEvent(target, &ev); + } }; tst_QWebEnginePage::tst_QWebEnginePage() @@ -392,15 +425,15 @@ void tst_QWebEnginePage::acceptNavigationRequest() page.setHtml(QString("<html><body><form name='tstform' action='foo' method='get'>" "<input type='text'><input type='submit'></form></body></html>"), QUrl("echo:/")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); evaluateJavaScriptSync(&page, "tstform.submit();"); - QTRY_COMPARE(loadSpy.count(), 2); + QTRY_COMPARE(loadSpy.size(), 2); // Content hasn't changed so the form submit will still work page.m_acceptNavigationRequest = true; evaluateJavaScriptSync(&page, "tstform.submit();"); - QTRY_COMPARE(loadSpy.count(), 3); + QTRY_COMPARE(loadSpy.size(), 3); // Now the content has changed QCOMPARE(toPlainTextSync(&page), QString("/foo?")); @@ -436,6 +469,7 @@ private: bool m_allowGeolocation; }; +#ifndef Q_OS_MACOS void tst_QWebEnginePage::geolocationRequestJS_data() { QTest::addColumn<bool>("allowed"); @@ -458,7 +492,7 @@ void tst_QWebEnginePage::geolocationRequestJS() QSignalSpy spyLoadFinished(newPage, SIGNAL(loadFinished(bool))); newPage->setHtml(QString("<html><body>test</body></html>"), QUrl("qrc://secure/origin")); - QTRY_COMPARE_WITH_TIMEOUT(spyLoadFinished.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(spyLoadFinished.size(), 1, 20000); // Geolocation is only enabled for visible WebContents. view.show(); @@ -475,6 +509,7 @@ void tst_QWebEnginePage::geolocationRequestJS() QEXPECT_FAIL("", "No location service available.", Continue); QCOMPARE(result, errorCode); } +#endif void tst_QWebEnginePage::loadFinished() { @@ -485,19 +520,19 @@ void tst_QWebEnginePage::loadFinished() page.load(QUrl("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html," "<head><meta http-equiv='refresh' content='1'></head>foo \">" "<frame src=\"data:text/html,bar\"></frameset>")); - QTRY_COMPARE_WITH_TIMEOUT(spyLoadFinished.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(spyLoadFinished.size(), 1, 20000); QEXPECT_FAIL("", "Behavior change: Load signals are emitted only for the main frame in QtWebEngine.", Continue); - QTRY_VERIFY_WITH_TIMEOUT(spyLoadStarted.count() > 1, 100); + QTRY_VERIFY_WITH_TIMEOUT(spyLoadStarted.size() > 1, 100); QEXPECT_FAIL("", "Behavior change: Load signals are emitted only for the main frame in QtWebEngine.", Continue); - QTRY_VERIFY_WITH_TIMEOUT(spyLoadFinished.count() > 1, 100); + QTRY_VERIFY_WITH_TIMEOUT(spyLoadFinished.size() > 1, 100); spyLoadFinished.clear(); page.load(QUrl("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html," "foo \"><frame src=\"data:text/html,bar\"></frameset>")); - QTRY_COMPARE(spyLoadFinished.count(), 1); - QCOMPARE(spyLoadFinished.count(), 1); + QTRY_COMPARE(spyLoadFinished.size(), 1); + QCOMPARE(spyLoadFinished.size(), 1); } void tst_QWebEnginePage::actionStates() @@ -584,7 +619,7 @@ void tst_QWebEnginePage::consoleOutput() ConsolePage page; // We don't care about the result but want this to be synchronous evaluateJavaScriptSync(&page, "this is not valid JavaScript"); - QCOMPARE(page.messages.count(), 1); + QCOMPARE(page.messages.size(), 1); QCOMPARE(page.lineNumbers.at(0), 1); } @@ -602,6 +637,7 @@ public: QWebEngineNavigationRequest::NavigationType type; QUrl url; bool isMainFrame; + bool hasFormData; }; QList<Navigation> navigations; @@ -619,6 +655,7 @@ private Q_SLOTS: n.url = request.url(); n.type = request.navigationType(); n.isMainFrame = request.isMainFrame(); + n.hasFormData = request.hasFormData(); navigations.append(n); request.accept(); } @@ -636,47 +673,71 @@ private Q_SLOTS: } }; -void tst_QWebEnginePage::acceptNavigationRequestNavigationType() +void tst_QWebEnginePage::acceptNavigationRequestWithFormData() { + QWebEngineProfile profile; + profile.installUrlSchemeHandler("echo", new EchoingUrlSchemeHandler(&profile)); + TestPage page(nullptr, &profile); + QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); + + page.setHtml(QString("<html><body><form name='tstform' action='foo' method='post'>" + "<input type='text'><input type='submit'></form></body></html>"), + QUrl("echo:/")); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); + QCOMPARE(page.navigations[0].type, QWebEngineNavigationRequest::TypedNavigation); + QVERIFY(!page.navigations[0].hasFormData); + evaluateJavaScriptSync(&page, "tstform.submit();"); + QTRY_COMPARE(loadSpy.size(), 2); + QCOMPARE(page.navigations[1].type, QWebEngineNavigationRequest::FormSubmittedNavigation); + QVERIFY(page.navigations[1].hasFormData); + + page.triggerAction(QWebEnginePage::Reload); + QTRY_COMPARE(loadSpy.size(), 3); + QCOMPARE(page.navigations[2].type, QWebEngineNavigationRequest::ReloadNavigation); + QVERIFY(page.navigations[2].hasFormData); +} + +void tst_QWebEnginePage::acceptNavigationRequestNavigationType() +{ TestPage page; QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); page.load(QUrl("qrc:///resources/script.html")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); - QTRY_COMPARE(page.navigations.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); + QTRY_COMPARE(page.navigations.size(), 1); page.load(QUrl("qrc:///resources/content.html")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 2, 20000); - QTRY_COMPARE(page.navigations.count(), 2); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 2, 20000); + QTRY_COMPARE(page.navigations.size(), 2); page.triggerAction(QWebEnginePage::Stop); QVERIFY(page.history()->canGoBack()); page.triggerAction(QWebEnginePage::Back); - QTRY_COMPARE(loadSpy.count(), 3); - QTRY_COMPARE(page.navigations.count(), 3); + QTRY_COMPARE(loadSpy.size(), 3); + QTRY_COMPARE(page.navigations.size(), 3); page.triggerAction(QWebEnginePage::Reload); - QTRY_COMPARE(loadSpy.count(), 4); - QTRY_COMPARE(page.navigations.count(), 4); - - page.load(QUrl("qrc:///resources/reload.html")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 6, 20000); - QTRY_COMPARE(page.navigations.count(), 6); + QTRY_COMPARE(loadSpy.size(), 4); + QTRY_COMPARE(page.navigations.size(), 4); QList<QWebEngineNavigationRequest::NavigationType> expectedList; expectedList << QWebEngineNavigationRequest::TypedNavigation << QWebEngineNavigationRequest::TypedNavigation << QWebEngineNavigationRequest::BackForwardNavigation - << QWebEngineNavigationRequest::ReloadNavigation - << QWebEngineNavigationRequest::TypedNavigation - << QWebEngineNavigationRequest::RedirectNavigation; + << QWebEngineNavigationRequest::ReloadNavigation; // client side redirect + page.load(QUrl("qrc:///resources/reload.html")); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 6, 20000); + QTRY_COMPARE(page.navigations.size(), 6); + expectedList += { QWebEngineNavigationRequest::TypedNavigation, QWebEngineNavigationRequest::RedirectNavigation }; + + page.load(QUrl("qrc:///resources/redirect.html")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 7, 20000); - QTRY_COMPARE(page.navigations.count(), 8); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 7, 20000); + QTRY_COMPARE(page.navigations.size(), 8); expectedList += { QWebEngineNavigationRequest::TypedNavigation, QWebEngineNavigationRequest::RedirectNavigation }; // server side redirect @@ -697,18 +758,18 @@ void tst_QWebEnginePage::acceptNavigationRequestNavigationType() }); QVERIFY(server.start()); page.load(QUrl(server.url("/redirect1.html"))); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 8, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 8, 20000); expectedList += { QWebEngineNavigationRequest::TypedNavigation, QWebEngineNavigationRequest::RedirectNavigation, QWebEngineNavigationRequest::RedirectNavigation }; - for (int i = 0; i < expectedList.count(); ++i) { - QTRY_VERIFY(i < page.navigations.count()); + for (int i = 0; i < expectedList.size(); ++i) { + QTRY_VERIFY(i < page.navigations.size()); QCOMPARE(page.navigations[i].type, expectedList[i]); } - QVERIFY(expectedList.count() == page.navigations.count()); + QVERIFY(expectedList.size() == page.navigations.size()); } // Relative url without base url. @@ -721,18 +782,18 @@ void tst_QWebEnginePage::acceptNavigationRequestRelativeToNothing() page.setHtml(QString("<html><body><a id='link' href='S0'>limited time offer</a></body></html>"), /* baseUrl: */ QUrl()); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); page.runJavaScript(QStringLiteral("document.getElementById(\"link\").click()")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 2, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 2, 20000); page.setHtml(QString("<html><body><a id='link' href='S0'>limited time offer</a></body></html>"), /* baseUrl: */ QString("qrc:/")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 3, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 3, 20000); page.runJavaScript(QStringLiteral("document.getElementById(\"link\").click()")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 4, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 4, 20000); // The two setHtml and the second click are counted, while the // first click is ignored due to the empty base url. - QCOMPARE(page.navigations.count(), 3); + QCOMPARE(page.navigations.size(), 3); QCOMPARE(page.navigations[0].type, QWebEngineNavigationRequest::TypedNavigation); QCOMPARE(page.navigations[1].type, QWebEngineNavigationRequest::TypedNavigation); QCOMPARE(page.navigations[2].type, QWebEngineNavigationRequest::LinkClickedNavigation); @@ -751,11 +812,11 @@ void tst_QWebEnginePage::popupFormSubmission() page.setHtml("<form name='form1' method=get action='' target='myNewWin'>" " <input type='hidden' name='foo' value='bar'>" "</form>", QUrl("echo:")); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 20000); page.runJavaScript("window.open('', 'myNewWin', 'width=500,height=300,toolbar=0');"); evaluateJavaScriptSync(&page, "document.form1.submit();"); - QTRY_COMPARE(windowCreatedSpy.count(), 1); + QTRY_COMPARE(windowCreatedSpy.size(), 1); // The number of popup created should be one. QVERIFY(page.createdWindows.size() == 1); @@ -804,16 +865,16 @@ void tst_QWebEnginePage::multipleProfilesAndLocalStorage() page1.setHtml(QString("<html><body> </body></html>"), QUrl("http://wwww.example.com")); page2.setHtml(QString("<html><body> </body></html>"), QUrl("http://wwww.example.com")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy1.count(), 1, 20000); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy2.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy1.size(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy2.size(), 1, 20000); evaluateJavaScriptSync(&page1, "localStorage.setItem('test', 'value1');"); evaluateJavaScriptSync(&page2, "localStorage.setItem('test', 'value2');"); page1.setHtml(QString("<html><body> </body></html>"), QUrl("http://wwww.example.com")); page2.setHtml(QString("<html><body> </body></html>"), QUrl("http://wwww.example.com")); - QTRY_COMPARE(loadSpy1.count(), 2); - QTRY_COMPARE(loadSpy2.count(), 2); + QTRY_COMPARE(loadSpy1.size(), 2); + QTRY_COMPARE(loadSpy2.size(), 2); QVariant s1 = evaluateJavaScriptSync(&page1, "localStorage.getItem('test')"); QCOMPARE(s1.toString(), QString("value1")); @@ -862,7 +923,7 @@ void tst_QWebEnginePage::textSelection() QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); page.setHtml(content); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); // these actions must exist QVERIFY(page.action(QWebEnginePage::SelectAll) != 0); @@ -889,7 +950,7 @@ void tst_QWebEnginePage::textSelection() // navigate away and check that selection is cleared page.load(QUrl("about:blank")); - QTRY_COMPARE(loadSpy.count(), 2); + QTRY_COMPARE(loadSpy.size(), 2); QVERIFY(!page.hasSelection()); QVERIFY(page.selectedText().isEmpty()); @@ -910,7 +971,7 @@ void tst_QWebEnginePage::backActionUpdate() QVERIFY(!action->isEnabled()); page->load(QUrl("qrc:///resources/framedindex.html")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); QVERIFY(!action->isEnabled()); auto firstAnchorCenterInFrame = [](QWebEnginePage *page, const QString &frameName) { @@ -922,7 +983,7 @@ void tst_QWebEnginePage::backActionUpdate() "return [(rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2];" "})()").toList(); - if (rectList.count() != 2) { + if (rectList.size() != 2) { qWarning("firstAnchorCenterInFrame failed."); return QPoint(); } @@ -949,8 +1010,8 @@ void tst_QWebEnginePage::localStorageVisibility() QSignalSpy loadSpy2(&webPage2, &QWebEnginePage::loadFinished); webPage1.setHtml(QString("<html><body>test</body></html>"), QUrl("http://www.example.com/")); webPage2.setHtml(QString("<html><body>test</body></html>"), QUrl("http://www.example.com/")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy1.count(), 1, 20000); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy2.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy1.size(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy2.size(), 1, 20000); // The attribute determines the visibility of the window.localStorage object. QVERIFY(evaluateJavaScriptSync(&webPage1, QString("(window.localStorage != undefined)")).toBool()); @@ -969,8 +1030,8 @@ void tst_QWebEnginePage::localStorageVisibility() // The object disappears only after reloading. webPage1.triggerAction(QWebEnginePage::Reload); webPage2.triggerAction(QWebEnginePage::Reload); - QTRY_COMPARE(loadSpy1.count(), 2); - QTRY_COMPARE(loadSpy2.count(), 2); + QTRY_COMPARE(loadSpy1.size(), 2); + QTRY_COMPARE(loadSpy2.size(), 2); QVERIFY(!evaluateJavaScriptSync(&webPage1, QString("(window.localStorage != undefined)")).toBool()); QVERIFY(evaluateJavaScriptSync(&webPage2, QString("(window.localStorage != undefined)")).toBool()); } @@ -1071,7 +1132,7 @@ void tst_QWebEnginePage::testJSPrompt() bool res; QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); page.setHtml(QStringLiteral("<html><body></body></html>")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); // OK + QString() res = evaluateJavaScriptSync(&page, @@ -1105,7 +1166,7 @@ void tst_QWebEnginePage::findText() // Showing is required, otherwise all find operations fail. m_view->show(); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); // Select whole page contents. QTRY_VERIFY(m_view->page()->action(QWebEnginePage::SelectAll)->isEnabled()); @@ -1119,7 +1180,7 @@ void tst_QWebEnginePage::findText() QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished); m_view->findText("", {}, callbackSpy.ref()); QVERIFY(callbackSpy.wasCalled()); - QCOMPARE(signalSpy.count(), 1); + QCOMPARE(signalSpy.size(), 1); QTRY_COMPARE(m_view->selectedText(), QString("foo bar")); } @@ -1130,7 +1191,7 @@ void tst_QWebEnginePage::findText() QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished); m_view->findText("Will not be found", {}, callbackSpy.ref()); QCOMPARE(callbackSpy.waitForResult().numberOfMatches(), 0); - QTRY_COMPARE(signalSpy.count(), 1); + QTRY_COMPARE(signalSpy.size(), 1); auto result = signalSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); QCOMPARE(result.numberOfMatches(), 0); QTRY_VERIFY(m_view->selectedText().isEmpty()); @@ -1147,7 +1208,7 @@ void tst_QWebEnginePage::findText() QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished); m_view->findText("foo", {}, callbackSpy.ref()); QVERIFY(callbackSpy.waitForResult().numberOfMatches() > 0); - QTRY_COMPARE(signalSpy.count(), 1); + QTRY_COMPARE(signalSpy.size(), 1); QTRY_VERIFY(m_view->selectedText().isEmpty()); } @@ -1158,7 +1219,7 @@ void tst_QWebEnginePage::findText() QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished); m_view->findText("", {}, callbackSpy.ref()); QTRY_VERIFY(callbackSpy.wasCalled()); - QTRY_COMPARE(signalSpy.count(), 1); + QTRY_COMPARE(signalSpy.size(), 1); QTRY_COMPARE(m_view->selectedText(), QString("foo")); } @@ -1168,7 +1229,7 @@ void tst_QWebEnginePage::findText() QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished); m_view->findText("foo", {}); m_view->findText("foo", {}); - QTRY_COMPARE(signalSpy.count(), 2); + QTRY_COMPARE(signalSpy.size(), 2); QTRY_VERIFY(m_view->selectedText().isEmpty()); QCOMPARE(signalSpy.at(0).value(0).value<QWebEngineFindTextResult>().numberOfMatches(), 0); @@ -1180,7 +1241,7 @@ void tst_QWebEnginePage::findTextResult() { QSignalSpy findTextSpy(m_view->page(), &QWebEnginePage::findTextFinished); auto signalResult = [&findTextSpy]() -> QList<int> { - if (findTextSpy.count() != 1) + if (findTextSpy.size() != 1) return QList<int>({-1, -1}); auto r = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); return QList<int>({ r.numberOfMatches(), r.activeMatch() }); @@ -1192,7 +1253,7 @@ void tst_QWebEnginePage::findTextResult() QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool))); m_view->setHtml(QString("<html><head></head><body><div>foo bar</div></body></html>")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(findTextSync(m_page, ""), false); QCOMPARE(signalResult(), QList<int>({0, 0})); @@ -1221,7 +1282,7 @@ void tst_QWebEnginePage::findTextSuccessiveShouldCallAllCallbacks() CallbackSpy<QWebEngineFindTextResult> spy5; QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool))); m_view->setHtml(QString("<html><head></head><body><div>abcdefg abcdefg abcdefg abcdefg abcdefg</div></body></html>")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); m_page->findText("abcde", {}, spy1.ref()); m_page->findText("abcd", {}, spy2.ref()); m_page->findText("abc", {}, spy3.ref()); @@ -1243,7 +1304,7 @@ void tst_QWebEnginePage::findTextCalledOnMatch() m_view->resize(800, 600); m_view->show(); m_view->setHtml(QString("<html><head></head><body><div>foo bar</div></body></html>")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); // CALLBACK bool callbackCalled = false; @@ -1280,12 +1341,12 @@ void tst_QWebEnginePage::findTextActiveMatchOrdinal() m_view->resize(800, 600); m_view->show(); m_view->setHtml(QString("<html><head></head><body><div>foo bar foo bar foo</div></body></html>")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); // Iterate over all "foo" matches. for (int i = 1; i <= 3; ++i) { m_view->page()->findText("foo", {}); - QTRY_COMPARE(findTextSpy.count(), 1); + QTRY_COMPARE(findTextSpy.size(), 1); result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); QCOMPARE(result.numberOfMatches(), 3); QCOMPARE(result.activeMatch(), i); @@ -1293,28 +1354,28 @@ void tst_QWebEnginePage::findTextActiveMatchOrdinal() // The last match is followed by the fist one. m_view->page()->findText("foo", {}); - QTRY_COMPARE(findTextSpy.count(), 1); + QTRY_COMPARE(findTextSpy.size(), 1); result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); QCOMPARE(result.numberOfMatches(), 3); QCOMPARE(result.activeMatch(), 1); // The first match is preceded by the last one. m_view->page()->findText("foo", QWebEnginePage::FindBackward); - QTRY_COMPARE(findTextSpy.count(), 1); + QTRY_COMPARE(findTextSpy.size(), 1); result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); QCOMPARE(result.numberOfMatches(), 3); QCOMPARE(result.activeMatch(), 3); // Finding another word resets the activeMatch. m_view->page()->findText("bar", {}); - QTRY_COMPARE(findTextSpy.count(), 1); + QTRY_COMPARE(findTextSpy.size(), 1); result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); QCOMPARE(result.numberOfMatches(), 2); QCOMPARE(result.activeMatch(), 1); // If no match activeMatch is 0. m_view->page()->findText("bla", {}); - QTRY_COMPARE(findTextSpy.count(), 1); + QTRY_COMPARE(findTextSpy.size(), 1); result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); QCOMPARE(result.numberOfMatches(), 0); QCOMPARE(result.activeMatch(), 0); @@ -1324,7 +1385,9 @@ static QWindow *findNewTopLevelWindow(const QWindowList &oldTopLevelWindows) { const auto tlws = QGuiApplication::topLevelWindows(); for (auto w : tlws) { - if (!oldTopLevelWindows.contains(w)) { + // note 'offscreen' window is a top-level window + if (!oldTopLevelWindows.contains(w) + && !QQuickRenderControl::renderWindowFor(qobject_cast<QQuickWindow *>(w))) { return w; } } @@ -1340,37 +1403,42 @@ void tst_QWebEnginePage::comboBoxPopupPositionAfterMove_data() void tst_QWebEnginePage::comboBoxPopupPositionAfterMove() { +#if defined(Q_OS_MACOS) && (defined(__arm64__) || defined(__aarch64__)) + QSKIP("This test crashes for Apple M1"); +#endif QWebEngineView view; + QTRY_VERIFY(QGuiApplication::primaryScreen()); view.move(QGuiApplication::primaryScreen()->availableGeometry().topLeft()); view.resize(640, 480); view.show(); - - QSignalSpy loadSpy(&view, SIGNAL(loadFinished(bool))); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + QSignalSpy spyLoadFinished(&view, SIGNAL(loadFinished(bool))); view.setHtml(QLatin1String("<html><head></head><body><select id='foo'>" "<option>fran</option><option>troz</option>" "</select></body></html>")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(spyLoadFinished.size(), 1); const auto oldTlws = QGuiApplication::topLevelWindows(); - QFETCH(bool, withTouch); - QWindow *window = view.windowHandle(); - makeClick(window, withTouch, elementCenter(view.page(), "foo")); - + QPointer<QWindow> window = view.windowHandle(); + auto pos = elementCenter(view.page(), "foo"); + makeClick(window, withTouch, pos); QWindow *popup = nullptr; - QTRY_VERIFY(popup = findNewTopLevelWindow(oldTlws)); + QTRY_VERIFY((popup = findNewTopLevelWindow(oldTlws))); + QVERIFY(QTest::qWaitForWindowExposed(popup)); + QTRY_VERIFY(popup->width() > 0 && popup->height() > 0); QTRY_VERIFY(QGuiApplication::topLevelWindows().contains(popup)); QTRY_VERIFY(!popup->position().isNull()); QPoint popupPos = popup->position(); - + QPointer<QWindow> pw(popup); // Close the popup by clicking somewhere into the page. makeClick(window, withTouch, QPoint(1, 1)); QTRY_VERIFY(!QGuiApplication::topLevelWindows().contains(popup)); - + QTRY_VERIFY(!pw); auto jsViewPosition = [&view]() { QLatin1String script("(function() { return [window.screenX, window.screenY]; })()"); QVariantList posList = evaluateJavaScriptSync(view.page(), script).toList(); - if (posList.count() != 2) { + if (posList.size() != 2) { qWarning("jsViewPosition failed."); return QPoint(); } @@ -1383,7 +1451,8 @@ void tst_QWebEnginePage::comboBoxPopupPositionAfterMove() view.move(view.pos() + offset); QTRY_COMPARE(jsViewPosition(), view.pos()); makeClick(window, withTouch, elementCenter(view.page(), "foo")); - QTRY_VERIFY(popup = findNewTopLevelWindow(oldTlws)); + QTRY_VERIFY((popup = findNewTopLevelWindow(oldTlws))); + QTRY_VERIFY(popup->width() > 0 && popup->height() > 0); QTRY_VERIFY(QGuiApplication::topLevelWindows().contains(popup)); QTRY_VERIFY(!popup->position().isNull()); QCOMPARE(popupPos + offset, popup->position()); @@ -1400,6 +1469,9 @@ void tst_QWebEnginePage::comboBoxPopupPositionAfterChildMove_data() void tst_QWebEnginePage::comboBoxPopupPositionAfterChildMove() { +#if defined(Q_OS_MACOS) && (defined(__arm64__) || defined(__aarch64__)) + QSKIP("This test crashes for Apple M1"); +#endif QWidget mainWidget; mainWidget.setLayout(new QHBoxLayout); @@ -1410,23 +1482,27 @@ void tst_QWebEnginePage::comboBoxPopupPositionAfterChildMove() mainWidget.layout()->addWidget(&view); QScreen *screen = QGuiApplication::primaryScreen(); + Q_ASSERT(screen); mainWidget.move(screen->availableGeometry().topLeft()); mainWidget.resize(640, 480); mainWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&mainWidget)); QSignalSpy loadSpy(&view, SIGNAL(loadFinished(bool))); view.setHtml(QLatin1String("<html><head></head><body><select autofocus id='foo'>" "<option value=\"narf\">narf</option><option>zort</option>" "</select></body></html>")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); const auto oldTlws = QGuiApplication::topLevelWindows(); QFETCH(bool, withTouch); - QWindow *window = view.window()->windowHandle(); + QPointer<QWindow> window = view.window()->windowHandle(); makeClick(window, withTouch, view.mapTo(view.window(), elementCenter(view.page(), "foo"))); QWindow *popup = nullptr; - QTRY_VERIFY(popup = findNewTopLevelWindow(oldTlws)); + QTRY_VERIFY((popup = findNewTopLevelWindow(oldTlws))); + QVERIFY(QTest::qWaitForWindowExposed(popup)); + QTRY_VERIFY(popup->width() > 0 && popup->height() > 0); QTRY_VERIFY(QGuiApplication::topLevelWindows().contains(popup)); QTRY_VERIFY(!popup->position().isNull()); QPoint popupPos = popup->position(); @@ -1451,9 +1527,13 @@ void tst_QWebEnginePage::comboBoxPopupPositionAfterChildMove() QTRY_COMPARE(jsViewWidth(), originalViewWidth - offset); makeClick(window, withTouch, view.mapTo(view.window(), elementCenter(view.page(), "foo"))); - QTRY_VERIFY(popup = findNewTopLevelWindow(oldTlws)); + QTRY_VERIFY((popup = findNewTopLevelWindow(oldTlws))); + QVERIFY(QTest::qWaitForWindowExposed(popup)); + QTRY_VERIFY(popup->width() > 0 && popup->height() > 0); QTRY_VERIFY(!popup->position().isNull()); QCOMPARE(popupPos + QPoint(offset, 0), popup->position()); + makeClick(window, withTouch, QPoint(1, 1)); + QTRY_VERIFY(!QGuiApplication::topLevelWindows().contains(popup)); } #ifdef Q_OS_MACOS @@ -1850,14 +1930,14 @@ void tst_QWebEnginePage::openWindowDefaultSize() page.settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true); view.setUrl(QUrl("about:blank")); view.show(); - QTRY_COMPARE(spyFinished.count(), 1); + QTRY_COMPARE(spyFinished.size(), 1); // Open a default window. page.runJavaScript("window.open()"); - QTRY_COMPARE(windowCreatedSpy.count(), 1); + QTRY_COMPARE(windowCreatedSpy.size(), 1); // Open a too small window. evaluateJavaScriptSync(&page, "window.open('','about:blank','width=10,height=10')"); - QTRY_COMPARE(windowCreatedSpy.count(), 2); + QTRY_COMPARE(windowCreatedSpy.size(), 2); // The number of popups created should be two. QCOMPARE(page.createdWindows.size(), 2); @@ -1928,7 +2008,7 @@ void tst_QWebEnginePage::runJavaScriptDisabled() // Settings changes take effect asynchronously. The load and wait ensure // that the settings are applied by the time we start to execute JavaScript. page.load(QStringLiteral("about:blank")); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 20000); QCOMPARE(evaluateJavaScriptSyncInWorld(&page, QStringLiteral("1+1"), QWebEngineScript::MainWorld), QVariant()); QCOMPARE(evaluateJavaScriptSyncInWorld(&page, QStringLiteral("1+1"), QWebEngineScript::ApplicationWorld), @@ -1945,7 +2025,7 @@ void tst_QWebEnginePage::runJavaScriptFromSlot() page.setHtml("<html><body>" " <input type='text' id='input1' value='QtWebEngine' size='50' />" "</body></html>"); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); bool done = false; connect(&page, &QWebEnginePage::selectionChanged, [&]() { @@ -1969,7 +2049,7 @@ void tst_QWebEnginePage::fullScreenRequested() QSignalSpy loadSpy(&view, SIGNAL(loadFinished(bool))); page->load(QUrl("qrc:///resources/fullscreen.html")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QTRY_VERIFY(isTrueJavaScriptResult(page, "document.webkitFullscreenEnabled")); QVERIFY(isFalseJavaScriptResult(page, "document.webkitIsFullScreen")); @@ -1986,7 +2066,7 @@ void tst_QWebEnginePage::fullScreenRequested() QTest::mouseMove(view.windowHandle(), QPoint(10,10)); QTest::mouseClick(view.windowHandle(), Qt::RightButton); - QTRY_COMPARE(view.findChildren<QMenu *>().count(), 1); + QTRY_COMPARE(view.findChildren<QMenu *>().size(), 1); auto menu = view.findChildren<QMenu *>().first(); QVERIFY(menu->actions().contains(page->action(QWebEnginePage::ExitFullScreen))); @@ -2000,8 +2080,18 @@ void tst_QWebEnginePage::fullScreenRequested() QTRY_VERIFY(isFalseJavaScriptResult(page, "document.webkitIsFullScreen")); } -void tst_QWebEnginePage::quotaRequested() +void tst_QWebEnginePage::requestQuota_data() +{ + QTest::addColumn<QString>("storage"); + QTest::addRow("webkitPersistentStorage") << "navigator.webkitPersistentStorage"; + QTest::addRow("webkitTemporaryStorage") << "navigator.webkitTemporaryStorage"; + +} + +void tst_QWebEnginePage::requestQuota() { + QFETCH(QString, storage); + ConsolePage page; QWebEngineView view; view.setPage(&page); @@ -2009,35 +2099,23 @@ void tst_QWebEnginePage::quotaRequested() page.load(QUrl("qrc:///resources/content.html")); QVERIFY(loadFinishedSpy.wait()); - connect(&page, &QWebEnginePage::quotaRequested, - [] (QWebEngineQuotaRequest request) - { - if (request.requestedSize() <= 5000) - request.accept(); - else - request.reject(); - }); - - evaluateJavaScriptSync(&page, - "navigator.webkitPersistentStorage.requestQuota(1024, function(grantedSize) {" \ - "console.log(grantedSize);" \ - "});"); - QTRY_COMPARE(page.messages.count(), 1); + evaluateJavaScriptSync(&page, QString( + "var storage = %1;" + "storage.requestQuota(1024, function(grantedSize) {" + " console.log(grantedSize);" + "});").arg(storage)); + QTRY_COMPARE(page.messages.size(), 1); QTRY_COMPARE(page.messages[0], QString("1024")); - evaluateJavaScriptSync(&page, - "navigator.webkitPersistentStorage.requestQuota(6000, function(grantedSize) {" \ - "console.log(grantedSize);" \ - "});"); - QTRY_COMPARE(page.messages.count(), 2); - QTRY_COMPARE(page.messages[1], QString("1024")); - - evaluateJavaScriptSync(&page, - "navigator.webkitPersistentStorage.queryUsageAndQuota(function(usedBytes, grantedBytes) {" \ - "console.log(usedBytes + ', ' + grantedBytes);" \ - "});"); - QTRY_COMPARE(page.messages.count(), 3); - QTRY_COMPARE(page.messages[2], QString("0, 1024")); + evaluateJavaScriptSync(&page, QString( + "var storage = %1;" + "storage.queryUsageAndQuota(function(usedBytes, grantedBytes) {" + " console.log(usedBytes);" + " console.log(grantedBytes);" + "});").arg(storage)); + QTRY_COMPARE(page.messages.size(), 3); + QTRY_COMPARE(page.messages[1], QString("0")); + QTRY_VERIFY(page.messages[2].toLongLong() >= 1024); } void tst_QWebEnginePage::symmetricUrl() @@ -2059,7 +2137,7 @@ void tst_QWebEnginePage::symmetricUrl() // loading is _not_ immediate, so the text isn't set just yet. QVERIFY(toPlainTextSync(view.page()).isEmpty()); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 20000); QCOMPARE(view.history()->count(), 1); QCOMPARE(toPlainTextSync(view.page()), QString("Test")); @@ -2073,8 +2151,8 @@ void tst_QWebEnginePage::symmetricUrl() QCOMPARE(view.url(), dataUrl3); // setUrl(dataUrl3) might override the pending load for dataUrl2. Or not. - QTRY_VERIFY(loadFinishedSpy.count() >= 2); - QTRY_VERIFY(loadFinishedSpy.count() <= 3); + QTRY_VERIFY(loadFinishedSpy.size() >= 2); + QTRY_VERIFY(loadFinishedSpy.size() <= 3); // setUrl(dataUrl3) might stop Chromium from adding a navigation entry for dataUrl2, // depending on whether the load of dataUrl2 could be completed in time. @@ -2233,7 +2311,7 @@ void tst_QWebEnginePage::requestedUrlAfterSetAndLoadFailures() const QUrl first("http://abcdef.abcdef/"); page.setUrl(first); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 20000); QCOMPARE(page.url(), first); QCOMPARE(page.requestedUrl(), first); QVERIFY(!spy.at(0).first().toBool()); @@ -2242,7 +2320,7 @@ void tst_QWebEnginePage::requestedUrlAfterSetAndLoadFailures() QVERIFY(first != second); page.load(second); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 2, 20000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 2, 20000); QCOMPARE(page.url(), first); QCOMPARE(page.requestedUrl(), second); QVERIFY(!spy.at(1).first().toBool()); @@ -2288,7 +2366,7 @@ void tst_QWebEnginePage::setHtmlWithImageResource() QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); page.setHtml(html, QUrl("file:///path/to/file")); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 12000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 12000); QCOMPARE(evaluateJavaScriptSync(&page, "document.images.length").toInt(), 1); QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].width").toInt(), 128); @@ -2297,7 +2375,7 @@ void tst_QWebEnginePage::setHtmlWithImageResource() // Now we test the opposite: without a baseUrl as a local file, we can still request qrc resources. page.setHtml(html); - QTRY_COMPARE(spy.count(), 2); + QTRY_COMPARE(spy.size(), 2); QCOMPARE(evaluateJavaScriptSync(&page, "document.images.length").toInt(), 1); QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].width").toInt(), 128); QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].height").toInt(), 128); @@ -2360,7 +2438,7 @@ void tst_QWebEnginePage::setHtmlWithBaseURL() QString("%1/foo.html").arg(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath()))); QSignalSpy spyFinished(&page, &QWebEnginePage::loadFinished); QVERIFY(spyFinished.wait()); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(evaluateJavaScriptSync(&page, "document.images.length").toInt(), 1); QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].width").toInt(), 128); @@ -2423,7 +2501,7 @@ void tst_QWebEnginePage::setHtmlWithModuleImport() QWebEnginePage page; QSignalSpy spy(&page, &QWebEnginePage::loadFinished); page.setHtml(html, server.url()); - QVERIFY(spy.count() || spy.wait()); + QVERIFY(spy.size() || spy.wait()); QCOMPARE(evaluateJavaScriptSync(&page, "fib7"), QVariant(13)); } @@ -2459,7 +2537,7 @@ void tst_QWebEnginePage::baseUrl() QSignalSpy loadSpy(m_page, SIGNAL(loadFinished(bool))); m_page->setHtml(html, loadUrl); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(m_page->url(), url); QEXPECT_FAIL("null", "Slight change: We now translate QUrl() to about:blank for the virtual url, but not for the baseUrl", Continue); QCOMPARE(baseUrlSync(m_page), baseUrl); @@ -2468,7 +2546,8 @@ void tst_QWebEnginePage::baseUrl() void tst_QWebEnginePage::scrollPosition() { // enlarged image in a small viewport, to provoke the scrollbars to appear - QString html("<html><body><img src='qrc:/image.png' height=500 width=500/></body></html>"); + QString html( + "<html><body><img src='qrc:/resources/image.png' height=500 width=500/></body></html>"); QWebEngineView view; view.setFixedSize(200,200); @@ -2478,12 +2557,12 @@ void tst_QWebEnginePage::scrollPosition() QSignalSpy loadSpy(view.page(), SIGNAL(loadFinished(bool))); view.setHtml(html); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); // try to set the scroll offset programmatically view.page()->runJavaScript("window.scrollTo(23, 29);"); - QTRY_COMPARE(view.page()->scrollPosition().x(), 23 * view.windowHandle()->devicePixelRatio()); - QCOMPARE(view.page()->scrollPosition().y(), 29 * view.windowHandle()->devicePixelRatio()); + QTRY_COMPARE(view.page()->scrollPosition().x(), 23); + QCOMPARE(view.page()->scrollPosition().y(), 29); int x = evaluateJavaScriptSync(view.page(), "window.scrollX").toInt(); int y = evaluateJavaScriptSync(view.page(), "window.scrollY").toInt(); @@ -2504,7 +2583,7 @@ void tst_QWebEnginePage::scrollbarsOff() QSignalSpy loadSpy(&view, SIGNAL(loadFinished(bool))); view.setHtml(html); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QVERIFY(evaluateJavaScriptSync(view.page(), "innerWidth == document.documentElement.offsetWidth").toBool()); } @@ -2539,7 +2618,7 @@ void tst_QWebEnginePage::evaluateWillCauseRepaint() QSignalSpy loadSpy(&view, SIGNAL(loadFinished(bool))); view.setHtml(html); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); evaluateJavaScriptSync(view.page(), "document.getElementById('junk').style.display = 'none';"); QSignalSpy repaintSpy(&view, &WebView::repaintRequested); @@ -2633,7 +2712,7 @@ void tst_QWebEnginePage::setUrlToEmpty() expectedLoadFinishedCount++; QVERIFY(spy.wait()); - QCOMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(spy.size(), expectedLoadFinishedCount); QCOMPARE(page.url(), url); QCOMPARE(page.requestedUrl(), url); QCOMPARE(baseUrlSync(&page), url); @@ -2642,7 +2721,7 @@ void tst_QWebEnginePage::setUrlToEmpty() page.setUrl(QUrl()); expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QTRY_COMPARE(spy.size(), expectedLoadFinishedCount); QCOMPARE(page.url(), aboutBlank); QCOMPARE(page.requestedUrl(), QUrl()); QCOMPARE(baseUrlSync(&page), aboutBlank); @@ -2651,7 +2730,7 @@ void tst_QWebEnginePage::setUrlToEmpty() page.setUrl(url); expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QTRY_COMPARE(spy.size(), expectedLoadFinishedCount); QCOMPARE(page.url(), url); QCOMPARE(page.requestedUrl(), url); QCOMPARE(baseUrlSync(&page), url); @@ -2660,7 +2739,7 @@ void tst_QWebEnginePage::setUrlToEmpty() page.load(QUrl()); expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QTRY_COMPARE(spy.size(), expectedLoadFinishedCount); QCOMPARE(page.url(), aboutBlank); QCOMPARE(page.requestedUrl(), QUrl()); QCOMPARE(baseUrlSync(&page), aboutBlank); @@ -2715,9 +2794,9 @@ void tst_QWebEnginePage::setUrlToBadDomain() page.setUrl(url1); - QTRY_COMPARE(urlSpy.count(), 1); - QTRY_COMPARE_WITH_TIMEOUT(titleSpy.count(), 1, 20000); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(urlSpy.size(), 1); + QTRY_COMPARE_WITH_TIMEOUT(titleSpy.size(), 1, 20000); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), url1); QCOMPARE(titleSpy.takeFirst().value(0).toString(), url1.host()); @@ -2728,9 +2807,9 @@ void tst_QWebEnginePage::setUrlToBadDomain() page.setUrl(url2); - QTRY_COMPARE(urlSpy.count(), 1); - QTRY_COMPARE_WITH_TIMEOUT(titleSpy.count(), 1, 20000); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(urlSpy.size(), 1); + QTRY_COMPARE_WITH_TIMEOUT(titleSpy.size(), 1, 20000); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), url2); QCOMPARE(titleSpy.takeFirst().value(0).toString(), url2.host()); @@ -2754,9 +2833,9 @@ void tst_QWebEnginePage::setUrlToBadPort() page.setUrl(url1); - QTRY_COMPARE(urlSpy.count(), 1); - QTRY_COMPARE(titleSpy.count(), 2); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(urlSpy.size(), 1); + QTRY_COMPARE(titleSpy.size(), 2); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), url1); QCOMPARE(titleSpy.takeFirst().value(0).toString(), url1.authority()); @@ -2768,9 +2847,9 @@ void tst_QWebEnginePage::setUrlToBadPort() page.setUrl(url2); - QTRY_COMPARE(urlSpy.count(), 1); - QTRY_COMPARE(titleSpy.count(), 2); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(urlSpy.size(), 1); + QTRY_COMPARE(titleSpy.size(), 2); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), url2); QCOMPARE(titleSpy.takeFirst().value(0).toString(), url2.authority()); @@ -2801,7 +2880,7 @@ void tst_QWebEnginePage::setUrlHistory() m_page->setUrl(QUrl()); expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QTRY_COMPARE(spy.size(), expectedLoadFinishedCount); QCOMPARE(m_page->url(), aboutBlank); QCOMPARE(m_page->requestedUrl(), QUrl()); // Chromium stores navigation entry for every successful loads. The load of the empty page is committed and stored as about:blank. @@ -2810,7 +2889,7 @@ void tst_QWebEnginePage::setUrlHistory() url = QUrl("http://url.invalid/"); m_page->setUrl(url); expectedLoadFinishedCount++; - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), expectedLoadFinishedCount, 20000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), expectedLoadFinishedCount, 20000); // When error page is disabled in case of LoadFail the entry of the unavailable page is not stored. // We expect the url of the previously loaded page here. QCOMPARE(m_page->url(), aboutBlank); @@ -2821,14 +2900,14 @@ void tst_QWebEnginePage::setUrlHistory() url = QUrl("qrc:/resources/test1.html"); m_page->setUrl(url); expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QTRY_COMPARE(spy.size(), expectedLoadFinishedCount); QCOMPARE(m_page->url(), url); QCOMPARE(m_page->requestedUrl(), url); QCOMPARE(collectHistoryUrls(m_page->history()), QStringList() << aboutBlank.toString() << QStringLiteral("qrc:/resources/test1.html")); m_page->setUrl(QUrl()); expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QTRY_COMPARE(spy.size(), expectedLoadFinishedCount); QCOMPARE(m_page->url(), aboutBlank); QCOMPARE(m_page->requestedUrl(), QUrl()); // Chromium stores navigation entry for every successful loads. The load of the empty page is committed and stored as about:blank. @@ -2840,7 +2919,7 @@ void tst_QWebEnginePage::setUrlHistory() url = QUrl("qrc:/resources/test1.html"); m_page->setUrl(url); expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QTRY_COMPARE(spy.size(), expectedLoadFinishedCount); QCOMPARE(m_page->url(), url); QCOMPARE(m_page->requestedUrl(), url); // The history count DOES change since the about:blank is in the list. @@ -2853,7 +2932,7 @@ void tst_QWebEnginePage::setUrlHistory() url = QUrl("qrc:/resources/test2.html"); m_page->setUrl(url); expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QTRY_COMPARE(spy.size(), expectedLoadFinishedCount); QCOMPARE(m_page->url(), url); QCOMPARE(m_page->requestedUrl(), url); QCOMPARE(collectHistoryUrls(m_page->history()), QStringList() @@ -2876,22 +2955,22 @@ void tst_QWebEnginePage::setUrlUsingStateObject() url = QUrl("qrc:/resources/test1.html"); m_page->setUrl(url); expectedUrlChangeCount++; - QTRY_COMPARE(urlChangedSpy.count(), expectedUrlChangeCount); + QTRY_COMPARE(urlChangedSpy.size(), expectedUrlChangeCount); QCOMPARE(m_page->url(), url); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QCOMPARE(m_page->url(), url); QCOMPARE(m_page->history()->count(), 1); evaluateJavaScriptSync(m_page, "window.history.pushState(null, 'push', 'navigate/to/here')"); expectedUrlChangeCount++; - QTRY_COMPARE(urlChangedSpy.count(), expectedUrlChangeCount); + QTRY_COMPARE(urlChangedSpy.size(), expectedUrlChangeCount); QCOMPARE(m_page->url(), QUrl("qrc:/resources/navigate/to/here")); QCOMPARE(m_page->history()->count(), 2); QVERIFY(m_page->history()->canGoBack()); evaluateJavaScriptSync(m_page, "window.history.replaceState(null, 'replace', 'another/location')"); expectedUrlChangeCount++; - QTRY_COMPARE(urlChangedSpy.count(), expectedUrlChangeCount); + QTRY_COMPARE(urlChangedSpy.size(), expectedUrlChangeCount); QCOMPARE(m_page->url(), QUrl("qrc:/resources/navigate/to/another/location")); QCOMPARE(m_page->history()->count(), 2); QVERIFY(!m_page->history()->canGoForward()); @@ -2899,7 +2978,7 @@ void tst_QWebEnginePage::setUrlUsingStateObject() evaluateJavaScriptSync(m_page, "window.history.back()"); expectedUrlChangeCount++; - QTRY_COMPARE(urlChangedSpy.count(), expectedUrlChangeCount); + QTRY_COMPARE(urlChangedSpy.size(), expectedUrlChangeCount); QCOMPARE(m_page->url(), QUrl("qrc:/resources/test1.html")); QVERIFY(m_page->history()->canGoForward()); QVERIFY(!m_page->history()->canGoBack()); @@ -2928,9 +3007,9 @@ void tst_QWebEnginePage::setUrlThenLoads() QSignalSpy finishedSpy(m_page, SIGNAL(loadFinished(bool))); m_page->setUrl(url); - QTRY_COMPARE(startedSpy.count(), 1); - QTRY_COMPARE(urlChangedSpy.count(), 1); - QTRY_COMPARE(finishedSpy.count(), 1); + QTRY_COMPARE(startedSpy.size(), 1); + QTRY_COMPARE(urlChangedSpy.size(), 1); + QTRY_COMPARE(finishedSpy.size(), 1); QVERIFY(finishedSpy.at(0).first().toBool()); QCOMPARE(m_page->url(), url); QCOMPARE(m_page->requestedUrl(), url); @@ -2944,11 +3023,11 @@ void tst_QWebEnginePage::setUrlThenLoads() QTRY_COMPARE(m_page->requestedUrl(), urlToLoad1); // baseUrlSync spins an event loop and this sometimes return the next result. // QCOMPARE(baseUrlSync(m_page), baseUrl); - QTRY_COMPARE(startedSpy.count(), 2); + QTRY_COMPARE(startedSpy.size(), 2); // After first URL changed. - QTRY_COMPARE(urlChangedSpy.count(), 2); - QTRY_COMPARE(finishedSpy.count(), 2); + QTRY_COMPARE(urlChangedSpy.size(), 2); + QTRY_COMPARE(finishedSpy.size(), 2); QVERIFY(finishedSpy.at(1).first().toBool()); QCOMPARE(m_page->url(), urlToLoad1); QCOMPARE(m_page->requestedUrl(), urlToLoad1); @@ -2958,11 +3037,11 @@ void tst_QWebEnginePage::setUrlThenLoads() QCOMPARE(m_page->url(), urlToLoad1); QCOMPARE(m_page->requestedUrl(), urlToLoad2); QCOMPARE(baseUrlSync(m_page), extractBaseUrl(urlToLoad1)); - QTRY_COMPARE(startedSpy.count(), 3); + QTRY_COMPARE(startedSpy.size(), 3); // After second URL changed. - QTRY_COMPARE(urlChangedSpy.count(), 3); - QTRY_COMPARE(finishedSpy.count(), 3); + QTRY_COMPARE(urlChangedSpy.size(), 3); + QTRY_COMPARE(finishedSpy.size(), 3); QVERIFY(finishedSpy.at(2).first().toBool()); QCOMPARE(m_page->url(), urlToLoad2); QCOMPARE(m_page->requestedUrl(), urlToLoad2); @@ -3050,7 +3129,7 @@ void tst_QWebEnginePage::loadInSignalHandlers() URLSetter setter(m_page, signal, type, urlForSetter); QSignalSpy spy(&setter, &URLSetter::finished); m_page->load(url); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 20000); + QTRY_VERIFY_WITH_TIMEOUT(spy.size() >= 1, 20000); QCOMPARE(m_page->url(), urlForSetter); } @@ -3061,31 +3140,31 @@ void tst_QWebEnginePage::loadFromQrc() // Standard case. page.load(QStringLiteral("qrc:///resources/foo.txt")); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().value(0).toBool(), true); QCOMPARE(toPlainTextSync(&page), QStringLiteral("foo\n")); // Query and fragment parts are ignored. page.load(QStringLiteral("qrc:///resources/bar.txt?foo=1#bar")); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().value(0).toBool(), true); QCOMPARE(toPlainTextSync(&page), QStringLiteral("bar\n")); // Literal spaces are OK. page.load(QStringLiteral("qrc:///resources/path with spaces.txt")); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().value(0).toBool(), true); QCOMPARE(toPlainTextSync(&page), QStringLiteral("contents with spaces\n")); // Escaped spaces are OK too. page.load(QStringLiteral("qrc:///resources/path%20with%20spaces.txt")); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().value(0).toBool(), true); QCOMPARE(toPlainTextSync(&page), QStringLiteral("contents with spaces\n")); // Resource not found, loading fails. page.load(QStringLiteral("qrc:///nope")); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 10000); QCOMPARE(spy.takeFirst().value(0).toBool(), false); } @@ -3102,7 +3181,7 @@ void tst_QWebEnginePage::restoreHistory() QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); page.load(QUrl(QStringLiteral("qrc:/resources/test1.html"))); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(page.webChannel(), &channel); QVERIFY(page.scripts().contains(script)); @@ -3112,7 +3191,7 @@ void tst_QWebEnginePage::restoreHistory() out << *page.history(); QDataStream in(&data, QIODevice::ReadOnly); in >> *page.history(); - QTRY_COMPARE(spy.count(), 2); + QTRY_COMPARE(spy.size(), 2); QCOMPARE(page.webChannel(), &channel); QVERIFY(page.scripts().contains(script)); @@ -3135,19 +3214,19 @@ void tst_QWebEnginePage::toPlainTextLoadFinishedRace() QSignalSpy spy(page.data(), SIGNAL(loadFinished(bool))); page->load(QUrl("data:text/plain,foobarbaz")); - QTRY_VERIFY(spy.count() == 1); + QTRY_VERIFY(spy.size() == 1); QCOMPARE(toPlainTextSync(page.data()), QString("foobarbaz")); page->load(QUrl("http://fail.invalid/")); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 2, 20000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 2, 20000); QString s = toPlainTextSync(page.data()); QVERIFY(s.contains("foobarbaz") == !enableErrorPage); page->load(QUrl("data:text/plain,lalala")); - QTRY_COMPARE(spy.count(), 3); + QTRY_COMPARE(spy.size(), 3); QTRY_COMPARE(toPlainTextSync(page.data()), QString("lalala")); page.reset(); - QCOMPARE(spy.count(), 3); + QCOMPARE(spy.size(), 3); } void tst_QWebEnginePage::setZoomFactor() @@ -3161,7 +3240,7 @@ void tst_QWebEnginePage::setZoomFactor() const QUrl url1("qrc:/resources/test1.html"), url2(QUrl("qrc:/resources/test2.html")); page.load(url1); - QTRY_COMPARE(page.loadSpy.count(), 1); + QTRY_COMPARE(page.loadSpy.size(), 1); QVERIFY(page.loadSpy.at(0).first().toBool()); QCOMPARE(page.zoomFactor(), 2.5); @@ -3179,7 +3258,7 @@ void tst_QWebEnginePage::setZoomFactor() }) { auto &&page = *p.first; auto zoomFactor = p.second; page.load(url2); - QTRY_COMPARE(page.loadSpy.count(), 1); + QTRY_COMPARE(page.loadSpy.size(), 1); QVERIFY(page.loadSpy.last().first().toBool()); QCOMPARE(page.zoomFactor(), zoomFactor); } @@ -3207,17 +3286,20 @@ void tst_QWebEnginePage::mouseButtonTranslation() view.resize(640, 480); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); - QTRY_VERIFY(spy.count() == 1); + QTRY_VERIFY(spy.size() == 1); QVERIFY(view.focusProxy() != nullptr); - QMouseEvent evpres(QEvent::MouseButtonPress, view.rect().center(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + const QPoint mousePos = view.rect().center(); + QMouseEvent evpres(QEvent::MouseButtonPress, mousePos, view.mapToGlobal(mousePos), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QGuiApplication::sendEvent(view.focusProxy(), &evpres); QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "lastEvent.button").toInt(), 0); QCOMPARE(evaluateJavaScriptSync(view.page(), "lastEvent.buttons").toInt(), 1); - QMouseEvent evpres2(QEvent::MouseButtonPress, view.rect().center(), Qt::RightButton, Qt::LeftButton | Qt::RightButton, Qt::NoModifier); + QMouseEvent evpres2(QEvent::MouseButtonPress, mousePos, view.mapToGlobal(mousePos), + Qt::RightButton, Qt::LeftButton | Qt::RightButton, Qt::NoModifier); QGuiApplication::sendEvent(view.focusProxy(), &evpres2); QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "lastEvent.button").toInt(), 2); @@ -3246,34 +3328,17 @@ void tst_QWebEnginePage::mouseMovementProperties() loadFinishedSpy.wait(); QTest::mouseMove(&view, QPoint(20, 20)); - QTRY_COMPARE(page.messages.count(), 1); + QTRY_COMPARE(page.messages.size(), 1); QTest::mouseMove(&view, QPoint(30, 30)); - QTRY_COMPARE(page.messages.count(), 2); + QTRY_COMPARE(page.messages.size(), 2); QTRY_COMPARE(page.messages[1], QString("10, 10")); QTest::mouseMove(&view, QPoint(20, 20)); - QTRY_COMPARE(page.messages.count(), 3); + QTRY_COMPARE(page.messages.size(), 3); QTRY_COMPARE(page.messages[2], QString("-10, -10")); } -QPoint tst_QWebEnginePage::elementCenter(QWebEnginePage *page, const QString &id) -{ - QVariantList rectList = evaluateJavaScriptSync(page, - "(function(){" - "var elem = document.getElementById('" + id + "');" - "var rect = elem.getBoundingClientRect();" - "return [(rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2];" - "})()").toList(); - - if (rectList.count() != 2) { - qWarning("elementCenter failed."); - return QPoint(); - } - - return QPoint(rectList.at(0).toInt(), rectList.at(1).toInt()); -} - void tst_QWebEnginePage::viewSource() { TestPage page; @@ -3282,12 +3347,12 @@ void tst_QWebEnginePage::viewSource() const QUrl url("qrc:/resources/test1.html"); page.load(url); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QCOMPARE(page.title(), QStringLiteral("Test page 1")); QVERIFY(page.action(QWebEnginePage::ViewSource)->isEnabled()); page.triggerAction(QWebEnginePage::ViewSource); - QTRY_COMPARE(windowCreatedSpy.count(), 1); + QTRY_COMPARE(windowCreatedSpy.size(), 1); QCOMPARE(page.createdWindows.size(), 1); QTRY_COMPARE(page.createdWindows[0]->url().toString(), QStringLiteral("view-source:%1").arg(url.toString())); @@ -3342,7 +3407,7 @@ void tst_QWebEnginePage::viewSourceURL() QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); page.load(userInputUrl); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 12000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 12000); QList<QVariant> arguments = loadFinishedSpy.takeFirst(); QCOMPARE(arguments.at(0).toBool(), loadSucceed); @@ -3377,7 +3442,7 @@ void tst_QWebEnginePage::viewSourceCredentials() QVERIFY(page.action(QWebEnginePage::ViewSource)->isEnabled()); page.triggerAction(QWebEnginePage::ViewSource); - QTRY_COMPARE(windowCreatedSpy.count(), 1); + QTRY_COMPARE(windowCreatedSpy.size(), 1); QCOMPARE(page.createdWindows.size(), 1); QTRY_COMPARE(page.createdWindows[0]->url().toString(), QString("view-source:" + url.toDisplayString(QUrl::RemoveUserInfo))); @@ -3399,7 +3464,7 @@ void tst_QWebEnginePage::proxyConfigWithUnexpectedHostPortPair() QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool))); m_page->load(QStringLiteral("http://127.0.0.1:245/")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); } void tst_QWebEnginePage::registerProtocolHandler_data() @@ -3432,7 +3497,7 @@ void tst_QWebEnginePage::registerProtocolHandler() QSignalSpy permissionSpy(&page, &QWebEnginePage::registerProtocolHandlerRequested); page.setUrl(server.url("/")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0).toBool(), true); QString callFormat = QStringLiteral("window.navigator.registerProtocolHandler(\"%1\", \"%2\", \"%3\")"); @@ -3442,7 +3507,7 @@ void tst_QWebEnginePage::registerProtocolHandler() QString call = callFormat.arg(scheme).arg(url).arg(title); page.runJavaScript(call); - QTRY_COMPARE(permissionSpy.count(), 1); + QTRY_COMPARE(permissionSpy.size(), 1); auto request = permissionSpy.takeFirst().value(0).value<QWebEngineRegisterProtocolHandlerRequest>(); QCOMPARE(request.origin(), QUrl(url)); QCOMPARE(request.scheme(), scheme); @@ -3453,7 +3518,7 @@ void tst_QWebEnginePage::registerProtocolHandler() page.runJavaScript(QStringLiteral("document.getElementById(\"link\").click()")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0).toBool(), permission); QCOMPARE(mailRequestCount, permission ? 1 : 0); QVERIFY(server.stop()); @@ -3469,7 +3534,7 @@ void tst_QWebEnginePage::dataURLFragment() m_page->setHtml("<html><body>" "<a id='link' href='#anchor'>anchor</a>" "</body></html>", QUrl("http://test.qt.io/mytest.html")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QTest::mouseClick(m_view->focusProxy(), Qt::LeftButton, {}, elementCenter(m_page, "link")); QVERIFY(urlChangedSpy.wait()); @@ -3493,7 +3558,7 @@ void tst_QWebEnginePage::devTools() QCOMPARE(devToolsPage.devToolsPage(), nullptr); QCOMPARE(devToolsPage.inspectedPage(), &inspectedPage1); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 90000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 90000); QVERIFY(spy.takeFirst().value(0).toBool()); devToolsPage.setInspectedPage(&inspectedPage2); @@ -3505,7 +3570,7 @@ void tst_QWebEnginePage::devTools() QCOMPARE(devToolsPage.devToolsPage(), nullptr); QCOMPARE(devToolsPage.inspectedPage(), &inspectedPage2); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 90000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 90000); QVERIFY(spy.takeFirst().value(0).toBool()); devToolsPage.setInspectedPage(nullptr); @@ -3516,6 +3581,8 @@ void tst_QWebEnginePage::devTools() QCOMPARE(inspectedPage2.inspectedPage(), nullptr); QCOMPARE(devToolsPage.devToolsPage(), nullptr); QCOMPARE(devToolsPage.inspectedPage(), nullptr); + + QVERIFY(!inspectedPage1.devToolsId().isEmpty()); } void tst_QWebEnginePage::openLinkInDifferentProfile() @@ -3537,11 +3604,11 @@ void tst_QWebEnginePage::openLinkInDifferentProfile() page1.setHtml("<html><body>" "<a id='link' href='hello'>link</a>" "</body></html>", QUrl("echo:/")); - QTRY_COMPARE(spy1.count(), 1); + QTRY_COMPARE(spy1.size(), 1); QVERIFY(spy1.takeFirst().value(0).toBool()); targetPage = &page2; QTest::mouseClick(view.focusProxy(), Qt::MiddleButton, {}, elementCenter(&page1, "link")); - QTRY_COMPARE(spy2.count(), 1); + QTRY_COMPARE(spy2.size(), 1); QVERIFY(spy2.takeFirst().value(0).toBool()); } @@ -3651,7 +3718,7 @@ void tst_QWebEnginePage::openLinkInNewPage() page1.setHtml("<html><body>" "<a id='link' href='hello' target='_blank'>link</a>" "</body></html>", QUrl("echo:/")); - QTRY_COMPARE(page1.spy.count(), 1); + QTRY_COMPARE(page1.spy.size(), 1); QVERIFY(page1.spy.takeFirst().value(0).toBool()); switch (decision) { @@ -3666,7 +3733,7 @@ void tst_QWebEnginePage::openLinkInNewPage() break; } - Qt::MouseButton button; + Qt::MouseButton button = Qt::NoButton; switch (cause) { case Cause::TargetBlank: button = Qt::LeftButton; @@ -3681,13 +3748,13 @@ void tst_QWebEnginePage::openLinkInNewPage() case Effect::Blocked: // Test nothing new loaded QTest::qWait(500); - QCOMPARE(page1.spy.count(), 0); - QCOMPARE(page2.spy.count(), 0); + QCOMPARE(page1.spy.size(), 0); + QCOMPARE(page2.spy.size(), 0); break; case Effect::LoadInSelf: - QTRY_COMPARE(page1.spy.count(), 1); + QTRY_COMPARE(page1.spy.size(), 1); QVERIFY(page1.spy.takeFirst().value(0).toBool()); - QCOMPARE(page2.spy.count(), 0); + QCOMPARE(page2.spy.size(), 0); if (decision == Decision::ReturnSelf && cause == Cause::TargetBlank) // History was discarded due to AddNewContents QCOMPARE(page1.history()->count(), 1); @@ -3696,9 +3763,9 @@ void tst_QWebEnginePage::openLinkInNewPage() QCOMPARE(page2.history()->count(), 0); break; case Effect::LoadInOther: - QTRY_COMPARE(page2.spy.count(), 1); + QTRY_COMPARE(page2.spy.size(), 1); QVERIFY(page2.spy.takeFirst().value(0).toBool()); - QCOMPARE(page1.spy.count(), 0); + QCOMPARE(page1.spy.size(), 0); QCOMPARE(page1.history()->count(), 1); QCOMPARE(page2.history()->count(), 1); break; @@ -3720,7 +3787,7 @@ void tst_QWebEnginePage::dynamicFrame() page.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); QSignalSpy spy(&page, &QWebEnginePage::loadFinished); page.load(QStringLiteral("qrc:/resources/dynamicFrame.html")); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(toPlainTextSync(&page).trimmed(), QStringLiteral("foo")); } @@ -3796,7 +3863,7 @@ void tst_QWebEnginePage::notificationPermission() QSignalSpy spy(&page, &QWebEnginePage::loadFinished); page.setHtml(QString("<html><body>Test</body></html>"), baseUrl); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), setOnInit ? permission : QLatin1String("default")); @@ -3854,6 +3921,149 @@ void tst_QWebEnginePage::sendNotification() QTRY_VERIFY2(page.messages.contains("onclose"), page.messages.join("\n").toLatin1().constData()); } +static QString clipboardPermissionQuery(QString variableName, QString permissionName) +{ + return QString("var %1; navigator.permissions.query({ name:'%2' }).then((p) => { %1 = p.state; " + "});") + .arg(variableName) + .arg(permissionName); +} + + +void tst_QWebEnginePage::clipboardReadWritePermissionInitialState_data() +{ + QTest::addColumn<bool>("canAccessClipboard"); + QTest::addColumn<bool>("canPaste"); + QTest::addColumn<QString>("permission"); + QTest::newRow("access and paste should grant") << true << true << "granted"; + QTest::newRow("no access should prompt") << false << true << "prompt"; + QTest::newRow("no paste should prompt") << true << false << "prompt"; + QTest::newRow("no access or paste should prompt") << false << false << "prompt"; +} + +void tst_QWebEnginePage::clipboardReadWritePermissionInitialState() +{ + QFETCH(bool, canAccessClipboard); + QFETCH(bool, canPaste); + QFETCH(QString, permission); + + QWebEngineProfile otr; + QWebEngineView view(&otr); + QWebEnginePage &page = *view.page(); + view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true); + page.settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, + canAccessClipboard); + page.settings()->setAttribute(QWebEngineSettings::JavascriptCanPaste, canPaste); + + QSignalSpy spy(&page, &QWebEnginePage::loadFinished); + QUrl baseUrl("https://www.example.com/somepage.html"); + page.setHtml(QString("<html><body>Test</body></html>"), baseUrl); + QTRY_COMPARE(spy.size(), 1); + + evaluateJavaScriptSync(&page, clipboardPermissionQuery("readPermission", "clipboard-read")); + QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("readPermission")), permission); + evaluateJavaScriptSync(&page, clipboardPermissionQuery("writePermission", "clipboard-write")); + QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("writePermission")), permission); +} + +void tst_QWebEnginePage::clipboardReadWritePermission_data() +{ + QTest::addColumn<bool>("canAccessClipboard"); + QTest::addColumn<QWebEnginePage::PermissionPolicy>("initialPolicy"); + QTest::addColumn<QString>("initialPermission"); + QTest::addColumn<QWebEnginePage::PermissionPolicy>("requestPolicy"); + QTest::addColumn<QString>("finalPermission"); + + QTest::newRow("noAccessGrantGrant") + << false << QWebEnginePage::PermissionGrantedByUser << "granted" + << QWebEnginePage::PermissionGrantedByUser << "granted"; + QTest::newRow("noAccessGrantDeny") + << false << QWebEnginePage::PermissionGrantedByUser << "granted" + << QWebEnginePage::PermissionDeniedByUser << "denied"; + QTest::newRow("noAccessDenyGrant") + << false << QWebEnginePage::PermissionDeniedByUser << "denied" + << QWebEnginePage::PermissionGrantedByUser << "granted"; + QTest::newRow("noAccessDenyDeny") << false << QWebEnginePage::PermissionDeniedByUser << "denied" + << QWebEnginePage::PermissionDeniedByUser << "denied"; + QTest::newRow("noAccessAskGrant") << false << QWebEnginePage::PermissionUnknown << "prompt" + << QWebEnginePage::PermissionGrantedByUser << "granted"; + + // All policies are ignored and overridden by setting JsCanAccessClipboard and JsCanPaste to + // true + QTest::newRow("accessGrantGrant") + << true << QWebEnginePage::PermissionGrantedByUser << "granted" + << QWebEnginePage::PermissionGrantedByUser << "granted"; + QTest::newRow("accessDenyDeny") << true << QWebEnginePage::PermissionDeniedByUser << "granted" + << QWebEnginePage::PermissionDeniedByUser << "granted"; + QTest::newRow("accessAskAsk") << true << QWebEnginePage::PermissionUnknown << "granted" + << QWebEnginePage::PermissionUnknown << "granted"; +} + +void tst_QWebEnginePage::clipboardReadWritePermission() +{ + QFETCH(bool, canAccessClipboard); + QFETCH(QWebEnginePage::PermissionPolicy, initialPolicy); + QFETCH(QString, initialPermission); + QFETCH(QWebEnginePage::PermissionPolicy, requestPolicy); + QFETCH(QString, finalPermission); + + QWebEngineProfile otr; + QWebEngineView view(&otr); + QWebEnginePage &page = *view.page(); + view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true); + page.settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, + canAccessClipboard); + page.settings()->setAttribute(QWebEngineSettings::JavascriptCanPaste, true); + + QUrl baseUrl("https://www.example.com/somepage.html"); + + int permissionRequestCount = 0; + bool errorState = false; + + // if JavascriptCanAccessClipboard is true, this never fires + connect(&page, &QWebEnginePage::featurePermissionRequested, &page, + [&](const QUrl &o, QWebEnginePage::Feature f) { + if (f != QWebEnginePage::ClipboardReadWrite) + return; + if (o != baseUrl.url(QUrl::RemoveFilename)) { + qWarning() << "Unexpected case. Can't proceed." << o; + errorState = true; + return; + } + permissionRequestCount++; + page.setFeaturePermission(o, f, requestPolicy); + }); + + page.setFeaturePermission(baseUrl, QWebEnginePage::ClipboardReadWrite, initialPolicy); + + QSignalSpy spy(&page, &QWebEnginePage::loadFinished); + page.setHtml(QString("<html><body>Test</body></html>"), baseUrl); + QTRY_COMPARE(spy.size(), 1); + + evaluateJavaScriptSync(&page, clipboardPermissionQuery("readPermission", "clipboard-read")); + QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("readPermission")), initialPermission); + evaluateJavaScriptSync(&page, clipboardPermissionQuery("writePermission", "clipboard-write")); + QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("writePermission")), initialPermission); + + auto triggerRequest = [&page](QString variableName, QString apiCall) + { + auto js = QString("var %1; navigator.clipboard.%2.then((v) => { %1 = 'granted' }, (v) => { %1 = " + "'denied' });") + .arg(variableName) + .arg(apiCall); + evaluateJavaScriptSync(&page, js); + }; + + // permission is not 'remembered' from api standpoint, hence is not suppressed on explicit call + // from JS + triggerRequest("readState", "readText()"); + QTRY_COMPARE(evaluateJavaScriptSync(&page, "readState"), finalPermission); + triggerRequest("writeState", "writeText('foo')"); + QTRY_COMPARE(evaluateJavaScriptSync(&page, "writeState"), finalPermission); + QCOMPARE(permissionRequestCount, canAccessClipboard ? 0 : 2); + QVERIFY(!errorState); +} + void tst_QWebEnginePage::contentsSize() { m_view->resize(800, 600); @@ -3864,8 +4074,8 @@ void tst_QWebEnginePage::contentsSize() m_view->setHtml(QString("<html><body style=\"width: 1600px; height: 1200px;\"><p>hi</p></body></html>")); - QTRY_COMPARE(loadSpy.count(), 1); - QTRY_COMPARE(contentsSizeChangedSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); + QTRY_COMPARE(contentsSizeChangedSpy.size(), 1); // Verify the page's contents size is not limited by the view's size. QCOMPARE(m_page->contentsSize().width(), 1608); @@ -3882,6 +4092,60 @@ void tst_QWebEnginePage::contentsSize() QCOMPARE(m_page->contentsSize().height(), 1216); } +void tst_QWebEnginePage::localFontAccessPermission_data() +{ + QTest::addColumn<QWebEnginePage::PermissionPolicy>("policy"); + QTest::addColumn<bool>("ignore"); + QTest::addColumn<bool>("shouldBeEmpty"); + + QTest::newRow("ignore") << QWebEnginePage::PermissionDeniedByUser << true << true; + QTest::newRow("setDeny") << QWebEnginePage::PermissionDeniedByUser << false << true; + QTest::newRow("setGrant") << QWebEnginePage::PermissionGrantedByUser << false << false; +} + +void tst_QWebEnginePage::localFontAccessPermission() { + QFETCH(QWebEnginePage::PermissionPolicy, policy); + QFETCH(bool, ignore); + QFETCH(bool, shouldBeEmpty); + + QWebEngineView view; + QWebEnginePage page(&view); + view.setPage(&page); + + connect(&page, &QWebEnginePage::featurePermissionRequested, &page, [&] (const QUrl &o, QWebEnginePage::Feature f) { + if (f != QWebEnginePage::LocalFontsAccess) + return; + + if (!ignore) + page.setFeaturePermission(o, f, policy); + }); + + QSignalSpy spy(&page, &QWebEnginePage::loadFinished); + page.load(QUrl("qrc:///resources/fontaccess.html")); + QTRY_COMPARE(spy.size(), 1); + + // Font access is only enabled for visible WebContents. + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + if (evaluateJavaScriptSync(&page, QStringLiteral("!window.queryLocalFonts")).toBool()) + W_QSKIP("Local fonts access is not supported.", SkipSingle); + + // Access to the API requires recent user interaction + QTest::keyPress(view.focusProxy(), Qt::Key_Space); + QTRY_COMPARE(evaluateJavaScriptSync(&page, QStringLiteral("activated")).toBool(), true); + + if (ignore) { + QTRY_COMPARE_NE_WITH_TIMEOUT(evaluateJavaScriptSync(&page, QStringLiteral("done")).toBool(), true, 1000); + } else { + QTRY_VERIFY_WITH_TIMEOUT(evaluateJavaScriptSync(&page, QStringLiteral("done")).toBool() == true, 1000); + QVERIFY((evaluateJavaScriptSync(&page, QStringLiteral("fonts.length")).toInt() == 0) == shouldBeEmpty); + } + + // Reset permission, since otherwise it will be stored between runs + page.setFeaturePermission(QUrl("qrc:///resources/fontaccess.html"), QWebEnginePage::LocalFontsAccess, QWebEnginePage::PermissionUnknown); +} + void tst_QWebEnginePage::setLifecycleState() { qRegisterMetaType<QWebEnginePage::LifecycleState>("LifecycleState"); @@ -3893,64 +4157,64 @@ void tst_QWebEnginePage::setLifecycleState() QSignalSpy visibleSpy(&page, &QWebEnginePage::visibleChanged); page.load(QStringLiteral("qrc:/resources/lifecycle.html")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); - QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(lifecycleSpy.size(), 0); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(visibleSpy.size(), 0); QCOMPARE(page.isVisible(), false); QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant(false)); QCOMPARE(evaluateJavaScriptSync(&page, "frozenness"), QVariant(0)); // Active -> Frozen page.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Frozen); - QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(visibleSpy.size(), 0); QCOMPARE(page.isVisible(), false); QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant(false)); QCOMPARE(evaluateJavaScriptSync(&page, "frozenness"), QVariant(1)); // Frozen -> Active page.setLifecycleState(QWebEnginePage::LifecycleState::Active); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(visibleSpy.size(), 0); QCOMPARE(page.isVisible(), false); QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant(false)); QCOMPARE(evaluateJavaScriptSync(&page, "frozenness"), QVariant(0)); // Active -> Discarded page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Discarded); - QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(visibleSpy.size(), 0); QCOMPARE(page.isVisible(), false); QTest::ignoreMessage(QtWarningMsg, "runJavaScript: disabled in Discarded state"); QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant()); QTest::ignoreMessage(QtWarningMsg, "runJavaScript: disabled in Discarded state"); QCOMPARE(evaluateJavaScriptSync(&page, "frozenness"), QVariant()); - QCOMPARE(loadSpy.count(), 0); + QCOMPARE(loadSpy.size(), 0); // Discarded -> Frozen (illegal!) QTest::ignoreMessage(QtWarningMsg, "setLifecycleState: failed to transition from Discarded to Frozen state: " "illegal transition"); page.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); - QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(lifecycleSpy.size(), 0); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Discarded); // Discarded -> Active page.setLifecycleState(QWebEnginePage::LifecycleState::Active); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(visibleSpy.size(), 0); QCOMPARE(page.isVisible(), false); QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant(true)); QCOMPARE(evaluateJavaScriptSync(&page, "frozenness"), QVariant(0)); @@ -3959,21 +4223,21 @@ void tst_QWebEnginePage::setLifecycleState() page.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); page.setLifecycleState(QWebEnginePage::LifecycleState::Active); - QCOMPARE(lifecycleSpy.count(), 3); + QCOMPARE(lifecycleSpy.size(), 3); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(visibleSpy.size(), 0); QCOMPARE(page.isVisible(), false); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant(true)); QCOMPARE(evaluateJavaScriptSync(&page, "frozenness"), QVariant(0)); // Reload clears document.wasDiscarded page.triggerAction(QWebEnginePage::Reload); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant(false)); } @@ -3989,18 +4253,18 @@ void tst_QWebEnginePage::setVisible() QSignalSpy visibleSpy(&page, &QWebEnginePage::visibleChanged); page.load(QStringLiteral("about:blank")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); - QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(lifecycleSpy.size(), 0); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(visibleSpy.size(), 0); QCOMPARE(page.isVisible(), false); // hidden -> visible page.setVisible(true); - QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(lifecycleSpy.size(), 0); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(visibleSpy.count(), 1); + QCOMPARE(visibleSpy.size(), 1); QCOMPARE(visibleSpy.takeFirst().value(0), QVariant(true)); QCOMPARE(page.isVisible(), true); @@ -4009,28 +4273,28 @@ void tst_QWebEnginePage::setVisible() QtWarningMsg, "setLifecycleState: failed to transition from Active to Frozen state: page is visible"); page.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); - QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(lifecycleSpy.size(), 0); // visible -> hidden page.setVisible(false); - QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(lifecycleSpy.size(), 0); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(visibleSpy.count(), 1); + QCOMPARE(visibleSpy.size(), 1); QCOMPARE(visibleSpy.takeFirst().value(0), QVariant(false)); QCOMPARE(page.isVisible(), false); // Active -> Frozen page.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Frozen); // hidden -> visible (triggers Frozen -> Active) page.setVisible(true); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(visibleSpy.count(), 1); + QCOMPARE(visibleSpy.size(), 1); QCOMPARE(visibleSpy.takeFirst().value(0), QVariant(true)); QCOMPARE(page.isVisible(), true); @@ -4039,31 +4303,31 @@ void tst_QWebEnginePage::setVisible() "setLifecycleState: failed to transition from Active to Discarded state: " "page is visible"); page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); - QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(lifecycleSpy.size(), 0); // visible -> hidden page.setVisible(false); - QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(lifecycleSpy.size(), 0); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(visibleSpy.count(), 1); + QCOMPARE(visibleSpy.size(), 1); QCOMPARE(visibleSpy.takeFirst().value(0), QVariant(false)); QCOMPARE(page.isVisible(), false); // Active -> Discarded page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Discarded); // hidden -> visible (triggers Discarded -> Active) page.setVisible(true); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(visibleSpy.count(), 1); + QCOMPARE(visibleSpy.size(), 1); QCOMPARE(visibleSpy.takeFirst().value(0), QVariant(true)); QCOMPARE(page.isVisible(), true); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); } @@ -4074,7 +4338,7 @@ void tst_QWebEnginePage::discardPreservesProperties() QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); page.load(QStringLiteral("about:blank")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); // Change as many properties as possible to non-default values @@ -4111,7 +4375,7 @@ void tst_QWebEnginePage::discardPreservesProperties() // Discard + undiscard page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); page.setLifecycleState(QWebEnginePage::LifecycleState::Active); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); // Property changes should be preserved @@ -4150,7 +4414,7 @@ void tst_QWebEnginePage::automaticUndiscard() QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); page.load(QStringLiteral("about:blank")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); // setUrl @@ -4175,16 +4439,16 @@ void tst_QWebEnginePage::setLifecycleStateWithDevTools() // Ensure pages are initialized inspectedPage.load(QStringLiteral("about:blank")); devToolsPage.load(QStringLiteral("about:blank")); - QTRY_COMPARE_WITH_TIMEOUT(inspectedSpy.count(), 1, 90000); + QTRY_COMPARE_WITH_TIMEOUT(inspectedSpy.size(), 1, 90000); QCOMPARE(inspectedSpy.takeFirst().value(0), QVariant(true)); - QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.count(), 1, 90000); + QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.size(), 1, 90000); QCOMPARE(devToolsSpy.takeFirst().value(0), QVariant(true)); // Open DevTools with Frozen inspectedPage inspectedPage.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); inspectedPage.setDevToolsPage(&devToolsPage); QCOMPARE(inspectedPage.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.count(), 1, 90000); + QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.size(), 1, 90000); QCOMPARE(devToolsSpy.takeFirst().value(0), QVariant(true)); inspectedPage.setDevToolsPage(nullptr); @@ -4192,9 +4456,9 @@ void tst_QWebEnginePage::setLifecycleStateWithDevTools() inspectedPage.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); inspectedPage.setDevToolsPage(&devToolsPage); QCOMPARE(inspectedPage.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.count(), 1, 90000); + QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.size(), 1, 90000); QCOMPARE(devToolsSpy.takeFirst().value(0), QVariant(true)); - QTRY_COMPARE(inspectedSpy.count(), 1); + QTRY_COMPARE(inspectedSpy.size(), 1); QCOMPARE(inspectedSpy.takeFirst().value(0), QVariant(true)); inspectedPage.setDevToolsPage(nullptr); @@ -4202,7 +4466,7 @@ void tst_QWebEnginePage::setLifecycleStateWithDevTools() devToolsPage.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); devToolsPage.setInspectedPage(&inspectedPage); QCOMPARE(devToolsPage.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.count(), 1, 90000); + QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.size(), 1, 90000); QCOMPARE(devToolsSpy.takeFirst().value(0), QVariant(true)); devToolsPage.setInspectedPage(nullptr); @@ -4210,7 +4474,7 @@ void tst_QWebEnginePage::setLifecycleStateWithDevTools() devToolsPage.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); devToolsPage.setInspectedPage(&inspectedPage); QCOMPARE(devToolsPage.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.count(), 1, 90000); + QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.size(), 1, 90000); QCOMPARE(devToolsSpy.takeFirst().value(0), QVariant(true)); // keep DevTools open @@ -4248,35 +4512,35 @@ void tst_QWebEnginePage::discardPreservesCommittedLoad() QString url = QStringLiteral("qrc:/resources/lifecycle.html"); page.setUrl(url); - QTRY_COMPARE(loadStartedSpy.count(), 1); + QTRY_COMPARE(loadStartedSpy.size(), 1); loadStartedSpy.clear(); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QCOMPARE(loadFinishedSpy.takeFirst().value(0), QVariant(true)); - QCOMPARE(urlChangedSpy.count(), 1); + QCOMPARE(urlChangedSpy.size(), 1); QCOMPARE(urlChangedSpy.takeFirst().value(0), QVariant(QUrl(url))); QCOMPARE(page.url(), url); - QCOMPARE(titleChangedSpy.count(), 2); + QCOMPARE(titleChangedSpy.size(), 2); QCOMPARE(titleChangedSpy.takeFirst().value(0), QVariant(url)); QString title = QStringLiteral("Lifecycle"); QCOMPARE(titleChangedSpy.takeFirst().value(0), QVariant(title)); QCOMPARE(page.title(), title); page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); - QCOMPARE(loadStartedSpy.count(), 0); - QCOMPARE(loadFinishedSpy.count(), 0); - QCOMPARE(urlChangedSpy.count(), 0); + QCOMPARE(loadStartedSpy.size(), 0); + QCOMPARE(loadFinishedSpy.size(), 0); + QCOMPARE(urlChangedSpy.size(), 0); QCOMPARE(page.url(), QUrl(url)); - QCOMPARE(titleChangedSpy.count(), 0); + QCOMPARE(titleChangedSpy.size(), 0); QCOMPARE(page.title(), title); page.setLifecycleState(QWebEnginePage::LifecycleState::Active); - QTRY_COMPARE(loadStartedSpy.count(), 1); + QTRY_COMPARE(loadStartedSpy.size(), 1); loadStartedSpy.clear(); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QCOMPARE(loadFinishedSpy.takeFirst().value(0), QVariant(true)); - QCOMPARE(urlChangedSpy.count(), 0); + QCOMPARE(urlChangedSpy.size(), 0); QCOMPARE(page.url(), url); - QCOMPARE(titleChangedSpy.count(), 0); + QCOMPARE(titleChangedSpy.size(), 0); QCOMPARE(page.title(), title); } @@ -4293,21 +4557,21 @@ void tst_QWebEnginePage::discardAbortsPendingLoad() [&]() { page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); }); QUrl url = QStringLiteral("qrc:/resources/lifecycle.html"); page.setUrl(url); - QTRY_COMPARE(loadStartedSpy.count(), 1); + QTRY_COMPARE(loadStartedSpy.size(), 1); loadStartedSpy.clear(); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QCOMPARE(loadFinishedSpy.takeFirst().value(0), QVariant(false)); - QCOMPARE(urlChangedSpy.count(), 2); + QCOMPARE(urlChangedSpy.size(), 2); QCOMPARE(urlChangedSpy.takeFirst().value(0), QVariant(url)); QCOMPARE(urlChangedSpy.takeFirst().value(0), QVariant(QUrl())); - QCOMPARE(titleChangedSpy.count(), 0); + QCOMPARE(titleChangedSpy.size(), 0); QCOMPARE(page.url(), QUrl()); QCOMPARE(page.title(), QString()); page.setLifecycleState(QWebEnginePage::LifecycleState::Active); - QCOMPARE(loadStartedSpy.count(), 0); - QCOMPARE(loadFinishedSpy.count(), 0); - QCOMPARE(urlChangedSpy.count(), 0); + QCOMPARE(loadStartedSpy.size(), 0); + QCOMPARE(loadFinishedSpy.size(), 0); + QCOMPARE(urlChangedSpy.size(), 0); QCOMPARE(page.url(), QUrl()); QCOMPARE(page.title(), QString()); } @@ -4323,14 +4587,14 @@ void tst_QWebEnginePage::discardAbortsPendingLoadAndPreservesCommittedLoad() QString url1 = QStringLiteral("qrc:/resources/lifecycle.html"); page.setUrl(url1); - QTRY_COMPARE(loadStartedSpy.count(), 1); + QTRY_COMPARE(loadStartedSpy.size(), 1); loadStartedSpy.clear(); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QCOMPARE(loadFinishedSpy.takeFirst().value(0), QVariant(true)); - QCOMPARE(urlChangedSpy.count(), 1); + QCOMPARE(urlChangedSpy.size(), 1); QCOMPARE(urlChangedSpy.takeFirst().value(0), QVariant(QUrl(url1))); QCOMPARE(page.url(), url1); - QCOMPARE(titleChangedSpy.count(), 2); + QCOMPARE(titleChangedSpy.size(), 2); QCOMPARE(titleChangedSpy.takeFirst().value(0), QVariant(url1)); QString title = QStringLiteral("Lifecycle"); QCOMPARE(titleChangedSpy.takeFirst().value(0), QVariant(title)); @@ -4340,21 +4604,21 @@ void tst_QWebEnginePage::discardAbortsPendingLoadAndPreservesCommittedLoad() [&]() { page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); }); QString url2 = QStringLiteral("about:blank"); page.setUrl(url2); - QTRY_COMPARE(loadStartedSpy.count(), 1); + QTRY_COMPARE(loadStartedSpy.size(), 1); loadStartedSpy.clear(); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QCOMPARE(loadFinishedSpy.takeFirst().value(0), QVariant(false)); - QCOMPARE(urlChangedSpy.count(), 2); + QCOMPARE(urlChangedSpy.size(), 2); QCOMPARE(urlChangedSpy.takeFirst().value(0), QVariant(QUrl(url2))); QCOMPARE(urlChangedSpy.takeFirst().value(0), QVariant(QUrl(url1))); - QCOMPARE(titleChangedSpy.count(), 0); + QCOMPARE(titleChangedSpy.size(), 0); QCOMPARE(page.url(), url1); QCOMPARE(page.title(), title); page.setLifecycleState(QWebEnginePage::LifecycleState::Active); - QCOMPARE(loadStartedSpy.count(), 0); - QCOMPARE(loadFinishedSpy.count(), 0); - QCOMPARE(urlChangedSpy.count(), 0); + QCOMPARE(loadStartedSpy.size(), 0); + QCOMPARE(loadFinishedSpy.size(), 0); + QCOMPARE(urlChangedSpy.size(), 0); QCOMPARE(page.url(), url1); QCOMPARE(page.title(), title); } @@ -4450,32 +4714,32 @@ void tst_QWebEnginePage::recommendedStateAuto() connect(&page, &QWebEnginePage::recommendedStateChanged, &page, &QWebEnginePage::setLifecycleState); page.load(QStringLiteral("qrc:/resources/lifecycle.html")); - QTRY_COMPARE(lifecycleSpy.count(), 2); + QTRY_COMPARE(lifecycleSpy.size(), 2); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); page.setVisible(true); - QTRY_COMPARE(lifecycleSpy.count(), 1); + QTRY_COMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); page.setVisible(false); - QTRY_COMPARE(lifecycleSpy.count(), 2); + QTRY_COMPARE(lifecycleSpy.size(), 2); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); page.triggerAction(QWebEnginePage::Reload); - QTRY_COMPARE(lifecycleSpy.count(), 3); + QTRY_COMPARE(lifecycleSpy.size(), 3); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); QWebEnginePage devTools; page.setDevToolsPage(&devTools); - QTRY_COMPARE(lifecycleSpy.count(), 1); + QTRY_COMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); page.setDevToolsPage(nullptr); - QTRY_COMPARE(lifecycleSpy.count(), 2); + QTRY_COMPARE(lifecycleSpy.size(), 2); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); } @@ -4490,33 +4754,33 @@ void tst_QWebEnginePage::setLifecycleStateAndReload() QSignalSpy lifecycleSpy(&page, &QWebEnginePage::lifecycleStateChanged); page.load(QStringLiteral("qrc:/resources/lifecycle.html")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); - QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(lifecycleSpy.size(), 0); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); page.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Frozen); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); page.triggerAction(QWebEnginePage::Reload); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Discarded); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); page.triggerAction(QWebEnginePage::Reload); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); } @@ -4535,20 +4799,20 @@ void tst_QWebEnginePage::editActionsWithExplicitFocus() QVERIFY(!page->action(QWebEnginePage::SelectAll)->isEnabled()); page->setHtml(QString("<html><body><div>foo bar</div></body></html>")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); // Still no focus because focus on navigation is disabled. Edit actions don't do anything (should not crash). QVERIFY(!page->action(QWebEnginePage::SelectAll)->isEnabled()); view.page()->triggerAction(QWebEnginePage::SelectAll); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); QCOMPARE(page->hasSelection(), false); // Focus content by focusing window from JavaScript. Edit actions should be enabled and functional. evaluateJavaScriptSync(page, "window.focus();"); - QTRY_COMPARE(actionChangedSpy.count(), 1); + QTRY_COMPARE(actionChangedSpy.size(), 1); QVERIFY(page->action(QWebEnginePage::SelectAll)->isEnabled()); view.page()->triggerAction(QWebEnginePage::SelectAll); - QTRY_COMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(selectionChangedSpy.size(), 1); QCOMPARE(page->hasSelection(), true); QCOMPARE(page->selectedText(), QStringLiteral("foo bar")); } @@ -4568,13 +4832,13 @@ void tst_QWebEnginePage::editActionsWithInitialFocus() QVERIFY(!page->action(QWebEnginePage::SelectAll)->isEnabled()); page->setHtml(QString("<html><body><div>foo bar</div></body></html>")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); // Content gets initial focus. - QTRY_COMPARE(actionChangedSpy.count(), 1); + QTRY_COMPARE(actionChangedSpy.size(), 1); QVERIFY(page->action(QWebEnginePage::SelectAll)->isEnabled()); view.page()->triggerAction(QWebEnginePage::SelectAll); - QTRY_COMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(selectionChangedSpy.size(), 1); QCOMPARE(page->hasSelection(), true); QCOMPARE(page->selectedText(), QStringLiteral("foo bar")); } @@ -4594,15 +4858,15 @@ void tst_QWebEnginePage::editActionsWithFocusOnIframe() QVERIFY(!page->action(QWebEnginePage::SelectAll)->isEnabled()); page->load(QUrl("qrc:///resources/iframe2.html")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QVERIFY(!page->action(QWebEnginePage::SelectAll)->isEnabled()); // Focusing an iframe. evaluateJavaScriptSync(page, "document.getElementsByTagName('iframe')[0].contentWindow.focus()"); - QTRY_COMPARE(actionChangedSpy.count(), 1); + QTRY_COMPARE(actionChangedSpy.size(), 1); QVERIFY(page->action(QWebEnginePage::SelectAll)->isEnabled()); view.page()->triggerAction(QWebEnginePage::SelectAll); - QTRY_COMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(selectionChangedSpy.size(), 1); QCOMPARE(page->hasSelection(), true); QCOMPARE(page->selectedText(), QStringLiteral("inner")); } @@ -4618,8 +4882,8 @@ void tst_QWebEnginePage::editActionsWithoutSelection() QSignalSpy actionChangedSpy(page->action(QWebEnginePage::SelectAll), &QAction::changed); page->setHtml(QString("<html><body><div>foo bar</div></body></html>")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); - QTRY_COMPARE(actionChangedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + QTRY_COMPARE(actionChangedSpy.size(), 1); QVERIFY(!page->action(QWebEnginePage::Cut)->isEnabled()); QVERIFY(!page->action(QWebEnginePage::Copy)->isEnabled()); @@ -4631,7 +4895,7 @@ void tst_QWebEnginePage::editActionsWithoutSelection() QVERIFY(!page->action(QWebEnginePage::Unselect)->isEnabled()); page->triggerAction(QWebEnginePage::SelectAll); - QTRY_COMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(selectionChangedSpy.size(), 1); QCOMPARE(page->hasSelection(), true); QCOMPARE(page->selectedText(), QStringLiteral("foo bar")); @@ -4691,12 +4955,12 @@ void tst_QWebEnginePage::customUserAgentInNewTab() page.setHtml(QString("<html><body><a id='link' target='_blank' href='") + server.url("/test1").toEncoded() + QString("'>link</a></body></html>")); - QTRY_COMPARE(page.loadSpy.count(), 1); + QTRY_COMPARE(page.loadSpy.size(), 1); QVERIFY(page.loadSpy.takeFirst().value(0).toBool()); QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("navigator.userAgent")).toString(), expectedUserAgent); QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, elementCenter(&page, "link")); QTRY_VERIFY(page.newPage); - QTRY_COMPARE(page.newPage->loadSpy.count(), 1); + QTRY_COMPARE(page.newPage->loadSpy.size(), 1); QTRY_VERIFY(!lastUserAgent.isEmpty()); QCOMPARE(lastUserAgent, expectedUserAgent); QCOMPARE(evaluateJavaScriptSync(page.newPage.get(), QStringLiteral("navigator.userAgent")).toString(), expectedUserAgent); @@ -4710,11 +4974,11 @@ void tst_QWebEnginePage::customUserAgentInNewTab() page.setHtml(QString("<html><body><a id='link' target='_blank' href='") + server.url("/test2").toEncoded() + QString("'>link</a></body></html>")); - QTRY_COMPARE(page.loadSpy.count(), 1); + QTRY_COMPARE(page.loadSpy.size(), 1); QVERIFY(page.loadSpy.takeFirst().value(0).toBool()); QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, elementCenter(&page, "link")); QTRY_VERIFY(page.newPage); - QTRY_COMPARE(page.newPage->loadSpy.count(), 1); + QTRY_COMPARE(page.newPage->loadSpy.size(), 1); QTRY_VERIFY(!lastUserAgent.isEmpty()); QCOMPARE(lastUserAgent, expectedUserAgent); QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("navigator.userAgent")).toString(), expectedUserAgent); @@ -4747,7 +5011,7 @@ void tst_QWebEnginePage::openNewTabInDifferentProfile() QVERIFY(QTest::qWaitForWindowExposed(&view)); page.setHtml(QString("<html><body><a id='link' target='_blank' href='%1'>link</a></body></html>").arg(server.url("/first.html").toEncoded())); - QTRY_COMPARE(page.loadSpy.count(), 1); + QTRY_COMPARE(page.loadSpy.size(), 1); QVERIFY(page.loadSpy.takeFirst().value(0).toBool()); QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, elementCenter(&page, "link")); @@ -4874,7 +5138,7 @@ void tst_QWebEnginePage::testChooseFilesParameters() "</body></html>"), QString("qrc:/")); } QVERIFY(spyFinished.wait()); - QTRY_COMPARE(spyFinished.count(), 1); + QTRY_COMPARE(spyFinished.size(), 1); evaluateJavaScriptSync(view.page(), "document.getElementById('filePicker').focus()"); QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("filePicker")); @@ -4919,7 +5183,7 @@ void tst_QWebEnginePage::fileSystemAccessDialog() "</body></html>"), QString("qrc:/")); QVERIFY(spyFinished.wait()); - QTRY_COMPARE(spyFinished.count(), 1); + QTRY_COMPARE(spyFinished.size(), 1); evaluateJavaScriptSync(view.page(), "document.getElementById('triggerDialog').focus()"); QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), @@ -4985,11 +5249,11 @@ void tst_QWebEnginePage::audioMuted() page.setAudioMuted(true); loadSync(&page, QUrl("about:blank")); QCOMPARE(page.isAudioMuted(), true); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy[0][0], QVariant(true)); page.setAudioMuted(false); QCOMPARE(page.isAudioMuted(), false); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); QCOMPARE(spy[1][0], QVariant(false)); } @@ -4999,9 +5263,9 @@ void tst_QWebEnginePage::closeContents() QSignalSpy spyFinished(&page, &QWebEnginePage::loadFinished); QSignalSpy windowCreatedSpy(&page, &TestPage::windowCreated); page.setUrl(QUrl("about:blank")); - QTRY_COMPARE(spyFinished.count(), 1); + QTRY_COMPARE(spyFinished.size(), 1); page.runJavaScript("var dialog = window.open('', '', 'width=100, height=100');"); - QTRY_COMPARE(windowCreatedSpy.count(), 1); + QTRY_COMPARE(windowCreatedSpy.size(), 1); QWebEngineView *dialogView = new QWebEngineView; QWebEnginePage *dialogPage = page.createdWindows[0]; @@ -5042,7 +5306,7 @@ void tst_QWebEnginePage::isSafeRedirect() TestPage page; QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); page.setUrl(requestedUrl); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 20000); QCOMPARE(page.url(), expectedUrl); spy.clear(); } @@ -5082,18 +5346,18 @@ void tst_QWebEnginePage::localToRemoteNavigation() view.setPage(&page); page.setUrl(QUrl("local://test.html")); QVERIFY(QTest::qWaitForWindowExposed(&view)); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); QVERIFY(local.loaded); // Should navigate: QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, elementCenter(&page, "link")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 2, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 2, 20000); QVERIFY(remote.loaded); local.loaded = false; remote.loaded = false; page.setUrl(QUrl("local://test.html")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 3, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 3, 20000); QVERIFY(local.loaded && !remote.loaded); // Should not navigate: @@ -5102,6 +5366,392 @@ void tst_QWebEnginePage::localToRemoteNavigation() QVERIFY(!remote.loaded); } +void tst_QWebEnginePage::clientHints_data() +{ + QTest::addColumn<bool>("clientHintsEnabled"); + QTest::addColumn<QString>("arch"); + QTest::addColumn<QString>("platform"); + QTest::addColumn<QString>("model"); + QTest::addColumn<bool>("isMobile"); + QTest::addColumn<QString>("fullVersion"); + QTest::addColumn<QString>("platformVersion"); + QTest::addColumn<QString>("bitness"); + QTest::addColumn<bool>("isWOW64"); + QTest::addColumn<QHash<QString, QString>>("fullVersionList"); + + QTest::newRow("Modify values") << true << "Abc" << "AmigaOS" << "Ultra" << true << "1.99" << "3" << "x64" << true << QHash<QString, QString>({{"APITest", "1.0.0"}, {"App", "5.0"}}); + QTest::newRow("Empty values") << true << "" << "" << "" << false << "" << "" << "" << false << QHash<QString, QString>(); + QTest::newRow("Disable headers") << false << "" << "" << "" << false << "" << "" << "" << false << QHash<QString, QString>(); +} + +void tst_QWebEnginePage::clientHints() +{ + QFETCH(bool, clientHintsEnabled); + QFETCH(QString, arch); + QFETCH(QString, platform); + QFETCH(QString, model); + QFETCH(bool, isMobile); + QFETCH(QString, fullVersion); + QFETCH(QString, platformVersion); + QFETCH(QString, bitness); + QFETCH(bool, isWOW64); + typedef QHash<QString, QString> brandVersionPairs; + QFETCH(brandVersionPairs, fullVersionList); + + QWebEnginePage page; + QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); + + QWebEngineClientHints *clientHints = page.profile()->clientHints(); + clientHints->setAllClientHintsEnabled(clientHintsEnabled); + + HttpServer server; + int requestCount = 0; + connect(&server, &HttpServer::newRequest, [&] (HttpReqRep *r) { + // Platform and Mobile hints are always sent and can't be disabled with this API + QVERIFY(r->hasRequestHeader("Sec-CH-UA-Platform")); + QVERIFY(r->hasRequestHeader("Sec-CH-UA-Mobile")); + if (!clientHintsEnabled) { + QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Arch")); + QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Model")); + QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Full-Version")); + QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Platform-Version")); + QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Bitness")); + QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Wow64")); + QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Full-Version-List")); + } + + // The first request header won't contain any hints, only after a response with "Accept-CH" + if (requestCount > 1 && clientHintsEnabled) { + // All hint values are lower case in the headers + QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Arch")).remove("\""), arch.toLower()); + QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Platform")).remove("\""), platform.toLower()); + QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Model")).remove("\""), model.toLower()); + QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Mobile")).remove("\""), isMobile ? "?1" : "?0"); + QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Full-Version")).remove("\""), fullVersion.toLower()); + QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Platform-Version")).remove("\""), platformVersion.toLower()); + QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Bitness")).remove("\""), bitness.toLower()); + QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Wow64")).remove("\""), isWOW64 ? "?1" : "?0"); + for (auto i = fullVersionList.cbegin(), end = fullVersionList.cend(); i != end; ++i) + QVERIFY(QString(r->requestHeader("Sec-CH-UA-Full-Version-List")).contains(i.key().toLower())); + } + + r->setResponseHeader("Accept-CH", "Sec-CH-UA-Arch, Sec-CH-UA-Bitness, Sec-CH-UA-Full-Version, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform-Version, Sec-CH-UA-Platform, Sec-CH-UA-Wow64, Sec-CH-UA"); + r->sendResponse(); + requestCount++; + }); + QVERIFY(server.start()); + + clientHints->setArch(arch); + clientHints->setPlatform(platform); + clientHints->setModel(model); + clientHints->setIsMobile(isMobile); + clientHints->setFullVersion(fullVersion); + clientHints->setPlatformVersion(platformVersion); + clientHints->setBitness(bitness); + clientHints->setIsWow64(isWOW64); + clientHints->setFullVersionList(fullVersionList); + + page.setUrl(server.url()); + QTRY_COMPARE(loadSpy.size(), 1); + QVERIFY(loadSpy.takeFirst().value(0).toBool()); + + QCOMPARE(clientHints->arch(), arch); + QCOMPARE(clientHints->platform(), platform); + QCOMPARE(clientHints->model(), model); + QCOMPARE(clientHints->isMobile(), isMobile); + QCOMPARE(clientHints->fullVersion(), fullVersion); + QCOMPARE(clientHints->platformVersion(), platformVersion); + QCOMPARE(clientHints->bitness(), bitness); + QCOMPARE(clientHints->isWow64(), isWOW64); + for (auto i = fullVersionList.cbegin(), end = fullVersionList.cend(); i != end; ++i) + QCOMPARE(clientHints->fullVersionList()[i.key()], i.value()); + + // A new user agent string should not override/disable client hints + page.profile()->setHttpUserAgent(QStringLiteral("Custom user agent")); + page.triggerAction(QWebEnginePage::Reload); + QTRY_COMPARE(loadSpy.size(), 1); + + // Reset all to default values + clientHints->resetAll(); + QCOMPARE_NE(clientHints->arch(), arch); +#ifdef Q_OS_LINUX + QCOMPARE(clientHints->platform().toLower(), "linux"); +#elif defined (Q_OS_MACOS) + QCOMPARE(clientHints->platform().toLower(), "macos"); +#elif defined (Q_OS_WIN) + QCOMPARE(clientHints->platform().toLower(), "windows"); +#endif + QCOMPARE_NE(clientHints->fullVersion(), fullVersion); + QCOMPARE_NE(clientHints->platformVersion(), platformVersion); + QCOMPARE_NE(clientHints->bitness(), bitness); + for (auto i = fullVersionList.cbegin(), end = fullVersionList.cend(); i != end; ++i) + QVERIFY(!clientHints->fullVersionList().contains(i.key())); + QVERIFY(clientHints->fullVersionList().contains("Chromium")); +} + +void tst_QWebEnginePage::childFrameInput() +{ + HttpServer server; + server.setHostDomain("localhost"); + + // The cross-origin policy blocks scripting this frame with QWebEnginePage::runJavaScript. + // Use console messages to validate events. + QString innerHtml( + "<html><head><style>body{height:1200px;width:1200px;}</style></head><body>test<script>" + " let lastX, lastY = 0;" + " document.onscroll = (e) => {" + " if (window.scrollY > lastY) console.log(\"Down\");" + " if (window.scrollY < lastY) console.log(\"Up\");" + " if (window.scrollX > lastX) console.log(\"Right\");" + " if (window.scrollX < lastX) console.log(\"Left\");" + " lastX = window.scrollX;" + " lastY = window.scrollY;" + " };" + " window.onload = () => {console.log('loaded');};" + "</script></body></html>"); + + QVERIFY(server.start()); + connect(&server, &HttpServer::newRequest, [&](HttpReqRep *rr) { + if (rr->requestPath() == "/main.html") { + // the Origin-Agent-Cluster header enables dedicated processes for origins + rr->setResponseHeader("Origin-Agent-Cluster", "?1"); + // the same-site-cross-origin page forces to create the frame in a different process + server.setHostDomain("sub.localhost"); + rr->setResponseBody(("<html><body>" + "<iframe id=\"iframe\" width=90% height=90% src=\"" + + server.url().toString().toUtf8() + + "inner.html\"></iframe>" + "</body></html>")); + } + if (rr->requestPath() == "/inner.html") + rr->setResponseBody(innerHtml.toUtf8()); + rr->sendResponse(); + }); + + QWebEngineView view; + ConsolePage page; + view.setPage(&page); + view.resize(640, 480); + QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); + page.load(server.url("/main.html")); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); + + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + QTRY_VERIFY(evaluateJavaScriptSync(&page, "window.originAgentCluster").toBool()); + + // make sure the frame is loaded + QTRY_COMPARE(page.messages.size(), 1); + QTRY_COMPARE(page.messages[0], QString("loaded")); + + // focus + evaluateJavaScriptSync(&page, "document.getElementById('iframe').contentWindow.focus()"); + QTRY_COMPARE(evaluateJavaScriptSync(&page, "document.activeElement.id").toString(), + QStringLiteral("iframe")); + + QPoint globalPos = view.windowHandle()->position(); + QPoint p = elementCenter(&page, QString("iframe")); + + // Even if the document is loaded, it is not necessarily drawn. + // Hit-testing (in Viz) for pointer events will be flacky in this scenario. + // Send keyClick events first so the target frame will be cached for wheel events. + QTest::keyClick(view.focusProxy(), Qt::Key_Down); + QTRY_COMPARE(page.messages.size(), 2); + QTRY_COMPARE(page.messages[1], QString("Down")); + + QTest::keyClick(view.focusProxy(), Qt::Key_Up); + QTRY_COMPARE(page.messages.size(), 3); + QTRY_COMPARE(page.messages[2], QString("Up")); + + QTest::keyClick(view.focusProxy(), Qt::Key_Right); + QTRY_COMPARE(page.messages.size(), 4); + QTRY_COMPARE(page.messages[3], QString("Right")); + + QTest::keyClick(view.focusProxy(), Qt::Key_Left); + QTRY_COMPARE(page.messages.size(), 5); + QTRY_COMPARE(page.messages[4], QString("Left")); + + makeScroll(view.focusProxy(), p, globalPos, QPoint(0, -120)); + QTRY_COMPARE(page.messages.size(), 6); + QTRY_COMPARE(page.messages[5], QString("Down")); + + makeScroll(view.focusProxy(), p, globalPos, QPoint(0, 120)); + QTRY_COMPARE(page.messages.size(), 7); + QTRY_COMPARE(page.messages[6], QString("Up")); + + makeScroll(view.focusProxy(), p, globalPos, QPoint(-120, 0)); + QTRY_COMPARE(page.messages.size(), 8); + QTRY_COMPARE(page.messages[7], QString("Right")); + + makeScroll(view.focusProxy(), p, globalPos, QPoint(120, 0)); + QTRY_COMPARE(page.messages.size(), 9); + QTRY_COMPARE(page.messages[8], QString("Left")); +} + +void tst_QWebEnginePage::openLinkInNewPageWithWebWindowType_data() +{ + QTest::addColumn<QWebEnginePage::WebWindowType>("webWindowType"); + QTest::addColumn<QString>("elementId"); + QTest::addColumn<Qt::MouseButton>("button"); + QTest::addColumn<Qt::KeyboardModifier>("keyboardModififer"); + QTest::newRow("webBrowserWindow") + << QWebEnginePage::WebBrowserWindow << "link" << Qt::LeftButton << Qt::ShiftModifier; + QTest::newRow("webBrowserTab") + << QWebEnginePage::WebBrowserTab << "link" << Qt::LeftButton << Qt::NoModifier; + QTest::newRow("webDialog") << QWebEnginePage::WebDialog << "openWindow" << Qt::LeftButton + << Qt::NoModifier; + QTest::newRow("webBrowserBackgroundTab") << QWebEnginePage::WebBrowserBackgroundTab << "link" + << Qt::MiddleButton << Qt::NoModifier; +} + +class WebWindowTypeTestPage : public QWebEnginePage +{ + Q_OBJECT + +public: + WebWindowType windowType; + +signals: + void windowCreated(); + +private: + QWebEnginePage *createWindow(WebWindowType type) override + { + windowType = type; + emit windowCreated(); + return nullptr; + } +}; + +void tst_QWebEnginePage::openLinkInNewPageWithWebWindowType() +{ + QFETCH(QWebEnginePage::WebWindowType, webWindowType); + QFETCH(QString, elementId); + QFETCH(Qt::MouseButton, button); + QFETCH(Qt::KeyboardModifier, keyboardModififer); + + WebWindowTypeTestPage page; + QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); + QSignalSpy windowCreatedSpy(&page, &WebWindowTypeTestPage::windowCreated); + QWebEngineView view(&page); + view.resize(640, 480); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + page.settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, true); + page.settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true); + QString html = "<html><body>" + "<a id='link' href='hello' target='_blank'>link</a>" + "<br><br>" + "<button id='openWindow' onclick='myFunction()'>Try it</button>" + "<script>" + "function myFunction() {" + " const myWindow = window.open('', '', 'width=300,height=300');" + "}" + "</script>" + "</body></html>"; + + page.setHtml(html); + QVERIFY(loadFinishedSpy.wait()); + + QTest::mouseClick(view.focusProxy(), button, keyboardModififer, + elementCenter(&page, elementId)); + QVERIFY(windowCreatedSpy.wait()); + QCOMPARE(page.windowType, webWindowType); +} + +class DoNothingInterceptor : public QWebEngineUrlRequestInterceptor +{ +public: + DoNothingInterceptor() { } + + void interceptRequest(QWebEngineUrlRequestInfo &) override + { + ran = true; + } + bool ran = false; +}; + +void tst_QWebEnginePage::keepInterceptorAfterNewWindowRequested() +{ + DoNothingInterceptor interceptor; + QWebEnginePage page; + page.setUrlRequestInterceptor(&interceptor); + connect(&page, &QWebEnginePage::newWindowRequested, [&](QWebEngineNewWindowRequest &request) { + request.openIn(&page); + }); + QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); + + QWebEngineView view; + view.resize(500, 500); + view.setPage(&page); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + page.setHtml("<html><body>" + "<a id='link' href='hello' target='_blank'>link</a>" + "</body></html>"); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + QVERIFY(loadFinishedSpy.takeFirst().value(0).toBool()); + QVERIFY(interceptor.ran); + interceptor.ran = false; + + QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, elementCenter(&page, "link")); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + QVERIFY(loadFinishedSpy.takeFirst().value(0).toBool()); + QVERIFY(!interceptor.ran); + + page.setHtml("<html><body></body></html>"); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + QVERIFY(loadFinishedSpy.takeFirst().value(0).toBool()); + QVERIFY(interceptor.ran); +} + +void tst_QWebEnginePage::chooseDesktopMedia() +{ +#if QT_CONFIG(webengine_extensions) && QT_CONFIG(webengine_webrtc) + HttpServer server; + server.setHostDomain("localhost"); + connect(&server, &HttpServer::newRequest, &server, [&] (HttpReqRep *r) { + if (r->requestMethod() == "GET") + r->setResponseBody("<html></html>"); + }); + QVERIFY(server.start()); + + QWebEnginePage page; + QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); + page.settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true); + + bool desktopMediaRequested = false; + bool permissionRequested = false; + + connect(&page, &QWebEnginePage::desktopMediaRequested, + [&](const QWebEngineDesktopMediaRequest &) { + desktopMediaRequested = true; + }); + + connect(&page, &QWebEnginePage::featurePermissionRequested, + [&](const QUrl &securityOrigin, QWebEnginePage::Feature feature) { + permissionRequested = true; + // Handle permission to 'complete' the media request + page.setFeaturePermission(securityOrigin, feature, + QWebEnginePage::PermissionGrantedByUser); + }); + + page.load(QUrl(server.url())); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 20000); + + const QString extensionId("nkeimhogjdpnpccoofpliimaahmaaome"); + page.runJavaScript(QString("(() => {" + " let port = chrome.runtime.connect(\"%1\", {name: \"chooseDesktopMedia\"});" + " port.postMessage({method: \"chooseDesktopMedia\"});" + "})()").arg(extensionId)); + + QTRY_VERIFY(desktopMediaRequested); + QTRY_VERIFY(permissionRequested); +#endif // QT_CONFIG(webengine_extensions) && QT_CONFIG(webengine_webrtc) +} + static QByteArrayList params = {QByteArrayLiteral("--use-fake-device-for-media-stream")}; W_QTEST_MAIN(tst_QWebEnginePage, params) diff --git a/tests/auto/widgets/qwebengineprofile/CMakeLists.txt b/tests/auto/widgets/qwebengineprofile/CMakeLists.txt index 7a4b578ea..d7393eaef 100644 --- a/tests/auto/widgets/qwebengineprofile/CMakeLists.txt +++ b/tests/auto/widgets/qwebengineprofile/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../httpserver/httpserver.cmake) include(../../util/util.cmake) diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp index 3b595f199..095d4c8f2 100644 --- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp +++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp @@ -41,6 +41,7 @@ private Q_SLOTS: void urlSchemeHandlerFailRequest(); void urlSchemeHandlerFailOnRead(); void urlSchemeHandlerStreaming(); + void urlSchemeHandlerStreaming2(); void urlSchemeHandlerRequestHeaders(); void urlSchemeHandlerInstallation(); void urlSchemeHandlerXhrStatus(); @@ -189,6 +190,7 @@ void tst_QWebEngineProfile::clearDataFromCache() AutoDir cacheDir("./tst_QWebEngineProfile_clearDataFromCache"); QWebEngineProfile profile(QStringLiteral("clearDataFromCache")); + QSignalSpy cacheSpy(&profile, &QWebEngineProfile::clearHttpCacheCompleted); profile.setCachePath(cacheDir.path()); profile.setHttpCacheType(QWebEngineProfile::DiskHttpCache); @@ -200,8 +202,7 @@ void tst_QWebEngineProfile::clearDataFromCache() QVERIFY(cacheDir.exists("Cache")); qint64 sizeBeforeClear = totalSize(cacheDir); profile.clearHttpCache(); - // Wait for cache to be cleared. - QTest::qWait(1000); + QTRY_COMPARE(cacheSpy.size(), 1); QVERIFY(sizeBeforeClear > totalSize(cacheDir)); (void)server.stop(); @@ -264,10 +265,12 @@ public: QList<QPointer<QBuffer>> m_buffers; }; -class StreamingIODevice : public QIODevice { +// an evil version constantly claiming to be at end, similar to QNetworkReply +class StreamingIODeviceBasic : public QIODevice +{ Q_OBJECT public: - StreamingIODevice(QObject *parent) : QIODevice(parent), m_bytesRead(0), m_bytesAvailable(0) + StreamingIODeviceBasic(QObject *parent) : QIODevice(parent), m_bytesRead(0), m_bytesAvailable(0) { setOpenMode(QIODevice::ReadOnly); m_timer.start(100, this); @@ -278,12 +281,11 @@ public: const std::lock_guard<QRecursiveMutex> lock(m_mutex); return m_bytesAvailable; } - bool atEnd() const override +protected: + bool internalAtEnd() const { - const std::lock_guard<QRecursiveMutex> lock(m_mutex); return (m_data.size() >= 1000 && m_bytesRead >= 1000); } -protected: void timerEvent(QTimerEvent *) override { const std::lock_guard<QRecursiveMutex> lock(m_mutex); @@ -304,7 +306,7 @@ protected: memcpy(data, m_data.constData() + m_bytesRead, len); m_bytesAvailable -= len; m_bytesRead += len; - } else if (atEnd()) + } else if (internalAtEnd()) return -1; return len; @@ -314,14 +316,26 @@ protected: return 0; } -private: mutable QRecursiveMutex m_mutex; +private: QByteArray m_data; QBasicTimer m_timer; int m_bytesRead; int m_bytesAvailable; }; +// A nicer version implementing atEnd +class StreamingIODevice : public StreamingIODeviceBasic +{ +public: + StreamingIODevice(QObject *parent) : StreamingIODeviceBasic(parent) {} + bool atEnd() const override + { + const std::lock_guard<QRecursiveMutex> lock(m_mutex); + return internalAtEnd(); + } +}; + class StreamingUrlSchemeHandler : public QWebEngineUrlSchemeHandler { public: @@ -336,6 +350,20 @@ public: } }; +class StreamingUrlSchemeHandler2 : public QWebEngineUrlSchemeHandler +{ +public: + StreamingUrlSchemeHandler2(QObject *parent = nullptr) + : QWebEngineUrlSchemeHandler(parent) + { + } + + void requestStarted(QWebEngineUrlRequestJob *job) override + { + job->reply("text/plain;charset=utf-8", new StreamingIODeviceBasic(job)); + } +}; + void tst_QWebEngineProfile::urlSchemeHandlers() { RedirectingUrlSchemeHandler lettertoHandler; @@ -379,8 +407,8 @@ void tst_QWebEngineProfile::urlSchemeHandlers() QCOMPARE(toPlainTextSync(view.page()), url.toString()); // Check that all buffers got deleted - QCOMPARE(gopherHandler.m_buffers.count(), 2); - for (int i = 0; i < gopherHandler.m_buffers.count(); ++i) + QCOMPARE(gopherHandler.m_buffers.size(), 2); + for (int i = 0; i < gopherHandler.m_buffers.size(); ++i) QVERIFY(gopherHandler.m_buffers.at(i).isNull()); } @@ -441,7 +469,7 @@ void tst_QWebEngineProfile::urlSchemeHandlerFailRequest() view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); view.load(QUrl(QStringLiteral("foo://bar"))); view.show(); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); QCOMPARE(toPlainTextSync(view.page()), QString()); } @@ -456,7 +484,7 @@ void tst_QWebEngineProfile::urlSchemeHandlerFailOnRead() view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); view.load(QUrl(QStringLiteral("foo://bar"))); view.show(); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); QCOMPARE(toPlainTextSync(view.page()), QString()); } @@ -471,7 +499,24 @@ void tst_QWebEngineProfile::urlSchemeHandlerStreaming() view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); view.load(QUrl(QStringLiteral("stream://whatever"))); view.show(); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); + QByteArray result; + result.append(1000, 'c'); + QCOMPARE(toPlainTextSync(view.page()), QString::fromLatin1(result)); +} + +void tst_QWebEngineProfile::urlSchemeHandlerStreaming2() +{ + StreamingUrlSchemeHandler2 handler; + QWebEngineProfile profile; + profile.installUrlSchemeHandler("stream", &handler); + QWebEngineView view; + QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); + view.setPage(new QWebEnginePage(&profile, &view)); + view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + view.load(QUrl(QStringLiteral("stream://whatever"))); + view.show(); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); QByteArray result; result.append(1000, 'c'); QCOMPARE(toPlainTextSync(view.page()), QString::fromLatin1(result)); @@ -532,7 +577,7 @@ void tst_QWebEngineProfile::urlSchemeHandlerRequestHeaders() QWebEnginePage page(&profile); QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); page.load(QUrl(QStringLiteral("myscheme://whatever"))); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); } void tst_QWebEngineProfile::urlSchemeHandlerInstallation() @@ -698,12 +743,12 @@ void tst_QWebEngineProfile::urlSchemeHandlerScriptModule() QWebEnginePage page(&profile); QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); page.setHtml(QStringLiteral("<html><head><script src=\"aviancarrier:///\"></script></head><body>Test1</body></html>")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("test")).toString(), QStringLiteral("SUCCESS")); loadFinishedSpy.clear(); page.setHtml(QStringLiteral("<html><head><script type=\"module\" src=\"aviancarrier:///\"></script></head><body>Test2</body></html>")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("test")).toString(), QStringLiteral("SUCCESS")); } @@ -738,7 +783,7 @@ void tst_QWebEngineProfile::customUserAgent() QWebEnginePage page; QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); page.setHtml(QStringLiteral("<html><body>Hello world!</body></html>")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); // First test the user-agent is default QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("navigator.userAgent")).toString(), defaultUserAgent); @@ -751,7 +796,7 @@ void tst_QWebEngineProfile::customUserAgent() QWebEnginePage page2(&testProfile); QSignalSpy loadFinishedSpy2(&page2, SIGNAL(loadFinished(bool))); page2.setHtml(QStringLiteral("<html><body>Hello again!</body></html>")); - QTRY_COMPARE(loadFinishedSpy2.count(), 1); + QTRY_COMPARE(loadFinishedSpy2.size(), 1); QCOMPARE(evaluateJavaScriptSync(&page2, QStringLiteral("navigator.userAgent")).toString(), testUserAgent); QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("navigator.userAgent")).toString(), defaultUserAgent); @@ -765,7 +810,7 @@ void tst_QWebEngineProfile::httpAcceptLanguage() QWebEnginePage page; QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); page.setHtml(QStringLiteral("<html><body>Hello world!</body></html>")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QStringList defaultLanguages = evaluateJavaScriptSync(&page, QStringLiteral("navigator.languages")).toStringList(); @@ -777,7 +822,7 @@ void tst_QWebEngineProfile::httpAcceptLanguage() QWebEnginePage page2(&testProfile); QSignalSpy loadFinishedSpy2(&page2, SIGNAL(loadFinished(bool))); page2.setHtml(QStringLiteral("<html><body>Hello again!</body></html>")); - QTRY_COMPARE(loadFinishedSpy2.count(), 1); + QTRY_COMPARE(loadFinishedSpy2.size(), 1); QCOMPARE(evaluateJavaScriptSync(&page2, QStringLiteral("navigator.languages")).toStringList(), QStringList(testLang)); // Test the old one wasn't affected QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("navigator.languages")).toStringList(), defaultLanguages); @@ -794,7 +839,7 @@ void tst_QWebEngineProfile::downloadItem() QWebEnginePage page(&testProfile); QSignalSpy downloadSpy(&testProfile, SIGNAL(downloadRequested(QWebEngineDownloadRequest *))); page.load(QUrl::fromLocalFile(QCoreApplication::applicationFilePath())); - QTRY_COMPARE(downloadSpy.count(), 1); + QTRY_COMPARE(downloadSpy.size(), 1); } void tst_QWebEngineProfile::changePersistentPath() @@ -920,29 +965,29 @@ void tst_QWebEngineProfile::initiator() QWebEnginePage page(&profile, nullptr); QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); page.load(QUrl("about:blank")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); loadFinishedSpy.clear(); // about:blank has a unique origin, so initiator should be QUrl("null") evaluateJavaScriptSync(&page, "window.location = 'foo:bar'"); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); loadFinishedSpy.clear(); QCOMPARE(handler.initiator, QUrl("null")); page.setHtml("", QUrl("http://test:123/foo%20bar")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); loadFinishedSpy.clear(); // baseUrl determines the origin, so QUrl("http://test:123") evaluateJavaScriptSync(&page, "window.location = 'foo:bar'"); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); loadFinishedSpy.clear(); QCOMPARE(handler.initiator, QUrl("http://test:123")); // Directly calling load/setUrl should have initiator QUrl(), meaning // browser-initiated, trusted. page.load(QUrl("foo:bar")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 10000); QCOMPARE(handler.initiator, QUrl()); } @@ -958,7 +1003,7 @@ void tst_QWebEngineProfile::badDeleteOrder() QSignalSpy spyLoadFinished(page, SIGNAL(loadFinished(bool))); page->setHtml(QStringLiteral("<html><body><h1>Badly handled page!</h1></body></html>")); - QTRY_COMPARE(spyLoadFinished.count(), 1); + QTRY_COMPARE(spyLoadFinished.size(), 1); delete profile; delete view; @@ -974,7 +1019,7 @@ void tst_QWebEngineProfile::qtbug_71895() view.page()->profile()->setHttpCacheType(QWebEngineProfile::NoCache); view.page()->profile()->cookieStore()->deleteAllCookies(); view.page()->profile()->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies); - bool gotSignal = loadSpy.count() || loadSpy.wait(20000); + bool gotSignal = loadSpy.size() || loadSpy.wait(20000); if (!gotSignal) QSKIP("Couldn't load page from network, skipping test."); } diff --git a/tests/auto/widgets/qwebenginescript/CMakeLists.txt b/tests/auto/widgets/qwebenginescript/CMakeLists.txt index 80173096f..d0d499b84 100644 --- a/tests/auto/widgets/qwebenginescript/CMakeLists.txt +++ b/tests/auto/widgets/qwebenginescript/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../util/util.cmake) diff --git a/tests/auto/widgets/qwebenginescript/resources/test_window_open.html b/tests/auto/widgets/qwebenginescript/resources/test_window_open.html index 3f72d176d..3ceafc49d 100644 --- a/tests/auto/widgets/qwebenginescript/resources/test_window_open.html +++ b/tests/auto/widgets/qwebenginescript/resources/test_window_open.html @@ -3,7 +3,7 @@ <head> <title>window.open</title> <script> - window.open("qrc:/resource/test_iframe_main.html", "iframe_main"); + window.open("qrc:/resources/test_iframe_main.html", "iframe_main"); </script> </head> <body></body> diff --git a/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp b/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp index 26ba71ae3..9ba13589f 100644 --- a/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp +++ b/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp @@ -76,6 +76,7 @@ private Q_SLOTS: void scriptsInNestedIframes(); void matchQrcUrl(); void injectionOrder(); + void reloadWithSubframes(); }; void tst_QWebEngineScript::domEditing() @@ -184,7 +185,7 @@ void tst_QWebEngineScript::loadEvents() // Single frame / setHtml page.setHtml(QStringLiteral("<!DOCTYPE html><html><head><title>mr</title></head><body></body></html>")); - QTRY_COMPARE_WITH_TIMEOUT(page.spy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(page.spy.size(), 1, 20000); QVERIFY(page.spy.takeFirst().value(0).toBool()); QVERIFY(verifyOrder(page.eval("window.log", QWebEngineScript::MainWorld).toStringList())); QVERIFY(verifyOrder(page.eval("window.log", QWebEngineScript::ApplicationWorld).toStringList())); @@ -192,14 +193,14 @@ void tst_QWebEngineScript::loadEvents() // After discard page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); page.setLifecycleState(QWebEnginePage::LifecycleState::Active); - QTRY_COMPARE_WITH_TIMEOUT(page.spy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(page.spy.size(), 1, 20000); QVERIFY(page.spy.takeFirst().value(0).toBool()); 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_WITH_TIMEOUT(page.spy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(page.spy.size(), 1, 20000); QVERIFY(page.spy.takeFirst().value(0).toBool()); QVERIFY(verifyOrder(page.eval("window.log", QWebEngineScript::MainWorld).toStringList())); QVERIFY(verifyOrder(page.eval("window.log", QWebEngineScript::ApplicationWorld).toStringList())); @@ -210,7 +211,7 @@ void tst_QWebEngineScript::loadEvents() // Cross-process navigation page.load(QUrl("chrome://gpu")); - QTRY_COMPARE_WITH_TIMEOUT(page.spy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(page.spy.size(), 1, 20000); QVERIFY(page.spy.takeFirst().value(0).toBool()); QVERIFY(verifyOrder(page.eval("window.log", QWebEngineScript::MainWorld).toStringList())); QVERIFY(verifyOrder(page.eval("window.log", QWebEngineScript::ApplicationWorld).toStringList())); @@ -219,8 +220,8 @@ void tst_QWebEngineScript::loadEvents() QVERIFY(profile.pages.size() == 1); page.load(QUrl("qrc:/resources/test_window_open.html")); QTRY_COMPARE(profile.pages.size(), 2u); - QTRY_COMPARE(profile.pages.front().spy.count(), 1); - QTRY_COMPARE(profile.pages.back().spy.count(), 1); + QTRY_COMPARE(profile.pages.front().spy.size(), 1); + QTRY_COMPARE(profile.pages.back().spy.size(), 1); 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())); @@ -271,7 +272,7 @@ void tst_QWebEngineScript::scriptDisabled() page.scripts().insert(script); page.load(QUrl("about:blank")); QSignalSpy spy(&page, &QWebEnginePage::loadFinished); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().value(0).toBool(), true); // MainWorld scripts are disabled by the setting... QCOMPARE(evaluateJavaScriptSyncInWorld(&page, "foo", QWebEngineScript::MainWorld), QVariant()); @@ -280,7 +281,7 @@ void tst_QWebEngineScript::scriptDisabled() page.scripts().clear(); page.scripts().insert(script); page.load(QUrl("about:blank")); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().value(0).toBool(), true); // ...but ApplicationWorld scripts should still work QCOMPARE(evaluateJavaScriptSyncInWorld(&page, "foo", QWebEngineScript::MainWorld), QVariant()); @@ -298,7 +299,7 @@ void tst_QWebEngineScript::viewSource() page.scripts().insert(script); page.load(QUrl("view-source:about:blank")); QSignalSpy spy(&page, &QWebEnginePage::loadFinished); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().value(0).toBool(), true); QCOMPARE(evaluateJavaScriptSync(&page, "foo"), QVariant(42)); } @@ -457,7 +458,7 @@ void tst_QWebEngineScript::scriptsInNestedIframes() QSignalSpy spyFinished(&page, &QWebEnginePage::loadFinished); page.load(QUrl("qrc:/resources/test_iframe_main.html")); view.show(); - QVERIFY(spyFinished.wait()); + QTRY_VERIFY_WITH_TIMEOUT(spyFinished.size() > 0, 20000); // Check that main frame has modified content. QCOMPARE( @@ -557,22 +558,22 @@ void tst_QWebEngineScript::navigation() QString url1 = QStringLiteral("about:blank"); page.setUrl(url1); - QTRY_COMPARE(spyTextChanged.count(), 1); + QTRY_COMPARE(spyTextChanged.size(), 1); QCOMPARE(testObject.text(), url1); QString url2 = QStringLiteral("chrome://gpu/"); page.setUrl(url2); - QTRY_COMPARE(spyTextChanged.count(), 2); + QTRY_COMPARE(spyTextChanged.size(), 2); QCOMPARE(testObject.text(), url2); QString url3 = QStringLiteral("qrc:/resources/test_iframe_main.html"); page.setUrl(url3); - QTRY_COMPARE(spyTextChanged.count(), 3); + QTRY_COMPARE(spyTextChanged.size(), 3); QCOMPARE(testObject.text(), url3); page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); page.setUrl(url1); - QTRY_COMPARE(spyTextChanged.count(), 4); + QTRY_COMPARE(spyTextChanged.size(), 4); QCOMPARE(testObject.text(), url1); } @@ -694,6 +695,38 @@ void tst_QWebEngineScript::injectionOrder() QTRY_COMPARE(page.log, expected); } +void tst_QWebEngineScript::reloadWithSubframes() +{ + class Page : public QWebEnginePage + { + public: + Page() : QWebEnginePage() {} + QVector<QString> log; + + protected: + void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel, const QString &message, int, + const QString &) override + { + log.append(message); + } + } page; + + QWebEngineScript s; + s.setInjectionPoint(QWebEngineScript::DocumentCreation); + s.setSourceCode(QStringLiteral("console.log('Hello');")); + page.scripts().insert(s); + + page.setHtml(QStringLiteral("<body>" + " <h1>Test scripts working on reload </h1>" + " <iframe src='about://blank'>" + " </iframe>" + "</body>")); + QTRY_COMPARE(page.log.size(), 1); + + page.triggerAction(QWebEnginePage::Reload); + QTRY_COMPARE(page.log.size(), 2); +} + QTEST_MAIN(tst_QWebEngineScript) #include "tst_qwebenginescript.moc" diff --git a/tests/auto/widgets/qwebengineview/BLACKLIST b/tests/auto/widgets/qwebengineview/BLACKLIST index ed022878e..26f2da4bb 100644 --- a/tests/auto/widgets/qwebengineview/BLACKLIST +++ b/tests/auto/widgets/qwebengineview/BLACKLIST @@ -6,3 +6,7 @@ windows [navigateOnDrop:file_no_navigate] windows + +[horizontalScrollbarTest] +macos +rhel # flaky diff --git a/tests/auto/widgets/qwebengineview/CMakeLists.txt b/tests/auto/widgets/qwebengineview/CMakeLists.txt index 903f15f12..9583184d0 100644 --- a/tests/auto/widgets/qwebengineview/CMakeLists.txt +++ b/tests/auto/widgets/qwebengineview/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../util/util.cmake) diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index fd6c56a3e..f4ed06e14 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp @@ -18,6 +18,9 @@ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses + #include <QtWebEngineCore/private/qtwebenginecore-config_p.h> #include <qtest.h> #include <util.h> @@ -72,7 +75,8 @@ namespace QTest { { QTest::qWait(QTest::defaultMouseDelay()); lastMouseTimestamp += QTest::defaultMouseDelay(); - QMouseEvent me(type, pos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + QMouseEvent me(type, pos, widget->mapToGlobal(pos), Qt::LeftButton, Qt::LeftButton, + Qt::NoModifier); me.setTimestamp(++lastMouseTimestamp); QSpontaneKeyEvent::setSpontaneous(&me); qApp->sendEvent(widget, &me); @@ -181,7 +185,10 @@ private Q_SLOTS: void inspectElement(); void navigateOnDrop_data(); void navigateOnDrop(); + void emptyUriListOnDrop(); void datalist(); + void longKeyEventText(); + void pageWithPaintListeners(); }; // This will be called before the first test function is executed. @@ -207,6 +214,95 @@ void tst_QWebEngineView::cleanup() QTRY_COMPARE(QApplication::topLevelWidgets().size(), 0); } +class PageWithPaintListeners : public QWebEnginePage +{ + Q_OBJECT +public: + PageWithPaintListeners(QObject *parent = nullptr) : QWebEnginePage(parent) + { + addFirstContentfulPaintListener(); + addLargestContentfulPaintListener(); + } + + void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString &message, + int lineNumber, const QString &sourceID) override + { + Q_UNUSED(level) + Q_UNUSED(lineNumber) + Q_UNUSED(sourceID) + if (message.contains("firstContentfulPaint")) + emit firstContentfulPaint(); + if (message.contains("largestContentfulPaint")) + emit largestContentfulPaint(); + } + + // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver + void addFirstContentfulPaintListener() + { + QObject::connect(this, &QWebEnginePage::loadFinished, [this]() { + runJavaScript(QStringLiteral( + "new PerformanceObserver((entryList) => {" + " if (entryList.getEntriesByType('first-contentful-paint'))" + " console.log('firstContentfulPaint');" + "}).observe({type: 'paint', buffered: true});")); + }); + } + + void addLargestContentfulPaintListener() + { + QObject::connect(this, &QWebEnginePage::loadFinished, [this]() { + runJavaScript(QStringLiteral( + "new PerformanceObserver((entryList) => {" + " console.log('largestContentfulPaint');" + "}).observe({type: 'largest-contentful-paint', buffered: true});")); + }); + } + +signals: + void firstContentfulPaint(); // https://web.dev/articles/fcp + void largestContentfulPaint(); // https://web.dev/articles/lcp +}; + +void tst_QWebEngineView::pageWithPaintListeners() +{ + PageWithPaintListeners page; + + QSignalSpy firstContentfulPaintSpy(&page, &PageWithPaintListeners::firstContentfulPaint); + QSignalSpy largestContentfulPaintSpy(&page, &PageWithPaintListeners::largestContentfulPaint); + + const QString empty = + QStringLiteral("<html><body style='width:100x;height:100px;'></body></html>"); + const QString scrollBars = + QStringLiteral("<html><body style='width:1000px;height:1000px;'></body></html>"); + const QString backgroundColor = + QStringLiteral("<html><body style='background-color:green'></body></html>"); + const QString text = QStringLiteral("<html><body>text</body></html>"); + + QWebEngineView view; + view.setPage(&page); + view.resize(600, 600); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + page.setHtml(empty); + QTest::qWait(500); // empty page should not trigger + QVERIFY(firstContentfulPaintSpy.size() == 0); + QVERIFY(largestContentfulPaintSpy.size() == 0); + + page.setHtml(backgroundColor); + QTRY_VERIFY(firstContentfulPaintSpy.size() == 1); + + page.setHtml(text); + QTRY_VERIFY(firstContentfulPaintSpy.size() == 2); + QTRY_VERIFY(largestContentfulPaintSpy.size() == 1); + +#if !QT_CONFIG(webengine_embedded_build) + // Embedded builds have different scrollbars that are only painted on hover + page.setHtml(scrollBars); + QTRY_VERIFY(firstContentfulPaintSpy.size() == 3); +#endif +} + void tst_QWebEngineView::renderHints() { #if !defined(QWEBENGINEVIEW_RENDERHINTS) @@ -303,10 +399,10 @@ void tst_QWebEngineView::changePage() QSignalSpy pageFromLoadSpy(pageFrom.get(), &QWebEnginePage::loadFinished); QSignalSpy pageFromIconLoadSpy(pageFrom.get(), &QWebEnginePage::iconChanged); pageFrom->load(urlFrom); - QTRY_COMPARE(pageFromLoadSpy.count(), 1); + QTRY_COMPARE(pageFromLoadSpy.size(), 1); QCOMPARE(pageFromLoadSpy.last().value(0).toBool(), true); if (!fromIsNullPage) { - QTRY_COMPARE(pageFromIconLoadSpy.count(), 1); + QTRY_COMPARE(pageFromIconLoadSpy.size(), 1); QVERIFY(!pageFromIconLoadSpy.last().value(0).isNull()); } @@ -314,13 +410,13 @@ void tst_QWebEngineView::changePage() QCOMPARE(view->page(), pageFrom.get()); QCOMPARE(QWebEngineView::forPage(pageFrom.get()), view.get()); - QTRY_COMPARE(spyUrl.count(), 1); + QTRY_COMPARE(spyUrl.size(), 1); QCOMPARE(spyUrl.last().value(0).toUrl(), pageFrom->url()); - QTRY_COMPARE(spyTitle.count(), 1); + QTRY_COMPARE(spyTitle.size(), 1); QCOMPARE(spyTitle.last().value(0).toString(), pageFrom->title()); - QTRY_COMPARE(spyIconUrl.count(), fromIsNullPage ? 0 : 1); - QTRY_COMPARE(spyIcon.count(), fromIsNullPage ? 0 : 1); + QTRY_COMPARE(spyIconUrl.size(), fromIsNullPage ? 0 : 1); + QTRY_COMPARE(spyIcon.size(), fromIsNullPage ? 0 : 1); if (!fromIsNullPage) { QVERIFY(!pageFrom->iconUrl().isEmpty()); QCOMPARE(spyIconUrl.last().value(0).toUrl(), pageFrom->iconUrl()); @@ -332,10 +428,10 @@ void tst_QWebEngineView::changePage() QSignalSpy pageToLoadSpy(pageTo.get(), &QWebEnginePage::loadFinished); QSignalSpy pageToIconLoadSpy(pageTo.get(), &QWebEnginePage::iconChanged); pageTo->load(urlTo); - QTRY_COMPARE(pageToLoadSpy.count(), 1); + QTRY_COMPARE(pageToLoadSpy.size(), 1); QCOMPARE(pageToLoadSpy.last().value(0).toBool(), true); if (!toIsNullPage) { - QTRY_COMPARE(pageToIconLoadSpy.count(), 1); + QTRY_COMPARE(pageToIconLoadSpy.size(), 1); QVERIFY(!pageToIconLoadSpy.last().value(0).isNull()); } @@ -344,16 +440,16 @@ void tst_QWebEngineView::changePage() QCOMPARE(QWebEngineView::forPage(pageTo.get()), view.get()); QCOMPARE(QWebEngineView::forPage(pageFrom.get()), nullptr); - QTRY_COMPARE(spyUrl.count(), 2); + QTRY_COMPARE(spyUrl.size(), 2); QCOMPARE(spyUrl.last().value(0).toUrl(), pageTo->url()); - QTRY_COMPARE(spyTitle.count(), 2); + QTRY_COMPARE(spyTitle.size(), 2); QCOMPARE(spyTitle.last().value(0).toString(), pageTo->title()); bool iconIsSame = fromIsNullPage == toIsNullPage; int iconChangeNotifyCount = fromIsNullPage ? (iconIsSame ? 0 : 1) : (iconIsSame ? 1 : 2); - QTRY_COMPARE(spyIconUrl.count(), iconChangeNotifyCount); - QTRY_COMPARE(spyIcon.count(), iconChangeNotifyCount); + QTRY_COMPARE(spyIconUrl.size(), iconChangeNotifyCount); + QTRY_COMPARE(spyIcon.size(), iconChangeNotifyCount); QCOMPARE(pageFrom->iconUrl() == pageTo->iconUrl(), iconIsSame); if (!iconIsSame) { QCOMPARE(spyIconUrl.last().value(0).toUrl(), pageTo->iconUrl()); @@ -364,10 +460,10 @@ void tst_QWebEngineView::changePage() // verify no emits on destroy with the same number of signals in spy view.reset(); qApp->processEvents(); - QTRY_COMPARE(spyUrl.count(), 2); - QTRY_COMPARE(spyTitle.count(), 2); - QTRY_COMPARE(spyIconUrl.count(), iconChangeNotifyCount); - QTRY_COMPARE(spyIcon.count(), iconChangeNotifyCount); + QTRY_COMPARE(spyUrl.size(), 2); + QTRY_COMPARE(spyTitle.size(), 2); + QTRY_COMPARE(spyIconUrl.size(), iconChangeNotifyCount); + QTRY_COMPARE(spyIcon.size(), iconChangeNotifyCount); } void tst_QWebEngineView::reusePage_data() @@ -423,7 +519,7 @@ void tst_QWebEngineView::setLoadedPage() QWebEnginePage page; QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); page.setHtml(QString("<html><body bgcolor=\"%1\"></body></html>").arg(QColor(Qt::yellow).name())); - QTRY_VERIFY(loadSpy.count() == 1 && loadSpy.first().first().toBool()); + QTRY_VERIFY(loadSpy.size() == 1 && loadSpy.first().first().toBool()); QWebEngineView view; view.resize(480, 320); @@ -508,7 +604,7 @@ void tst_QWebEngineView::microFocusCoordinates() QVariant initialMicroFocus = webView.focusProxy()->inputMethodQuery(Qt::ImCursorRectangle); evaluateJavaScriptSync(webView.page(), "window.scrollBy(0, 50)"); - QTRY_VERIFY(scrollSpy.count() > 0); + QTRY_VERIFY(scrollSpy.size() > 0); QTRY_VERIFY(webView.focusProxy()->inputMethodQuery(Qt::ImCursorRectangle).isValid()); QVariant currentMicroFocus = webView.focusProxy()->inputMethodQuery(Qt::ImCursorRectangle); @@ -636,7 +732,7 @@ void tst_QWebEngineView::unhandledKeyEventPropagation() QSignalSpy loadFinishedSpy(&webView, SIGNAL(loadFinished(bool))); webView.load(QUrl("qrc:///resources/keyboardEvents.html")); - QVERIFY(loadFinishedSpy.wait()); + QTRY_VERIFY_WITH_TIMEOUT(loadFinishedSpy.size() > 0, 20000); evaluateJavaScriptSync(webView.page(), "document.getElementById('first_div').focus()"); QTRY_COMPARE(evaluateJavaScriptSync(webView.page(), "document.activeElement.id").toString(), QStringLiteral("first_div")); @@ -682,19 +778,29 @@ void tst_QWebEngineView::unhandledKeyEventPropagation() void tst_QWebEngineView::horizontalScrollbarTest() { +#if QT_CONFIG(webengine_embedded_build) + // Embedded builds enable the OverlayScrollbar and Viewport features (see 'useEmbeddedSwitches' in web_engine_context.cpp). + // These features make the scrollbar simpler assuming we are on a device with small (usually touch) display. + // These scrollbars behave differently on mouse events. + QSKIP("Embedded builds have different scrollbar, skipping test."); +#endif QString html("<html><body>" "<div style='width: 1000px; height: 1000px; background-color: green' />" "</body></html>"); QWebEngineView view; + PageWithPaintListeners page; + view.setPage(&page); view.setFixedSize(600, 600); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); + QSignalSpy firstPaintSpy(&page, &PageWithPaintListeners::firstContentfulPaint); QSignalSpy loadSpy(view.page(), SIGNAL(loadFinished(bool))); view.setHtml(html); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); + QTRY_COMPARE(firstPaintSpy.size(), 1); QVERIFY(view.page()->scrollPosition() == QPoint(0, 0)); QSignalSpy scrollSpy(view.page(), SIGNAL(scrollPositionChanged(QPointF))); @@ -986,7 +1092,7 @@ void tst_QWebEngineView::doNotSendMouseKeyboardEventsWhenDisabled() QSignalSpy loadSpy(&webView, SIGNAL(loadFinished(bool))); webView.setHtml("<html><head><title>Title</title></head><body>Hello" "<input id=\"input\" type=\"text\"></body></html>"); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); // When the webView is enabled, the events are swallowed by it, and the parent widget // does not receive any events, otherwise all events are processed by the parent widget. @@ -1033,7 +1139,7 @@ void tst_QWebEngineView::stopSettingFocusWhenDisabled() QSignalSpy loadSpy(&webView, SIGNAL(loadFinished(bool))); webView.setHtml("<html><head><title>Title</title></head><body>Hello" "<input id=\"input\" type=\"text\"></body></html>"); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QTRY_COMPARE_WITH_TIMEOUT(webView.hasFocus(), focusResult, 1000); evaluateJavaScriptSync(webView.page(), "document.getElementById(\"input\").focus()"); @@ -1146,21 +1252,23 @@ void tst_QWebEngineView::focusInternalRenderWidgetHostViewQuickItem() QWebEngineView *webView = new QWebEngineView; QWebEngineSettings *settings = webView->page()->settings(); settings->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false); - webView->resize(300, 300); + webView->resize(300, 100); - QHBoxLayout *layout = new QHBoxLayout; + QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(label); layout->addWidget(webView); + containerWidget->resize(300, 200); containerWidget->setLayout(layout); containerWidget->show(); QVERIFY(QTest::qWaitForWindowExposed(containerWidget.data())); // Load the content, and check that focus is not set. QSignalSpy loadSpy(webView, SIGNAL(loadFinished(bool))); - webView->setHtml("<html><head><title>Title</title></head><body>Hello" - "<input id=\"input\" type=\"text\"></body></html>"); - QTRY_COMPARE(loadSpy.count(), 1); + webView->setHtml("<html><body>" + " <input id='input1' type='text'/>" + "</body></html>"); + QTRY_COMPARE(loadSpy.size(), 1); QTRY_COMPARE(webView->hasFocus(), false); // Manually trigger focus. @@ -1169,15 +1277,43 @@ void tst_QWebEngineView::focusInternalRenderWidgetHostViewQuickItem() // Check that focus is set in QWebEngineView and all internal classes. QTRY_COMPARE(webView->hasFocus(), true); - QQuickWidget *renderWidgetHostViewQtDelegateWidget = - qobject_cast<QQuickWidget *>(webView->focusProxy()); - QVERIFY(renderWidgetHostViewQtDelegateWidget); - QTRY_COMPARE(renderWidgetHostViewQtDelegateWidget->hasFocus(), true); + QQuickWidget *webEngineQuickWidget = qobject_cast<QQuickWidget *>(webView->focusProxy()); + QVERIFY(webEngineQuickWidget); + QTRY_COMPARE(webEngineQuickWidget->hasFocus(), true); + + QQuickItem *root = webEngineQuickWidget->rootObject(); + // The root item should not has focus, otherwise it would handle input events + // instead of the RenderWidgetHostViewQtDelegateItem. + QVERIFY(!root->hasFocus()); + + QCOMPARE(root->childItems().size(), 1); + QQuickItem *renderWidgetHostViewQtDelegateItem = root->childItems().at(0); + QVERIFY(renderWidgetHostViewQtDelegateItem); + QTRY_COMPARE(renderWidgetHostViewQtDelegateItem->hasFocus(), true); + // Test if QWebEngineView handles key events. + QTRY_COMPARE(renderWidgetHostViewQtDelegateItem->hasActiveFocus(), true); + + // Key events should not be forwarded to the unfocused input field. + QTRY_COMPARE(evaluateJavaScriptSync(webView->page(), + "document.getElementById('input1').value").toString(), + QStringLiteral("")); + QTest::keyClick(webView->focusProxy(), Qt::Key_X); + QTest::qWait(100); + QTRY_COMPARE(evaluateJavaScriptSync(webView->page(), + "document.getElementById('input1').value").toString(), + QStringLiteral("")); - QQuickItem *renderWidgetHostViewQuickItem = - renderWidgetHostViewQtDelegateWidget->rootObject(); - QVERIFY(renderWidgetHostViewQuickItem); - QTRY_COMPARE(renderWidgetHostViewQuickItem->hasFocus(), true); + // Focus the input field. Focus rectangle is expected to appear around the input field. + evaluateJavaScriptSync(webView->page(), "document.getElementById('input1').focus()"); + QTRY_COMPARE(evaluateJavaScriptSync(webView->page(), + "document.activeElement.id").toString(), + QStringLiteral("input1")); + + // Test the focused input field with a key event. + QTest::keyClick(webView->focusProxy(), Qt::Key_X); + QTRY_COMPARE(evaluateJavaScriptSync(webView->page(), + "document.getElementById('input1').value").toString(), + QStringLiteral("x")); } void tst_QWebEngineView::doNotBreakLayout() @@ -1217,7 +1353,7 @@ void tst_QWebEngineView::changeLocale() QWebEngineView viewDE; QSignalSpy loadFinishedSpyDE(&viewDE, SIGNAL(loadFinished(bool))); viewDE.load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpyDE.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpyDE.size(), 1, 20000); QTRY_VERIFY(!toPlainTextSync(viewDE.page()).isEmpty()); errorLines = toPlainTextSync(viewDE.page()).split(QRegularExpression("[\r\n]"), Qt::SkipEmptyParts); @@ -1227,7 +1363,7 @@ void tst_QWebEngineView::changeLocale() QWebEngineView viewEN; QSignalSpy loadFinishedSpyEN(&viewEN, SIGNAL(loadFinished(bool))); viewEN.load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpyEN.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpyEN.size(), 1, 20000); QTRY_VERIFY(!toPlainTextSync(viewEN.page()).isEmpty()); errorLines = toPlainTextSync(viewEN.page()).split(QRegularExpression("[\r\n]"), Qt::SkipEmptyParts); @@ -1240,7 +1376,7 @@ void tst_QWebEngineView::changeLocale() // Check whether an existing QWebEngineView keeps the language settings after changing the default locale viewDE.load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpyDE.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpyDE.size(), 1, 20000); QTRY_VERIFY(!toPlainTextSync(viewDE.page()).isEmpty()); errorLines = toPlainTextSync(viewDE.page()).split(QRegularExpression("[\r\n]"), Qt::SkipEmptyParts); @@ -1272,10 +1408,10 @@ void tst_QWebEngineView::mixLangLocale() auto sc = connect(view.page(), &QWebEnginePage::renderProcessTerminated, [&] () { terminated = true; }); view.load(QUrl("qrc:///resources/dummy.html")); - QTRY_VERIFY(terminated || loadSpy.count() == 1); + QTRY_VERIFY(terminated || loadSpy.size() == 1); QVERIFY2(!terminated, - qPrintable(QString("Locale [%1] terminated: %2, loaded: %3").arg(locale).arg(terminated).arg(loadSpy.count()))); + qPrintable(QString("Locale [%1] terminated: %2, loaded: %3").arg(locale).arg(terminated).arg(loadSpy.size()))); QVERIFY(loadSpy.first().first().toBool()); QString content = toPlainTextSync(view.page()); @@ -1322,18 +1458,19 @@ void tst_QWebEngineView::inputMethodsTextFormat_data() void tst_QWebEngineView::inputMethodsTextFormat() { - QWebEngineView view; - view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true); - QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); + QWebEnginePage page; + QWebEngineView view(&page); + page.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true); + QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); - view.setHtml("<html><body>" + page.setHtml("<html><body>" " <input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/>" "</body></html>"); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); - evaluateJavaScriptSync(view.page(), "document.getElementById('input1').focus()"); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); + evaluateJavaScriptSync(&page, "document.getElementById('input1').focus()"); QFETCH(QString, string); QFETCH(int, start); @@ -1357,8 +1494,8 @@ void tst_QWebEngineView::inputMethodsTextFormat() attrs.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, start, length, format)); QInputMethodEvent im(string, attrs); - QVERIFY(QApplication::sendEvent(view.focusProxy(), &im)); - QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), string); + QApplication::sendEvent(view.focusProxy(), &im); + QTRY_COMPARE_WITH_TIMEOUT(evaluateJavaScriptSync(&page, "document.getElementById('input1').value").toString(), string, 20000); } void tst_QWebEngineView::keyboardEvents() @@ -1367,7 +1504,7 @@ void tst_QWebEngineView::keyboardEvents() view.show(); QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); view.load(QUrl("qrc:///resources/keyboardEvents.html")); - QVERIFY(loadFinishedSpy.wait()); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); QStringList elements; elements << "first_div" << "second_div"; @@ -1498,7 +1635,7 @@ void tst_QWebEngineView::keyboardFocusAfterPopup() QTRY_COMPARE(QApplication::focusWidget(), window.webView->focusProxy()); // Keyboard events sent to the window should go to the <input> element. - QVERIFY(loadFinishedSpy.count() || loadFinishedSpy.wait()); + QVERIFY(loadFinishedSpy.size() || loadFinishedSpy.wait()); QTest::keyPress(QApplication::focusWindow(), Qt::Key_X); QTest::keyRelease(QApplication::focusWindow(), Qt::Key_X); QTRY_COMPARE(evaluateJavaScriptSync(window.webView->page(), "document.getElementById('input1').value").toString(), @@ -1529,7 +1666,7 @@ void tst_QWebEngineView::mouseClick() textInputCenter = elementCenter(view.page(), "input"); QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter); QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); QVERIFY(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString().isEmpty()); // Double click @@ -1544,13 +1681,13 @@ void tst_QWebEngineView::mouseClick() textInputCenter = elementCenter(view.page(), "input"); QTest::mouseMultiClick(view.focusProxy(), textInputCenter, 2); QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 1); + QCOMPARE(selectionChangedSpy.size(), 1); QCOMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input")); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QStringLiteral("Company")); QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter); QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 2); + QCOMPARE(selectionChangedSpy.size(), 2); QVERIFY(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString().isEmpty()); // Triple click @@ -1565,13 +1702,13 @@ void tst_QWebEngineView::mouseClick() textInputCenter = elementCenter(view.page(), "input"); QTest::mouseMultiClick(view.focusProxy(), textInputCenter, 3); QVERIFY(selectionChangedSpy.wait()); - QTRY_COMPARE(selectionChangedSpy.count(), 2); + QTRY_COMPARE(selectionChangedSpy.size(), 2); QCOMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input")); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QStringLiteral("The Qt Company")); QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter); QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 3); + QCOMPARE(selectionChangedSpy.size(), 3); QVERIFY(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString().isEmpty()); } @@ -1599,12 +1736,12 @@ void tst_QWebEngineView::postData() // examine request QStringList request = lines[0].split(" ", Qt::SkipEmptyParts); - bool requestOk = request.length() > 2 + bool requestOk = request.size() > 2 && request[2].toUpper().startsWith("HTTP/") && request[0].toUpper() == "POST" && request[1] == "/"; if (!requestOk) // POST and HTTP/... can be switched(?) - requestOk = request.length() > 2 + requestOk = request.size() > 2 && request[0].toUpper().startsWith("HTTP/") && request[2].toUpper() == "POST" && request[1] == "/"; @@ -1612,16 +1749,16 @@ void tst_QWebEngineView::postData() // examine headers int line = 1; bool headersOk = true; - for (; headersOk && line < lines.length(); line++) { + for (; headersOk && line < lines.size(); line++) { QStringList headerParts = lines[line].split(":"); - if (headerParts.length() < 2) + if (headerParts.size() < 2) break; QString headerKey = headerParts[0].trimmed().toLower(); QString headerValue = headerParts[1].trimmed().toLower(); if (headerKey == "host") headersOk = headersOk && (headerValue == "127.0.0.1") - && (headerParts.length() == 3) + && (headerParts.size() == 3) && (headerParts[2].trimmed() == QString::number(server.serverPort())); if (headerKey == "content-type") @@ -1630,12 +1767,12 @@ void tst_QWebEngineView::postData() // examine body bool bodyOk = true; - if (lines.length() == line+2) { + if (lines.size() == line+2) { QStringList postedFields = lines[line+1].split("&"); QMap<QString, QString> postedData; - for (int i = 0; bodyOk && i < postedFields.length(); i++) { + for (int i = 0; bodyOk && i < postedFields.size(); i++) { QStringList postedField = postedFields[i].split("="); - if (postedField.length() == 2) + if (postedField.size() == 2) postedData[QUrl::fromPercentEncoding(postedField[0].toLocal8Bit())] = QUrl::fromPercentEncoding(postedField[1].toLocal8Bit()); else @@ -1929,14 +2066,14 @@ void tst_QWebEngineView::inputContextQueryInput() view.setHtml("<html><body>" " <input type='text' id='input1' value='' size='50'/>" "</body></html>"); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QVERIFY(QTest::qWaitForWindowExposed(&view)); - QCOMPARE(testContext.infos.count(), 0); + QCOMPARE(testContext.infos.size(), 0); // Set focus on an input field. QPoint textInputCenter = elementCenter(view.page(), "input1"); QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter); - QTRY_COMPARE(testContext.infos.count(), 2); + QTRY_COMPARE(testContext.infos.size(), 2); QCOMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input1")); foreach (const InputMethodInfo &info, testContext.infos) { QCOMPARE(info.cursorPosition, 0); @@ -1948,7 +2085,7 @@ void tst_QWebEngineView::inputContextQueryInput() // Change content of an input field from JavaScript. evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value='QtWebEngine';"); - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 11); QCOMPARE(testContext.infos[0].anchorPosition, 11); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine")); @@ -1957,7 +2094,7 @@ void tst_QWebEngineView::inputContextQueryInput() // Change content of an input field by key press. QTest::keyClick(view.focusProxy(), Qt::Key_Exclam); - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 12); QCOMPARE(testContext.infos[0].anchorPosition, 12); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!")); @@ -1966,7 +2103,7 @@ void tst_QWebEngineView::inputContextQueryInput() // Change cursor position. QTest::keyClick(view.focusProxy(), Qt::Key_Left); - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 11); QCOMPARE(testContext.infos[0].anchorPosition, 11); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!")); @@ -1981,8 +2118,8 @@ void tst_QWebEngineView::inputContextQueryInput() QInputMethodEvent event("", attributes); QApplication::sendEvent(view.focusProxy(), &event); } - QTRY_COMPARE(testContext.infos.count(), 2); - QTRY_COMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 2); + QTRY_COMPARE(selectionChangedSpy.size(), 1); // As a first step, Chromium moves the cursor to the start of the selection. // We don't filter this in QtWebEngine because we don't know yet if this is part of a selection. @@ -2007,8 +2144,8 @@ void tst_QWebEngineView::inputContextQueryInput() QInputMethodEvent event("", attributes); QApplication::sendEvent(view.focusProxy(), &event); } - QTRY_COMPARE(testContext.infos.count(), 1); - QTRY_COMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); + QTRY_COMPARE(selectionChangedSpy.size(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 0); QCOMPARE(testContext.infos[0].anchorPosition, 0); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!")); @@ -2022,9 +2159,9 @@ void tst_QWebEngineView::inputContextQueryInput() QInputMethodEvent event("123", attributes); QApplication::sendEvent(view.focusProxy(), &event); } - QTRY_COMPARE(testContext.infos.count(), 1); - QCOMPARE(testContext.infos[0].cursorPosition, 3); - QCOMPARE(testContext.infos[0].anchorPosition, 3); + QTRY_COMPARE(testContext.infos.size(), 1); + QCOMPARE(testContext.infos[0].cursorPosition, 0); + QCOMPARE(testContext.infos[0].anchorPosition, 0); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!")); QCOMPARE(testContext.infos[0].selectedText, QStringLiteral("")); QCOMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), QStringLiteral("123QtWebEngine!")); @@ -2036,7 +2173,7 @@ void tst_QWebEngineView::inputContextQueryInput() QInputMethodEvent event("", attributes); QApplication::sendEvent(view.focusProxy(), &event); } - QTRY_COMPARE(testContext.infos.count(), 2); + QTRY_COMPARE(testContext.infos.size(), 2); foreach (const InputMethodInfo &info, testContext.infos) { QCOMPARE(info.cursorPosition, 0); QCOMPARE(info.anchorPosition, 0); @@ -2053,7 +2190,7 @@ void tst_QWebEngineView::inputContextQueryInput() event.setCommitString(QStringLiteral("123"), 0, 0); QApplication::sendEvent(view.focusProxy(), &event); } - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 3); QCOMPARE(testContext.infos[0].anchorPosition, 3); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("123QtWebEngine!")); @@ -2063,7 +2200,7 @@ void tst_QWebEngineView::inputContextQueryInput() // Focus out. QTest::keyPress(view.focusProxy(), Qt::Key_Tab); - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("")); testContext.infos.clear(); } @@ -2107,7 +2244,7 @@ void tst_QWebEngineView::inputMethods() QInputMethodEvent eventText(text, inputAttributes); QApplication::sendEvent(view.focusProxy(), &eventText); QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), text); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); } { @@ -2116,7 +2253,7 @@ void tst_QWebEngineView::inputMethods() eventText.setCommitString(text, 0, 0); QApplication::sendEvent(view.focusProxy(), &eventText); QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), text); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); } // ImMaximumTextLength @@ -2192,24 +2329,24 @@ void tst_QWebEngineView::textSelectionInInputField() QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 11); QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 11); // There was no selection to be changed by the click - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); QList<QInputMethodEvent::Attribute> attributes; QInputMethodEvent event(QString(), attributes); event.setCommitString("XXX", 0, 0); QApplication::sendEvent(view.focusProxy(), &event); QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("QtWebEngineXXX")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); event.setCommitString(QString(), -2, 2); // Erase two characters. QApplication::sendEvent(view.focusProxy(), &event); QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("QtWebEngineX")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); event.setCommitString(QString(), -1, 1); // Erase one character. QApplication::sendEvent(view.focusProxy(), &event); QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("QtWebEngine")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); // Move to the start of the line QTest::keyClick(view.focusProxy(), Qt::Key_Home); @@ -2221,7 +2358,7 @@ void tst_QWebEngineView::textSelectionInInputField() // Select to the end of the line QTest::keyClick(view.focusProxy(), Qt::Key_End, Qt::ShiftModifier); QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 1); + QCOMPARE(selectionChangedSpy.size(), 1); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 2); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 11); @@ -2231,7 +2368,7 @@ void tst_QWebEngineView::textSelectionInInputField() // Deselect the selection (this moves the current cursor to the end of the text) QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter); QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 2); + QCOMPARE(selectionChangedSpy.size(), 2); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 11); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 11); @@ -2244,7 +2381,7 @@ void tst_QWebEngineView::textSelectionInInputField() // Select to the start of the line QTest::keyClick(view.focusProxy(), Qt::Key_Home, Qt::ShiftModifier); QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 3); + QCOMPARE(selectionChangedSpy.size(), 3); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 9); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 0); @@ -2266,34 +2403,31 @@ void tst_QWebEngineView::textSelectionOutOfInputField() QVERIFY(loadFinishedSpy.wait()); QVERIFY(QTest::qWaitForWindowExposed(&view)); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); QVERIFY(!view.hasSelection()); QVERIFY(view.page()->selectedText().isEmpty()); // Simple click should not update text selection, however it updates selection bounds in Chromium QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, view.geometry().center()); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); QVERIFY(!view.hasSelection()); QVERIFY(view.page()->selectedText().isEmpty()); // Select text by ctrl+a QTest::keyClick(view.windowHandle(), Qt::Key_A, Qt::ControlModifier); - QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(selectionChangedSpy.size(), 1); QVERIFY(view.hasSelection()); QCOMPARE(view.page()->selectedText(), QString("This is a text")); // Deselect text by mouse click QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, view.geometry().center()); - QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 2); + QTRY_COMPARE(selectionChangedSpy.size(), 2); QVERIFY(!view.hasSelection()); QVERIFY(view.page()->selectedText().isEmpty()); // Select text by ctrl+a QTest::keyClick(view.windowHandle(), Qt::Key_A, Qt::ControlModifier); - QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 3); + QTRY_COMPARE(selectionChangedSpy.size(), 3); QVERIFY(view.hasSelection()); QCOMPARE(view.page()->selectedText(), QString("This is a text")); @@ -2301,8 +2435,7 @@ void tst_QWebEngineView::textSelectionOutOfInputField() view.hide(); view.page()->setLifecycleState(QWebEnginePage::LifecycleState::Discarded); view.show(); - QVERIFY(loadFinishedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 4); + QTRY_COMPARE(selectionChangedSpy.size(), 4); QVERIFY(!view.hasSelection()); QVERIFY(view.page()->selectedText().isEmpty()); @@ -2315,7 +2448,7 @@ void tst_QWebEngineView::textSelectionOutOfInputField() QVERIFY(loadFinishedSpy.wait()); QVERIFY(QTest::qWaitForWindowExposed(&view)); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); QVERIFY(!view.hasSelection()); QVERIFY(view.page()->selectedText().isEmpty()); @@ -2325,31 +2458,27 @@ void tst_QWebEngineView::textSelectionOutOfInputField() // Select the whole page by ctrl+a QTest::keyClick(view.windowHandle(), Qt::Key_A, Qt::ControlModifier); - QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(selectionChangedSpy.size(), 1); QVERIFY(view.hasSelection()); QVERIFY(view.page()->selectedText().startsWith(QString("This is a text"))); // Remove selection by clicking into an input field QPoint textInputCenter = elementCenter(view.page(), "input1"); QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter); - QVERIFY(selectionChangedSpy.wait()); + QTRY_COMPARE(selectionChangedSpy.size(), 2); QCOMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input1")); - QCOMPARE(selectionChangedSpy.count(), 2); QVERIFY(!view.hasSelection()); QVERIFY(view.page()->selectedText().isEmpty()); // Select the content of the input field by ctrl+a QTest::keyClick(view.windowHandle(), Qt::Key_A, Qt::ControlModifier); - QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 3); + QTRY_COMPARE(selectionChangedSpy.size(), 3); QVERIFY(view.hasSelection()); QCOMPARE(view.page()->selectedText(), QString("QtWebEngine")); // Deselect input field's text by mouse click QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, view.geometry().center()); - QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 4); + QTRY_COMPARE(selectionChangedSpy.size(), 4); QVERIFY(!view.hasSelection()); QVERIFY(view.page()->selectedText().isEmpty()); } @@ -2396,7 +2525,7 @@ void tst_QWebEngineView::emptyInputMethodEvent() QVERIFY(QTest::qWaitForWindowExposed(&view)); evaluateJavaScriptSync(view.page(), "var inputEle = document.getElementById('input1'); inputEle.focus(); inputEle.select();"); - QTRY_COMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(selectionChangedSpy.size(), 1); // 1. Empty input method event does not clear text QInputMethodEvent emptyEvent; @@ -2445,7 +2574,7 @@ void tst_QWebEngineView::imeComposition() QVERIFY(QTest::qWaitForWindowExposed(&view)); evaluateJavaScriptSync(view.page(), "var inputEle = document.getElementById('input1'); inputEle.focus(); inputEle.select();"); - QTRY_COMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(selectionChangedSpy.size(), 1); // Clear the selection, also cancel the ongoing composition if there is one. { @@ -2455,7 +2584,7 @@ void tst_QWebEngineView::imeComposition() QInputMethodEvent event("", attributes); QApplication::sendEvent(view.focusProxy(), &event); selectionChangedSpy.wait(); - QCOMPARE(selectionChangedSpy.count(), 2); + QCOMPARE(selectionChangedSpy.size(), 2); } QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("QtWebEngine inputMethod")); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 0); @@ -2476,7 +2605,7 @@ void tst_QWebEngineView::imeComposition() QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 0); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 0); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); // Send temporary text, which makes the editor has composition 'n'. { @@ -2488,7 +2617,7 @@ void tst_QWebEngineView::imeComposition() QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 0); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 0); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); // Send commit text, which makes the editor conforms composition. { @@ -2501,7 +2630,7 @@ void tst_QWebEngineView::imeComposition() QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 1); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 1); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); // 2. insert a character to the middle of the line. @@ -2515,7 +2644,7 @@ void tst_QWebEngineView::imeComposition() QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 1); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 1); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); // Send commit text, which makes the editor conforms composition. { @@ -2528,7 +2657,7 @@ void tst_QWebEngineView::imeComposition() QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 2); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 2); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); // 3. Insert a character to the end of the line. @@ -2546,7 +2675,7 @@ void tst_QWebEngineView::imeComposition() QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 25); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 25); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); // Send commit text, which makes the editor conforms composition. { @@ -2559,7 +2688,7 @@ void tst_QWebEngineView::imeComposition() QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 26); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 26); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); // 4. Replace the selection. @@ -2569,7 +2698,7 @@ void tst_QWebEngineView::imeComposition() QTest::keyClick(view.focusProxy(), Qt::Key_Left, Qt::ShiftModifier | Qt::AltModifier); #endif QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 1); + QCOMPARE(selectionChangedSpy.size(), 1); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("oeQtWebEngine inputMethodt")); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 14); @@ -2583,7 +2712,7 @@ void tst_QWebEngineView::imeComposition() QApplication::sendEvent(view.focusProxy(), &event); // The new composition should clear the previous selection QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 2); + QCOMPARE(selectionChangedSpy.size(), 2); } QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("oeQtWebEngine ")); // The cursor should be positioned at the end of the composition text @@ -2603,7 +2732,7 @@ void tst_QWebEngineView::imeComposition() QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 15); QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 15); QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("")); - QCOMPARE(selectionChangedSpy.count(), 2); + QCOMPARE(selectionChangedSpy.size(), 2); selectionChangedSpy.clear(); @@ -2628,8 +2757,8 @@ void tst_QWebEngineView::imeComposition() QApplication::sendEvent(view.focusProxy(), &event); } QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("")); - QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 11); - QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 11); + QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 0); + QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 0); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("")); QCOMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), QString("QtWebEngine")); @@ -2645,7 +2774,7 @@ void tst_QWebEngineView::imeComposition() QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 11); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("")); QCOMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), QString("QtWebEngine")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); } void tst_QWebEngineView::newlineInTextarea() @@ -2800,7 +2929,7 @@ void tst_QWebEngineView::imeJSInputEvents() } // Simply committing text should not trigger any JS composition event. - QTRY_COMPARE(logLines().count(), 3); + QTRY_COMPARE(logLines().size(), 3); QCOMPARE(logLines()[0], QStringLiteral("[object InputEvent] beforeinput commit")); QCOMPARE(logLines()[1], QStringLiteral("[object TextEvent] textInput commit")); QCOMPARE(logLines()[2], QStringLiteral("[object InputEvent] input commit")); @@ -2816,7 +2945,7 @@ void tst_QWebEngineView::imeJSInputEvents() qApp->processEvents(); } - QTRY_COMPARE(logLines().count(), 4); + QTRY_COMPARE(logLines().size(), 4); QCOMPARE(logLines()[0], QStringLiteral("[object CompositionEvent] compositionstart ")); QCOMPARE(logLines()[1], QStringLiteral("[object InputEvent] beforeinput preedit")); QCOMPARE(logLines()[2], QStringLiteral("[object CompositionEvent] compositionupdate preedit")); @@ -2830,7 +2959,7 @@ void tst_QWebEngineView::imeJSInputEvents() qApp->processEvents(); } - QTRY_COMPARE(logLines().count(), 9); + QTRY_COMPARE(logLines().size(), 9); QCOMPARE(logLines()[4], QStringLiteral("[object InputEvent] beforeinput commit")); QCOMPARE(logLines()[5], QStringLiteral("[object CompositionEvent] compositionupdate commit")); QCOMPARE(logLines()[6], QStringLiteral("[object TextEvent] textInput commit")); @@ -2848,7 +2977,7 @@ void tst_QWebEngineView::imeJSInputEvents() qApp->processEvents(); } - QTRY_COMPARE(logLines().count(), 4); + QTRY_COMPARE(logLines().size(), 4); QCOMPARE(logLines()[0], QStringLiteral("[object CompositionEvent] compositionstart ")); QCOMPARE(logLines()[1], QStringLiteral("[object InputEvent] beforeinput preedit")); QCOMPARE(logLines()[2], QStringLiteral("[object CompositionEvent] compositionupdate preedit")); @@ -2861,7 +2990,7 @@ void tst_QWebEngineView::imeJSInputEvents() qApp->processEvents(); } - QTRY_COMPARE(logLines().count(), 9); + QTRY_COMPARE(logLines().size(), 9); QCOMPARE(logLines()[4], QStringLiteral("[object InputEvent] beforeinput ")); QCOMPARE(logLines()[5], QStringLiteral("[object CompositionEvent] compositionupdate ")); QCOMPARE(logLines()[6], QStringLiteral("[object TextEvent] textInput ")); @@ -2928,6 +3057,7 @@ void tst_QWebEngineView::imeCompositionQueryEvent() } QInputMethodQueryEvent srrndTextQuery(Qt::ImSurroundingText); + QInputMethodQueryEvent absolutePosQuery(Qt::ImAbsolutePosition); QInputMethodQueryEvent cursorPosQuery(Qt::ImCursorPosition); QInputMethodQueryEvent anchorPosQuery(Qt::ImAnchorPosition); @@ -2939,16 +3069,18 @@ void tst_QWebEngineView::imeCompositionQueryEvent() qApp->processEvents(); } QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), QString("composition")); - QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 11); + QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 0); QApplication::sendEvent(input, &srrndTextQuery); + QApplication::sendEvent(input, &absolutePosQuery); QApplication::sendEvent(input, &cursorPosQuery); QApplication::sendEvent(input, &anchorPosQuery); qApp->processEvents(); QTRY_COMPARE(srrndTextQuery.value(Qt::ImSurroundingText).toString(), QString("")); - QTRY_COMPARE(cursorPosQuery.value(Qt::ImCursorPosition).toInt(), 11); - QTRY_COMPARE(anchorPosQuery.value(Qt::ImAnchorPosition).toInt(), 11); + QTRY_COMPARE(absolutePosQuery.value(Qt::ImAbsolutePosition).toInt(), 0); + QTRY_COMPARE(cursorPosQuery.value(Qt::ImCursorPosition).toInt(), 0); + QTRY_COMPARE(anchorPosQuery.value(Qt::ImAnchorPosition).toInt(), 0); // Send commit { @@ -2962,13 +3094,64 @@ void tst_QWebEngineView::imeCompositionQueryEvent() QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("composition")); QApplication::sendEvent(input, &srrndTextQuery); + QApplication::sendEvent(input, &absolutePosQuery); QApplication::sendEvent(input, &cursorPosQuery); QApplication::sendEvent(input, &anchorPosQuery); qApp->processEvents(); QTRY_COMPARE(srrndTextQuery.value(Qt::ImSurroundingText).toString(), QString("composition")); + QTRY_COMPARE(absolutePosQuery.value(Qt::ImAbsolutePosition).toInt(), 11); QTRY_COMPARE(cursorPosQuery.value(Qt::ImCursorPosition).toInt(), 11); QTRY_COMPARE(anchorPosQuery.value(Qt::ImAnchorPosition).toInt(), 11); + + // Test another composition to ensure that the cursor position is set correctly. + // In this case cursor will be at position 11 during input composition. + { + QList<QInputMethodEvent::Attribute> attributes; + QInputMethodEvent event("123", attributes); + QApplication::sendEvent(input, &event); + qApp->processEvents(); + } + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value") + .toString(), + QString("composition123")); + QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 11); + + QApplication::sendEvent(input, &srrndTextQuery); + QApplication::sendEvent(input, &absolutePosQuery); + QApplication::sendEvent(input, &cursorPosQuery); + QApplication::sendEvent(input, &anchorPosQuery); + qApp->processEvents(); + + QTRY_COMPARE(srrndTextQuery.value(Qt::ImSurroundingText).toString(), QString("composition")); + QTRY_COMPARE(absolutePosQuery.value(Qt::ImAbsolutePosition).toInt(), 11); + QTRY_COMPARE(cursorPosQuery.value(Qt::ImCursorPosition).toInt(), 11); + QTRY_COMPARE(anchorPosQuery.value(Qt::ImAnchorPosition).toInt(), 11); + + // Send commit + { + QList<QInputMethodEvent::Attribute> attributes; + QInputMethodEvent event("", attributes); + event.setCommitString("123"); + QApplication::sendEvent(input, &event); + qApp->processEvents(); + } + + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value") + .toString(), + QString("composition123")); + QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 14); + + QApplication::sendEvent(input, &srrndTextQuery); + QApplication::sendEvent(input, &absolutePosQuery); + QApplication::sendEvent(input, &cursorPosQuery); + QApplication::sendEvent(input, &anchorPosQuery); + qApp->processEvents(); + + QTRY_COMPARE(srrndTextQuery.value(Qt::ImSurroundingText).toString(), QString("composition123")); + QTRY_COMPARE(absolutePosQuery.value(Qt::ImAbsolutePosition).toInt(), 14); + QTRY_COMPARE(cursorPosQuery.value(Qt::ImCursorPosition).toInt(), 14); + QTRY_COMPARE(anchorPosQuery.value(Qt::ImAnchorPosition).toInt(), 14); } #if QT_CONFIG(clipboard) @@ -2993,20 +3176,20 @@ void tst_QWebEngineView::globalMouseSelection() // Select text via JavaScript evaluateJavaScriptSync(view.page(), "var inputEle = document.getElementById('input1'); inputEle.focus(); inputEle.select();"); - QTRY_COMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(selectionChangedSpy.size(), 1); QVERIFY(QApplication::clipboard()->text(QClipboard::Selection).isEmpty()); // Deselect the selection (this moves the current cursor to the end of the text) QPoint textInputCenter = elementCenter(view.page(), "input1"); QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter); QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 2); + QCOMPARE(selectionChangedSpy.size(), 2); QVERIFY(QApplication::clipboard()->text(QClipboard::Selection).isEmpty()); // Select to the start of the line QTest::keyClick(view.focusProxy(), Qt::Key_Home, Qt::ShiftModifier); QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 3); + QCOMPARE(selectionChangedSpy.size(), 3); QCOMPARE(QApplication::clipboard()->text(QClipboard::Selection), QStringLiteral("QtWebEngine")); } #endif @@ -3032,7 +3215,7 @@ void tst_QWebEngineView::noContextMenu() QTest::mouseMove(wrapper.windowHandle(), QPoint(10,10)); QTest::mouseClick(wrapper.windowHandle(), Qt::RightButton); - QTRY_COMPARE(wrapper.findChildren<QMenu *>().count(), 1); + QTRY_COMPARE(wrapper.findChildren<QMenu *>().size(), 1); QVERIFY(view.findChildren<QMenu *>().isEmpty()); } @@ -3072,7 +3255,7 @@ void tst_QWebEngineView::contextMenu() view.load(QUrl("about:blank")); view.resize(640, 480); view.show(); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QVERIFY(view.findChildren<QMenu *>().isEmpty()); QTest::mouseMove(view.windowHandle(), QPoint(10,10)); @@ -3080,9 +3263,9 @@ void tst_QWebEngineView::contextMenu() // verify for zero children will always succeed, so should be tested with at least minor timeout if (childrenCount <= 0) { - QVERIFY(!QTest::qWaitFor([&view] () { return view.findChildren<QMenu *>().count() > 0; }, 500)); + QVERIFY(!QTest::qWaitFor([&view] () { return view.findChildren<QMenu *>().size() > 0; }, 500)); } else { - QTRY_COMPARE(view.findChildren<QMenu *>().count(), childrenCount); + QTRY_COMPARE(view.findChildren<QMenu *>().size(), childrenCount); if (isCustomMenu) { QCOMPARE(view.findChildren<QMenu *>().first(), customMenu); } @@ -3200,7 +3383,7 @@ void tst_QWebEngineView::webUIURLs_data() QTest::newRow("process-internals") << QUrl("chrome://process-internals") << true; QTest::newRow("quota-internals") << QUrl("chrome://quota-internals") << true; QTest::newRow("safe-browsing") << QUrl("chrome://safe-browsing") << false; -#ifdef Q_OS_LINUX +#if defined(Q_OS_LINUX) || defined(Q_OS_WIN) QTest::newRow("sandbox") << QUrl("chrome://sandbox") << true; #else QTest::newRow("sandbox") << QUrl("chrome://sandbox") << false; @@ -3237,7 +3420,7 @@ void tst_QWebEngineView::webUIURLs() view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); view.load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 90000); QCOMPARE(loadFinishedSpy.takeFirst().at(0).toBool(), supported); } @@ -3246,7 +3429,7 @@ void tst_QWebEngineView::visibilityState() QWebEngineView view; QSignalSpy spy(&view, &QWebEngineView::loadFinished); view.load(QStringLiteral("about:blank")); - QVERIFY(spy.count() || spy.wait()); + QVERIFY(spy.size() || spy.wait()); QVERIFY(spy.takeFirst().takeFirst().toBool()); QCOMPARE(evaluateJavaScriptSync(view.page(), "document.visibilityState").toString(), QStringLiteral("hidden")); view.show(); @@ -3261,7 +3444,7 @@ void tst_QWebEngineView::visibilityState2() view.show(); view.load(QStringLiteral("about:blank")); view.hide(); - QVERIFY(spy.count() || spy.wait()); + QVERIFY(spy.size() || spy.wait()); QVERIFY(spy.takeFirst().takeFirst().toBool()); QCOMPARE(evaluateJavaScriptSync(view.page(), "document.visibilityState").toString(), QStringLiteral("hidden")); } @@ -3274,8 +3457,8 @@ void tst_QWebEngineView::visibilityState3() QSignalSpy spy2(&page2, &QWebEnginePage::loadFinished); page1.load(QStringLiteral("about:blank")); page2.load(QStringLiteral("about:blank")); - QVERIFY(spy1.count() || spy1.wait()); - QVERIFY(spy2.count() || spy2.wait()); + QVERIFY(spy1.size() || spy1.wait()); + QVERIFY(spy2.size() || spy2.wait()); QWebEngineView view; view.setPage(&page1); view.show(); @@ -3339,7 +3522,7 @@ void tst_QWebEngineView::deletePage() QVERIFY(view.page()); QSignalSpy spy(view.page(), &QWebEnginePage::loadFinished); view.page()->load(QStringLiteral("about:blank")); - QTRY_VERIFY(spy.count()); + QTRY_VERIFY(spy.size()); } void tst_QWebEngineView::autoDeleteOnExternalPageDelete() @@ -3353,7 +3536,7 @@ void tst_QWebEngineView::autoDeleteOnExternalPageDelete() view->show(); view->resize(320, 240); page->load(QUrl("about:blank")); - QTRY_VERIFY(spy.count()); + QTRY_VERIFY(spy.size()); QVERIFY(page->parent() != view); auto sc = QObject::connect(page, &QWebEnginePage::destroyed, view, &QWebEngineView::deleteLater); @@ -3386,7 +3569,7 @@ void tst_QWebEngineView::closeOpenerTab() testView->settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true); QSignalSpy loadFinishedSpy(testView, SIGNAL(loadFinished(bool))); testView->setUrl(QStringLiteral("about:blank")); - QTRY_VERIFY(loadFinishedSpy.count()); + QTRY_VERIFY(loadFinishedSpy.size()); testView->page()->runJavaScript(QStringLiteral("window.open('about:blank','_blank')")); QTRY_COMPARE(testView->createdWindows.size(), 1); auto *newView = testView->createdWindows.at(0); @@ -3411,7 +3594,7 @@ void tst_QWebEngineView::switchPage() QWebEngineView webView2(&page2, nullptr); page1.setHtml("<html><body bgcolor=\"#000000\"></body></html>"); page2.setHtml("<html><body bgcolor=\"#ffffff\"></body></html>"); - QTRY_VERIFY(loadFinishedSpy1.count() && loadFinishedSpy2.count()); + QTRY_VERIFY(loadFinishedSpy1.size() && loadFinishedSpy2.size()); QWebEngineView webView; webView.resize(300,300); webView.show(); @@ -3508,7 +3691,7 @@ void tst_QWebEngineView::loadAfterRendererCrashed() QSignalSpy loadSpy(&view, &QWebEngineView::loadFinished); view.load(QUrl("qrc:///resources/dummy.html")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QVERIFY(loadSpy.first().first().toBool()); } @@ -3526,7 +3709,7 @@ void tst_QWebEngineView::inspectElement() QSignalSpy spy(&view, &QWebEngineView::loadFinished); view.load(QUrl("data:text/plain,foobarbaz")); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 12000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 12000); // shouldn't do anything since inspector is not attached page->triggerAction(QWebEnginePage::InspectElement); @@ -3578,7 +3761,7 @@ void tst_QWebEngineView::navigateOnDrop() sendEvents(); if (navigateOnDrop) { - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QVERIFY(loadSpy.last().first().toBool()); QCOMPARE(view.url(), url); } else { @@ -3591,11 +3774,11 @@ void tst_QWebEngineView::navigateOnDrop() loadSpy.clear(); view.page()->settings()->setAttribute(QWebEngineSettings::NavigateOnDropEnabled, !navigateOnDrop); view.setUrl(QUrl("about:blank")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); sendEvents(); if (!navigateOnDrop) { - QTRY_COMPARE(loadSpy.count(), 2); + QTRY_COMPARE(loadSpy.size(), 2); QVERIFY(loadSpy.last().first().toBool()); QCOMPARE(view.url(), url); } else { @@ -3605,6 +3788,28 @@ void tst_QWebEngineView::navigateOnDrop() } } +void tst_QWebEngineView::emptyUriListOnDrop() +{ + QWebEngineView view; + view.resize(640, 480); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QMimeData mimeData; + mimeData.setUrls({}); // creates an empty uri-list MIME type entry + QVERIFY(mimeData.hasUrls()); + + QDragEnterEvent dee(view.rect().center(), Qt::CopyAction, &mimeData, Qt::LeftButton, + Qt::NoModifier); + QApplication::sendEvent(&view, &dee); + QDropEvent de(view.rect().center(), Qt::CopyAction, &mimeData, Qt::LeftButton, Qt::NoModifier); + QApplication::sendEvent(&view, &de); + + QSignalSpy loadSpy(&view, &QWebEngineView::loadFinished); + view.setUrl(QUrl("about:blank")); + QTRY_COMPARE(loadSpy.size(), 1); +} + void tst_QWebEngineView::datalist() { QString html("<html><body>" @@ -3626,7 +3831,7 @@ void tst_QWebEngineView::datalist() QSignalSpy loadSpy(&view, &QWebEngineView::loadFinished); view.setHtml(html); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QString listValuesJS("(function() {" " var browserDatalist = document.getElementById('browserDatalist');" @@ -3754,5 +3959,49 @@ void tst_QWebEngineView::datalist() QStringLiteral("fil")); } +class ConsolePage : public QWebEnginePage +{ + Q_OBJECT +public: + ConsolePage(QObject *parent = nullptr) : QWebEnginePage(parent) { } + void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString &message, + int lineNumber, const QString &sourceID) override + { + Q_UNUSED(level) + Q_UNUSED(lineNumber) + Q_UNUSED(sourceID) + if (message.contains("TEST_KEY:Shift")) + emit done(); + } +signals: + void done(); +}; + +//qtbug_113704 +void tst_QWebEngineView::longKeyEventText() +{ + const QString html(QStringLiteral("<html><body><p>TEST</p>" + "<script>" + "document.addEventListener('keydown', (event)=> {" + "console.log('TEST_KEY:' + event.key);" + "});" + "</script>" + "</body></html>")); + + QWebEngineView view; + ConsolePage page; + view.setPage(&page); + QSignalSpy loadFinishedSpy(view.page(), &QWebEnginePage::loadFinished); + view.resize(200, 400); + view.show(); + view.setHtml(html); + QTRY_VERIFY(loadFinishedSpy.size()); + QSignalSpy consoleMessageSpy(&page, &ConsolePage::done); + Qt::Key key(Qt::Key_Shift); + QKeyEvent event(QKeyEvent::KeyPress, key, Qt::NoModifier, QKeySequence(key).toString()); + QApplication::sendEvent(view.focusProxy(), &event); + QTRY_VERIFY(consoleMessageSpy.size()); +} + QTEST_MAIN(tst_QWebEngineView) #include "tst_qwebengineview.moc" diff --git a/tests/auto/widgets/schemes/CMakeLists.txt b/tests/auto/widgets/schemes/CMakeLists.txt index ed31a4e10..5299b3148 100644 --- a/tests/auto/widgets/schemes/CMakeLists.txt +++ b/tests/auto/widgets/schemes/CMakeLists.txt @@ -1,10 +1,13 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause + +include(../../util/util.cmake) qt_internal_add_test(tst_schemes SOURCES tst_schemes.cpp LIBRARIES Qt::WebEngineWidgets + Test::Util ) diff --git a/tests/auto/widgets/schemes/tst_schemes.cpp b/tests/auto/widgets/schemes/tst_schemes.cpp index 09aaecac4..188c112e4 100644 --- a/tests/auto/widgets/schemes/tst_schemes.cpp +++ b/tests/auto/widgets/schemes/tst_schemes.cpp @@ -3,20 +3,48 @@ #include <QtTest/QtTest> -#include <qwebengineview.h> #include <qwebenginepage.h> #include <qwebengineprofile.h> #include <qwebenginesettings.h> +#include <qwebengineurlrequestjob.h> +#include <qwebengineurlscheme.h> +#include <qwebengineurlschemehandler.h> +#include <qwebengineview.h> +#include <widgetutil.h> class tst_Schemes : public QObject { Q_OBJECT private Q_SLOTS: + void initTestCase(); void unknownUrlSchemePolicy_data(); void unknownUrlSchemePolicy(); + void customSchemeFragmentNavigation_data(); + void customSchemeFragmentNavigation(); }; +void tst_Schemes::initTestCase() +{ + QWebEngineUrlScheme pathScheme("path"); + pathScheme.setSyntax(QWebEngineUrlScheme::Syntax::Path); + QWebEngineUrlScheme::registerScheme(pathScheme); + + QWebEngineUrlScheme hostScheme("host"); + hostScheme.setSyntax(QWebEngineUrlScheme::Syntax::Host); + QWebEngineUrlScheme::registerScheme(hostScheme); + + QWebEngineUrlScheme hostAndPortScheme("hostandport"); + hostAndPortScheme.setSyntax(QWebEngineUrlScheme::Syntax::HostAndPort); + hostAndPortScheme.setDefaultPort(3000); + QWebEngineUrlScheme::registerScheme(hostAndPortScheme); + + QWebEngineUrlScheme hostPortUserInfoScheme("hostportuserinfo"); + hostPortUserInfoScheme.setSyntax(QWebEngineUrlScheme::Syntax::HostPortAndUserInformation); + hostPortUserInfoScheme.setDefaultPort(3000); + QWebEngineUrlScheme::registerScheme(hostPortUserInfoScheme); +} + class AcceptNavigationRequestHandler : public QWebEnginePage { public: @@ -93,5 +121,161 @@ void tst_Schemes::unknownUrlSchemePolicy() QCOMPARE(page.acceptNavigationRequestCalls, shouldAccept ? 1 : 0); } +class CustomScheme : public QWebEngineUrlSchemeHandler +{ +public: + CustomScheme(const QString &linkUrl) : m_linkUrl(linkUrl) { } + + void requestStarted(QWebEngineUrlRequestJob *requestJob) override + { + QString html = QString("<html><body>" + "<p style='height: 2000px;'>" + "<a href='%1' id='link'>Click link</a>" + "</p><p id='anchor'>Anchor</p>" + "</body></html>") + .arg(m_linkUrl); + QBuffer *buffer = new QBuffer(requestJob); + buffer->setData(html.toUtf8()); + requestJob->reply("text/html", buffer); + } + + QString m_linkUrl; +}; + +void tst_Schemes::customSchemeFragmentNavigation_data() +{ + QTest::addColumn<QUrl>("baseUrl"); + QTest::addColumn<QString>("linkUrl"); + QTest::addColumn<QUrl>("expectedUrl"); + + // Path syntax + // - Preserves each part of the URL after navigation + QTest::newRow("Path syntax, path only, relative url") + << QUrl("path://path") << "#anchor" << QUrl("path://path#anchor"); + QTest::newRow("Path syntax, path only, absolute url") + << QUrl("path://path") << "path://path#anchor" << QUrl("path://path#anchor"); + QTest::newRow("Path syntax, host/path, relative url") + << QUrl("path://host/path") << "#anchor" << QUrl("path://host/path#anchor"); + QTest::newRow("Path syntax, host/path, absolute url") + << QUrl("path://host/path") << "path://host/path#anchor" + << QUrl("path://host/path#anchor"); + QTest::newRow("Path syntax, host:port, relative url") + << QUrl("path://host:3000") << "#anchor" << QUrl("path://host:3000#anchor"); + QTest::newRow("Path syntax, host:port, absolute url") + << QUrl("path://host:3000") << "path://host:3000#anchor" + << QUrl("path://host:3000#anchor"); + QTest::newRow("Path syntax, userinfo@host:port, relative url") + << QUrl("path://user:password@host:3000") << "#anchor" + << QUrl("path://user:password@host:3000#anchor"); + QTest::newRow("Path syntax, userinfo@host:port, absolute url") + << QUrl("path://user:password@host:3000") << "path://user:password@host:3000#anchor" + << QUrl("path://user:password@host:3000#anchor"); + + // Host syntax + // - We lose the port and the user info from the authority after navigation + QTest::newRow("Host syntax, host only, relative url") + << QUrl("host://host") << "#anchor" << QUrl("host://host/#anchor"); + QTest::newRow("Host syntax, host only, absolute url") + << QUrl("host://host") << "host://host#anchor" << QUrl("host://host/#anchor"); + QTest::newRow("Host syntax, host/path, relative url") + << QUrl("host://host/path") << "#anchor" << QUrl("host://host/path#anchor"); + QTest::newRow("Host syntax, host/path, absolute url") + << QUrl("host://host/path") << "host://host/path#anchor" + << QUrl("host://host/path#anchor"); + QTest::newRow("Host syntax, host:port, relative url") + << QUrl("host://host:3000") << "#anchor" << QUrl("host://host/#anchor"); + QTest::newRow("Host syntax, host:port, absolute url") + << QUrl("host://host:3000") << "host://host:3000#anchor" << QUrl("host://host/#anchor"); + QTest::newRow("Host syntax, userinfo@host:port, relative url") + << QUrl("host://user:password@host:3000") << "#anchor" << QUrl("host://host/#anchor"); + QTest::newRow("Host syntax, userinfo@host:port, absolute url") + << QUrl("host://user:password@host:3000") << "host://user:password@host:3000#anchor" + << QUrl("host://host/#anchor"); + + // HostAndPort syntax + // - We lose the port and the user info from the authority after navigation + QTest::newRow("HostAndPort syntax, host only, relative url") + << QUrl("hostandport://host") << "#anchor" << QUrl("hostandport://host/#anchor"); + QTest::newRow("HostAndPort syntax, host only, absolute url") + << QUrl("hostandport://host") << "hostandport://host#anchor" + << QUrl("hostandport://host/#anchor"); + QTest::newRow("HostAndPort syntax, host/path, relative url") + << QUrl("hostandport://host/path") << "#anchor" + << QUrl("hostandport://host/path#anchor"); + QTest::newRow("HostAndPort syntax, host/path, absolute url") + << QUrl("hostandport://host/path") << "hostandport://host/path#anchor" + << QUrl("hostandport://host/path#anchor"); + QTest::newRow("HostAndPort syntax, host:port, relative url") + << QUrl("hostandport://host:3000") << "#anchor" << QUrl("hostandport://host/#anchor"); + QTest::newRow("HostAndPort syntax, host:port, absolute url") + << QUrl("hostandport://host:3000") << "hostandport://host:3000#anchor" + << QUrl("hostandport://host/#anchor"); + QTest::newRow("HostAndPort syntax, userinfo@host:port, relative url") + << QUrl("hostandport://user:password@host:3000") << "#anchor" + << QUrl("hostandport://host/#anchor"); + QTest::newRow("HostAndPort syntax, userinfo@host:port, absolute url") + << QUrl("hostandport://user:password@host:3000") + << "hostandport://user:password@host:3000#anchor" << QUrl("hostandport://host/#anchor"); + + // HostPortAndUserInformation syntax + // - We lose the port and it preserves the user info in the authority after navigation + QTest::newRow("HostPortAndUserInformation syntax, host only, relative url") + << QUrl("hostportuserinfo://host") << "#anchor" + << QUrl("hostportuserinfo://host/#anchor"); + QTest::newRow("HostPortAndUserInformation syntax, host only, absolute url") + << QUrl("hostportuserinfo://host") << "hostportuserinfo://host#anchor" + << QUrl("hostportuserinfo://host/#anchor"); + QTest::newRow("HostPortAndUserInformation syntax, host/path, relative url") + << QUrl("hostportuserinfo://host/path") << "#anchor" + << QUrl("hostportuserinfo://host/path#anchor"); + QTest::newRow("HostPortAndUserInformation syntax, host/path, absolute url") + << QUrl("hostportuserinfo://host/path") << "hostportuserinfo://host/path#anchor" + << QUrl("hostportuserinfo://host/path#anchor"); + QTest::newRow("HostPortAndUserInformation syntax, host:port, relative url") + << QUrl("hostportuserinfo://host:3000") << "#anchor" + << QUrl("hostportuserinfo://host/#anchor"); + QTest::newRow("HostPortAndUserInformation syntax, host:port, absolute url") + << QUrl("hostportuserinfo://host:3000") << "hostportuserinfo://host:3000#anchor" + << QUrl("hostportuserinfo://host/#anchor"); + QTest::newRow("HostPortAndUserInformation syntax, userinfo@host:port, relative url") + << QUrl("hostportuserinfo://user:password@host:3000") << "#anchor" + << QUrl("hostportuserinfo://user:password@host/#anchor"); + QTest::newRow("HostPortAndUserInformation syntax, userinfo@host:port, absolute url") + << QUrl("hostportuserinfo://user:password@host:3000") + << "hostportuserinfo://user:password@host:3000#anchor" + << QUrl("hostportuserinfo://user:password@host/#anchor"); +} + +void tst_Schemes::customSchemeFragmentNavigation() +{ + QFETCH(QUrl, baseUrl); + QFETCH(QUrl, expectedUrl); + QFETCH(QString, linkUrl); + + QWebEngineProfile profile; + QWebEnginePage page(&profile); + QWebEngineView view; + view.setPage(&page); + view.resize(800, 600); + view.show(); + QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); + QSignalSpy urlChangedSpy(&page, SIGNAL(urlChanged(QUrl))); + + CustomScheme *schemeHandler = new CustomScheme(linkUrl); + page.profile()->installUrlSchemeHandler(baseUrl.scheme().toUtf8(), schemeHandler); + + view.load(baseUrl); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "window.scrollY").toInt() == 0); + + QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, elementCenter(&page, "link")); + QVERIFY(urlChangedSpy.wait()); + QCOMPARE(page.url(), expectedUrl); + QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "window.scrollY").toInt() > 0); + + // Same document navigation doesn't emit loadFinished + QTRY_COMPARE(loadFinishedSpy.size(), 1); +} + QTEST_MAIN(tst_Schemes) #include "tst_schemes.moc" diff --git a/tests/auto/widgets/shutdown/CMakeLists.txt b/tests/auto/widgets/shutdown/CMakeLists.txt index b16d69aa8..e2ce9eeb9 100644 --- a/tests/auto/widgets/shutdown/CMakeLists.txt +++ b/tests/auto/widgets/shutdown/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause qt_internal_add_test(tst_shutdown SOURCES diff --git a/tests/auto/widgets/spellchecking/CMakeLists.txt b/tests/auto/widgets/spellchecking/CMakeLists.txt index 3289cb425..d0c7656c1 100644 --- a/tests/auto/widgets/spellchecking/CMakeLists.txt +++ b/tests/auto/widgets/spellchecking/CMakeLists.txt @@ -1,7 +1,8 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../util/util.cmake) +include(../../../../src/core/api/Qt6WebEngineCoreMacros.cmake) qt_internal_add_test(tst_spellchecking SOURCES diff --git a/tests/auto/widgets/spellchecking/tst_spellchecking.cpp b/tests/auto/widgets/spellchecking/tst_spellchecking.cpp index bb9cecae5..c643a56ba 100644 --- a/tests/auto/widgets/spellchecking/tst_spellchecking.cpp +++ b/tests/auto/widgets/spellchecking/tst_spellchecking.cpp @@ -151,7 +151,7 @@ void tst_Spellchecking::spellcheck() QTest::mousePress(m_view->focusWidget(), Qt::LeftButton, {}, QPoint(20,20)); QTest::mouseRelease(m_view->focusWidget(), Qt::LeftButton, {}, QPoint(20,20)); QString text("I lowe Qt ...."); - for (int i = 0; i < text.length(); i++) { + for (int i = 0; i < text.size(); i++) { QTest::keyClicks(m_view->focusWidget(), text.at(i)); QTest::qWait(60); } diff --git a/tests/auto/widgets/touchinput/CMakeLists.txt b/tests/auto/widgets/touchinput/CMakeLists.txt index e9312027a..bd76666d6 100644 --- a/tests/auto/widgets/touchinput/CMakeLists.txt +++ b/tests/auto/widgets/touchinput/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause include(../../util/util.cmake) diff --git a/tests/auto/widgets/touchinput/tst_touchinput.cpp b/tests/auto/widgets/touchinput/tst_touchinput.cpp index 562ccfcef..42178558c 100644 --- a/tests/auto/widgets/touchinput/tst_touchinput.cpp +++ b/tests/auto/widgets/touchinput/tst_touchinput.cpp @@ -130,7 +130,7 @@ void TouchInputTest::initTestCase() view.setHtml("<html><head><style>.rect { min-width: 240px; min-height: 120px; }</style></head><body>" "<p id='text' style='width: 150px;'>The Qt Company</p>" "<div id='notext' style='width: 150px; height: 100px; background-color: #f00;'></div>" - "<form><input id='input' width='150px' type='text' value='The Qt Company2' /></form>" + "<form><input id='input' style='width: 150px;' type='text' value='The Qt Company2' /></form>" "<button id='btn' type='button' onclick='alert(\"button clicked!\")'>Click Me!</button>" "<select id='select' onchange='alert(\"option changed to: \" + this.value)'>" "<option value='O1'>O1</option><option value='O2'>O2</option><option value='O3'>O3</option></select>" @@ -303,7 +303,7 @@ void TouchInputTest::pinchZoom() for (int i = 0; i < 3; ++i) { gesturePinch(/* zoomIn = */true, tapOneByOne); - QTRY_VERIFY2(getScaleFactor(&scale) > 1.5, qPrintable(QString("i: %1, scale: %2").arg(i).arg(scale))); + QTRY_VERIFY2(getScaleFactor(&scale) > 1.0, qPrintable(QString("i: %1, scale: %2").arg(i).arg(scale))); gesturePinch(/* zoomIn = */false, tapOneByOne); QTRY_COMPARE(getScaleFactor(&scale), 1.0); } |