diff options
Diffstat (limited to 'tests')
326 files changed, 11084 insertions, 2856 deletions
diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt index 6fff2b35c..1b0ff3e9d 100644 --- a/tests/auto/CMakeLists.txt +++ b/tests/auto/CMakeLists.txt @@ -1,5 +1,6 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause +add_subdirectory(cmake) if(TARGET Qt::WebEngineCore) add_subdirectory(httpserver) add_subdirectory(util) @@ -13,5 +14,7 @@ if(TARGET Qt::WebEngineWidgets) 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 83ebf5a23..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: 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 5615d6b2d..eb8e9266f 100644 --- a/tests/auto/core/CMakeLists.txt +++ b/tests/auto/core/CMakeLists.txt @@ -2,12 +2,25 @@ # 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/tst_certificateerror.cpp b/tests/auto/core/certificateerror/tst_certificateerror.cpp index 4ae840e72..252e1544d 100644 --- a/tests/auto/core/certificateerror/tst_certificateerror.cpp +++ b/tests/auto/core/certificateerror/tst_certificateerror.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <httpsserver.h> #include <util.h> @@ -19,6 +19,7 @@ private Q_SLOTS: void handleError_data(); void handleError(); void fatalError(); + void resourceError(); }; struct PageWithCertificateErrorHandler : QWebEnginePage @@ -67,8 +68,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 +93,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 +103,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() @@ -129,5 +131,20 @@ void tst_CertificateError::fatalError() } } +void tst_CertificateError::resourceError() +{ + PageWithCertificateErrorHandler page(false, false); + page.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + + page.setHtml("<img src=\"https://expired.badssl.com\">"); + if (!page.loadSpy.wait(10000)) { + QVERIFY2(!page.error, "There shouldn't be any certificate error if not loaded due to missing internet access!"); + QSKIP("Couldn't load page from network, skipping test."); + } + + QTRY_VERIFY(page.error); + QCOMPARE(page.error->isMainFrame(), false); +} + QTEST_MAIN(tst_CertificateError) #include <tst_certificateerror.moc> diff --git a/tests/auto/core/devtools/tst_devtools.cpp b/tests/auto/core/devtools/tst_devtools.cpp index 477b4cb20..7284f0d51 100644 --- a/tests/auto/core/devtools/tst_devtools.cpp +++ b/tests/auto/core/devtools/tst_devtools.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> @@ -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..f5b8910e8 --- /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 + +#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 4a4219267..306074994 100644 --- a/tests/auto/core/origins/CMakeLists.txt +++ b/tests/auto/core/origins/CMakeLists.txt @@ -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 53c8c83ff..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"; }, 3000); + 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..06af1123e 100644 --- a/tests/auto/core/origins/tst_origins.cpp +++ b/tests/auto/core/origins/tst_origins.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <util.h> #include "httpserver.h" @@ -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..66df04412 --- /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 + +#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 0507d80f1..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: 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..d203ab130 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 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +#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/tst_qwebenginecookiestore.cpp b/tests/auto/core/qwebenginecookiestore/tst_qwebenginecookiestore.cpp index e0fee6b08..a75002ae5 100644 --- a/tests/auto/core/qwebenginecookiestore/tst_qwebenginecookiestore.cpp +++ b/tests/auto/core/qwebenginecookiestore/tst_qwebenginecookiestore.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <util.h> #include <QtTest/QtTest> @@ -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..7d73e5a4a --- /dev/null +++ b/tests/auto/core/qwebengineframe/CMakeLists.txt @@ -0,0 +1,24 @@ +# 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" + "resources/printing-inner-document.html" + "resources/printing-outer-document.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/resources/printing-inner-document.html b/tests/auto/core/qwebengineframe/resources/printing-inner-document.html new file mode 100644 index 000000000..2e5a53af5 --- /dev/null +++ b/tests/auto/core/qwebengineframe/resources/printing-inner-document.html @@ -0,0 +1,7 @@ +<!doctype html> +<html> + <head><title>Printing Inner Document</title></head> + <body> + <h1>Inner Header</h1> + </body> +</html> diff --git a/tests/auto/core/qwebengineframe/resources/printing-outer-document.html b/tests/auto/core/qwebengineframe/resources/printing-outer-document.html new file mode 100644 index 000000000..c5947371e --- /dev/null +++ b/tests/auto/core/qwebengineframe/resources/printing-outer-document.html @@ -0,0 +1,8 @@ +<!doctype html> +<html> + <head><title>Printing Outer Document</title></head> + <body> + <h1>Outer Header</h1> + <iframe name="inner" src="printing-inner-document.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..8bbefd5f5 --- /dev/null +++ b/tests/auto/core/qwebengineframe/tst_qwebengineframe.cpp @@ -0,0 +1,346 @@ +/* + 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(); + void isMainFrame(); + void runJavaScript(); + void printRequestedByFrame(); + void printToPdfFile(); + void printToPdfFileFailures(); + void printToPdfFunction(); + +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()); +} + +void tst_QWebEngineFrame::isMainFrame() +{ + 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.isMainFrame()); + for (auto child : frame.children()) { + QVERIFY(!child.isMainFrame()); + } +} + +void tst_QWebEngineFrame::runJavaScript() +{ + 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(); + CallbackSpy<QVariant> spy; + children[0].runJavaScript("window.name", spy.ref()); + auto result = spy.waitForResult(); + QCOMPARE(result, QString("test-subframe0")); +} + +void tst_QWebEngineFrame::printRequestedByFrame() +{ + QWebEnginePage page; + QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) }; + QSignalSpy printRequestedSpy{ &page, SIGNAL(printRequestedByFrame(QWebEngineFrame)) }; + + page.load(QUrl("qrc:/resources/nesting-iframe.html")); + QTRY_COMPARE(loadSpy.size(), 1); + auto oFrame2 = page.findFrameByName("test-main-frame"); + QVERIFY(oFrame2.has_value()); + CallbackSpy<QVariant> spy; + oFrame2->runJavaScript("window.print()", spy.ref()); + spy.waitForResult(); + QCOMPARE(printRequestedSpy.size(), 1); + auto *framePtr = get_if<QWebEngineFrame>(&printRequestedSpy[0][0]); + QCOMPARE(*framePtr, *oFrame2); +} + +void tst_QWebEngineFrame::printToPdfFile() +{ + QTemporaryDir tempDir(QDir::tempPath() + "/tst_qwebengineframe-XXXXXX"); + QVERIFY(tempDir.isValid()); + + QWebEnginePage page; + QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) }; + page.load(QUrl("qrc:/resources/printing-outer-document.html")); + QTRY_COMPARE(loadSpy.size(), 1); + + auto outerFrame = page.mainFrame(); + auto maybeInnerFrame = page.findFrameByName("inner"); + QVERIFY(maybeInnerFrame); + auto innerFrame = *maybeInnerFrame; + + QSignalSpy savePdfSpy{ &page, SIGNAL(pdfPrintingFinished(QString, bool)) }; + + QString outerPath = tempDir.path() + "/outer.pdf"; + outerFrame.printToPdf(outerPath); + QTRY_COMPARE(savePdfSpy.size(), 1); + + QList<QVariant> outerArgs = savePdfSpy.takeFirst(); + QCOMPARE(outerArgs.at(0).toString(), outerPath); + QVERIFY(outerArgs.at(1).toBool()); + + QString innerPath = tempDir.path() + "/inner.pdf"; + innerFrame.printToPdf(innerPath); + QTRY_COMPARE(savePdfSpy.size(), 1); + + QList<QVariant> innerArgs = savePdfSpy.takeFirst(); + QCOMPARE(innerArgs.at(0).toString(), innerPath); + QVERIFY(innerArgs.at(1).toBool()); + + // The outer document encompasses more elements so its PDF should be larger. This is a + // roundabout way to check that we aren't just printing the same document twice. + auto outerSize = QFileInfo(outerPath).size(); + auto innerSize = QFileInfo(innerPath).size(); + QCOMPARE_GT(outerSize, innerSize); + QCOMPARE_GT(innerSize, 0); +} + +void tst_QWebEngineFrame::printToPdfFileFailures() +{ + QTemporaryDir tempDir(QDir::tempPath() + "/tst_qwebengineframe-XXXXXX"); + QVERIFY(tempDir.isValid()); + + QWebEnginePage page; + QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) }; + page.load(QUrl("qrc:/resources/printing-outer-document.html")); + QTRY_COMPARE(loadSpy.size(), 1); + + auto maybeInnerFrame = page.findFrameByName("inner"); + QVERIFY(maybeInnerFrame); + auto innerFrame = *maybeInnerFrame; + + QSignalSpy savePdfSpy{ &page, SIGNAL(pdfPrintingFinished(QString, bool)) }; + +#if !defined(Q_OS_WIN) + auto badPath = tempDir.path() + "/print_//2_failed.pdf"; +#else + auto badPath = tempDir.path() + "/print_|2_failed.pdf"; +#endif + innerFrame.printToPdf(badPath); + QTRY_COMPARE(savePdfSpy.size(), 1); + + QList<QVariant> badPathArgs = savePdfSpy.takeFirst(); + QCOMPARE(badPathArgs.at(0).toString(), badPath); + QVERIFY(!badPathArgs.at(1).toBool()); + + page.triggerAction(QWebEnginePage::WebAction::Reload); + QTRY_COMPARE(loadSpy.size(), 2); + + QVERIFY(!innerFrame.isValid()); + QString invalidFramePath = tempDir.path() + "/invalidFrame.pdf"; + innerFrame.printToPdf(invalidFramePath); + QTRY_COMPARE(savePdfSpy.size(), 1); + + QList<QVariant> invalidFrameArgs = savePdfSpy.takeFirst(); + QCOMPARE(invalidFrameArgs.at(0).toString(), invalidFramePath); + QVERIFY(!invalidFrameArgs.at(1).toBool()); +} + +void tst_QWebEngineFrame::printToPdfFunction() +{ + QWebEnginePage page; + QSignalSpy loadSpy{ &page, SIGNAL(loadFinished(bool)) }; + page.load(QUrl("qrc:/resources/printing-outer-document.html")); + QTRY_COMPARE(loadSpy.size(), 1); + + auto outerFrame = page.mainFrame(); + auto maybeInnerFrame = page.findFrameByName("inner"); + QVERIFY(maybeInnerFrame); + auto innerFrame = *maybeInnerFrame; + + CallbackSpy<QByteArray> outerSpy; + outerFrame.printToPdf(outerSpy.ref()); + auto outerPdfData = outerSpy.waitForResult(); + QCOMPARE_GT(outerPdfData.size(), 0); + + CallbackSpy<QByteArray> innerSpy; + innerFrame.printToPdf(innerSpy.ref()); + auto innerPdfData = innerSpy.waitForResult(); + QCOMPARE_GT(innerPdfData.size(), 0); + QCOMPARE_GT(outerPdfData.size(), innerPdfData.size()); + + page.triggerAction(QWebEnginePage::WebAction::Reload); + QTRY_COMPARE(loadSpy.size(), 2); + QVERIFY(!innerFrame.isValid()); + + CallbackSpy<QByteArray> invalidSpy; + innerFrame.printToPdf(invalidSpy.ref()); + auto invalidPdfData = invalidSpy.waitForResult(); + QVERIFY(invalidSpy.wasCalled()); + QCOMPARE(invalidPdfData.size(), 0); +} + +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..3e46a823e --- /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 + +#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..010932ea0 --- /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 + +#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 44e8f5252..756b99bbb 100644 --- a/tests/auto/core/qwebenginesettings/CMakeLists.txt +++ b/tests/auto/core/qwebenginesettings/CMakeLists.txt @@ -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 60add4567..c12c0c45c 100644 --- a/tests/auto/core/qwebengineurlrequestinterceptor/CMakeLists.txt +++ b/tests/auto/core/qwebengineurlrequestinterceptor/CMakeLists.txt @@ -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 7626d0442..c84de3599 100644 --- a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp +++ b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp @@ -1,9 +1,12 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#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> @@ -47,6 +50,11 @@ private Q_SLOTS: void replaceInterceptor_data(); void replaceInterceptor(); void replaceOnIntercept(); + void multipleRedirects(); + void postWithBody_data(); + void postWithBody(); + void profilePreventsPageInterception_data(); + void profilePreventsPageInterception(); }; tst_QWebEngineUrlRequestInterceptor::tst_QWebEngineUrlRequestInterceptor() @@ -133,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) @@ -181,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: @@ -210,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(); @@ -218,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()); @@ -226,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 @@ -274,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")); } @@ -310,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")); @@ -319,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")); @@ -355,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() @@ -385,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() @@ -423,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); @@ -513,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); @@ -749,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")); @@ -823,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); @@ -867,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 @@ -888,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..59520e4ed --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestjob/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +include(../../util/util.cmake) + +qt_internal_add_test(tst_qwebengineurlrequestjob + SOURCES + tst_qwebengineurlrequestjob.cpp + LIBRARIES + Qt::WebEngineCore + Test::Util +) + +# 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..acddfb0e0 --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestjob/tst_qwebengineurlrequestjob.cpp @@ -0,0 +1,241 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtTest/QtTest> +#include <util.h> +#include <QtWebEngineCore/qwebengineloadinginfo.h> +#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 SuccessHandler : public QWebEngineUrlSchemeHandler +{ +public: + SuccessHandler() { } + + void requestStarted(QWebEngineUrlRequestJob *requestJob) override + { + if (silentSuccess) + requestJob->reply("", nullptr); + else { + QBuffer *buffer = new QBuffer(requestJob); + buffer->setData(requestJob->requestUrl().toString().toUtf8()); + requestJob->reply("text/plain;charset=utf-8", buffer); + } + } + + static void registerUrlScheme() + { + QWebEngineUrlScheme successScheme(schemeName); + QWebEngineUrlScheme::registerScheme(successScheme); + } + + bool silentSuccess = false; + const static inline QByteArray schemeName = QByteArrayLiteral("success"); +}; + +class tst_QWebEngineUrlRequestJob : public QObject +{ + Q_OBJECT + +public: + tst_QWebEngineUrlRequestJob() { } + +private Q_SLOTS: + void initTestCase() + { + AdditionalResponseHeadersHandler::registerUrlScheme(); + RequestBodyHandler::registerUrlScheme(); + SuccessHandler::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); + } + + void notifySuccess() + { + QWebEngineProfile profile; + QWebEnginePage page(&profile); + QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); + + SuccessHandler handler; + page.profile()->installUrlSchemeHandler(SuccessHandler::schemeName, &handler); + + page.load(QUrl("success://one")); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + QTRY_COMPARE(loadFinishedSpy.at(0).first().toBool(), true); + QCOMPARE(toPlainTextSync(&page), "success://one"); + + handler.silentSuccess = true; + + page.load(QUrl("success://two")); + // Page load was successful + QTRY_COMPARE(loadFinishedSpy.size(), 2); + QTRY_COMPARE(loadFinishedSpy.at(1).first().toBool(), true); + // The content of the page did not change + QCOMPARE(toPlainTextSync(&page), "success://one"); + } +}; + +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..b43b06f2e --- /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 GPL-3.0-only + +#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..66e14e683 --- /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 + +#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/httpreqrep.cpp b/tests/auto/httpserver/httpreqrep.cpp index 34e148678..65a1df365 100644 --- a/tests/auto/httpserver/httpreqrep.cpp +++ b/tests/auto/httpserver/httpreqrep.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "httpreqrep.h" HttpReqRep::HttpReqRep(QTcpSocket *socket, QObject *parent) @@ -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..2b90b774b 100644 --- a/tests/auto/httpserver/httpreqrep.h +++ b/tests/auto/httpserver/httpreqrep.h @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef HTTPREQREP_H #define 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.cpp b/tests/auto/httpserver/httpserver.cpp index 6dd64ab88..8a1d8e6f6 100644 --- a/tests/auto/httpserver/httpserver.cpp +++ b/tests/auto/httpserver/httpserver.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "httpserver.h" #include <QFile> @@ -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..a1c2e3791 100644 --- a/tests/auto/httpserver/httpserver.h +++ b/tests/auto/httpserver/httpserver.h @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef HTTPSERVER_H #define 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..237314965 100644 --- a/tests/auto/httpserver/httpsserver.h +++ b/tests/auto/httpserver/httpsserver.h @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef HTTPSSERVER_H #define 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/httpserver/proxy_server.cpp b/tests/auto/httpserver/proxy_server.cpp index 338415311..e47214ee4 100644 --- a/tests/auto/httpserver/proxy_server.cpp +++ b/tests/auto/httpserver/proxy_server.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "proxy_server.h" #include <QDataStream> diff --git a/tests/auto/httpserver/proxy_server.h b/tests/auto/httpserver/proxy_server.h index 6be0c4e1a..8f7cd4cc6 100644 --- a/tests/auto/httpserver/proxy_server.h +++ b/tests/auto/httpserver/proxy_server.h @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef PROXY_SERVER_H #define PROXY_SERVER_H diff --git a/tests/auto/pdf/CMakeLists.txt b/tests/auto/pdf/CMakeLists.txt index 8bda0c3c3..205bd24d0 100644 --- a/tests/auto/pdf/CMakeLists.txt +++ b/tests/auto/pdf/CMakeLists.txt @@ -2,8 +2,11 @@ # 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 d0dc966f0..729bc9138 100644 --- a/tests/auto/pdf/qpdfbookmarkmodel/CMakeLists.txt +++ b/tests/auto/pdf/qpdfbookmarkmodel/CMakeLists.txt @@ -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..7c9216534 100644 --- a/tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp +++ b/tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp @@ -1,5 +1,5 @@ // 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 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> @@ -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 0d49bc6f1..b8300ef27 100644 --- a/tests/auto/pdf/qpdfdocument/CMakeLists.txt +++ b/tests/auto/pdf/qpdfdocument/CMakeLists.txt @@ -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 975708f65..45d1d5a7b 100644 --- a/tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp +++ b/tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp @@ -1,5 +1,5 @@ // 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 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> @@ -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; @@ -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..404475766 --- /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 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 029563071..53a68fe59 100644 --- a/tests/auto/pdf/qpdfpagerenderer/CMakeLists.txt +++ b/tests/auto/pdf/qpdfpagerenderer/CMakeLists.txt @@ -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..ca14338cc 100644 --- a/tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp +++ b/tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QPdfDocument> #include <QPdfPageRenderer> @@ -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 15fecd21b..668d1ea36 100644 --- a/tests/auto/pdf/qpdfsearchmodel/CMakeLists.txt +++ b/tests/auto/pdf/qpdfsearchmodel/CMakeLists.txt @@ -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..f5e946d19 100644 --- a/tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp +++ b/tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 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 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> @@ -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 index 9f79459b2..e6a3a460c 100644 --- a/tests/auto/pdfquick/CMakeLists.txt +++ b/tests/auto/pdfquick/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(multipageview) +add_subdirectory(pdfpageimage) diff --git a/tests/auto/pdfquick/multipageview/BLACKLIST b/tests/auto/pdfquick/multipageview/BLACKLIST index b608bef1c..9012902f6 100644 --- a/tests/auto/pdfquick/multipageview/BLACKLIST +++ b/tests/auto/pdfquick/multipageview/BLACKLIST @@ -1,3 +1,7 @@ -# QTBUG-106072 -[password] -windows +# 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 index 283bebc79..50f7d7d8f 100644 --- a/tests/auto/pdfquick/multipageview/CMakeLists.txt +++ b/tests/auto/pdfquick/multipageview/CMakeLists.txt @@ -20,11 +20,11 @@ qt_internal_add_test(tst_multipageview qt_internal_extend_target(tst_multipageview CONDITION ANDROID OR IOS DEFINES - QT_QMLTEST_DATADIR=\\\":/data\\\" + 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\\\" + QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" ) 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/tst_multipageview.cpp b/tests/auto/pdfquick/multipageview/tst_multipageview.cpp index eb70ebf17..71c09077c 100644 --- a/tests/auto/pdfquick/multipageview/tst_multipageview.cpp +++ b/tests/auto/pdfquick/multipageview/tst_multipageview.cpp @@ -1,17 +1,20 @@ // 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 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial 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 @@ -26,6 +29,8 @@ private Q_SLOTS: void password(); void selectionAndClipboard(); void search(); + void pinchDragPinch(); + void jumpOnDocumentReady(); public: enum NavigationAction { @@ -57,7 +62,7 @@ void tst_MultiPageView::internalLink_data() QTest::addColumn<qreal>("expectedZoom"); QTest::addColumn<QPoint>("expectedScroll"); - QTest::newRow("first link") << 0 << 1 << qreal(1) << QPoint(134, 1276); + 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); } @@ -73,7 +78,7 @@ void tst_MultiPageView::internalLink() QVERIFY(showView(window, testFileUrl("multiPageView.qml"))); QQuickItem *pdfView = window.rootObject(); QVERIFY(pdfView); - pdfView->setProperty("source", "bookmarksAndLinks.pdf"); + pdfView->setProperty("source", testFileUrl("bookmarksAndLinks.pdf")); QTRY_COMPARE(pdfView->property("currentPageRenderingStatus").toInt(), QQuickPdfPageImage::Ready); QQuickItem *table = static_cast<QQuickItem *>(findFirstChild(pdfView, "QQuickTableView")); @@ -257,27 +262,27 @@ void tst_MultiPageView::password() // actual QPdfDocument::pageCountChanged(int), for comparison with the illusory QQuickPdfDocument::pageCountChanged QVERIFY(extPageCountChangedSpy.isValid()); - QVERIFY(pdfView->setProperty("source", u"pdf-sample.protected.pdf"_qs)); + QVERIFY(pdfView->setProperty("source", testFileUrl(u"pdf-sample.protected.pdf"_s))); - QTRY_COMPARE(passwordRequiredSpy.count(), 1); + QTRY_COMPARE(passwordRequiredSpy.size(), 1); qCDebug(lcTests) << "error while awaiting password" << doc->error() - << "passwordRequired count" << passwordRequiredSpy.count() - << "statusChanged count" << statusChangedSpy.count(); + << "passwordRequired count" << passwordRequiredSpy.size() + << "statusChanged count" << statusChangedSpy.size(); QCOMPARE(doc->property("status").toInt(), int(QPdfDocument::Status::Error)); - QCOMPARE(pageCountChangedSpy.count(), 0); - QCOMPARE(extPageCountChangedSpy.count(), 0); - QCOMPARE(statusChangedSpy.count(), 2); // Loading and then 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"_qs)); - QCOMPARE(passwordChangedSpy.count(), 1); + 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.count() - << "statusChanged count" << statusChangedSpy.count() - << "pageCountChanged count" << pageCountChangedSpy.count(); - QCOMPARE(statusChangedSpy.count(), 2); // Loading and then Ready - QCOMPARE(pageCountChangedSpy.count(), 1); - QCOMPARE(extPageCountChangedSpy.count(), pageCountChangedSpy.count()); + << "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() @@ -288,13 +293,13 @@ void tst_MultiPageView::selectionAndClipboard() QVERIFY(pdfView); QQuickPdfDocument *doc = pdfView->property("document").value<QQuickPdfDocument*>(); QVERIFY(doc); - QVERIFY(doc->setProperty("password", u"Qt"_qs)); - QVERIFY(pdfView->setProperty("source", u"pdf-sample.protected.pdf"_qs)); + 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.length(), 1073); + QCOMPARE(sel.size(), 1073); #if QT_CONFIG(clipboard) QClipboard *clip = qApp->clipboard(); @@ -316,8 +321,8 @@ void tst_MultiPageView::search() QTRY_COMPARE(pdfView->width(), 200); QQuickPdfDocument *doc = pdfView->property("document").value<QQuickPdfDocument*>(); QVERIFY(doc); - QVERIFY(doc->setProperty("password", u"Qt"_qs)); - QVERIFY(pdfView->setProperty("source", u"pdf-sample.protected.pdf"_qs)); + 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); @@ -328,17 +333,17 @@ void tst_MultiPageView::search() QObject *multiline = findFirstChild(firstPage, "QQuickPathMultiline"); QVERIFY(multiline); - pdfView->setProperty("searchString", u"PDF"_qs); + 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.count(), 7); + 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).count(), 5); // 5-point polygon is a rectangle (including drawing back to the start, to close it) + 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")); @@ -353,5 +358,89 @@ void tst_MultiPageView::search() 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..3f98025fa --- /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 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 index c540ebfa6..1bf9b45b7 100644 --- a/tests/auto/pdfquick/shared/util.cpp +++ b/tests/auto/pdfquick/shared/util.cpp @@ -1,5 +1,5 @@ // 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 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "util.h" #include <QtQuick/QQuickItem> diff --git a/tests/auto/pdfquick/shared/util.h b/tests/auto/pdfquick/shared/util.h index 9ceb711af..2788e6104 100644 --- a/tests/auto/pdfquick/shared/util.h +++ b/tests/auto/pdfquick/shared/util.h @@ -1,5 +1,5 @@ // 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 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef QUICK_VISUAL_TEST_UTIL_H #define QUICK_VISUAL_TEST_UTIL_H diff --git a/tests/auto/quick/dialogs/WebView.qml b/tests/auto/quick/dialogs/WebView.qml index 45fafb42d..6db0e5777 100644 --- a/tests/auto/quick/dialogs/WebView.qml +++ b/tests/auto/quick/dialogs/WebView.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtWebEngine diff --git a/tests/auto/quick/dialogs/testhandler.cpp b/tests/auto/quick/dialogs/testhandler.cpp index f45852630..0596626f1 100644 --- a/tests/auto/quick/dialogs/testhandler.cpp +++ b/tests/auto/quick/dialogs/testhandler.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "testhandler.h" diff --git a/tests/auto/quick/dialogs/testhandler.h b/tests/auto/quick/dialogs/testhandler.h index c72e81841..fef41d075 100644 --- a/tests/auto/quick/dialogs/testhandler.h +++ b/tests/auto/quick/dialogs/testhandler.h @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TESTHANDLER_H #define TESTHANDLER_H diff --git a/tests/auto/quick/dialogs/tst_dialogs.cpp b/tests/auto/quick/dialogs/tst_dialogs.cpp index 086587cf4..6f54019c7 100644 --- a/tests/auto/quick/dialogs/tst_dialogs.cpp +++ b/tests/auto/quick/dialogs/tst_dialogs.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "testhandler.h" #include <quickutil.h> @@ -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/tst_inspectorserver.cpp b/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp index 58e8f7d11..32683ad1d 100644 --- a/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp +++ b/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QNetworkAccessManager> #include <QNetworkReply> @@ -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() diff --git a/tests/auto/quick/publicapi/tst_publicapi.cpp b/tests/auto/quick/publicapi/tst_publicapi.cpp index bd8c331ac..75df0091c 100644 --- a/tests/auto/quick/publicapi/tst_publicapi.cpp +++ b/tests/auto/quick/publicapi/tst_publicapi.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QMetaEnum> #include <QMetaMethod> @@ -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,9 @@ #include <QtWebEngineCore/QWebEngineDownloadRequest> #include <QtWebEngineCore/QWebEngineScript> #include <QtWebEngineCore/QWebEngineLoadingInfo> +#include <QtWebEngineCore/QWebEngineWebAuthUxRequest> +#include <QtWebEngineCore/QWebEngineFrame> +#include <QtWebEngineCore/QWebEnginePermission> #include <private/qquickwebengineview_p.h> #include <private/qquickwebengineaction_p.h> #include <private/qquickwebengineclientcertificateselection_p.h> @@ -61,18 +65,27 @@ 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 + << &QWebEnginePermission::staticMetaObject << &QWebEngineQuotaRequest::staticMetaObject << &QWebEngineRegisterProtocolHandlerRequest::staticMetaObject << &QQuickWebEngineTouchSelectionMenuRequest::staticMetaObject + << &QWebEngineWebAuthUxRequest::staticMetaObject + << &QWebEngineWebAuthPinRequest::staticMetaObject + << &QWebEngineFrame::staticMetaObject ; -static QList<QMetaEnum> knownEnumNames = QList<QMetaEnum>(); +static QList<QMetaEnum> knownEnumNames = QList<QMetaEnum>() + << QWebEngineDownloadRequest::staticMetaObject.enumerator(QWebEngineDownloadRequest::staticMetaObject.indexOfEnumerator("SavePageFormat")) + << QWebEnginePermission::staticMetaObject.enumerator(QWebEnginePermission::staticMetaObject.indexOfEnumerator("Feature")) + ; static const QStringList hardcodedTypes = QStringList() << "QJSValue" @@ -84,7 +97,10 @@ static const QStringList hardcodedTypes = QStringList() << "QWebEngineCookieStore*" << "Qt::LayoutDirection" << "QQuickWebEngineScriptCollection*" - << "QQmlComponent*"; + << "QQmlComponent*" + << "QMultiMap<QByteArray,QByteArray>" + << "QList<QWebEnginePermission>" + ; static const QStringList expectedAPI = QStringList() << "QQuickWebEngineAction.text --> QString" @@ -123,6 +139,7 @@ static const QStringList expectedAPI = QStringList() << "QWebEngineCertificateError.defer() --> void" << "QWebEngineCertificateError.description --> QString" << "QWebEngineCertificateError.type --> QWebEngineCertificateError::Type" + << "QWebEngineCertificateError.isMainFrame --> bool" << "QWebEngineCertificateError.acceptCertificate() --> void" << "QWebEngineCertificateError.overridable --> bool" << "QWebEngineCertificateError.rejectCertificate() --> void" @@ -261,6 +278,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 +315,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 +336,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" @@ -334,6 +358,29 @@ static const QStringList expectedAPI = QStringList() << "QWebEngineNewWindowRequest.InNewDialog --> DestinationType" << "QWebEngineNewWindowRequest.InNewTab --> DestinationType" << "QWebEngineNewWindowRequest.InNewWindow --> DestinationType" + << "QWebEnginePermission.Unsupported --> Feature" + << "QWebEnginePermission.MediaAudioCapture --> Feature" + << "QWebEnginePermission.MediaVideoCapture --> Feature" + << "QWebEnginePermission.MediaAudioVideoCapture --> Feature" + << "QWebEnginePermission.DesktopVideoCapture --> Feature" + << "QWebEnginePermission.DesktopAudioVideoCapture --> Feature" + << "QWebEnginePermission.MouseLock --> Feature" + << "QWebEnginePermission.Notifications --> Feature" + << "QWebEnginePermission.Geolocation --> Feature" + << "QWebEnginePermission.ClipboardReadWrite --> Feature" + << "QWebEnginePermission.LocalFontsAccess --> Feature" + << "QWebEnginePermission.Invalid --> State" + << "QWebEnginePermission.Ask --> State" + << "QWebEnginePermission.Granted --> State" + << "QWebEnginePermission.Denied --> State" + << "QWebEnginePermission.origin --> QUrl" + << "QWebEnginePermission.feature --> QWebEnginePermission::Feature" + << "QWebEnginePermission.state --> QWebEnginePermission::State" + << "QWebEnginePermission.isValid --> bool" + << "QWebEnginePermission.grant() --> void" + << "QWebEnginePermission.deny() --> void" + << "QWebEnginePermission.reset() --> void" + << "QWebEnginePermission.isTransient(QWebEnginePermission::Feature) --> bool" << "QQuickWebEngineNewWindowRequest.openIn(QQuickWebEngineView*) --> void" << "QQuickWebEngineProfile.AllowPersistentCookies --> PersistentCookiesPolicy" << "QQuickWebEngineProfile.DiskHttpCache --> HttpCacheType" @@ -341,13 +388,23 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineProfile.MemoryHttpCache --> HttpCacheType" << "QQuickWebEngineProfile.NoCache --> HttpCacheType" << "QQuickWebEngineProfile.NoPersistentCookies --> PersistentCookiesPolicy" + << "QQuickWebEngineProfile.NoPersistentPermissions --> PersistentPermissionsPolicy" + << "QQuickWebEngineProfile.PersistentPermissionsInMemory --> PersistentPermissionsPolicy" + << "QQuickWebEngineProfile.PersistentPermissionsOnDisk --> PersistentPermissionsPolicy" << "QQuickWebEngineProfile.cachePath --> QString" << "QQuickWebEngineProfile.cachePathChanged() --> void" << "QQuickWebEngineProfile.clearHttpCache() --> void" + << "QQuickWebEngineProfile.clearHttpCacheCompleted() --> void" << "QQuickWebEngineProfile.downloadFinished(QQuickWebEngineDownloadRequest*) --> void" << "QQuickWebEngineProfile.downloadRequested(QQuickWebEngineDownloadRequest*) --> void" << "QQuickWebEngineProfile.downloadPath --> QString" << "QQuickWebEngineProfile.downloadPathChanged() --> void" + << "QQuickWebEngineProfile.getPermission(QUrl,QWebEnginePermission::Feature) --> QWebEnginePermission" + << "QQuickWebEngineProfile.listPermissions() --> QList<QWebEnginePermission>" + << "QQuickWebEngineProfile.listPermissions(QUrl) --> QList<QWebEnginePermission>" + << "QQuickWebEngineProfile.listPermissions(QWebEnginePermission::Feature) --> QList<QWebEnginePermission>" + << "QQuickWebEngineProfile.persistentPermissionsPolicy --> QQuickWebEngineProfile::PersistentPermissionsPolicy" + << "QQuickWebEngineProfile.persistentPermissionsPolicyChanged() --> void" << "QQuickWebEngineProfile.presentNotification(QWebEngineNotification*) --> void" << "QQuickWebEngineProfile.httpAcceptLanguage --> QString" << "QQuickWebEngineProfile.httpAcceptLanguageChanged() --> void" @@ -363,8 +420,8 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineProfile.persistentCookiesPolicyChanged() --> void" << "QQuickWebEngineProfile.persistentStoragePath --> QString" << "QQuickWebEngineProfile.persistentStoragePathChanged() --> void" - << "QQuickWebEngineProfile.pushServiceEndpoint --> QUrl" - << "QQuickWebEngineProfile.pushServiceEndpointChanged() --> void" + << "QQuickWebEngineProfile.isPushServiceEnabled --> bool" + << "QQuickWebEngineProfile.pushServiceEnabledChanged() --> void" << "QQuickWebEngineProfile.spellCheckEnabled --> bool" << "QQuickWebEngineProfile.spellCheckEnabledChanged() --> void" << "QQuickWebEngineProfile.spellCheckLanguages --> QStringList" @@ -393,6 +450,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" @@ -439,6 +500,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" @@ -463,7 +526,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" @@ -477,6 +539,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" @@ -494,7 +557,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" @@ -504,9 +566,23 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.B7 --> PrintedPageSizeId" << "QQuickWebEngineView.B8 --> PrintedPageSizeId" << "QQuickWebEngineView.B9 --> PrintedPageSizeId" + << "QQuickWebEngineView.B10 --> PrintedPageSizeId" << "QQuickWebEngineView.Back --> WebAction" << "QQuickWebEngineView.C5E --> PrintedPageSizeId" +#if QT_DEPRECATED_SINCE(6, 8) + << "QQuickWebEngineView.ClipboardReadWrite --> Feature" + << "QQuickWebEngineView.DesktopAudioVideoCapture --> Feature" + << "QQuickWebEngineView.DesktopVideoCapture --> Feature" + << "QQuickWebEngineView.Geolocation --> Feature" + << "QQuickWebEngineView.LocalFontsAccess --> Feature" + << "QQuickWebEngineView.MediaAudioCapture --> Feature" + << "QQuickWebEngineView.MediaAudioVideoCapture --> Feature" + << "QQuickWebEngineView.MediaVideoCapture --> Feature" + << "QQuickWebEngineView.Notifications --> Feature" +#endif << "QQuickWebEngineView.CertificateErrorDomain --> ErrorDomain" + << "QQuickWebEngineView.ChangeTextDirectionLTR --> WebAction" + << "QQuickWebEngineView.ChangeTextDirectionRTL --> WebAction" << "QQuickWebEngineView.Comm10E --> PrintedPageSizeId" << "QQuickWebEngineView.ConnectionErrorDomain --> ErrorDomain" << "QQuickWebEngineView.Copy --> WebAction" @@ -518,8 +594,6 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.Custom --> PrintedPageSizeId" << "QQuickWebEngineView.Cut --> WebAction" << "QQuickWebEngineView.DLE --> PrintedPageSizeId" - << "QQuickWebEngineView.DesktopAudioVideoCapture --> Feature" - << "QQuickWebEngineView.DesktopVideoCapture --> Feature" << "QQuickWebEngineView.DnsErrorDomain --> ErrorDomain" << "QQuickWebEngineView.DoublePostcard --> PrintedPageSizeId" << "QQuickWebEngineView.DownloadImageToDisk --> WebAction" @@ -574,7 +648,6 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.Folio --> PrintedPageSizeId" << "QQuickWebEngineView.Forward --> WebAction" << "QQuickWebEngineView.FtpErrorDomain --> ErrorDomain" - << "QQuickWebEngineView.Geolocation --> Feature" << "QQuickWebEngineView.HttpErrorDomain --> ErrorDomain" << "QQuickWebEngineView.Imperial10x11 --> PrintedPageSizeId" << "QQuickWebEngineView.Imperial10x13 --> PrintedPageSizeId" @@ -619,13 +692,7 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.LoadStartedStatus --> LoadStatus" << "QQuickWebEngineView.LoadStoppedStatus --> LoadStatus" << "QQuickWebEngineView.LoadSucceededStatus --> LoadStatus" - << "QQuickWebEngineView.MediaAudioCapture --> Feature" - << "QQuickWebEngineView.MediaAudioVideoCapture --> Feature" - << "QQuickWebEngineView.MediaVideoCapture --> Feature" - << "QQuickWebEngineView.NPageSize --> PrintedPageSizeId" - << "QQuickWebEngineView.NPaperSize --> PrintedPageSizeId" << "QQuickWebEngineView.NoErrorDomain --> ErrorDomain" - << "QQuickWebEngineView.Notifications --> Feature" << "QQuickWebEngineView.NoWebAction --> WebAction" << "QQuickWebEngineView.NormalTerminationStatus --> RenderProcessTerminationStatus" << "QQuickWebEngineView.Note --> PrintedPageSizeId" @@ -663,6 +730,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" @@ -682,11 +750,14 @@ 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.findFrameByName(QString) --> QWebEngineFrame" << "QQuickWebEngineView.findText(QString) --> void" << "QQuickWebEngineView.findText(QString,FindFlags) --> void" << "QQuickWebEngineView.findText(QString,FindFlags,QJSValue) --> void" @@ -697,7 +768,9 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.goBack() --> void" << "QQuickWebEngineView.goBackOrForward(int) --> void" << "QQuickWebEngineView.goForward() --> void" - << "QQuickWebEngineView.grantFeaturePermission(QUrl,Feature,bool) --> void" +#if QT_DEPRECATED_SINCE(6, 8) + << "QQuickWebEngineView.grantFeaturePermission(QUrl,QQuickWebEngineView::Feature,bool) --> void" +#endif << "QQuickWebEngineView.history --> QWebEngineHistory*" << "QQuickWebEngineView.icon --> QUrl" << "QQuickWebEngineView.iconChanged() --> void" @@ -705,10 +778,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" @@ -716,6 +789,7 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.loadProgressChanged() --> void" << "QQuickWebEngineView.loading --> bool" << "QQuickWebEngineView.loadingChanged(QWebEngineLoadingInfo) --> void" + << "QQuickWebEngineView.mainFrame --> QWebEngineFrame" << "QQuickWebEngineView.navigationRequested(QWebEngineNavigationRequest*) --> void" << "QQuickWebEngineView.newWindowRequested(QQuickWebEngineNewWindowRequest*) --> void" << "QQuickWebEngineView.AcceptRequest --> NavigationRequestAction" @@ -731,8 +805,10 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.NewViewInDialog --> NewViewDestination" << "QQuickWebEngineView.NewViewInTab --> NewViewDestination" << "QQuickWebEngineView.NewViewInWindow --> NewViewDestination" + << "QQuickWebEngineView.permissionRequested(QWebEnginePermission) --> void" << "QQuickWebEngineView.pdfPrintingFinished(QString,bool) --> void" << "QQuickWebEngineView.printRequested() --> void" + << "QQuickWebEngineView.printRequestedByFrame(QWebEngineFrame) --> void" << "QQuickWebEngineView.printToPdf(QJSValue) --> void" << "QQuickWebEngineView.printToPdf(QJSValue,PrintedPageSizeId) --> void" << "QQuickWebEngineView.printToPdf(QJSValue,PrintedPageSizeId,PrintedPageOrientation) --> void" @@ -747,11 +823,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" @@ -783,6 +859,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" @@ -801,6 +879,67 @@ 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" + << "QWebEngineFrame.htmlName --> QString" + << "QWebEngineFrame.isMainFrame --> bool" + << "QWebEngineFrame.isValid --> bool" + << "QWebEngineFrame.name --> QString" + << "QWebEngineFrame.printToPdf(QJSValue) --> void" + << "QWebEngineFrame.printToPdf(QString) --> void" + << "QWebEngineFrame.runJavaScript(QString) --> void" + << "QWebEngineFrame.runJavaScript(QString,uint) --> void" + << "QWebEngineFrame.runJavaScript(QString,QJSValue) --> void" + << "QWebEngineFrame.runJavaScript(QString,uint,QJSValue) --> void" + << "QWebEngineFrame.size --> QSizeF" + << "QWebEngineFrame.url --> QUrl" ; static bool isCheckedEnum(QMetaType t) @@ -894,12 +1033,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; @@ -919,4 +1058,3 @@ void tst_publicapi::publicAPI() QTEST_MAIN(tst_publicapi) #include "tst_publicapi.moc" - diff --git a/tests/auto/quick/qmltests/BLACKLIST b/tests/auto/quick/qmltests/BLACKLIST index 36c737693..fc8f9f0d8 100644 --- a/tests/auto/quick/qmltests/BLACKLIST +++ b/tests/auto/quick/qmltests/BLACKLIST @@ -10,8 +10,3 @@ macos [CertificateError::test_error] * -[WebViewFindText::test_findTextInterruptedByLoad] -b2qt arm 64bit - -[WebEngineViewLoadUrl::test_loadStartedAfterInPageNavigation] -b2qt diff --git a/tests/auto/quick/qmltests/CMakeLists.txt b/tests/auto/quick/qmltests/CMakeLists.txt index 6f41b3e6d..daae6d60d 100644 --- a/tests/auto/quick/qmltests/CMakeLists.txt +++ b/tests/auto/quick/qmltests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2022 The Qt Company Ltd. +# Copyright (C) 2023 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause include(../../httpserver/httpserver.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 415985471..aefeb9af5 100644 --- a/tests/auto/quick/qmltests/data/TestWebEngineView.qml +++ b/tests/auto/quick/qmltests/data/TestWebEngineView.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest 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/titleupdate.js b/tests/auto/quick/qmltests/data/titleupdate.js index 720e83676..94bfd90df 100644 --- a/tests/auto/quick/qmltests/data/titleupdate.js +++ b/tests/auto/quick/qmltests/data/titleupdate.js @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only function updateTitle() { diff --git a/tests/auto/quick/qmltests/data/tst_action.qml b/tests/auto/quick/qmltests/data/tst_action.qml index abe5f71b0..52891197d 100644 --- a/tests/auto/quick/qmltests/data/tst_action.qml +++ b/tests/auto/quick/qmltests/data/tst_action.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest @@ -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_activeFocusOnPress.qml b/tests/auto/quick/qmltests/data/tst_activeFocusOnPress.qml index 77968f6b6..d9ca1e8e6 100644 --- a/tests/auto/quick/qmltests/data/tst_activeFocusOnPress.qml +++ b/tests/auto/quick/qmltests/data/tst_activeFocusOnPress.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_audioMuted.qml b/tests/auto/quick/qmltests/data/tst_audioMuted.qml index 85f813f0c..11527f848 100644 --- a/tests/auto/quick/qmltests/data/tst_audioMuted.qml +++ b/tests/auto/quick/qmltests/data/tst_audioMuted.qml @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_basicProfiles.qml b/tests/auto/quick/qmltests/data/tst_basicProfiles.qml index 97a25cdd8..11d631344 100644 --- a/tests/auto/quick/qmltests/data/tst_basicProfiles.qml +++ b/tests/auto/quick/qmltests/data/tst_basicProfiles.qml @@ -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: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_certificateError.qml b/tests/auto/quick/qmltests/data/tst_certificateError.qml index 220ef9ac8..6c8e680e0 100644 --- a/tests/auto/quick/qmltests/data/tst_certificateError.qml +++ b/tests/auto/quick/qmltests/data/tst_certificateError.qml @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_contextMenu.qml b/tests/auto/quick/qmltests/data/tst_contextMenu.qml index 58e27b8ba..9a34d3f33 100644 --- a/tests/auto/quick/qmltests/data/tst_contextMenu.qml +++ b/tests/auto/quick/qmltests/data/tst_contextMenu.qml @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_datalist.qml b/tests/auto/quick/qmltests/data/tst_datalist.qml index f739639b2..9e4d766ef 100644 --- a/tests/auto/quick/qmltests/data/tst_datalist.qml +++ b/tests/auto/quick/qmltests/data/tst_datalist.qml @@ -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: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtQuick.Controls @@ -105,10 +105,13 @@ TestWebEngineView { keyClick(Qt.Key_Escape); tryVerify(function() { return listView() == null; }); - // Key Down should open the popup and select the first suggestion. + // The first Key Down opens the popup. keyClick(Qt.Key_Down); tryVerify(function() { return listView() != null; }); - compare(listView().currentIndex, 0); + + // The second Key Down selects the first suggestion. + keyClick(Qt.Key_Down); + tryCompare(listView(), "currentIndex", 0); verify(listView().currentItem); } @@ -120,10 +123,13 @@ TestWebEngineView { // Make sure there is no open popup yet. verify(!listView()); - // Key Down should open the popup and select the first suggestion. + // The first Key Down opens the popup. keyClick(Qt.Key_Down); tryVerify(function() { return listView() != null; }); - compare(listView().currentIndex, 0); + + // The second Key Down selects the first suggestion. + keyClick(Qt.Key_Down); + tryCompare(listView(), "currentIndex", 0); // Test keyboard navigation in list. keyClick(Qt.Key_Up); diff --git a/tests/auto/quick/qmltests/data/tst_desktopBehaviorLoadHtml.qml b/tests/auto/quick/qmltests/data/tst_desktopBehaviorLoadHtml.qml index 6cb2841ec..5e2dd19c2 100644 --- a/tests/auto/quick/qmltests/data/tst_desktopBehaviorLoadHtml.qml +++ b/tests/auto/quick/qmltests/data/tst_desktopBehaviorLoadHtml.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_download.qml b/tests/auto/quick/qmltests/data/tst_download.qml index 61a363c39..a0cb8970b 100644 --- a/tests/auto/quick/qmltests/data/tst_download.qml +++ b/tests/auto/quick/qmltests/data/tst_download.qml @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest 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..35332b4e4 --- /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 + +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_favicon.qml b/tests/auto/quick/qmltests/data/tst_favicon.qml index 15f116e5d..fcc24dfb7 100644 --- a/tests/auto/quick/qmltests/data/tst_favicon.qml +++ b/tests/auto/quick/qmltests/data/tst_favicon.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_faviconDatabase.qml b/tests/auto/quick/qmltests/data/tst_faviconDatabase.qml index 774708af0..c16790001 100644 --- a/tests/auto/quick/qmltests/data/tst_faviconDatabase.qml +++ b/tests/auto/quick/qmltests/data/tst_faviconDatabase.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest @@ -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..8c769a042 100644 --- a/tests/auto/quick/qmltests/data/tst_filePicker.qml +++ b/tests/auto/quick/qmltests/data/tst_filePicker.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest @@ -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..5ed05b419 --- /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 + +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..bd2bfbb48 100644 --- a/tests/auto/quick/qmltests/data/tst_findText.qml +++ b/tests/auto/quick/qmltests/data/tst_findText.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest @@ -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_focusOnNavigation.qml b/tests/auto/quick/qmltests/data/tst_focusOnNavigation.qml index f070e4bc5..b440e5720 100644 --- a/tests/auto/quick/qmltests/data/tst_focusOnNavigation.qml +++ b/tests/auto/quick/qmltests/data/tst_focusOnNavigation.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_geopermission.qml b/tests/auto/quick/qmltests/data/tst_geopermission.qml index b99e50acc..1eb687497 100644 --- a/tests/auto/quick/qmltests/data/tst_geopermission.qml +++ b/tests/auto/quick/qmltests/data/tst_geopermission.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest @@ -13,20 +13,22 @@ TestWebEngineView { property bool deniedGeolocation: false property bool geoPermissionRequested: false + profile.persistentPermissionsPolicy: WebEngineProfile.NoPersistentPermissions + SignalSpy { - id: featurePermissionSpy + id: permissionSpy target: webEngineView - signalName: "featurePermissionRequested" + signalName: "permissionRequested" } - onFeaturePermissionRequested: function(securityOrigin, feature) { - if (feature === WebEngineView.Geolocation) { + onPermissionRequested: function(perm) { + if (perm.feature === WebEnginePermission.Geolocation) { geoPermissionRequested = true if (deniedGeolocation) { - webEngineView.grantFeaturePermission(securityOrigin, feature, false) + perm.deny() } else { - webEngineView.grantFeaturePermission(securityOrigin, feature, true) + perm.grant() } } } @@ -55,15 +57,15 @@ TestWebEngineView { function init() { deniedGeolocation = false - featurePermissionSpy.clear() + permissionSpy.clear() } function test_geoPermissionRequest() { - compare(featurePermissionSpy.count, 0) + compare(permissionSpy.count, 0) webEngineView.url = Qt.resolvedUrl("geolocation.html") - featurePermissionSpy.wait() + permissionSpy.wait() verify(geoPermissionRequested) - compare(featurePermissionSpy.count, 1) + compare(permissionSpy.count, 1) tryVerify(isHandled, 5000) verify(getErrorMessage() === "") } @@ -71,7 +73,7 @@ TestWebEngineView { function test_deniedGeolocationByUser() { deniedGeolocation = true webEngineView.url = Qt.resolvedUrl("geolocation.html") - featurePermissionSpy.wait() + permissionSpy.wait() tryVerify(isHandled, 5000) compare(getErrorMessage(), "User denied Geolocation") } diff --git a/tests/auto/quick/qmltests/data/tst_getUserMedia.qml b/tests/auto/quick/qmltests/data/tst_getUserMedia.qml index 3b33b7abe..fbc863a45 100644 --- a/tests/auto/quick/qmltests/data/tst_getUserMedia.qml +++ b/tests/auto/quick/qmltests/data/tst_getUserMedia.qml @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest @@ -11,6 +11,7 @@ TestWebEngineView { height: 400 settings.screenCaptureEnabled: true + profile.persistentPermissionsPolicy: WebEngineProfile.NoPersistentPermissions TestCase { name: "GetUserMedia" @@ -21,17 +22,17 @@ TestWebEngineView { { tag: "device audio", constraints: { audio: true }, - feature: WebEngineView.MediaAudioCapture, + feature: WebEnginePermission.MediaAudioCapture, }, { tag: "device video", constraints: { video: true }, - feature: WebEngineView.MediaVideoCapture, + feature: WebEnginePermission.MediaVideoCapture, }, { tag: "device audio+video", constraints: { audio: true, video: true }, - feature: WebEngineView.MediaAudioVideoCapture, + feature: WebEnginePermission.MediaAudioVideoCapture, }, { tag: "desktop video", @@ -42,7 +43,7 @@ TestWebEngineView { } } }, - feature: WebEngineView.DesktopVideoCapture, + feature: WebEnginePermission.DesktopVideoCapture, }, { tag: "desktop audio+video", @@ -58,7 +59,7 @@ TestWebEngineView { } } }, - feature: WebEngineView.DesktopAudioVideoCapture, + feature: WebEnginePermission.DesktopAudioVideoCapture, } ] } @@ -115,28 +116,24 @@ TestWebEngineView { //// // synchronous permission requests - property variant requestedFeature - property variant requestedSecurityOrigin + property variant permissionObject - onFeaturePermissionRequested: function(securityOrigin, feature) { - requestedFeature = feature - requestedSecurityOrigin = securityOrigin + onPermissionRequested: function(perm) { + permissionObject = perm } function gotFeatureRequest(expectedFeature) { - return requestedFeature == expectedFeature + return permissionObject && permissionObject.feature == expectedFeature } function acceptPendingRequest() { - webEngineView.grantFeaturePermission(requestedSecurityOrigin, requestedFeature, true) - requestedFeature = undefined - requestedSecurityOrigin = undefined + permissionObject.grant() + permissionObject = undefined } function rejectPendingRequest() { - webEngineView.grantFeaturePermission(requestedSecurityOrigin, requestedFeature, false) - requestedFeature = undefined - requestedSecurityOrigin = undefined + permissionObject.deny() + permissionObject = undefined } //// diff --git a/tests/auto/quick/qmltests/data/tst_inputMethod.qml b/tests/auto/quick/qmltests/data/tst_inputMethod.qml index cf79e8a4d..d8ed8e55e 100644 --- a/tests/auto/quick/qmltests/data/tst_inputMethod.qml +++ b/tests/auto/quick/qmltests/data/tst_inputMethod.qml @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest 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..52675983a --- /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 + +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_javaScriptDialogs.qml b/tests/auto/quick/qmltests/data/tst_javaScriptDialogs.qml index 6e91b2e77..49ba5ae47 100644 --- a/tests/auto/quick/qmltests/data/tst_javaScriptDialogs.qml +++ b/tests/auto/quick/qmltests/data/tst_javaScriptDialogs.qml @@ -1,5 +1,5 @@ // Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies). -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_keyboardEvents.qml b/tests/auto/quick/qmltests/data/tst_keyboardEvents.qml index 0f69a7e81..6a67b6983 100644 --- a/tests/auto/quick/qmltests/data/tst_keyboardEvents.qml +++ b/tests/auto/quick/qmltests/data/tst_keyboardEvents.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_keyboardModifierMapping.qml b/tests/auto/quick/qmltests/data/tst_keyboardModifierMapping.qml index d0bc75619..33be0329a 100644 --- a/tests/auto/quick/qmltests/data/tst_keyboardModifierMapping.qml +++ b/tests/auto/quick/qmltests/data/tst_keyboardModifierMapping.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_linkHovered.qml b/tests/auto/quick/qmltests/data/tst_linkHovered.qml index a11bd2450..593beb735 100644 --- a/tests/auto/quick/qmltests/data/tst_linkHovered.qml +++ b/tests/auto/quick/qmltests/data/tst_linkHovered.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_loadFail.qml b/tests/auto/quick/qmltests/data/tst_loadFail.qml index 8e9224bbf..b6d3419f7 100644 --- a/tests/auto/quick/qmltests/data/tst_loadFail.qml +++ b/tests/auto/quick/qmltests/data/tst_loadFail.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_loadHtml.qml b/tests/auto/quick/qmltests/data/tst_loadHtml.qml index 8f94cd4a2..db2035842 100644 --- a/tests/auto/quick/qmltests/data/tst_loadHtml.qml +++ b/tests/auto/quick/qmltests/data/tst_loadHtml.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_loadProgress.qml b/tests/auto/quick/qmltests/data/tst_loadProgress.qml index 2c06a0207..30995d276 100644 --- a/tests/auto/quick/qmltests/data/tst_loadProgress.qml +++ b/tests/auto/quick/qmltests/data/tst_loadProgress.qml @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_loadRecursionCrash.qml b/tests/auto/quick/qmltests/data/tst_loadRecursionCrash.qml index c0eb5932b..d6e6e108a 100644 --- a/tests/auto/quick/qmltests/data/tst_loadRecursionCrash.qml +++ b/tests/auto/quick/qmltests/data/tst_loadRecursionCrash.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_loadUrl.qml b/tests/auto/quick/qmltests/data/tst_loadUrl.qml index 25a62c878..757343703 100644 --- a/tests/auto/quick/qmltests/data/tst_loadUrl.qml +++ b/tests/auto/quick/qmltests/data/tst_loadUrl.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_mouseClick.qml b/tests/auto/quick/qmltests/data/tst_mouseClick.qml index c0c6a6967..cc06416c1 100644 --- a/tests/auto/quick/qmltests/data/tst_mouseClick.qml +++ b/tests/auto/quick/qmltests/data/tst_mouseClick.qml @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_mouseMove.qml b/tests/auto/quick/qmltests/data/tst_mouseMove.qml index 5ded24c57..6ee58fcb0 100644 --- a/tests/auto/quick/qmltests/data/tst_mouseMove.qml +++ b/tests/auto/quick/qmltests/data/tst_mouseMove.qml @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_navigationHistory.qml b/tests/auto/quick/qmltests/data/tst_navigationHistory.qml index 2ea76c387..54d2ffd4b 100644 --- a/tests/auto/quick/qmltests/data/tst_navigationHistory.qml +++ b/tests/auto/quick/qmltests/data/tst_navigationHistory.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_navigationRequested.qml b/tests/auto/quick/qmltests/data/tst_navigationRequested.qml index 31c0cf44e..785cfa27a 100644 --- a/tests/auto/quick/qmltests/data/tst_navigationRequested.qml +++ b/tests/auto/quick/qmltests/data/tst_navigationRequested.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_newViewRequest.qml b/tests/auto/quick/qmltests/data/tst_newViewRequest.qml index a47862565..671426771 100644 --- a/tests/auto/quick/qmltests/data/tst_newViewRequest.qml +++ b/tests/auto/quick/qmltests/data/tst_newViewRequest.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest @@ -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_notification.qml b/tests/auto/quick/qmltests/data/tst_notification.qml index 5d55e1201..4f064af3d 100644 --- a/tests/auto/quick/qmltests/data/tst_notification.qml +++ b/tests/auto/quick/qmltests/data/tst_notification.qml @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest @@ -13,21 +13,26 @@ TestWebEngineView { property bool permissionRequested: false property bool grantPermission: false - property url securityOrigin: '' + property var permissionObject + + profile.persistentPermissionsPolicy: WebEngineProfile.NoPersistentPermissions signal consoleMessage(string message) SignalSpy { id: spyRequest target: view - signalName: 'featurePermissionRequested' + signalName: 'permissionRequested' } - onFeaturePermissionRequested: function(securityOrigin, feature) { - if (feature === WebEngineView.Notifications) { + onPermissionRequested: function(perm) { + if (perm.feature === WebEnginePermission.Notifications) { view.permissionRequested = true - view.securityOrigin = securityOrigin - view.grantFeaturePermission(securityOrigin, feature, grantPermission) + view.permissionObject = perm + if (grantPermission) + perm.grant() + else + perm.deny() } } @@ -91,7 +96,7 @@ TestWebEngineView { compare(notification.title, title) compare(notification.message, message) compare(notification.direction, Qt.RightToLeft) - compare(notification.origin, securityOrigin) + compare(notification.origin, permissionObject.origin) compare(notification.tag, 'tst') compare(notification.language, 'de') } diff --git a/tests/auto/quick/qmltests/data/tst_properties.qml b/tests/auto/quick/qmltests/data/tst_properties.qml index 13d40ed11..e3d6ed625 100644 --- a/tests/auto/quick/qmltests/data/tst_properties.qml +++ b/tests/auto/quick/qmltests/data/tst_properties.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_runJavaScript.qml b/tests/auto/quick/qmltests/data/tst_runJavaScript.qml index f16cd9c41..542a3aac6 100644 --- a/tests/auto/quick/qmltests/data/tst_runJavaScript.qml +++ b/tests/auto/quick/qmltests/data/tst_runJavaScript.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest 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..e90f87913 100644 --- a/tests/auto/quick/qmltests/data/tst_scrollPosition.qml +++ b/tests/auto/quick/qmltests/data/tst_scrollPosition.qml @@ -1,8 +1,7 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only 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..4cd0bf6a7 100644 --- a/tests/auto/quick/qmltests/data/tst_settings.qml +++ b/tests/auto/quick/qmltests/data/tst_settings.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest @@ -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_titleChanged.qml b/tests/auto/quick/qmltests/data/tst_titleChanged.qml index 66a7c115f..16dafa593 100644 --- a/tests/auto/quick/qmltests/data/tst_titleChanged.qml +++ b/tests/auto/quick/qmltests/data/tst_titleChanged.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_unhandledKeyEventPropagation.qml b/tests/auto/quick/qmltests/data/tst_unhandledKeyEventPropagation.qml index 76363fa71..75d0d38d9 100644 --- a/tests/auto/quick/qmltests/data/tst_unhandledKeyEventPropagation.qml +++ b/tests/auto/quick/qmltests/data/tst_unhandledKeyEventPropagation.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_userScriptCollection.qml b/tests/auto/quick/qmltests/data/tst_userScriptCollection.qml index 94c993771..fbf4f0833 100644 --- a/tests/auto/quick/qmltests/data/tst_userScriptCollection.qml +++ b/tests/auto/quick/qmltests/data/tst_userScriptCollection.qml @@ -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: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_userScripts.qml b/tests/auto/quick/qmltests/data/tst_userScripts.qml index 30704f47b..a64de389d 100644 --- a/tests/auto/quick/qmltests/data/tst_userScripts.qml +++ b/tests/auto/quick/qmltests/data/tst_userScripts.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_viewSource.qml b/tests/auto/quick/qmltests/data/tst_viewSource.qml index d4449f7de..a79de3e3a 100644 --- a/tests/auto/quick/qmltests/data/tst_viewSource.qml +++ b/tests/auto/quick/qmltests/data/tst_viewSource.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_webchannel.qml b/tests/auto/quick/qmltests/data/tst_webchannel.qml index 780b55934..0000a2125 100644 --- a/tests/auto/quick/qmltests/data/tst_webchannel.qml +++ b/tests/auto/quick/qmltests/data/tst_webchannel.qml @@ -1,5 +1,5 @@ // Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com> -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/AlertDialog.qml b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/AlertDialog.qml index 7d7efda0c..424c3fdb2 100644 --- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/AlertDialog.qml +++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/AlertDialog.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only // Both dialogs are basically expected to behave in the same way from an API point of view ConfirmDialog { } diff --git a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/ConfirmDialog.qml b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/ConfirmDialog.qml index 6125d0b98..db7c2d05a 100644 --- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/ConfirmDialog.qml +++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/ConfirmDialog.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml import QtTest 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..da423e441 --- /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 + +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/QtWebEngine/ControlsDelegates/FilePicker.qml b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/FilePicker.qml index 247088bcb..6dac9973e 100644 --- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/FilePicker.qml +++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/FilePicker.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import "../../TestParams" diff --git a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/Menu.qml b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/Menu.qml index cd7ed4821..35e3b274f 100644 --- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/Menu.qml +++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/Menu.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 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 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml import "../../TestParams" diff --git a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/MenuItem.qml b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/MenuItem.qml index 67dab1bba..8d3a2df51 100644 --- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/MenuItem.qml +++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/MenuItem.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 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 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml diff --git a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/PromptDialog.qml b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/PromptDialog.qml index 81a63d918..62e489067 100644 --- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/PromptDialog.qml +++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/PromptDialog.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQml import QtTest diff --git a/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml b/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml index 4a1ffeb02..342933365 100644 --- a/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml +++ b/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only pragma Singleton import QtQuick @@ -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/mock-delegates/TestParams/JSDialogParams.qml b/tests/auto/quick/qmltests/mock-delegates/TestParams/JSDialogParams.qml index 1033b509e..4f9831197 100644 --- a/tests/auto/quick/qmltests/mock-delegates/TestParams/JSDialogParams.qml +++ b/tests/auto/quick/qmltests/mock-delegates/TestParams/JSDialogParams.qml @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only pragma Singleton import QtQml diff --git a/tests/auto/quick/qmltests/mock-delegates/TestParams/MenuParams.qml b/tests/auto/quick/qmltests/mock-delegates/TestParams/MenuParams.qml index d8a01764c..8b6bcb285 100644 --- a/tests/auto/quick/qmltests/mock-delegates/TestParams/MenuParams.qml +++ b/tests/auto/quick/qmltests/mock-delegates/TestParams/MenuParams.qml @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only pragma Singleton import QtQml diff --git a/tests/auto/quick/qmltests/tst_qmltests.cpp b/tests/auto/quick/qmltests/tst_qmltests.cpp index 5018c7e78..01a657e8b 100644 --- a/tests/auto/quick/qmltests/tst_qmltests.cpp +++ b/tests/auto/quick/qmltests/tst_qmltests.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <httpserver.h> @@ -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/tst_qquickwebenginedefaultsurfaceformat.cpp b/tests/auto/quick/qquickwebenginedefaultsurfaceformat/tst_qquickwebenginedefaultsurfaceformat.cpp index b4c95d671..64c1cfaa4 100644 --- a/tests/auto/quick/qquickwebenginedefaultsurfaceformat/tst_qquickwebenginedefaultsurfaceformat.cpp +++ b/tests/auto/quick/qquickwebenginedefaultsurfaceformat/tst_qquickwebenginedefaultsurfaceformat.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "testwindow.h" #include "quickutil.h" diff --git a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp index 5b5003846..abe5e1f3d 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 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#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(); @@ -91,7 +99,8 @@ private: tst_QQuickWebEngineView::tst_QQuickWebEngineView() { QtWebEngineQuick::initialize(); - QQuickWebEngineProfile::defaultProfile()->setOffTheRecord(true); + + QVERIFY(QQuickWebEngineProfile::defaultProfile()->isOffTheRecord()); m_testSourceDirPath = QDir(QT_TESTCASE_SOURCEDIR).canonicalPath(); if (!m_testSourceDirPath.endsWith(QLatin1Char('/'))) @@ -423,10 +432,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 +608,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 +625,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 +634,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 +643,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 +658,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 +682,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 +695,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 +709,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 +726,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 +736,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 +847,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 +858,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 +1126,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 +1153,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 +1169,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 +1189,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 +1252,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 +1281,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/tst_qquickwebengineviewgraphics.cpp b/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp index 71c3a1cf2..b7cc8e7de 100644 --- a/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp +++ b/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <quickutil.h> #include <QtTest/QtTest> @@ -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/tst_qtbug-70248.cpp b/tests/auto/quick/qtbug-70248/tst_qtbug-70248.cpp index cf5c187c3..1cf62e800 100644 --- a/tests/auto/quick/qtbug-70248/tst_qtbug-70248.cpp +++ b/tests/auto/quick/qtbug-70248/tst_qtbug-70248.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qtwebenginequickglobal.h" #include <QQuickWebEngineProfile> diff --git a/tests/auto/quick/uidelegates/tst_uidelegates.cpp b/tests/auto/quick/uidelegates/tst_uidelegates.cpp index fb8734f83..1c3024d94 100644 --- a/tests/auto/quick/uidelegates/tst_uidelegates.cpp +++ b/tests/auto/quick/uidelegates/tst_uidelegates.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "testwindow.h" #include "quickutil.h" diff --git a/tests/auto/util/qt_webengine_quicktest.h b/tests/auto/util/qt_webengine_quicktest.h index bd98693de..4d68f90f0 100644 --- a/tests/auto/util/qt_webengine_quicktest.h +++ b/tests/auto/util/qt_webengine_quicktest.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef QT_WEBENGINE_QUICKTEST_H #define QT_WEBENGINE_QUICKTEST_H diff --git a/tests/auto/util/quickutil.h b/tests/auto/util/quickutil.h index f7e08f842..87fd527f7 100644 --- a/tests/auto/util/quickutil.h +++ b/tests/auto/util/quickutil.h @@ -1,8 +1,8 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -#ifndef UTIL_H -#define UTIL_H +#ifndef QUICKUTIL_H +#define QUICKUTIL_H #include <QEventLoop> #include <QQmlEngine> @@ -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(); } @@ -162,5 +162,5 @@ int main(int argc, char *argv[]) \ QTEST_SET_MAIN_SOURCE_PATH \ return QTest::qExec(&tc, argc, argv); \ } -#endif /* UTIL_H */ +#endif /* QUICKUTIL_H */ diff --git a/tests/auto/util/testwindow.h b/tests/auto/util/testwindow.h index f9ffd381a..dfce32b43 100644 --- a/tests/auto/util/testwindow.h +++ b/tests/auto/util/testwindow.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TESTWINDOW_H #define TESTWINDOW_H diff --git a/tests/auto/util/util.h b/tests/auto/util/util.h index 455dff20c..5013bb5fa 100644 --- a/tests/auto/util/util.h +++ b/tests/auto/util/util.h @@ -1,5 +1,8 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef UTIL_H +#define UTIL_H // Functions and macros that really need to be in QTestLib @@ -43,7 +46,7 @@ public: bool ensureSignalEmitted() { - bool result = count() > 0; + bool result = size() > 0; if (!result) result = wait(); clear(); @@ -145,11 +148,28 @@ static inline QUrl baseUrlSync(QWebEnginePage *page) return spy.waitForResult().toUrl(); } -static inline bool loadSync(QWebEnginePage *page, const QUrl &url, bool ok = true) +static inline bool loadSync(QWebEnginePage *page, const QUrl &url, bool ok = true, + int timeout = 20000) { QSignalSpy spy(page, &QWebEnginePage::loadFinished); page->load(url); - return (!spy.empty() || spy.wait(20000)) && (spy.front().value(0).toBool() == ok); + return (!spy.empty() || spy.wait(timeout)) && (spy.front().value(0).toBool() == ok); +} + +static inline bool setHtmlSync(QWebEnginePage *page, const QString &html, + const QUrl &baseUrl = QUrl()) +{ + QSignalSpy spy(page, &QWebEnginePage::loadFinished); + page->setHtml(html, baseUrl); + return (!spy.empty() || spy.wait(20000)) && spy.front().value(0).toBool(); +} + +static inline bool webActionLoadSync(QWebEnginePage *page, QWebEnginePage::WebAction action, + bool ok = true, int timeout = 20000) +{ + QSignalSpy spy(page, &QWebEnginePage::loadFinished); + page->triggerAction(action); + return (!spy.empty() || spy.wait(timeout)) && (spy.front().value(0).toBool() == ok); } static inline QRect elementGeometry(QWebEnginePage *page, const QString &id) @@ -162,8 +182,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(); } @@ -176,3 +196,5 @@ static inline QPoint elementCenter(QWebEnginePage *page, const QString &id) } #define W_QSKIP(a, b) QSKIP(a) + +#endif /* UTIL_H */ diff --git a/tests/auto/util/widgetutil.h b/tests/auto/util/widgetutil.h index 67d09ee4f..356d3c826 100644 --- a/tests/auto/util/widgetutil.h +++ b/tests/auto/util/widgetutil.h @@ -1,5 +1,8 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef WIDGETUTIL_H +#define WIDGETUTIL_H // Functions and macros that really need to be in QTestLib @@ -26,3 +29,5 @@ int main(int argc, char *argv[]) \ QTEST_SET_MAIN_SOURCE_PATH \ return QTest::qExec(&tc, argc, argv); \ } + +#endif /* WIDGETUTIL_H */ diff --git a/tests/auto/widgets/CMakeLists.txt b/tests/auto/widgets/CMakeLists.txt index 34d165c94..9246be68a 100644 --- a/tests/auto/widgets/CMakeLists.txt +++ b/tests/auto/widgets/CMakeLists.txt @@ -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 4c0bb17ee..f6a08c9d3 100644 --- a/tests/auto/widgets/accessibility/CMakeLists.txt +++ b/tests/auto/widgets/accessibility/CMakeLists.txt @@ -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 5bb4e822b..0a65e7a95 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. @@ -92,13 +94,13 @@ void tst_Accessibility::noPage() void tst_Accessibility::hierarchy() { QWebEngineView webView; + QSignalSpy spyFinished(&webView, &QWebEngineView::loadFinished); webView.setHtml("<html><body>" \ "Hello world" \ "<input type='text' value='some text'></input>" \ "</body></html>"); webView.show(); - QSignalSpy spyFinished(&webView, &QWebEngineView::loadFinished); - QVERIFY(spyFinished.wait()); + QTRY_VERIFY(spyFinished.size()); QAccessibleInterface *view = QAccessible::queryAccessibleInterface(&webView); QVERIFY(view); @@ -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; @@ -532,7 +534,7 @@ void tst_Accessibility::roles() QSignalSpy spyFinished(&webView, &QWebEngineView::loadFinished); webView.setHtml("<html><body>" + html + "</body></html>"); webView.show(); - QTRY_COMPARE_WITH_TIMEOUT(spyFinished.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(spyFinished.size(), 1, 20000); QAccessibleInterface *view = QAccessible::queryAccessibleInterface(&webView); @@ -579,35 +581,90 @@ void tst_Accessibility::crossTreeParent() webView.show(); QVERIFY(spyFinished.wait()); QAccessibleInterface *view = QAccessible::queryAccessibleInterface(&webView); - QAccessibleInterface *document = view->child(0); - QCOMPARE(document->role(), QAccessible::WebDocument); - QTRY_COMPARE(document->childCount(), 1); - QAccessibleInterface *p = document->child(0); + QCOMPARE(view->child(0)->role(), QAccessible::WebDocument); + QTRY_COMPARE(view->child(0)->childCount(), 1); + QAccessibleInterface *p = view->child(0)->child(0); QVERIFY(p); - QCOMPARE(p->parent(), document); + QCOMPARE(p->parent(), view->child(0)); p = p->child(0); QVERIFY(p); QCOMPARE(p->role(), QAccessible::WebDocument); - QCOMPARE(p->parent()->parent(), document); + QCOMPARE(p->parent()->parent(), view->child(0)); QTRY_COMPARE(p->childCount(), 1); p = p->child(0); QVERIFY(p); QAccessibleInterface *subdocument = p; QCOMPARE(p->role(), QAccessible::WebDocument); - QCOMPARE(p->parent()->parent()->parent(), document); + QCOMPARE(p->parent()->parent()->parent(), view->child(0)); p = p->child(0); QVERIFY(p); QVERIFY(p->object()); QCOMPARE(p->role(), QAccessible::Paragraph); QCOMPARE(p->parent(), subdocument); - QCOMPARE(p->parent()->parent()->parent()->parent(), document); + QCOMPARE(p->parent()->parent()->parent()->parent(), view->child(0)); QCOMPARE(p->parent()->parent()->parent()->parent()->parent(), view); 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/tst_defaultsurfaceformat.cpp b/tests/auto/widgets/defaultsurfaceformat/tst_defaultsurfaceformat.cpp index c53f6f5b3..3fdba71c2 100644 --- a/tests/auto/widgets/defaultsurfaceformat/tst_defaultsurfaceformat.cpp +++ b/tests/auto/widgets/defaultsurfaceformat/tst_defaultsurfaceformat.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> #include <util.h> diff --git a/tests/auto/widgets/favicon/tst_favicon.cpp b/tests/auto/widgets/favicon/tst_favicon.cpp index dc1e9f096..e4b5619a6 100644 --- a/tests/auto/widgets/favicon/tst_favicon.cpp +++ b/tests/auto/widgets/favicon/tst_favicon.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <util.h> @@ -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/tst_loadsignals.cpp b/tests/auto/widgets/loadsignals/tst_loadsignals.cpp index 9f196972d..b465f9858 100644 --- a/tests/auto/widgets/loadsignals/tst_loadsignals.cpp +++ b/tests/auto/widgets/loadsignals/tst_loadsignals.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> @@ -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(); @@ -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/tst_offscreen.cpp b/tests/auto/widgets/offscreen/tst_offscreen.cpp index 9553a0394..258178dd7 100644 --- a/tests/auto/widgets/offscreen/tst_offscreen.cpp +++ b/tests/auto/widgets/offscreen/tst_offscreen.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSignalSpy> @@ -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/tst_printing.cpp b/tests/auto/widgets/printing/tst_printing.cpp index 2e04bc03c..c9f6e8cfa 100644 --- a/tests/auto/widgets/printing/tst_printing.cpp +++ b/tests/auto/widgets/printing/tst_printing.cpp @@ -1,8 +1,9 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #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/tst_proxy.cpp b/tests/auto/widgets/proxy/tst_proxy.cpp index 961d29303..c16b8cf5a 100644 --- a/tests/auto/widgets/proxy/tst_proxy.cpp +++ b/tests/auto/widgets/proxy/tst_proxy.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2019 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "proxy_server.h" #include <QTest> @@ -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/proxyserver.cpp b/tests/auto/widgets/proxypac/proxyserver.cpp index f7a859747..a7bbd6934 100644 --- a/tests/auto/widgets/proxypac/proxyserver.cpp +++ b/tests/auto/widgets/proxypac/proxyserver.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "proxyserver.h" #include <QDataStream> diff --git a/tests/auto/widgets/proxypac/proxyserver.h b/tests/auto/widgets/proxypac/proxyserver.h index c95856da9..4d622175e 100644 --- a/tests/auto/widgets/proxypac/proxyserver.h +++ b/tests/auto/widgets/proxypac/proxyserver.h @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef PROXY_SERVER_H #define PROXY_SERVER_H diff --git a/tests/auto/widgets/proxypac/tst_proxypac.cpp b/tests/auto/widgets/proxypac/tst_proxypac.cpp index d372f77fa..19654d1a9 100644 --- a/tests/auto/widgets/proxypac/tst_proxypac.cpp +++ b/tests/auto/widgets/proxypac/tst_proxypac.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "proxy_server.h" #include <QTest> @@ -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..904d08493 --- /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 + +#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/tst_qwebenginedownloadrequest.cpp b/tests/auto/widgets/qwebenginedownloadrequest/tst_qwebenginedownloadrequest.cpp index 4e9a9fecd..a47bdbcc4 100644 --- a/tests/auto/widgets/qwebenginedownloadrequest/tst_qwebenginedownloadrequest.cpp +++ b/tests/auto/widgets/qwebenginedownloadrequest/tst_qwebenginedownloadrequest.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <util.h> @@ -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/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..01d1ffe3d 100644 --- a/tests/auto/widgets/qwebenginepage/BLACKLIST +++ b/tests/auto/widgets/qwebenginepage/BLACKLIST @@ -5,11 +5,15 @@ osx windows macos # Can't move cursor (QTBUG-76312) -[acceptNavigationRequestNavigationType] -b2qt arm - [comboBoxPopupPositionAfterMove] macos [comboBoxPopupPositionAfterChildMove] macos + +[backgroundColor] +macos + +[dynamicFrame] +ubuntu-22.04 + diff --git a/tests/auto/widgets/qwebenginepage/CMakeLists.txt b/tests/auto/widgets/qwebenginepage/CMakeLists.txt index af2767500..f63d6211c 100644 --- a/tests/auto/widgets/qwebenginepage/CMakeLists.txt +++ b/tests/auto/widgets/qwebenginepage/CMakeLists.txt @@ -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 ed1c1cae6..976a650ec 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,10 +67,12 @@ #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 <qwebenginepermission.h> #include <qimagewriter.h> #include <QColorSpace> #include <QQuickRenderControl> @@ -76,7 +82,7 @@ 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 @@ -112,10 +118,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(); @@ -164,10 +173,10 @@ private Q_SLOTS: void runJavaScriptDisabled(); void runJavaScriptFromSlot(); void fullScreenRequested(); - void quotaRequested(); - + void requestQuota_data(); + void requestQuota(); - // Tests from tst_QWebEngineFrame + // Tests from pre-6.8 tst_QWebEngineFrame void symmetricUrl(); void progressSignal(); void urlChange(); @@ -226,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(); @@ -262,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-" @@ -278,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() @@ -394,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?")); @@ -420,12 +451,12 @@ public: return true; } public Q_SLOTS: - void requestPermission(const QUrl &origin, QWebEnginePage::Feature feature) + void requestPermission(QWebEnginePermission permission) { if (m_allowGeolocation) - setFeaturePermission(origin, feature, PermissionGrantedByUser); + permission.grant(); else - setFeaturePermission(origin, feature, PermissionDeniedByUser); + permission.deny(); } public: @@ -438,6 +469,7 @@ private: bool m_allowGeolocation; }; +#ifndef Q_OS_MACOS void tst_QWebEnginePage::geolocationRequestJS_data() { QTest::addColumn<bool>("allowed"); @@ -453,14 +485,15 @@ void tst_QWebEnginePage::geolocationRequestJS() QWebEngineView view; JSTestPage *newPage = new JSTestPage(&view); view.setPage(newPage); + newPage->profile()->setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions); newPage->setGeolocationPermission(allowed); - connect(newPage, SIGNAL(featurePermissionRequested(const QUrl&, QWebEnginePage::Feature)), - newPage, SLOT(requestPermission(const QUrl&, QWebEnginePage::Feature))); + connect(newPage, SIGNAL(permissionRequested(QWebEnginePermission)), + newPage, SLOT(requestPermission(QWebEnginePermission))); 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(); @@ -477,6 +510,7 @@ void tst_QWebEnginePage::geolocationRequestJS() QEXPECT_FAIL("", "No location service available.", Continue); QCOMPARE(result, errorCode); } +#endif void tst_QWebEnginePage::loadFinished() { @@ -487,19 +521,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() @@ -586,7 +620,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); } @@ -604,6 +638,7 @@ public: QWebEngineNavigationRequest::NavigationType type; QUrl url; bool isMainFrame; + bool hasFormData; }; QList<Navigation> navigations; @@ -621,6 +656,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(); } @@ -638,47 +674,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 @@ -699,18 +759,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. @@ -723,18 +783,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); @@ -753,11 +813,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); @@ -806,16 +866,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")); @@ -864,7 +924,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); @@ -891,7 +951,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()); @@ -912,7 +972,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) { @@ -924,7 +984,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(); } @@ -951,8 +1011,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()); @@ -971,8 +1031,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()); } @@ -1073,7 +1133,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, @@ -1107,7 +1167,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()); @@ -1121,7 +1181,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")); } @@ -1132,7 +1192,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()); @@ -1149,7 +1209,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()); } @@ -1160,7 +1220,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")); } @@ -1170,7 +1230,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); @@ -1182,7 +1242,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() }); @@ -1194,7 +1254,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})); @@ -1223,7 +1283,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()); @@ -1245,7 +1305,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; @@ -1282,12 +1342,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); @@ -1295,28 +1355,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); @@ -1344,7 +1404,11 @@ 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(); @@ -1353,14 +1417,14 @@ void tst_QWebEnginePage::comboBoxPopupPositionAfterMove() view.setHtml(QLatin1String("<html><head></head><body><select id='foo'>" "<option>fran</option><option>troz</option>" "</select></body></html>")); - QTRY_COMPARE(spyLoadFinished.count(), 1); + QTRY_COMPARE(spyLoadFinished.size(), 1); const auto oldTlws = QGuiApplication::topLevelWindows(); QFETCH(bool, withTouch); - QWindow *window = view.windowHandle(); + 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)); @@ -1375,7 +1439,7 @@ void tst_QWebEnginePage::comboBoxPopupPositionAfterMove() 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(); } @@ -1388,7 +1452,7 @@ 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()); @@ -1406,6 +1470,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); @@ -1416,6 +1483,7 @@ 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(); @@ -1425,15 +1493,15 @@ void tst_QWebEnginePage::comboBoxPopupPositionAfterChildMove() 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)); @@ -1460,7 +1528,7 @@ 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()); @@ -1588,11 +1656,13 @@ public: GetUserMediaTestPage() : m_gotRequest(false) , m_loadSucceeded(false) + , m_permission(nullptr) { - connect(this, &QWebEnginePage::featurePermissionRequested, this, &GetUserMediaTestPage::onFeaturePermissionRequested); + connect(this, &QWebEnginePage::permissionRequested, this, &GetUserMediaTestPage::onPermissionRequested); connect(this, &QWebEnginePage::loadFinished, [this](bool success){ m_loadSucceeded = success; }); + profile()->setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions); // We need to load content from a resource in order for the securityOrigin to be valid. load(QUrl("qrc:///resources/content.html")); } @@ -1626,18 +1696,20 @@ public: void rejectPendingRequest() { - setFeaturePermission(m_requestSecurityOrigin, m_requestedFeature, QWebEnginePage::PermissionDeniedByUser); + QVERIFY(m_permission); + m_permission->deny(); m_gotRequest = false; } void acceptPendingRequest() { - setFeaturePermission(m_requestSecurityOrigin, m_requestedFeature, QWebEnginePage::PermissionGrantedByUser); + QVERIFY(m_permission); + m_permission->grant(); m_gotRequest = false; } - bool gotFeatureRequest(QWebEnginePage::Feature feature) + bool gotFeatureRequest(QWebEnginePermission::Feature feature) { - return m_gotRequest && m_requestedFeature == feature; + return m_gotRequest && m_permission && m_permission->feature() == feature; } bool gotFeatureRequest() const @@ -1651,50 +1723,47 @@ public: } private Q_SLOTS: - void onFeaturePermissionRequested(const QUrl &securityOrigin, QWebEnginePage::Feature feature) + void onPermissionRequested(QWebEnginePermission permission) { - m_requestedFeature = feature; - m_requestSecurityOrigin = securityOrigin; + m_permission.reset(new QWebEnginePermission(permission)); m_gotRequest = true; } private: bool m_gotRequest; bool m_loadSucceeded; - QWebEnginePage::Feature m_requestedFeature; - QUrl m_requestSecurityOrigin; - + std::unique_ptr<QWebEnginePermission> m_permission; }; void tst_QWebEnginePage::getUserMediaRequest_data() { QTest::addColumn<QString>("call"); - QTest::addColumn<QWebEnginePage::Feature>("feature"); + QTest::addColumn<QWebEnginePermission::Feature>("feature"); QTest::addRow("device audio") - << "getUserMedia({audio: true})" << QWebEnginePage::MediaAudioCapture; + << "getUserMedia({audio: true})" << QWebEnginePermission::MediaAudioCapture; QTest::addRow("device video") - << "getUserMedia({video: true})" << QWebEnginePage::MediaVideoCapture; + << "getUserMedia({video: true})" << QWebEnginePermission::MediaVideoCapture; QTest::addRow("device audio+video") - << "getUserMedia({audio: true, video: true})" << QWebEnginePage::MediaAudioVideoCapture; + << "getUserMedia({audio: true, video: true})" << QWebEnginePermission::MediaAudioVideoCapture; QTest::addRow("desktop video") << "getUserMedia({video: { mandatory: { chromeMediaSource: 'desktop' }}})" - << QWebEnginePage::DesktopVideoCapture; + << QWebEnginePermission::DesktopVideoCapture; QTest::addRow("desktop audio+video") << "getUserMedia({audio: { mandatory: { chromeMediaSource: 'desktop' }}, video: { mandatory: { chromeMediaSource: 'desktop' }}})" - << QWebEnginePage::DesktopAudioVideoCapture; + << QWebEnginePermission::DesktopAudioVideoCapture; QTest::addRow("display video") - << "getDisplayMedia()" << QWebEnginePage::DesktopVideoCapture; + << "getDisplayMedia()" << QWebEnginePermission::DesktopVideoCapture; } void tst_QWebEnginePage::getUserMediaRequest() { QFETCH(QString, call); - QFETCH(QWebEnginePage::Feature, feature); + QFETCH(QWebEnginePermission::Feature, feature); GetUserMediaTestPage page; QWebEngineView view; - if (feature == QWebEnginePage::DesktopVideoCapture || feature == QWebEnginePage::DesktopAudioVideoCapture) { + if (feature == QWebEnginePermission::DesktopVideoCapture || feature == QWebEnginePermission::DesktopAudioVideoCapture) { // Desktop capture needs to be on a desktop. view.setPage(&page); view.resize(640, 480); @@ -1764,7 +1833,7 @@ void tst_QWebEnginePage::getUserMediaRequestSettingDisabled() void tst_QWebEnginePage::getUserMediaRequestDesktopVideoManyPages() { const QString constraints = QStringLiteral("{video: { mandatory: { chromeMediaSource: 'desktop' }}}"); - const QWebEnginePage::Feature feature = QWebEnginePage::DesktopVideoCapture; + const QWebEnginePermission::Feature feature = QWebEnginePermission::DesktopVideoCapture; std::vector<GetUserMediaTestPage> pages(10); // Desktop capture needs to be on a desktop @@ -1797,7 +1866,7 @@ void tst_QWebEnginePage::getUserMediaRequestDesktopVideoManyPages() void tst_QWebEnginePage::getUserMediaRequestDesktopVideoManyRequests() { const QString constraints = QStringLiteral("{video: { mandatory: { chromeMediaSource: 'desktop' }}}"); - const QWebEnginePage::Feature feature = QWebEnginePage::DesktopVideoCapture; + const QWebEnginePermission::Feature feature = QWebEnginePermission::DesktopVideoCapture; GetUserMediaTestPage page; // Desktop capture needs to be on a desktop @@ -1863,14 +1932,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); @@ -1941,7 +2010,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), @@ -1958,7 +2027,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, [&]() { @@ -1982,7 +2051,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")); @@ -1999,7 +2068,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))); @@ -2013,8 +2082,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); @@ -2022,35 +2101,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() @@ -2072,7 +2139,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")); @@ -2086,8 +2153,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. @@ -2246,7 +2313,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()); @@ -2255,7 +2322,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()); @@ -2301,7 +2368,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); @@ -2310,7 +2377,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); @@ -2373,7 +2440,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); @@ -2436,7 +2503,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)); } @@ -2472,7 +2539,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); @@ -2481,7 +2548,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); @@ -2491,12 +2559,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(); @@ -2517,7 +2585,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()); } @@ -2552,7 +2620,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); @@ -2646,7 +2714,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); @@ -2655,7 +2723,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); @@ -2664,7 +2732,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); @@ -2673,7 +2741,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); @@ -2728,9 +2796,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()); @@ -2741,9 +2809,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()); @@ -2767,9 +2835,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()); @@ -2781,9 +2849,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()); @@ -2814,7 +2882,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. @@ -2823,7 +2891,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); @@ -2834,14 +2902,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. @@ -2853,7 +2921,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. @@ -2866,7 +2934,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() @@ -2889,22 +2957,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()); @@ -2912,7 +2980,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()); @@ -2941,9 +3009,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); @@ -2957,11 +3025,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); @@ -2971,11 +3039,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); @@ -3063,7 +3131,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); } @@ -3074,31 +3142,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_WITH_TIMEOUT(spy.count(), 1, 10000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 10000); QCOMPARE(spy.takeFirst().value(0).toBool(), false); } @@ -3115,7 +3183,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)); @@ -3125,7 +3193,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)); @@ -3148,19 +3216,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() @@ -3174,7 +3242,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); @@ -3192,7 +3260,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); } @@ -3220,17 +3288,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); @@ -3259,34 +3330,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; @@ -3295,12 +3349,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())); @@ -3355,7 +3409,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); @@ -3390,7 +3444,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))); @@ -3412,7 +3466,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() @@ -3445,7 +3499,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\")"); @@ -3455,7 +3509,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); @@ -3466,7 +3520,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()); @@ -3482,7 +3536,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()); @@ -3506,7 +3560,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); @@ -3518,7 +3572,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); @@ -3529,6 +3583,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() @@ -3550,11 +3606,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()); } @@ -3664,7 +3720,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) { @@ -3679,7 +3735,7 @@ void tst_QWebEnginePage::openLinkInNewPage() break; } - Qt::MouseButton button; + Qt::MouseButton button = Qt::NoButton; switch (cause) { case Cause::TargetBlank: button = Qt::LeftButton; @@ -3694,13 +3750,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); @@ -3709,9 +3765,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; @@ -3733,26 +3789,38 @@ 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")); } struct NotificationPage : ConsolePage { Q_OBJECT - const QWebEnginePage::PermissionPolicy policy; + const QWebEnginePermission::State policy; public: - NotificationPage(QWebEnginePage::PermissionPolicy ppolicy) : policy(ppolicy) { + NotificationPage(QWebEnginePermission::State ppolicy) : policy(ppolicy) { connect(this, &QWebEnginePage::loadFinished, [load = spyLoad.ref()] (bool result) mutable { load(result); }); - connect(this, &QWebEnginePage::featurePermissionRequested, - [this] (const QUrl &origin, QWebEnginePage::Feature feature) { - if (feature != QWebEnginePage::Notifications) + connect(this, &QWebEnginePage::permissionRequested, + [this] (QWebEnginePermission permission) { + if (permission.feature() != QWebEnginePermission::Notifications) return; if (spyRequest.wasCalled()) QFAIL("request executed twise!"); - setFeaturePermission(origin, feature, policy); - spyRequest.ref()(origin); + switch (policy) { + case QWebEnginePermission::Granted: + permission.grant(); + break; + case QWebEnginePermission::Denied: + permission.deny(); + break; + case QWebEnginePermission::Ask: + permission.reset(); + break; + default: + break; + } + spyRequest.ref()(permission.origin()); }); load(QStringLiteral("qrc:///shared/notification.html")); @@ -3772,49 +3840,62 @@ public: void tst_QWebEnginePage::notificationPermission_data() { QTest::addColumn<bool>("setOnInit"); - QTest::addColumn<QWebEnginePage::PermissionPolicy>("policy"); + QTest::addColumn<QWebEnginePermission::State>("policy"); QTest::addColumn<QString>("permission"); - QTest::newRow("denyOnInit") << true << QWebEnginePage::PermissionDeniedByUser << "denied"; - QTest::newRow("deny") << false << QWebEnginePage::PermissionDeniedByUser << "denied"; - QTest::newRow("grant") << false << QWebEnginePage::PermissionGrantedByUser << "granted"; - QTest::newRow("grantOnInit") << true << QWebEnginePage::PermissionGrantedByUser << "granted"; + QTest::newRow("denyOnInit") << true << QWebEnginePermission::Denied << "denied"; + QTest::newRow("deny") << false << QWebEnginePermission::Denied << "denied"; + QTest::newRow("grant") << false << QWebEnginePermission::Granted << "granted"; + QTest::newRow("grantOnInit") << true << QWebEnginePermission::Granted << "granted"; } void tst_QWebEnginePage::notificationPermission() { QFETCH(bool, setOnInit); - QFETCH(QWebEnginePage::PermissionPolicy, policy); + QFETCH(QWebEnginePermission::State, policy); QFETCH(QString, permission); QWebEngineProfile otr; + otr.setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions); QWebEnginePage page(&otr, nullptr); QUrl baseUrl("https://www.example.com/somepage.html"); bool permissionRequested = false, errorState = false; - connect(&page, &QWebEnginePage::featurePermissionRequested, &page, [&] (const QUrl &o, QWebEnginePage::Feature f) { - if (f != QWebEnginePage::Notifications) + connect(&page, &QWebEnginePage::permissionRequested, &page, [&] (QWebEnginePermission permission) { + if (permission.feature() != QWebEnginePermission::Notifications) return; - if (permissionRequested || o != baseUrl.url(QUrl::RemoveFilename)) { - qWarning() << "Unexpected case. Can't proceed." << setOnInit << permissionRequested << o; + if (permissionRequested || permission.origin() != baseUrl.url(QUrl::RemoveFilename)) { + qWarning() << "Unexpected case. Can't proceed." << setOnInit << permissionRequested << permission.origin(); errorState = true; return; } permissionRequested = true; - page.setFeaturePermission(o, f, policy); + + if (policy == QWebEnginePermission::Granted) + permission.grant(); + else + permission.deny(); }); - if (setOnInit) - page.setFeaturePermission(baseUrl, QWebEnginePage::Notifications, policy); + QWebEnginePermission permissionObject = otr.getPermission(baseUrl, QWebEnginePermission::Notifications); + if (setOnInit) { + if (policy == QWebEnginePermission::Granted) + permissionObject.grant(); + else + permissionObject.deny(); + } 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")); if (!setOnInit) { - page.setFeaturePermission(baseUrl, QWebEnginePage::Notifications, policy); + if (policy == QWebEnginePermission::Granted) + permissionObject.grant(); + else + permissionObject.deny(); QTRY_COMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), permission); } @@ -3828,7 +3909,7 @@ void tst_QWebEnginePage::notificationPermission() void tst_QWebEnginePage::sendNotification() { - NotificationPage page(QWebEnginePage::PermissionGrantedByUser); + NotificationPage page(QWebEnginePermission::Granted); QVERIFY(page.spyLoad.waitForResult()); page.resetPermission(); @@ -3867,6 +3948,176 @@ 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; + otr.setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions); + 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<QWebEnginePermission::State>("initialPolicy"); + QTest::addColumn<QString>("initialPermission"); + QTest::addColumn<QWebEnginePermission::State>("requestPolicy"); + QTest::addColumn<QString>("finalPermission"); + + QTest::newRow("noAccessGrantGrant") + << false << QWebEnginePermission::Granted << "granted" + << QWebEnginePermission::Granted << "granted"; + QTest::newRow("noAccessGrantDeny") + << false << QWebEnginePermission::Granted << "granted" + << QWebEnginePermission::Denied << "denied"; + QTest::newRow("noAccessDenyGrant") + << false << QWebEnginePermission::Denied << "denied" + << QWebEnginePermission::Granted << "granted"; + QTest::newRow("noAccessDenyDeny") << false << QWebEnginePermission::Denied << "denied" + << QWebEnginePermission::Denied << "denied"; + QTest::newRow("noAccessAskGrant") << false << QWebEnginePermission::Ask << "prompt" + << QWebEnginePermission::Granted << "granted"; + + // All policies are ignored and overridden by setting JsCanAccessClipboard and JsCanPaste to + // true + QTest::newRow("accessGrantGrant") + << true << QWebEnginePermission::Granted << "granted" + << QWebEnginePermission::Granted << "granted"; + QTest::newRow("accessDenyDeny") << true << QWebEnginePermission::Denied << "granted" + << QWebEnginePermission::Denied << "granted"; + QTest::newRow("accessAskAsk") << true << QWebEnginePermission::Ask << "granted" + << QWebEnginePermission::Ask << "granted"; +} + +void tst_QWebEnginePage::clipboardReadWritePermission() +{ + QFETCH(bool, canAccessClipboard); + QFETCH(QWebEnginePermission::State, initialPolicy); + QFETCH(QString, initialPermission); + QFETCH(QWebEnginePermission::State, requestPolicy); + QFETCH(QString, finalPermission); + + QWebEngineProfile otr; + otr.setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions); + 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::permissionRequested, &page, + [&](QWebEnginePermission permission) { + if (permission.feature() != QWebEnginePermission::ClipboardReadWrite) + return; + if (permission.origin() != baseUrl.url(QUrl::RemoveFilename)) { + qWarning() << "Unexpected case. Can't proceed." << permission.origin(); + errorState = true; + return; + } + permissionRequestCount++; + switch (requestPolicy) { + case QWebEnginePermission::Granted: + permission.grant(); + break; + case QWebEnginePermission::Denied: + permission.deny(); + break; + case QWebEnginePermission::Ask: + permission.reset(); + break; + default: + break; + } + }); + + QWebEnginePermission permissionObject = otr.getPermission(baseUrl, QWebEnginePermission::ClipboardReadWrite); + switch (initialPolicy) { + case QWebEnginePermission::Granted: + permissionObject.grant(); + break; + case QWebEnginePermission::Denied: + permissionObject.deny(); + break; + case QWebEnginePermission::Ask: + permissionObject.reset(); + break; + default: + break; + } + + 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); @@ -3877,8 +4128,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); @@ -3895,6 +4146,62 @@ void tst_QWebEnginePage::contentsSize() QCOMPARE(m_page->contentsSize().height(), 1216); } +void tst_QWebEnginePage::localFontAccessPermission_data() +{ + QTest::addColumn<QWebEnginePermission::State>("policy"); + QTest::addColumn<bool>("ignore"); + QTest::addColumn<bool>("shouldBeEmpty"); + + QTest::newRow("ignore") << QWebEnginePermission::Denied << true << true; + QTest::newRow("setDeny") << QWebEnginePermission::Denied << false << true; + QTest::newRow("setGrant") << QWebEnginePermission::Granted << false << false; +} + +void tst_QWebEnginePage::localFontAccessPermission() { + QFETCH(QWebEnginePermission::State, policy); + QFETCH(bool, ignore); + QFETCH(bool, shouldBeEmpty); + + QWebEngineView view; + QWebEnginePage page(&view); + page.profile()->setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions); + view.setPage(&page); + + connect(&page, &QWebEnginePage::permissionRequested, &page, [&] (QWebEnginePermission permission) { + if (permission.feature() != QWebEnginePermission::LocalFontsAccess) + return; + + if (!ignore) { + if (policy == QWebEnginePermission::Granted) + permission.grant(); + else + permission.deny(); + } + }); + + 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); + } +} + void tst_QWebEnginePage::setLifecycleState() { qRegisterMetaType<QWebEnginePage::LifecycleState>("LifecycleState"); @@ -3906,64 +4213,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)); @@ -3972,21 +4279,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)); } @@ -4002,18 +4309,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); @@ -4022,28 +4329,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); @@ -4052,31 +4359,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)); } @@ -4087,7 +4394,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 @@ -4124,7 +4431,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 @@ -4163,7 +4470,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 @@ -4188,16 +4495,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); @@ -4205,9 +4512,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); @@ -4215,7 +4522,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); @@ -4223,7 +4530,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 @@ -4261,35 +4568,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); } @@ -4306,21 +4613,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()); } @@ -4336,14 +4643,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)); @@ -4353,21 +4660,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); } @@ -4463,32 +4770,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)); } @@ -4503,33 +4810,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)); } @@ -4548,20 +4855,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")); } @@ -4581,13 +4888,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")); } @@ -4607,15 +4914,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")); } @@ -4631,8 +4938,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()); @@ -4644,7 +4951,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")); @@ -4704,12 +5011,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); @@ -4723,11 +5030,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); @@ -4760,7 +5067,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")); @@ -4887,7 +5194,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")); @@ -4932,7 +5239,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(), @@ -4998,11 +5305,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)); } @@ -5012,9 +5319,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]; @@ -5055,7 +5362,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(); } @@ -5095,18 +5402,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: @@ -5115,6 +5422,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); + page.profile()->setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions); + + bool desktopMediaRequested = false; + bool permissionRequested = false; + + connect(&page, &QWebEnginePage::desktopMediaRequested, + [&](const QWebEngineDesktopMediaRequest &) { + desktopMediaRequested = true; + }); + + connect(&page, &QWebEnginePage::permissionRequested, + [&](QWebEnginePermission permission) { + permissionRequested = true; + // Handle permission to 'complete' the media request + permission.grant(); + }); + + 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/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp index 35c66e9ed..d4973c99d 100644 --- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp +++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <util.h> #include <QtCore/qbuffer.h> @@ -56,6 +56,11 @@ private Q_SLOTS: void changePersistentCookiesPolicy(); void initiator(); void badDeleteOrder(); + void permissionPersistence_data(); + void permissionPersistence(); + void getPermission_data(); + void getPermission(); + void listPermissions(); void qtbug_71895(); // this should be the last test }; @@ -188,8 +193,10 @@ void tst_QWebEngineProfile::clearDataFromCache() QVERIFY(server.start()); AutoDir cacheDir("./tst_QWebEngineProfile_clearDataFromCache"); + QVERIFY(!cacheDir.exists("Cache")); QWebEngineProfile profile(QStringLiteral("clearDataFromCache")); + QSignalSpy cacheSpy(&profile, &QWebEngineProfile::clearHttpCacheCompleted); profile.setCachePath(cacheDir.path()); profile.setHttpCacheType(QWebEngineProfile::DiskHttpCache); @@ -200,10 +207,14 @@ void tst_QWebEngineProfile::clearDataFromCache() QVERIFY(cacheDir.exists("Cache")); qint64 sizeBeforeClear = totalSize(cacheDir); + QCOMPARE_GT(sizeBeforeClear, 0); profile.clearHttpCache(); - // Wait for cache to be cleared. - QTest::qWait(1000); - QVERIFY(sizeBeforeClear > totalSize(cacheDir)); + QTRY_COMPARE(cacheSpy.size(), 1); +#if defined(Q_OS_WIN) + QTRY_COMPARE_GT(sizeBeforeClear, totalSize(cacheDir)); +#else + QCOMPARE_GT(sizeBeforeClear, totalSize(cacheDir)); +#endif (void)server.stop(); } @@ -407,8 +418,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()); } @@ -469,7 +480,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()); } @@ -484,7 +495,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()); } @@ -499,7 +510,7 @@ 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)); @@ -516,7 +527,7 @@ void tst_QWebEngineProfile::urlSchemeHandlerStreaming2() 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)); @@ -577,7 +588,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() @@ -743,12 +754,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")); } @@ -783,7 +794,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); @@ -796,7 +807,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); @@ -810,7 +821,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(); @@ -822,7 +833,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); @@ -839,7 +850,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() @@ -965,29 +976,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_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 10000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 10000); QCOMPARE(handler.initiator, QUrl()); } @@ -1003,12 +1014,193 @@ 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; } +void tst_QWebEngineProfile::permissionPersistence_data() +{ + QTest::addColumn<QWebEngineProfile::PersistentPermissionsPolicy>("policy"); + QTest::addColumn<bool>("granted"); + + QTest::newRow("noPersistenceNotificationsNoGrant") << QWebEngineProfile::NoPersistentPermissions << false; + QTest::newRow("noPersistenceNotificationsGrant") << QWebEngineProfile::NoPersistentPermissions << true; + QTest::newRow("memoryPersistenceNotificationsNoGrant") << QWebEngineProfile::PersistentPermissionsInMemory << false; + QTest::newRow("diskPersistenceNotificationsGrant") << QWebEngineProfile::PersistentPermissionsOnDisk << true; +} + +void tst_QWebEngineProfile::permissionPersistence() +{ + QFETCH(QWebEngineProfile::PersistentPermissionsPolicy, policy); + QFETCH(bool, granted); + + TestServer server; + QVERIFY(server.start()); + + std::unique_ptr<QWebEngineProfile> profile(new QWebEngineProfile("tst_persistence")); + profile->setPersistentPermissionsPolicy(policy); + + std::unique_ptr<QWebEnginePage> page(new QWebEnginePage(profile.get())); + std::unique_ptr<QSignalSpy> loadSpy(new QSignalSpy(page.get(), &QWebEnginePage::loadFinished)); + QDir storageDir = QDir(profile->persistentStoragePath()); + + // Delete permissions file if it somehow survived on disk + storageDir.remove("permissions.json"); + + page->load(server.url("/hedgehog.html")); + QTRY_COMPARE(loadSpy->size(), 1); + + QVariant variant = granted ? "granted" : "denied"; + QVariant defaultVariant = "default"; + + QWebEnginePermission permissionObject = profile->getPermission(server.url("/hedgehog.html"), QWebEnginePermission::Notifications); + if (granted) + permissionObject.grant(); + else + permissionObject.deny(); + QCOMPARE(evaluateJavaScriptSync(page.get(), "Notification.permission"), variant); + + page.reset(); + profile.reset(); + loadSpy.reset(); + + bool expectSame = false; + if (policy == QWebEngineProfile::PersistentPermissionsOnDisk) { + expectSame = true; + + // File is written asynchronously, wait for it to be created + QTRY_COMPARE(storageDir.exists("permissions.json"), true); + } + + profile.reset(new QWebEngineProfile("tst_persistence")); + profile->setPersistentPermissionsPolicy(policy); + + page.reset(new QWebEnginePage(profile.get())); + loadSpy.reset(new QSignalSpy(page.get(), &QWebEnginePage::loadFinished)); + page->load(server.url("/hedgehog.html")); + QTRY_COMPARE(loadSpy->size(), 1); + QTRY_COMPARE(evaluateJavaScriptSync(page.get(), "Notification.permission"), + expectSame ? variant : defaultVariant); + + // Re-acquire the permission, since deleting the Profile makes it invalid + permissionObject = profile->getPermission(server.url("/hedgehog.html"), QWebEnginePermission::Notifications); + permissionObject.reset(); + QCOMPARE(evaluateJavaScriptSync(page.get(), "Notification.permission"), defaultVariant); + + page.reset(); + profile.reset(); + loadSpy.reset(); + + if (policy == QWebEngineProfile::PersistentPermissionsOnDisk) { + // Wait for file to be written to before deleting + QTest::qWait(1000); + storageDir.remove("permissions.json"); + } + + QVERIFY(server.stop()); +} + +void tst_QWebEngineProfile::getPermission_data() +{ + QTest::addColumn<QWebEnginePermission::Feature>("feature"); + QTest::addColumn<QUrl>("url"); + QTest::addColumn<bool>("expectedValid"); + + QTest::newRow("badUrl") << QWebEnginePermission::Notifications << QUrl(QStringLiteral("//:bad-url")) << false; + QTest::newRow("badFeature") << QWebEnginePermission::Unsupported << QUrl(QStringLiteral("qrc:/resources/hedgehog.html")) << false; + QTest::newRow("transientFeature") << QWebEnginePermission::MouseLock << QUrl(QStringLiteral("qrc:/resources/hedgehog.html")) << false; + QTest::newRow("good") << QWebEnginePermission::Notifications << QUrl(QStringLiteral("qrc:/resources/hedgehog.html")) << true; +} + +void tst_QWebEngineProfile::getPermission() +{ + QFETCH(QWebEnginePermission::Feature, feature); + QFETCH(QUrl, url); + QFETCH(bool, expectedValid); + + QWebEngineProfile profile; + // In-memory is the default for otr profiles + QVERIFY(profile.persistentPermissionsPolicy() == QWebEngineProfile::PersistentPermissionsInMemory); + + QWebEnginePermission permission = profile.getPermission(url, feature); + bool valid = permission.isValid(); + QVERIFY(valid == expectedValid); + if (!valid) + QVERIFY(permission.state() == QWebEnginePermission::Invalid); + + // Verify that we can grant a valid permission, and we can't grant an invalid one... + permission.grant(); + QVERIFY(permission.state() == (valid ? QWebEnginePermission::Granted : QWebEnginePermission::Invalid)); + + // ...and that doing so twice doesn't mess up the state... + permission.grant(); + QVERIFY(permission.state() == (valid ? QWebEnginePermission::Granted : QWebEnginePermission::Invalid)); + + // ...and that the same thing applies to denying them... + permission.deny(); + QVERIFY(permission.state() == (valid ? QWebEnginePermission::Denied : QWebEnginePermission::Invalid)); + permission.deny(); + QVERIFY(permission.state() == (valid ? QWebEnginePermission::Denied : QWebEnginePermission::Invalid)); + + // ...and that resetting works + permission.reset(); + QVERIFY(permission.state() == (valid ? QWebEnginePermission::Ask : QWebEnginePermission::Invalid)); + permission.reset(); + QVERIFY(permission.state() == (valid ? QWebEnginePermission::Ask : QWebEnginePermission::Invalid)); +} + +void tst_QWebEngineProfile::listPermissions() +{ + QWebEngineProfile profile; + // In-memory is the default for otr profiles + QVERIFY(profile.persistentPermissionsPolicy() == QWebEngineProfile::PersistentPermissionsInMemory); + + QUrl commonUrl = QUrl(QStringLiteral("http://www.bing.com/maps")); + QWebEnginePermission::Feature commonFeature = QWebEnginePermission::Notifications; + + // First, set several permissions at once + profile.getPermission(commonUrl, QWebEnginePermission::Geolocation).deny(); + profile.getPermission(commonUrl, QWebEnginePermission::Unsupported).grant(); // Invalid + profile.getPermission(commonUrl, commonFeature).grant(); + profile.getPermission(QUrl(QStringLiteral("http://www.google.com/translate")), commonFeature).grant(); + + QList<QWebEnginePermission> permissionsListAll = profile.listPermissions(); + QList<QWebEnginePermission> permissionsListUrl = profile.listPermissions(commonUrl); + QList<QWebEnginePermission> permissionsListFeature = profile.listPermissions(commonFeature); + + // Order of returned permissions is not guaranteed, so we must iterate until we find the one we need + auto findInList = [](QList<QWebEnginePermission> list, const QUrl &url, + QWebEnginePermission::Feature feature, QWebEnginePermission::State state) + { + bool found = false; + for (auto &permission : list) { + if (permission.origin().adjusted(QUrl::RemovePath) == url.adjusted(QUrl::RemovePath) && permission.feature() == feature && permission.state() == state) { + found = true; + break; + } + } + return found; + }; + + // Check full list + QVERIFY(permissionsListAll.size() == 3); + QVERIFY(findInList(permissionsListAll, commonUrl, QWebEnginePermission::Geolocation, QWebEnginePermission::Denied)); + QVERIFY(findInList(permissionsListAll, commonUrl, commonFeature, QWebEnginePermission::Granted)); + QVERIFY(findInList(permissionsListAll, QUrl(QStringLiteral("http://www.google.com")), commonFeature, QWebEnginePermission::Granted)); + + // Check list filtered by URL + QVERIFY(permissionsListUrl.size() == 2); + QVERIFY(findInList(permissionsListUrl, commonUrl, QWebEnginePermission::Geolocation, QWebEnginePermission::Denied)); + QVERIFY(findInList(permissionsListAll, commonUrl, commonFeature, QWebEnginePermission::Granted)); + + // Check list filtered by feature + QVERIFY(permissionsListFeature.size() == 2); + QVERIFY(findInList(permissionsListAll, commonUrl, commonFeature, QWebEnginePermission::Granted)); + QVERIFY(findInList(permissionsListAll, QUrl(QStringLiteral("http://www.google.com")), commonFeature, QWebEnginePermission::Granted)); +} + void tst_QWebEngineProfile::qtbug_71895() { QWebEngineView view; @@ -1019,7 +1211,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/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 6a97e5db0..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(); - QTRY_VERIFY_WITH_TIMEOUT(spyFinished.count() > 0, 20000); + 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 5d9cc038a..26f2da4bb 100644 --- a/tests/auto/widgets/qwebengineview/BLACKLIST +++ b/tests/auto/widgets/qwebengineview/BLACKLIST @@ -8,4 +8,5 @@ windows windows [horizontalScrollbarTest] -b2qt # different scrollbar +macos +rhel # flaky diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index 7cfe072b3..03acd69fe 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")); - QTRY_VERIFY_WITH_TIMEOUT(loadFinishedSpy.count() > 0, 20000); + 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()"); @@ -1162,7 +1268,7 @@ void tst_QWebEngineView::focusInternalRenderWidgetHostViewQuickItem() webView->setHtml("<html><body>" " <input id='input1' type='text'/>" "</body></html>"); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QTRY_COMPARE(webView->hasFocus(), false); // Manually trigger focus. @@ -1247,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); @@ -1257,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); @@ -1270,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); @@ -1302,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()); @@ -1352,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); @@ -1387,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() @@ -1397,7 +1504,7 @@ void tst_QWebEngineView::keyboardEvents() view.show(); QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); view.load(QUrl("qrc:///resources/keyboardEvents.html")); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); QStringList elements; elements << "first_div" << "second_div"; @@ -1528,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(), @@ -1559,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 @@ -1574,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 @@ -1595,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()); } @@ -1629,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] == "/"; @@ -1642,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") @@ -1660,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 @@ -1959,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); @@ -1978,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")); @@ -1987,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!")); @@ -1996,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!")); @@ -2011,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. @@ -2037,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!")); @@ -2052,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!")); @@ -2066,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); @@ -2083,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!")); @@ -2093,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(); } @@ -2137,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); } { @@ -2146,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 @@ -2222,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); @@ -2251,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); @@ -2261,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); @@ -2274,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); @@ -2296,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")); @@ -2331,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()); @@ -2345,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()); @@ -2355,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()); } @@ -2426,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; @@ -2475,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. { @@ -2485,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); @@ -2506,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'. { @@ -2518,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. { @@ -2531,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. @@ -2545,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. { @@ -2558,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. @@ -2576,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. { @@ -2589,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. @@ -2599,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); @@ -2613,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 @@ -2633,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(); @@ -2658,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")); @@ -2675,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() @@ -2830,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")); @@ -2846,10 +2945,10 @@ 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")); + QCOMPARE(logLines()[1], QStringLiteral("[object CompositionEvent] compositionupdate preedit")); + QCOMPARE(logLines()[2], QStringLiteral("[object InputEvent] beforeinput preedit")); QCOMPARE(logLines()[3], QStringLiteral("[object InputEvent] input preedit")); { @@ -2860,9 +2959,9 @@ void tst_QWebEngineView::imeJSInputEvents() qApp->processEvents(); } - QTRY_COMPARE(logLines().count(), 9); - QCOMPARE(logLines()[4], QStringLiteral("[object InputEvent] beforeinput commit")); - QCOMPARE(logLines()[5], QStringLiteral("[object CompositionEvent] compositionupdate commit")); + QTRY_COMPARE(logLines().size(), 9); + QCOMPARE(logLines()[4], QStringLiteral("[object CompositionEvent] compositionupdate commit")); + QCOMPARE(logLines()[5], QStringLiteral("[object InputEvent] beforeinput commit")); QCOMPARE(logLines()[6], QStringLiteral("[object TextEvent] textInput commit")); QCOMPARE(logLines()[7], QStringLiteral("[object InputEvent] input commit")); QCOMPARE(logLines()[8], QStringLiteral("[object CompositionEvent] compositionend commit")); @@ -2878,10 +2977,10 @@ 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")); + QCOMPARE(logLines()[1], QStringLiteral("[object CompositionEvent] compositionupdate preedit")); + QCOMPARE(logLines()[2], QStringLiteral("[object InputEvent] beforeinput preedit")); QCOMPARE(logLines()[3], QStringLiteral("[object InputEvent] input preedit")); { @@ -2891,9 +2990,9 @@ void tst_QWebEngineView::imeJSInputEvents() qApp->processEvents(); } - QTRY_COMPARE(logLines().count(), 9); - QCOMPARE(logLines()[4], QStringLiteral("[object InputEvent] beforeinput ")); - QCOMPARE(logLines()[5], QStringLiteral("[object CompositionEvent] compositionupdate ")); + QTRY_COMPARE(logLines().size(), 9); + QCOMPARE(logLines()[4], QStringLiteral("[object CompositionEvent] compositionupdate ")); + QCOMPARE(logLines()[5], QStringLiteral("[object InputEvent] beforeinput ")); QCOMPARE(logLines()[6], QStringLiteral("[object TextEvent] textInput ")); QCOMPARE(logLines()[7], QStringLiteral("[object InputEvent] input null")); QCOMPARE(logLines()[8], QStringLiteral("[object CompositionEvent] compositionend ")); @@ -2958,6 +3057,7 @@ void tst_QWebEngineView::imeCompositionQueryEvent() } QInputMethodQueryEvent srrndTextQuery(Qt::ImSurroundingText); + QInputMethodQueryEvent absolutePosQuery(Qt::ImAbsolutePosition); QInputMethodQueryEvent cursorPosQuery(Qt::ImCursorPosition); QInputMethodQueryEvent anchorPosQuery(Qt::ImAnchorPosition); @@ -2969,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 { @@ -2992,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) @@ -3023,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 @@ -3062,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()); } @@ -3102,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)); @@ -3110,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); } @@ -3230,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; @@ -3267,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, 90000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 90000); QCOMPARE(loadFinishedSpy.takeFirst().at(0).toBool(), supported); } @@ -3276,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(); @@ -3291,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")); } @@ -3304,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(); @@ -3369,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() @@ -3383,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); @@ -3416,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); @@ -3441,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(); @@ -3538,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()); } @@ -3556,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); @@ -3608,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 { @@ -3621,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 { @@ -3635,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>" @@ -3656,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');" @@ -3719,10 +3894,13 @@ void tst_QWebEngineView::datalist() QTest::keyClick(view.windowHandle(), Qt::Key_Escape); QTRY_VERIFY(!listView()); - // Key Down should open the popup and select the first suggestion. + // The first Key Down opens the popup. QTest::keyClick(view.windowHandle(), Qt::Key_Down); QTRY_VERIFY(listView()); - QCOMPARE(listView()->currentIndex().row(), 0); + + // The second Key Down selects the first suggestion. + QTest::keyClick(view.windowHandle(), Qt::Key_Down); + QTRY_COMPARE(listView()->currentIndex().row(), 0); // Test keyboard navigation in list. QTest::keyClick(view.windowHandle(), Qt::Key_Up); @@ -3784,5 +3962,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 ae3a38f91..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: 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..19bab06da 100644 --- a/tests/auto/widgets/schemes/tst_schemes.cpp +++ b/tests/auto/widgets/schemes/tst_schemes.cpp @@ -1,22 +1,50 @@ // Copyright (C) 2017 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #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/tst_shutdown.cpp b/tests/auto/widgets/shutdown/tst_shutdown.cpp index c2b31bb80..88cbc6aa7 100644 --- a/tests/auto/widgets/shutdown/tst_shutdown.cpp +++ b/tests/auto/widgets/shutdown/tst_shutdown.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2017 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 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> diff --git a/tests/auto/widgets/spellchecking/CMakeLists.txt b/tests/auto/widgets/spellchecking/CMakeLists.txt index 8c538377d..d0c7656c1 100644 --- a/tests/auto/widgets/spellchecking/CMakeLists.txt +++ b/tests/auto/widgets/spellchecking/CMakeLists.txt @@ -2,6 +2,7 @@ # 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..f5525c9f1 100644 --- a/tests/auto/widgets/spellchecking/tst_spellchecking.cpp +++ b/tests/auto/widgets/spellchecking/tst_spellchecking.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <util.h> #include <QtTest/QtTest> @@ -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/tst_touchinput.cpp b/tests/auto/widgets/touchinput/tst_touchinput.cpp index 562ccfcef..abc56a8e6 100644 --- a/tests/auto/widgets/touchinput/tst_touchinput.cpp +++ b/tests/auto/widgets/touchinput/tst_touchinput.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2020 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <util.h> @@ -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); } diff --git a/tests/manual/CMakeLists.txt b/tests/manual/CMakeLists.txt index c6d3e6e50..d9f3e11ea 100644 --- a/tests/manual/CMakeLists.txt +++ b/tests/manual/CMakeLists.txt @@ -1,2 +1,7 @@ -add_subdirectory(quick) -add_subdirectory(widgets) +add_subdirectory(examples) +if(TARGET Qt::WebEngineQuick) + add_subdirectory(quick) +endif() +if(TARGET Qt::WebEngineWidgets) + add_subdirectory(widgets) +endif() diff --git a/tests/manual/examples/CMakeLists.txt b/tests/manual/examples/CMakeLists.txt new file mode 100644 index 000000000..c56302f20 --- /dev/null +++ b/tests/manual/examples/CMakeLists.txt @@ -0,0 +1,6 @@ +if(TARGET Qt::WebEngineWidgets) + add_subdirectory(widgets) +endif() +if(TARGET Qt::WebEngineQuick) + add_subdirectory(quick) +endif() diff --git a/tests/manual/examples/quick/CMakeLists.txt b/tests/manual/examples/quick/CMakeLists.txt new file mode 100644 index 000000000..c461f2dbb --- /dev/null +++ b/tests/manual/examples/quick/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(minimal) +add_subdirectory(customdialogs) +add_subdirectory(customtouchhandle) +add_subdirectory(webengineaction) diff --git a/tests/manual/examples/quick/customdialogs/CMakeLists.txt b/tests/manual/examples/quick/customdialogs/CMakeLists.txt new file mode 100644 index 000000000..bfbff79eb --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/CMakeLists.txt @@ -0,0 +1,56 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(customdialogs LANGUAGES CXX) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_manual_test(customdialogs + SOURCES main.cpp server.cpp server.h +) + +set_target_properties(customdialogs PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(customdialogs PUBLIC + Qt::Core + Qt::Gui + Qt::WebEngineQuick +) + +set(customdialogs_resource_files + "MessageRectangle.qml" + "SwitchButton.qml" + "WebView.qml" + "forms/Authentication.qml" + "forms/AuthenticationForm.ui.qml" + "forms/ColorCell.qml" + "forms/ColorPicker.qml" + "forms/ColorPickerForm.ui.qml" + "forms/CustomButton.qml" + "forms/FilePicker.qml" + "forms/FilePickerForm.ui.qml" + "forms/FileRow.qml" + "forms/JavaScript.qml" + "forms/JavaScriptForm.ui.qml" + "forms/Menu.qml" + "forms/MenuForm.ui.qml" + "forms/TouchSelectionMenu.qml" + "forms/TouchSelectionMenuForm.ui.qml" + "icon.svg" + "index.html" + "main.qml" + "style.css" +) + +qt_add_resources(customdialogs "customdialogs" + PREFIX + "/" + FILES + ${customdialogs_resource_files} +) + diff --git a/tests/manual/examples/quick/customdialogs/MessageRectangle.qml b/tests/manual/examples/quick/customdialogs/MessageRectangle.qml new file mode 100644 index 000000000..09a202cf3 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/MessageRectangle.qml @@ -0,0 +1,18 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Rectangle { + property alias text: messageText.text + width: parent.width + height: 30 + visible: false + color: "#80c342" + Text { + id: messageText + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + font.pointSize: 12 + } +} diff --git a/tests/manual/examples/quick/customdialogs/SwitchButton.qml b/tests/manual/examples/quick/customdialogs/SwitchButton.qml new file mode 100644 index 000000000..69fc1427e --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/SwitchButton.qml @@ -0,0 +1,23 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +Item { + width: parent.width + height: 40 + property alias checked: switcher.checked + RowLayout { + anchors.centerIn: parent + Text { + text: qsTr("Use default dialogs") + font.pointSize: 12 + } + Switch { + id: switcher + checked: true + } + } +} diff --git a/tests/manual/examples/quick/customdialogs/WebView.qml b/tests/manual/examples/quick/customdialogs/WebView.qml new file mode 100644 index 000000000..5c99ee7e7 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/WebView.qml @@ -0,0 +1,117 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtWebEngine + +WebEngineView { + id: view + url: "qrc:/index.html" + property bool useDefaultDialogs: true + signal openForm(var form) + + Rectangle { + id: tooltip + width: 200 + height: 30 + z: 50 + visible: false + color: "gray" + border.color: "black" + border.width: 2 + radius: 3 + + property string text: "" + + Text { + x: 0 + y: 0 + color: "#ffffff" + text: parent.text + font.pointSize: 12 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + font.bold: false + } + + } + + onContextMenuRequested: function(request) { + // we only show menu for links with #openMenu + if (!request.linkUrl.toString().endsWith("#openMenu")) { + request.accepted = true; + return; + } + // return early to show default menu + if (useDefaultDialogs) + return; + + request.accepted = true; + openForm({item: Qt.resolvedUrl("forms/Menu.qml"), + properties: {"request": request}}); + } + + onTooltipRequested: function(request) { + if (useDefaultDialogs) + return; + + if (request.type == TooltipRequest.Show) { + tooltip.visible = true; + tooltip.x = request.x; + tooltip.y = request.y; + tooltip.text = request.text; + } else { + tooltip.visible = false; + } + + request.accepted = true; + } + + onAuthenticationDialogRequested: function(request) { + if (useDefaultDialogs) { + // do not show proxy error page + view.url = "qrc:/index.html" + return; + } + request.accepted = true; + openForm({item: Qt.resolvedUrl("forms/Authentication.qml"), + properties: {"request": request}}); + } + + onJavaScriptDialogRequested: function(request) { + if (useDefaultDialogs) + return; + + request.accepted = true; + openForm({item: Qt.resolvedUrl("forms/JavaScript.qml"), + properties: {"request": request}}); + } + + onColorDialogRequested: function(request) { + if (useDefaultDialogs) + return; + + request.accepted = true; + openForm({item: Qt.resolvedUrl("forms/ColorPicker.qml"), + properties: {"request": request}}); + } + + onFileDialogRequested: function(request) { + if (useDefaultDialogs) + return; + + request.accepted = true; + openForm({item: Qt.resolvedUrl("forms/FilePicker.qml"), + properties: {"request": request}}); + + } + + onTouchSelectionMenuRequested: function(request) { + if (useDefaultDialogs) + return; + + request.accepted = true; + openForm({item: Qt.resolvedUrl("forms/TouchSelectionMenu.qml"), + properties: {"request": request}}); + } +} diff --git a/tests/manual/examples/quick/customdialogs/customdialogs.pro b/tests/manual/examples/quick/customdialogs/customdialogs.pro new file mode 100644 index 000000000..1b9a6778e --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/customdialogs.pro @@ -0,0 +1,18 @@ +QT += webenginequick + +HEADERS += \ + server.h + +SOURCES += \ + main.cpp \ + server.cpp + +RESOURCES += \ + customdialogs.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/webenginequick/customdialogs +INSTALLS += target + +qtHaveModule(widgets) { + QT += widgets # QApplication is required to get native styling with QtQuickControls +} diff --git a/tests/manual/examples/quick/customdialogs/customdialogs.qrc b/tests/manual/examples/quick/customdialogs/customdialogs.qrc new file mode 100644 index 000000000..bb2677198 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/customdialogs.qrc @@ -0,0 +1,24 @@ +<RCC> + <qresource prefix="/"> + <file>forms/AuthenticationForm.ui.qml</file> + <file>forms/Authentication.qml</file> + <file>forms/ColorCell.qml</file> + <file>forms/ColorPickerForm.ui.qml</file> + <file>forms/ColorPicker.qml</file> + <file>forms/CustomButton.qml</file> + <file>forms/FilePickerForm.ui.qml</file> + <file>forms/FilePicker.qml</file> + <file>forms/FileRow.qml</file> + <file>forms/JavaScriptForm.ui.qml</file> + <file>forms/JavaScript.qml</file> + <file>forms/MenuForm.ui.qml</file> + <file>forms/Menu.qml</file> + <file>icon.svg</file> + <file>index.html</file> + <file>main.qml</file> + <file>MessageRectangle.qml</file> + <file>style.css</file> + <file>SwitchButton.qml</file> + <file>WebView.qml</file> + </qresource> +</RCC> diff --git a/tests/manual/examples/quick/customdialogs/forms/Authentication.qml b/tests/manual/examples/quick/customdialogs/forms/Authentication.qml new file mode 100644 index 000000000..151a7c4aa --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/Authentication.qml @@ -0,0 +1,31 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtWebEngine + +AuthenticationForm { + property QtObject request + signal closeForm() + + cancelButton.onClicked: { + request.dialogReject(); + closeForm(); + } + + loginButton.onClicked: { + request.dialogReject(); + closeForm(); + } + + Component.onCompleted: { + switch (request.type) { + case AuthenticationDialogRequest.AuthenticationTypeHTTP: + console.log("HTTP Authentication Required. Host says: " + request.realm); + break; + case AuthenticationDialogRequest.AuthenticationTypeProxy: + console.log("Proxy Authentication Required for: " + request.proxyHost); + break; + } + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/AuthenticationForm.ui.qml b/tests/manual/examples/quick/customdialogs/forms/AuthenticationForm.ui.qml new file mode 100644 index 000000000..f14986b20 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/AuthenticationForm.ui.qml @@ -0,0 +1,137 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls + +Item { + id: item1 + property alias cancelButton: cancelButton + property alias loginButton: loginButton + property alias userName: userName + property alias password: password + + ColumnLayout { + id: columnLayout + anchors.topMargin: 20 + anchors.top: parent.top + anchors.bottomMargin: 20 + anchors.bottom: parent.bottom + anchors.rightMargin: 20 + anchors.right: parent.right + anchors.leftMargin: 20 + anchors.left: parent.left + + Image { + id: image + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + source: "qrc:/icon.svg" + } + + Rectangle { + id: rectangle + width: parent.width + height: 30 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + gradient: Gradient { + GradientStop { + position: 0 + color: "#25a6e2" + } + GradientStop { + color: "#188bd0" + } + } + + Text { + id: textArea + x: 54 + y: 5 + color: "#ffffff" + text: qsTr("Restricted Area") + font.pointSize: 12 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + } + } + + Item { + width: 40 + height: 40 + } + + Text { + id: userNameText + text: qsTr("Username:") + font.pointSize: 12 + } + + TextField { + id: userName + width: 300 + height: 22 + Layout.fillWidth: true + font.pointSize: 12 + color: "black" + + background: Rectangle { + color: "white" + border.color: "black" + border.width: 1 + } + } + + Text { + id: passwordText + text: qsTr("Password:") + font.pointSize: 12 + } + + TextField { + id: password + width: 300 + height: 26 + Layout.fillWidth: true + font.pointSize: 12 + color: "black" + echoMode: TextInput.Password + + background: Rectangle { + color: "white" + border.color: "black" + border.width: 1 + } + } + + Item { + Layout.fillHeight: true + } + + RowLayout { + id: rowLayout + width: 100 + height: 100 + + Item { + Layout.fillWidth: true + } + + CustomButton { + id: cancelButton + width: 90 + height: 30 + btnText: qsTr("Cancel") + btnBlue: false + } + + CustomButton { + id: loginButton + width: 90 + height: 30 + btnText: qsTr("Login") + btnBlue: false + } + } + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/ColorCell.qml b/tests/manual/examples/quick/customdialogs/forms/ColorCell.qml new file mode 100644 index 000000000..57151780c --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/ColorCell.qml @@ -0,0 +1,16 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Rectangle { + id: rectangle + width: 50 + height: 50 + signal clicked() + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: rectangle.clicked() + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/ColorPicker.qml b/tests/manual/examples/quick/customdialogs/forms/ColorPicker.qml new file mode 100644 index 000000000..63269ddff --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/ColorPicker.qml @@ -0,0 +1,31 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +ColorPickerForm { + property QtObject request + signal closeForm() + + okButton.onClicked: { + request.dialogAccept(colorPicker.color); + closeForm(); + } + + cancelButton.onClicked: { + request.dialogReject(); + closeForm(); + } + + function createCallback(color) { + return function() { colorPicker.color = color }; + } + + Component.onCompleted:{ + for (var i = 0; i < grid.children.length; i++) { + var cell = grid.children[i]; + cell.clicked.connect(createCallback(cell.color)); + } + colorPicker.color = request.color; + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/ColorPickerForm.ui.qml b/tests/manual/examples/quick/customdialogs/forms/ColorPickerForm.ui.qml new file mode 100644 index 000000000..060aeef7d --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/ColorPickerForm.ui.qml @@ -0,0 +1,186 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Layouts + +Item { + property alias cancelButton: cancelButton + property alias okButton: okButton + property string message: "Message" + property string title: "Title" + property alias blue1: blue1 + property alias grid: grid + property alias colorPicker: colorPicker + + ColumnLayout { + id: columnLayout + anchors.topMargin: 20 + anchors.top: parent.top + anchors.bottomMargin: 20 + anchors.bottom: parent.bottom + anchors.rightMargin: 20 + anchors.right: parent.right + anchors.leftMargin: 20 + anchors.left: parent.left + + Image { + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + source: "qrc:/icon.svg" + } + + Rectangle { + width: parent.width + height: 30 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + gradient: Gradient { + GradientStop { + position: 0 + color: "#25a6e2" + } + + GradientStop { + color: "#188bd0" + } + } + + Text { + id: title + x: 54 + y: 5 + color: "#ffffff" + text: qsTr("Select Color") + font.pointSize: 12 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + } + } + + Item { + width: 40 + height: 40 + } + + GridLayout { + id: grid + columns: 5 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + + ColorCell { + id: blue1 + color: "#26d5f8" + } + ColorCell { + id: green1 + color: "#25f93d" + } + ColorCell { + id: red1 + color: "#f71111" + } + ColorCell { + id: yellow1 + color: "#faf23c" + } + ColorCell { + id: orange1 + color: "#ec8505" + } + ColorCell { + id: blue2 + color: "#037eaa" + } + ColorCell { + id: green2 + color: "#389a13" + } + ColorCell { + id: red2 + color: "#b2001b" + } + ColorCell { + id: yellow2 + color: "#caca03" + } + ColorCell { + id: orange2 + color: "#bb4900" + } + ColorCell { + id: blue3 + color: "#01506c" + } + ColorCell { + id: green3 + color: "#37592b" + } + ColorCell { + id: red3 + color: "#700113" + } + ColorCell { + id: yellow3 + color: "#848404" + } + + ColorCell { + id: orange3 + color: "#563100" + } + } + + Item { + width: 10 + height: 10 + } + + Rectangle { + width: 90 + height: 90 + color: "#000000" + radius: 4 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + + Rectangle { + id: colorPicker + height: 80 + color: "#ffffff" + anchors.rightMargin: 5 + anchors.leftMargin: 5 + anchors.bottomMargin: 5 + anchors.topMargin: 5 + anchors.fill: parent + } + } + + Item { + Layout.fillHeight: true + } + + RowLayout { + id: rowLayout + width: 100 + height: 100 + + Item { + Layout.fillWidth: true + } + + CustomButton { + id: cancelButton + width: 90 + height: 30 + btnText: qsTr("Cancel") + btnBlue: false + } + + CustomButton { + id: okButton + width: 90 + height: 30 + btnText: qsTr("OK") + btnBlue: false + } + } + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/CustomButton.qml b/tests/manual/examples/quick/customdialogs/forms/CustomButton.qml new file mode 100644 index 000000000..00a06d558 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/CustomButton.qml @@ -0,0 +1,61 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Rectangle { + id: root + width: 200 + height: 30 + radius: 5 + property string btnText: "Name" + property bool btnEnable: true + property bool btnBlue: true + opacity: btnEnable ? 1.0 : 0.5 + signal clicked() + gradient: btnBlue ? blueButton : greenButton + Text { + id: textArea + x: 54 + y: 5 + color: "#ffffff" + text: parent.btnText + font.pointSize: 12 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + font.bold: false + } + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: { + if (btnEnable) + root.clicked(); + } + } + + Gradient { + id: blueButton + GradientStop { + position: 0 + color: "#25a6e2" + } + GradientStop { + position: mouseArea.pressed && root.btnEnable ? 0.7 :1 + color: "#188bd0" + } + } + + Gradient { + id: greenButton + GradientStop { + position: 0 + color: "#80c342" + } + GradientStop { + position: mouseArea.pressed && root.btnEnable ? 0.7 :1 + color: "#5fac18" + } + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/FilePicker.qml b/tests/manual/examples/quick/customdialogs/forms/FilePicker.qml new file mode 100644 index 000000000..45ffefb3a --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/FilePicker.qml @@ -0,0 +1,44 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +FilePickerForm { + property QtObject request + property string selectedFile + signal closeForm() + + cancelButton.onClicked: { + request.dialogReject(); + closeForm(); + } + + okButton.onClicked: { + request.dialogAccept('/' + selectedFile); + closeForm(); + } + + function createCallback(fileIndex) { + return function() { + for (var i = 0; i < files.children.length; i++) { + var file = files.children[i]; + if (i === fileIndex) { + selectedFile = file.text; + file.selected = true; + } else { + file.selected = false; + } + } + } + } + + Component.onCompleted: { + selectedFile = request.defaultFileName; + for (var i = 0; i < files.children.length; i++) { + var file = files.children[i]; + file.clicked.connect(createCallback(i)); + if (file.text === selectedFile) + file.selected = true; + } + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/FilePickerForm.ui.qml b/tests/manual/examples/quick/customdialogs/forms/FilePickerForm.ui.qml new file mode 100644 index 000000000..1e99b1a91 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/FilePickerForm.ui.qml @@ -0,0 +1,128 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Layouts + +Item { + property alias cancelButton: cancelButton + property alias okButton: okButton + property string message: "Message" + property string title: "Title" + property alias files: files + + ColumnLayout { + id: columnLayout + anchors.topMargin: 20 + anchors.top: parent.top + anchors.bottomMargin: 20 + anchors.bottom: parent.bottom + anchors.rightMargin: 20 + anchors.right: parent.right + anchors.leftMargin: 20 + anchors.left: parent.left + + Image { + id: image + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + source: "qrc:/icon.svg" + } + + Rectangle { + id: rectangle + width: parent.width + height: 30 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + gradient: Gradient { + GradientStop { + position: 0 + color: "#25a6e2" + } + + GradientStop { + color: "#188bd0" + } + } + + Text { + id: title + x: 54 + y: 5 + color: "#ffffff" + text: qsTr("Select File") + font.pointSize: 12 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + } + } + + Item { + width: 40 + height: 40 + } + + ColumnLayout { + id: files + + FileRow { + id: filename1 + text: "example.qdoc" + } + + FileRow { + id: filename2 + text: "factory.cpp" + } + + FileRow { + id: filename3 + text: "index.html" + } + + FileRow { + id: filename4 + text: "main.qml" + } + + FileRow { + id: filename5 + text: "qt-logo.png" + } + + FileRow { + id: filename6 + text: "window.h" + } + } + + Item { + Layout.fillHeight: true + } + + RowLayout { + id: rowLayout + width: 20 + height: 100 + + Item { + Layout.fillWidth: true + } + + CustomButton { + id: cancelButton + width: 90 + height: 30 + btnText: qsTr("Cancel") + btnBlue: false + } + + CustomButton { + id: okButton + width: 90 + height: 30 + btnText: qsTr("OK") + btnBlue: false + } + } + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/FileRow.qml b/tests/manual/examples/quick/customdialogs/forms/FileRow.qml new file mode 100644 index 000000000..1a0cfc0a0 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/FileRow.qml @@ -0,0 +1,44 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Layouts + +Item { + id: root + height: 30 + property string text: "Filename" + property bool selected: false + signal clicked() + + RowLayout { + id: fileRow + width: 100 + + Item { + id: item5 + width: 10 + height: 10 + } + + Rectangle { + id: rectangle2 + width: 10 + height: 10 + color: selected ? "#80c342" : "#25a6e2" + } + + Text { + id: filename + text: root.text + font.pointSize: 12 + } + } + + MouseArea { + id: mouseArea + width: 200 + height: 30 + onClicked: root.clicked() + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/JavaScript.qml b/tests/manual/examples/quick/customdialogs/forms/JavaScript.qml new file mode 100644 index 000000000..132c95697 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/JavaScript.qml @@ -0,0 +1,44 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtWebEngine + +JavaScriptForm { + property QtObject request + signal closeForm() + + cancelButton.onClicked: { + request.dialogReject(); + closeForm(); + } + + okButton.onClicked: { + request.dialogAccept(prompt.text); + closeForm(); + } + + Component.onCompleted: { + switch (request.type) { + case JavaScriptDialogRequest.DialogTypeAlert: + cancelButton.visible = false; + title = qsTr("Alert"); + message = request.message; + prompt.text = ""; + prompt.visible = false; + break; + case JavaScriptDialogRequest.DialogTypeConfirm: + title = qsTr("Confirm"); + message = request.message; + prompt.text = ""; + prompt.visible = false; + break; + case JavaScriptDialogRequest.DialogTypePrompt: + title = qsTr("Prompt"); + message = request.message; + prompt.text = request.defaultText; + prompt.visible = true; + break; + } + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/JavaScriptForm.ui.qml b/tests/manual/examples/quick/customdialogs/forms/JavaScriptForm.ui.qml new file mode 100644 index 000000000..b535e7ef9 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/JavaScriptForm.ui.qml @@ -0,0 +1,117 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls + +Item { + id: root + property alias cancelButton: cancelButton + property alias okButton: okButton + property string message: "Message" + property string title: "Title" + property alias prompt: prompt + + ColumnLayout { + id: columnLayout + anchors.topMargin: 20 + anchors.top: parent.top + anchors.bottomMargin: 20 + anchors.bottom: parent.bottom + anchors.rightMargin: 20 + anchors.right: parent.right + anchors.leftMargin: 20 + anchors.left: parent.left + + Image { + id: image + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + source: "qrc:/icon.svg" + } + + Rectangle { + id: rectangle + width: parent.width + height: 30 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + gradient: Gradient { + GradientStop { + position: 0 + color: "#25a6e2" + } + + GradientStop { + color: "#188bd0" + } + } + + Text { + id: title + x: 54 + y: 5 + color: "#ffffff" + text: qsTr("Title") + font.pointSize: 12 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + } + } + + Item { + width: 40 + height: 40 + } + + Text { + id: message + text: root.message + font.pointSize: 12 + } + + TextField { + id: prompt + width: 300 + height: 22 + Layout.fillWidth: true + font.pointSize: 12 + color: "black" + + background: Rectangle { + color: "white" + border.color: "black" + border.width: 1 + } + } + + Item { + Layout.fillHeight: true + } + + RowLayout { + id: rowLayout + width: 100 + height: 100 + + Item { + Layout.fillWidth: true + } + + CustomButton { + id: cancelButton + width: 90 + height: 30 + btnText: qsTr("Cancel") + btnBlue: false + } + + CustomButton { + id: okButton + width: 90 + height: 30 + btnText: qsTr("OK") + btnBlue: false + } + } + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/Menu.qml b/tests/manual/examples/quick/customdialogs/forms/Menu.qml new file mode 100644 index 000000000..b90802a0c --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/Menu.qml @@ -0,0 +1,22 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +MenuForm { + property QtObject request + signal closeForm() + + followLink.onClicked: closeForm() + back.onClicked: closeForm() + forward.onClicked: closeForm() + reload.onClicked: closeForm() + copyLinkUrl.onClicked: closeForm() + saveLink.onClicked: closeForm() + close.onClicked: closeForm() + + Component.onCompleted: { + back.btnEnable = false; + forward.btnEnable = false; + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/MenuForm.ui.qml b/tests/manual/examples/quick/customdialogs/forms/MenuForm.ui.qml new file mode 100644 index 000000000..b4c06bb7d --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/MenuForm.ui.qml @@ -0,0 +1,65 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Layouts + +Item { + property alias followLink: followLink + property alias back: back + property alias forward: forward + property alias reload: reload + property alias copyLinkUrl: copyLinkUrl + property alias saveLink: saveLink + property alias close: close + + ColumnLayout { + id: columnLayout + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + + Image { + id: image + width: 100 + height: 100 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + source: "qrc:/icon.svg" + } + + CustomButton { + id: followLink + btnText: qsTr("Follow") + } + + CustomButton { + id: back + btnText: qsTr("Back") + } + + CustomButton { + id: forward + btnText: qsTr("Forward") + } + + CustomButton { + id: reload + btnText: qsTr("Reload") + } + + CustomButton { + id: copyLinkUrl + btnText: qsTr("Copy Link URL") + } + + CustomButton { + id: saveLink + btnText: qsTr("Save Link") + } + + CustomButton { + id: close + btnBlue: false + btnText: qsTr("Close") + } + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/TouchSelectionMenu.qml b/tests/manual/examples/quick/customdialogs/forms/TouchSelectionMenu.qml new file mode 100644 index 000000000..1b0c19789 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/TouchSelectionMenu.qml @@ -0,0 +1,14 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +TouchSelectionMenuForm { + property QtObject request + signal closeForm() + + cut.onClicked: closeForm() + copy.onClicked: closeForm() + paste.onClicked: closeForm() + contextMenu.onClicked: closeForm() +} diff --git a/tests/manual/examples/quick/customdialogs/forms/TouchSelectionMenuForm.ui.qml b/tests/manual/examples/quick/customdialogs/forms/TouchSelectionMenuForm.ui.qml new file mode 100644 index 000000000..bed39566f --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/TouchSelectionMenuForm.ui.qml @@ -0,0 +1,39 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Layouts + +Item { + property alias cut: cut + property alias copy: copy + property alias paste: paste + property alias contextMenu: contextMenu + + ColumnLayout { + id: columnLayout + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + + CustomButton { + id: cut + btnText: qsTr("Cut") + } + + CustomButton { + id: copy + btnText: qsTr("Copy") + } + + CustomButton { + id: paste + btnText: qsTr("Paste") + } + + CustomButton { + id: contextMenu + btnText: qsTr("...") + } + + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/forms.qmlproject b/tests/manual/examples/quick/customdialogs/forms/forms.qmlproject new file mode 100644 index 000000000..b06afaaf1 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/forms.qmlproject @@ -0,0 +1,45 @@ +import QmlProject + +Project { + mainFile: "MenuForm.ui.qml" + + /* Include .qml, .js, and image files from current directory and subdirectories */ + QmlFiles { + directory: "." + } + + JavaScriptFiles { + directory: "." + } + + ImageFiles { + directory: "." + } + + Files { + filter: "*.conf" + files: ["qtquickcontrols2.conf"] + } + + Files { + filter: "qmldir" + directory: "." + } + + Files { + filter: "*.ttf;*.otf" + } + + Environment { + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + QT_AUTO_SCREEN_SCALE_FACTOR: "1" + } + + qt6Project: true + + /* List of plugin directories passed to QML runtime */ + importPaths: [ ".", "imports" ] + + /* Required for deployment */ + targetDirectory: "/opt/forms" +} diff --git a/tests/manual/examples/quick/customdialogs/icon.svg b/tests/manual/examples/quick/customdialogs/icon.svg new file mode 100644 index 000000000..48271180b --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/icon.svg @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="94px" height="94px" viewBox="0 0 94 94" enable-background="new 0 0 94 94" xml:space="preserve"> +<g> + <circle fill="none" cx="47" cy="47" r="47"/> + <g> + <path fill="#46A2DA" d="M47,92.979c-11.779,0-23.559-4.484-32.526-13.451C-3.461,61.591-3.461,32.409,14.472,14.474 + C32.41-3.463,61.592-3.461,79.526,14.473c17.935,17.936,17.935,47.119,0.002,65.054l-0.002,0.001 + C70.559,88.495,58.779,92.979,47,92.979z"/> + </g> + <path fill="#80C342" d="M93,47C93,21.595,72.405,1,47,1C34.297,1,22.797,6.149,14.473,14.473l65.054,65.054 + C87.851,71.203,93,59.703,93,47z"/> + <g> + <path fill="#46A2DA" d="M47,65c-4.808,0-9.328-1.873-12.728-5.272c-7.018-7.019-7.018-18.438,0-25.456 + C37.672,30.873,42.192,29,47,29s9.328,1.873,12.728,5.272c7.018,7.019,7.018,18.438,0,25.456C56.328,63.127,51.808,65,47,65z"/> + <path fill="#FFFFFF" d="M62.248,59.919c6.671-7.858,6.312-19.644-1.105-27.061C57.237,28.953,52.118,27,47,27 + c-5.118,0-10.237,1.953-14.142,5.858c-7.81,7.81-7.81,20.474,0,28.284C36.763,65.047,41.882,67,47,67 + c4.379,0,8.752-1.441,12.372-4.3L77.88,81.209c0.989-0.895,1.935-1.837,2.843-2.814L62.248,59.919z M35.686,58.314 + c-6.238-6.238-6.238-16.389,0-22.627C38.708,32.664,42.726,31,47,31c4.274,0,8.292,1.664,11.314,4.686 + c6.238,6.238,6.238,16.389,0,22.627C55.292,61.336,51.274,63,47,63C42.726,63,38.708,61.336,35.686,58.314z"/> + </g> +</g> +</svg> diff --git a/tests/manual/examples/quick/customdialogs/index.html b/tests/manual/examples/quick/customdialogs/index.html new file mode 100644 index 000000000..d5de2827c --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/index.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Custom UI</title> + <link rel="stylesheet" type="text/css" href="style.css"> + </head> + <body> + <table align="center"> + <tr> + <td><div class="div"><a href="#openMenu" class="link">Right click on text to see link context menu</a></div></td> + </tr> + <tr> + <td><div class="div"><p title="I am a tooltip.">Hover this text to display a tooltip</a></div></td> + </tr> + <tr> + <td><div class="div"><p>Touch devices only: long press on this text to see the touch selection menu</p></div></td> + </tr> + <tr> + <td><button class="button" onclick="window.location = 'http://localhost.:5555/OPEN_AUTH'"> + Open Authentication Dialog</button></td> + </tr> + <tr> + <td><button class="button" onclick="window.location = 'http://www.qt.io'"> + Open Proxy Dialog</button></td> + </tr> + <tr> + <td><button class="button" onclick="alert('This is the Alert Dialog !')"> + Open Alert Dialog</button></td> + </tr> + <tr> + <td><button class="button" onclick="confirm('This is the Confirm Dialog.')"> + Open Confirm Dialog</button></td> + </tr> + <tr> + <td><button class="button" onclick="prompt('Is this the Prompt Dialog ?', 'Yes')"> + Open Prompt Dialog</button></td> + </tr> + <tr> + <td><button class="button" onclick="document.getElementById('colorpicker').click()"> + Open Color Dialog</button></td> + </tr> + <tr> + <td><button class="button" onclick="document.getElementById('filepicker').click()"> + Open File Dialog</button></td> + </tr> + </table> + <input type="color" id="colorpicker" value="#ff0000" style="visibility:hidden"/> + <input type="file" id="filepicker" accept=".cpp, .html, .h, .png, .qdoc, .qml" style="visibility:hidden"/> + </body> +</html> diff --git a/tests/manual/examples/quick/customdialogs/main.cpp b/tests/manual/examples/quick/customdialogs/main.cpp new file mode 100644 index 000000000..c114ea935 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/main.cpp @@ -0,0 +1,32 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "server.h" +#include <QtWebEngineQuick/qtwebenginequickglobal.h> +#include <QNetworkProxy> +#include <QQmlApplicationEngine> +#include <QTimer> +#include <QtGui/QGuiApplication> + +int main(int argc, char *argv[]) +{ + QCoreApplication::setOrganizationName("QtExamples"); + QtWebEngineQuick::initialize(); + + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + Server *server = new Server(&engine); + + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + QTimer::singleShot(0, server, &Server::run); + + QNetworkProxy proxy; + proxy.setType(QNetworkProxy::HttpProxy); + proxy.setHostName("localhost"); + proxy.setPort(5555); + QNetworkProxy::setApplicationProxy(proxy); + + return app.exec(); +} + diff --git a/tests/manual/examples/quick/customdialogs/main.qml b/tests/manual/examples/quick/customdialogs/main.qml new file mode 100644 index 000000000..d0cb6f324 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/main.qml @@ -0,0 +1,56 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Window + +Window { + id: mainWindow + width: 800 + height: 610 + visible: true + + StackView { + id: stackView + anchors.fill: parent + focus: true + initialItem: Item { + id: main + width: mainWindow.width + height: mainWindow.height + ColumnLayout { + anchors.fill: parent + SwitchButton { + id: switcher + Layout.fillWidth: true + } + WebView { + id: webView + useDefaultDialogs: switcher.checked + Layout.fillWidth: true + Layout.fillHeight: true + } + } + } + + function closeForm() + { + pop(main); + // reset url in case of proxy error + webView.url = "qrc:/index.html" + } + + function openForm(form) + { + push(form.item, form.properties); + currentItem.closeForm.connect(closeForm); + } + + } + + Component.onCompleted: { + webView.openForm.connect(stackView.openForm); + } +} diff --git a/tests/manual/examples/quick/customdialogs/server.cpp b/tests/manual/examples/quick/customdialogs/server.cpp new file mode 100644 index 000000000..efb870618 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/server.cpp @@ -0,0 +1,47 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "server.h" +#include <QDataStream> +#include <QTcpSocket> + +Server::Server(QObject *parent) : QObject(parent) +{ + connect(&m_server, &QTcpServer::newConnection, this, &Server::handleNewConnection); +} + +void Server::run() +{ + if (!m_server.listen(QHostAddress::LocalHost, 5555)) + qWarning() << "Could not start the server -> http/proxy authentication dialog" + " will not work. Error:" << m_server.errorString(); +} + +void Server::handleNewConnection() +{ + QTcpSocket *socket = m_server.nextPendingConnection(); + connect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater); + connect(socket, &QAbstractSocket::readyRead, this, &Server::handleReadReady); +} + +void Server::handleReadReady() +{ + QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender()); + Q_ASSERT(socket); + m_data.append(socket->readAll()); + + // simply wait for whole request + if (!m_data.endsWith("\r\n\r\n")) + return; + if (m_data.contains(QByteArrayLiteral("OPEN_AUTH"))) { + socket->write("HTTP/1.1 401 Unauthorized\nWWW-Authenticate: " + "Basic realm=\"Very Restricted Area\"\r\n\r\n"); + m_data.clear(); + return; + } + + socket->write("HTTP/1.1 407 Proxy Auth Required\nProxy-Authenticate: " + "Basic realm=\"Proxy requires authentication\"\r\n" + "content-length: 0\r\n\r\n"); + m_data.clear(); +} diff --git a/tests/manual/examples/quick/customdialogs/server.h b/tests/manual/examples/quick/customdialogs/server.h new file mode 100644 index 000000000..563465013 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/server.h @@ -0,0 +1,29 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef SERVER_H +#define SERVER_H + +#include <QObject> +#include <QTcpServer> + +class Server : public QObject +{ + Q_OBJECT + +public: + explicit Server(QObject *parent = nullptr); + +public slots: + void run(); + +private slots: + void handleNewConnection(); + void handleReadReady(); + +private: + QTcpServer m_server; + QByteArray m_data; +}; + +#endif // SERVER_H diff --git a/tests/manual/examples/quick/customdialogs/style.css b/tests/manual/examples/quick/customdialogs/style.css new file mode 100644 index 000000000..e4c25e7eb --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/style.css @@ -0,0 +1,37 @@ +.div { + padding:8px 4px; + border: 5px solid #188BD0; + width: 280px; + font-family: sans-serif; + font-size:10pt; +} +.link { + text-decoration: none; + color: #888888; +} +.button { + background: -webkit-linear-gradient(top,#25A6E2 0%,#188BD0 100%); + padding:8px 13px; + color:#fff; + font-family: sans-serif; + font-size:17px; + -webkit-border-radius:5px; + border:1px solid #1A87FF; + width: 300px; +} +.button:focus { + outline: none; +} +.button:active { + background: -webkit-linear-gradient(top,#25A6E2 0%,#188BD0 70%); +} +.input { + padding:8px 4px; + border: 5px solid #188BD0; + width: 280px; + font-family: sans-serif; + font-size:10pt; +} +.input:focus { + outline: none; +} diff --git a/tests/manual/examples/quick/customtouchhandle/CMakeLists.txt b/tests/manual/examples/quick/customtouchhandle/CMakeLists.txt new file mode 100644 index 000000000..5574c28b8 --- /dev/null +++ b/tests/manual/examples/quick/customtouchhandle/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(customtouchhandle LANGUAGES CXX) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_manual_test(customtouchhandle + SOURCES main.cpp +) + +set_target_properties(customtouchhandle PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(customtouchhandle PUBLIC + Qt::Core + Qt::Gui + Qt::WebEngineQuick +) + +# Resources: +set(qml_resource_files + "main.qml" +) + +qt6_add_resources(customtouchhandle "qml" + PREFIX + "/" + FILES + ${qml_resource_files} +) + diff --git a/tests/manual/examples/quick/customtouchhandle/customtouchhandle.pro b/tests/manual/examples/quick/customtouchhandle/customtouchhandle.pro new file mode 100644 index 000000000..a74ef3146 --- /dev/null +++ b/tests/manual/examples/quick/customtouchhandle/customtouchhandle.pro @@ -0,0 +1,10 @@ +TEMPLATE = app + +QT += webenginequick + +SOURCES += main.cpp + +RESOURCES += qml.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/webenginequick/customtouchhandle +INSTALLS += target diff --git a/tests/manual/examples/quick/customtouchhandle/main.cpp b/tests/manual/examples/quick/customtouchhandle/main.cpp new file mode 100644 index 000000000..f1b70b024 --- /dev/null +++ b/tests/manual/examples/quick/customtouchhandle/main.cpp @@ -0,0 +1,19 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QGuiApplication> +#include <QQmlApplicationEngine> +#include <QtWebEngineQuick/qtwebenginequickglobal.h> + +int main(int argc, char *argv[]) +{ + QCoreApplication::setOrganizationName("QtExamples"); + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); + QtWebEngineQuick::initialize(); + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + + return app.exec(); +} diff --git a/tests/manual/examples/quick/customtouchhandle/main.qml b/tests/manual/examples/quick/customtouchhandle/main.qml new file mode 100644 index 000000000..c40b4c73b --- /dev/null +++ b/tests/manual/examples/quick/customtouchhandle/main.qml @@ -0,0 +1,96 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Window +import QtWebEngine +import QtQuick.Layouts +import QtQuick.Controls + +ApplicationWindow { + width: 1024 + height: 750 + visible: true + header: ToolBar { + RowLayout { + anchors.fill: parent + + ToolButton { + property int itemAction: WebEngineView.Back + text: webEngineView.action(itemAction).text + enabled: webEngineView.action(itemAction).enabled + onClicked: webEngineView.action(itemAction).trigger() + icon.name: webEngineView.action(itemAction).iconName + display: AbstractButton.TextUnderIcon + } + + ToolButton { + property int itemAction: WebEngineView.Forward + text: webEngineView.action(itemAction).text + enabled: webEngineView.action(itemAction).enabled + onClicked: webEngineView.action(itemAction).trigger() + icon.name: webEngineView.action(itemAction).iconName + display: AbstractButton.TextUnderIcon + } + + ToolButton { + property int itemAction: webEngineView.loading ? WebEngineView.Stop : WebEngineView.Reload + text: webEngineView.action(itemAction).text + enabled: webEngineView.action(itemAction).enabled + onClicked: webEngineView.action(itemAction).trigger() + icon.name: webEngineView.action(itemAction).iconName + display: AbstractButton.TextUnderIcon + } + + TextField { + Layout.fillWidth: true + text: webEngineView.url + selectByMouse: true + onEditingFinished: webEngineView.url = text + } + + Label { text: 'Handle: ' } + ComboBox { + model: [ 'Default', 'Circle', 'Square' ] + + onCurrentValueChanged: { + if (currentValue == 'Circle') + webEngineView.touchHandleDelegate = circleTouchHandle + else if (currentValue == 'Square') + webEngineView.touchHandleDelegate = rectTouchHandle + else + webEngineView.touchHandleDelegate = null + } + + Component.onCompleted: currentIndex = indexOfValue('Square') + } + } + } + + Component { + id: circleTouchHandle + Rectangle { + color: "blue" + border.color: "black" + border.width: 2 + radius: 50 + } + } + + Component { + id: rectTouchHandle + Rectangle { + border.color: "black" + border.width: 2 + radius: 2 + onVisibleChanged: if (visible) { color = 'yellow'; cAnim.restart(); } + ColorAnimation on color { id: cAnim; to: 'red'; duration: 1000 } + } + } + + WebEngineView { + anchors.fill: parent + id: webEngineView + url: "https://www.qt.io" + } +} diff --git a/tests/manual/examples/quick/customtouchhandle/qml.qrc b/tests/manual/examples/quick/customtouchhandle/qml.qrc new file mode 100644 index 000000000..5f6483ac3 --- /dev/null +++ b/tests/manual/examples/quick/customtouchhandle/qml.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>main.qml</file> + </qresource> +</RCC> diff --git a/tests/manual/examples/quick/minimal/CMakeLists.txt b/tests/manual/examples/quick/minimal/CMakeLists.txt new file mode 100644 index 000000000..42e2078f7 --- /dev/null +++ b/tests/manual/examples/quick/minimal/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(minimal LANGUAGES CXX) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_manual_test(webengine-minimal-qml + SOURCES + main.cpp +) + +set_target_properties(webengine-minimal-qml PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(webengine-minimal-qml PUBLIC + Qt::Core + Qt::Gui + Qt::WebEngineQuick +) + +# Resources: +set(qml_resource_files + "main.qml" +) + +qt_add_resources(webengine-minimal-qml "qml" + PREFIX + "/" + FILES + ${qml_resource_files} +) diff --git a/tests/manual/examples/quick/minimal/main.cpp b/tests/manual/examples/quick/minimal/main.cpp new file mode 100644 index 000000000..16466ae06 --- /dev/null +++ b/tests/manual/examples/quick/minimal/main.cpp @@ -0,0 +1,19 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QGuiApplication> +#include <QQmlApplicationEngine> +#include <QtWebEngineQuick/qtwebenginequickglobal.h> + +int main(int argc, char *argv[]) +{ + QCoreApplication::setOrganizationName("QtExamples"); + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); + QtWebEngineQuick::initialize(); + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + + return app.exec(); +} diff --git a/tests/manual/examples/quick/minimal/main.qml b/tests/manual/examples/quick/minimal/main.qml new file mode 100644 index 000000000..6890b501b --- /dev/null +++ b/tests/manual/examples/quick/minimal/main.qml @@ -0,0 +1,16 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Window +import QtWebEngine + +Window { + width: 1024 + height: 750 + visible: true + WebEngineView { + anchors.fill: parent + url: "chrome://qt" + } +} diff --git a/tests/manual/examples/quick/minimal/minimal.pro b/tests/manual/examples/quick/minimal/minimal.pro new file mode 100644 index 000000000..acca6477c --- /dev/null +++ b/tests/manual/examples/quick/minimal/minimal.pro @@ -0,0 +1,10 @@ +TEMPLATE = app + +QT += webenginequick + +SOURCES += main.cpp + +RESOURCES += qml.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/webenginequick/minimal +INSTALLS += target diff --git a/tests/manual/examples/quick/minimal/qml.qrc b/tests/manual/examples/quick/minimal/qml.qrc new file mode 100644 index 000000000..0ff3892d9 --- /dev/null +++ b/tests/manual/examples/quick/minimal/qml.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/"> + <file>main.qml</file> + </qresource> +</RCC> + diff --git a/tests/manual/examples/quick/webengineaction/CMakeLists.txt b/tests/manual/examples/quick/webengineaction/CMakeLists.txt new file mode 100644 index 000000000..e16772faf --- /dev/null +++ b/tests/manual/examples/quick/webengineaction/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(webengineaction LANGUAGES CXX) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_manual_test(webengineaction + SOURCES + main.cpp + utils.h +) + +set_target_properties(webengineaction PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(webengineaction PUBLIC + Qt::Core + Qt::Gui + Qt::WebEngineQuick +) + +# Resources: +set(qml_resource_files + "main.qml" +) + +qt_add_resources(webengineaction "qml" + PREFIX + "/" + FILES + ${qml_resource_files} +) diff --git a/tests/manual/examples/quick/webengineaction/main.cpp b/tests/manual/examples/quick/webengineaction/main.cpp new file mode 100644 index 000000000..a7bfaaf36 --- /dev/null +++ b/tests/manual/examples/quick/webengineaction/main.cpp @@ -0,0 +1,25 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "utils.h" + +#include <QGuiApplication> +#include <QQmlApplicationEngine> +#include <QQmlContext> +#include <QtWebEngineQuick/qtwebenginequickglobal.h> + +int main(int argc, char *argv[]) +{ + QCoreApplication::setOrganizationName("QtExamples"); + QtWebEngineQuick::initialize(); + + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + Utils utils; + + engine.rootContext()->setContextProperty("utils", &utils); + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + + return app.exec(); +} diff --git a/tests/manual/examples/quick/webengineaction/main.qml b/tests/manual/examples/quick/webengineaction/main.qml new file mode 100644 index 000000000..a1483b126 --- /dev/null +++ b/tests/manual/examples/quick/webengineaction/main.qml @@ -0,0 +1,119 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Window +import QtWebEngine +import QtQuick.Controls +import QtQuick.Layouts + +ApplicationWindow { + id: window + visible: true + width: 800 + height: 600 + title: qsTr("WebEngineAction Example") + + header: ToolBar { + RowLayout { + anchors.fill: parent + + ToolButton { + property int itemAction: WebEngineView.Back + text: webEngineView.action(itemAction).text + enabled: webEngineView.action(itemAction).enabled + onClicked: webEngineView.action(itemAction).trigger() + icon.name: webEngineView.action(itemAction).iconName + display: AbstractButton.TextUnderIcon + } + + ToolButton { + property int itemAction: WebEngineView.Forward + text: webEngineView.action(itemAction).text + enabled: webEngineView.action(itemAction).enabled + onClicked: webEngineView.action(itemAction).trigger() + icon.name: webEngineView.action(itemAction).iconName + display: AbstractButton.TextUnderIcon + } + + ToolButton { + property int itemAction: webEngineView.loading ? WebEngineView.Stop : WebEngineView.Reload + text: webEngineView.action(itemAction).text + enabled: webEngineView.action(itemAction).enabled + onClicked: webEngineView.action(itemAction).trigger() + icon.name: webEngineView.action(itemAction).iconName + display: AbstractButton.TextUnderIcon + } + + TextField { + Layout.fillWidth: true + + text: webEngineView.url + selectByMouse: true + onEditingFinished: webEngineView.url = utils.fromUserInput(text) + } + + ToolButton { + id: settingsButton + text: "Settings" + icon.name: "settings-configure" + display: AbstractButton.TextUnderIcon + onClicked: settingsMenu.open() + checked: settingsMenu.visible + + Menu { + id: settingsMenu + y: settingsButton.height + + MenuItem { + id: customContextMenuOption + checkable: true + checked: true + + text: "Custom context menu" + } + } + } + } + } + + WebEngineView { + id: webEngineView + url: "https://qt.io" + anchors.fill: parent + + Component.onCompleted: { + profile.downloadRequested.connect(function(download){ + download.accept(); + }) + } + + property Menu contextMenu: Menu { + Repeater { + model: [ + WebEngineView.Back, + WebEngineView.Forward, + WebEngineView.Reload, + WebEngineView.SavePage, + WebEngineView.Copy, + WebEngineView.Paste, + WebEngineView.Cut + ] + MenuItem { + text: webEngineView.action(modelData).text + enabled: webEngineView.action(modelData).enabled + onClicked: webEngineView.action(modelData).trigger() + icon.name: webEngineView.action(modelData).iconName + display: MenuItem.TextBesideIcon + } + } + } + + onContextMenuRequested: function(request) { + if (customContextMenuOption.checked) { + request.accepted = true; + contextMenu.popup(); + } + } + } +} diff --git a/tests/manual/examples/quick/webengineaction/qml.qrc b/tests/manual/examples/quick/webengineaction/qml.qrc new file mode 100644 index 000000000..5f6483ac3 --- /dev/null +++ b/tests/manual/examples/quick/webengineaction/qml.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>main.qml</file> + </qresource> +</RCC> diff --git a/tests/manual/examples/quick/webengineaction/utils.h b/tests/manual/examples/quick/webengineaction/utils.h new file mode 100644 index 000000000..d9a803907 --- /dev/null +++ b/tests/manual/examples/quick/webengineaction/utils.h @@ -0,0 +1,25 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef UTILS_H +#define UTILS_H + +#include <QtCore/QFileInfo> +#include <QtCore/QUrl> + +class Utils : public QObject +{ + Q_OBJECT +public: + Q_INVOKABLE static QUrl fromUserInput(const QString &userInput); +}; + +inline QUrl Utils::fromUserInput(const QString &userInput) +{ + QFileInfo fileInfo(userInput); + if (fileInfo.exists()) + return QUrl::fromLocalFile(fileInfo.absoluteFilePath()); + return QUrl::fromUserInput(userInput); +} + +#endif // UTILS_H diff --git a/tests/manual/examples/quick/webengineaction/webengineaction.pro b/tests/manual/examples/quick/webengineaction/webengineaction.pro new file mode 100644 index 000000000..9286604a1 --- /dev/null +++ b/tests/manual/examples/quick/webengineaction/webengineaction.pro @@ -0,0 +1,8 @@ +TEMPLATE = app + +QT += webenginequick + +HEADERS += utils.h +SOURCES += main.cpp + +RESOURCES += qml.qrc diff --git a/tests/manual/examples/widgets/CMakeLists.txt b/tests/manual/examples/widgets/CMakeLists.txt new file mode 100644 index 000000000..d853ba634 --- /dev/null +++ b/tests/manual/examples/widgets/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(minimal) diff --git a/tests/manual/examples/widgets/minimal/CMakeLists.txt b/tests/manual/examples/widgets/minimal/CMakeLists.txt new file mode 100644 index 000000000..3b39683ba --- /dev/null +++ b/tests/manual/examples/widgets/minimal/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(minimal LANGUAGES CXX) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) + endif() + +qt_internal_add_manual_test(minimal-widgets + SOURCES + main.cpp +) + +set_target_properties(minimal-widgets PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(minimal-widgets PUBLIC + Qt::Core + Qt::Gui + Qt::WebEngineWidgets +) diff --git a/tests/manual/examples/widgets/minimal/main.cpp b/tests/manual/examples/widgets/minimal/main.cpp new file mode 100644 index 000000000..425973116 --- /dev/null +++ b/tests/manual/examples/widgets/minimal/main.cpp @@ -0,0 +1,28 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QApplication> +#include <QWebEngineView> + +QUrl commandLineUrlArgument() +{ + const QStringList args = QCoreApplication::arguments(); + for (const QString &arg : args.mid(1)) { + if (!arg.startsWith(QLatin1Char('-'))) + return QUrl::fromUserInput(arg); + } + return QUrl(QStringLiteral("chrome://qt")); +} + +int main(int argc, char *argv[]) +{ + QCoreApplication::setOrganizationName("QtExamples"); + QApplication app(argc, argv); + + QWebEngineView view; + view.setUrl(commandLineUrlArgument()); + view.resize(1024, 750); + view.show(); + + return app.exec(); +} diff --git a/tests/manual/examples/widgets/minimal/minimal.pro b/tests/manual/examples/widgets/minimal/minimal.pro new file mode 100644 index 000000000..849f4b9b6 --- /dev/null +++ b/tests/manual/examples/widgets/minimal/minimal.pro @@ -0,0 +1,8 @@ +TEMPLATE = app + +QT += webenginewidgets + +SOURCES += main.cpp + +target.path = $$[QT_INSTALL_EXAMPLES]/webenginewidgets/minimal +INSTALLS += target diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro deleted file mode 100644 index edf95846c..000000000 --- a/tests/manual/manual.pro +++ /dev/null @@ -1,7 +0,0 @@ -TEMPLATE = subdirs - -SUBDIRS += \ - widgets \ - quick - -!qtHaveModule(webenginewidgets): SUBDIRS -= widgets diff --git a/tests/manual/quick/CMakeLists.txt b/tests/manual/quick/CMakeLists.txt index 0562237b4..d6c4b88a9 100644 --- a/tests/manual/quick/CMakeLists.txt +++ b/tests/manual/quick/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(touchbrowser) +add_subdirectory(geopermission) diff --git a/tests/manual/quick/geopermission/CMakeLists.txt b/tests/manual/quick/geopermission/CMakeLists.txt new file mode 100644 index 000000000..088f248e1 --- /dev/null +++ b/tests/manual/quick/geopermission/CMakeLists.txt @@ -0,0 +1,63 @@ +# 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_geopermission LANGUAGES CXX) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_manual_test(tst_geopermission + SOURCES + main.cpp + LIBRARIES + Qt::Core + Qt::Gui + Qt::Qml + Qt::Quick + Qt::WebEngineQuick +) + +if(WIN32) + set_property( + TARGET tst_geopermission + APPEND PROPERTY + SOURCES tst_geopermission.exe.manifest) +endif() + +set_target_properties(tst_geopermission PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_GUI_IDENTIFIER "io.qt.dev.webenginequick.tst_geopermission" +) + +# Resources: +set(resources_resource_files + "tst_geopermission.qml" + "geolocation.html" +) + +qt_add_resources(tst_geopermission "resources" + PREFIX + "/" + FILES + ${resources_resource_files} +) + +foreach(permission_plugin IN LISTS QT_ALL_PLUGINS_FOUND_BY_FIND_PACKAGE_permissions) + set(permission_plugin "${QT_CMAKE_EXPORT_NAMESPACE}::${permission_plugin}") + qt6_import_plugins(tst_geopermission INCLUDE ${permission_plugin}) +endforeach() + +if (APPLE) + set_target_properties(tst_geopermission PROPERTIES + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist" + ) + + if (NOT CMAKE_GENERATOR STREQUAL "Xcode") + # Need to sign application for location permissions to work + add_custom_command(TARGET tst_geopermission + POST_BUILD COMMAND codesign -s - tst_geopermission.app) + endif() +endif() + diff --git a/tests/manual/quick/geopermission/Info.plist b/tests/manual/quick/geopermission/Info.plist new file mode 100644 index 000000000..9853e1900 --- /dev/null +++ b/tests/manual/quick/geopermission/Info.plist @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleName</key> + <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string> + <key>CFBundleIdentifier</key> + <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string> + <key>CFBundleExecutable</key> + <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string> + <key>CFBundleVersion</key> + <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string> + <key>CFBundleShortVersionString</key> + <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string> + <key>LSMinimumSystemVersion</key> + <string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string> + <key>NSHumanReadableCopyright</key> + <string>${MACOSX_BUNDLE_COPYRIGHT}</string> + <key>CFBundleIconFile</key> + <string>${MACOSX_BUNDLE_ICON_FILE}</string> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>NSSupportsAutomaticGraphicsSwitching</key> + <true/> + <key>NSLocationUsageDescription</key> + <string>Geolocation test would like to give web sites access to your location for demo purposes.</string> +</dict> +</plist> diff --git a/tests/manual/quick/geopermission/geolocation.html b/tests/manual/quick/geopermission/geolocation.html new file mode 100644 index 000000000..e8c54bc58 --- /dev/null +++ b/tests/manual/quick/geopermission/geolocation.html @@ -0,0 +1,32 @@ +<html> +<head> +<title>Geolocation Permission API Test</title> +<script> + +var errorMessage; +var handled = false; + +function successHandler(location) { + var message = document.getElementById("message"); + message.innerHTML = "Latitude: " + location.coords.latitude + + "<br>Longitude: " + location.coords.longitude; + + errorMessage = ""; + handled = true; +} + +function errorHandler(error) { + errorMessage = error.message; + handled = true; +} + +<!-- One shot example --> +navigator.geolocation.getCurrentPosition(successHandler, errorHandler); + +</script> +</head> +<body> +<div id="message">Location unknown</div> +</body> +</html> + diff --git a/tests/manual/quick/geopermission/main.cpp b/tests/manual/quick/geopermission/main.cpp new file mode 100644 index 000000000..b0e9a7eda --- /dev/null +++ b/tests/manual/quick/geopermission/main.cpp @@ -0,0 +1,39 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QtWebEngineQuick/qtwebenginequickglobal.h> + +#include <QtQml/QQmlApplicationEngine> +#include <QtQml/QQmlContext> +#include <QQuickView> + +#include <QtGui/QGuiApplication> + +#include <QtCore/QCommandLineParser> +#include <QtCore/QCommandLineOption> +#include <QtCore/QLoggingCategory> + +int main(int argc, char **argv) +{ + QCoreApplication::setApplicationName("Geopermission test"); + QCoreApplication::setOrganizationName("QtProject"); + + QtWebEngineQuick::initialize(); + + QGuiApplication app(argc, argv); + + QQuickView view; + + view.setTitle("Touch Browser"); + view.setFlags(Qt::Window | Qt::WindowTitleHint); + view.setResizeMode(QQuickView::SizeRootObjectToView); + view.setSource(QUrl("qrc:/tst_geopermission.qml")); + + QObject::connect(view.engine(), SIGNAL(quit()), &app, SLOT(quit())); + + view.show(); + if (view.size().isEmpty()) + view.setGeometry(0, 0, 800, 600); + + return app.exec(); +} diff --git a/tests/manual/quick/geopermission/tst_geopermission.qml b/tests/manual/quick/geopermission/tst_geopermission.qml new file mode 100644 index 000000000..6d6f4de8b --- /dev/null +++ b/tests/manual/quick/geopermission/tst_geopermission.qml @@ -0,0 +1,28 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +import QtQuick +import QtTest +import QtWebEngine + +WebEngineView { + id: webEngineView + width: 200 + height: 200 + url: Qt.resolvedUrl("qrc:/geolocation.html") + property bool deniedGeolocation: false + property bool geoPermissionRequested: false + + onPermissionRequested: function(perm) { + if (perm.feature === WebEnginePermission.Geolocation) { + geoPermissionRequested = true + if (deniedGeolocation) { + perm.deny() + } + else { + perm.grant() + } + } + } + +} diff --git a/tests/manual/quick/pdf/bookmarks-list.qml b/tests/manual/quick/pdf/bookmarks-list.qml index 2be0d6848..e0af43054 100644 --- a/tests/manual/quick/pdf/bookmarks-list.qml +++ b/tests/manual/quick/pdf/bookmarks-list.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtQuick.Controls import QtQuick.Dialogs diff --git a/tests/manual/quick/pdf/bookmarks.qml b/tests/manual/quick/pdf/bookmarks.qml index e12629b31..86ae4e347 100644 --- a/tests/manual/quick/pdf/bookmarks.qml +++ b/tests/manual/quick/pdf/bookmarks.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtQuick.Controls import QtQuick.Dialogs diff --git a/tests/manual/quick/pdf/gridview.qml b/tests/manual/quick/pdf/gridview.qml index 773e72388..69b85470d 100644 --- a/tests/manual/quick/pdf/gridview.qml +++ b/tests/manual/quick/pdf/gridview.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtQuick.Pdf diff --git a/tests/manual/quick/pdf/listview.qml b/tests/manual/quick/pdf/listview.qml index d01be9e86..1c997fdce 100644 --- a/tests/manual/quick/pdf/listview.qml +++ b/tests/manual/quick/pdf/listview.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtQuick.Pdf diff --git a/tests/manual/quick/pdf/multipleDocuments.qml b/tests/manual/quick/pdf/multipleDocuments.qml index 9d08178f0..7826c9a8f 100644 --- a/tests/manual/quick/pdf/multipleDocuments.qml +++ b/tests/manual/quick/pdf/multipleDocuments.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtQuick.Controls import QtQuick.Dialogs @@ -44,7 +44,7 @@ ApplicationWindow { action: Action { shortcut: StandardKey.ZoomIn enabled: pageView.sourceSize.width < 10000 - icon.source: "../../../../examples/pdf/pdfviewer/resources/zoom-in.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-in.svg" onTriggered: pageView.renderScale *= root.scaleStep } } @@ -52,20 +52,20 @@ ApplicationWindow { action: Action { shortcut: StandardKey.ZoomOut enabled: pageView.sourceSize.width > 50 - icon.source: "../../../../examples/pdf/pdfviewer/resources/zoom-out.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-out.svg" onTriggered: pageView.renderScale /= root.scaleStep } } ToolButton { action: Action { shortcut: "Ctrl+0" - icon.source: "../../../../examples/pdf/pdfviewer/resources/zoom-original.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-original.svg" onTriggered: pageView.resetScale() } } ToolButton { action: Action { - icon.source: "../../../../examples/pdf/pdfviewer/resources/go-previous-view-page.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/go-previous-view-page.svg" enabled: pageView.backEnabled onTriggered: pageView.back() } @@ -84,7 +84,7 @@ ApplicationWindow { } ToolButton { action: Action { - icon.source: "../../../../examples/pdf/pdfviewer/resources/go-next-view-page.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/go-next-view-page.svg" enabled: pageView.forwardEnabled onTriggered: pageView.forward() } diff --git a/tests/manual/quick/pdf/pdfPageView.qml b/tests/manual/quick/pdf/pdfPageView.qml index 475e1d608..edf3aa05b 100644 --- a/tests/manual/quick/pdf/pdfPageView.qml +++ b/tests/manual/quick/pdf/pdfPageView.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtQuick.Controls import QtQuick.Dialogs @@ -24,7 +24,7 @@ ApplicationWindow { ToolButton { action: Action { shortcut: StandardKey.Open - icon.source: "../../../../examples/pdf/pdfviewer/resources/document-open.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/document-open.svg" onTriggered: fileDialog.open() } } @@ -32,7 +32,7 @@ ApplicationWindow { action: Action { shortcut: StandardKey.ZoomIn enabled: pageView.sourceSize.width < 10000 - icon.source: "../../../../examples/pdf/pdfviewer/resources/zoom-in.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-in.svg" onTriggered: pageView.renderScale *= root.scaleStep } } @@ -40,46 +40,46 @@ ApplicationWindow { action: Action { shortcut: StandardKey.ZoomOut enabled: pageView.sourceSize.width > 50 - icon.source: "../../../../examples/pdf/pdfviewer/resources/zoom-out.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-out.svg" onTriggered: pageView.renderScale /= root.scaleStep } } ToolButton { action: Action { - icon.source: "../../../../examples/pdf/pdfviewer/resources/zoom-fit-width.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-fit-width.svg" onTriggered: pageView.scaleToWidth(root.contentItem.width, root.contentItem.height) } } ToolButton { action: Action { - icon.source: "../../../../examples/pdf/pdfviewer/resources/zoom-fit-best.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-fit-best.svg" onTriggered: pageView.scaleToPage(root.contentItem.width, root.contentItem.height) } } ToolButton { action: Action { shortcut: "Ctrl+0" - icon.source: "../../../../examples/pdf/pdfviewer/resources/zoom-original.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-original.svg" onTriggered: pageView.resetScale() } } ToolButton { action: Action { shortcut: "Ctrl+L" - icon.source: "../../../../examples/pdf/pdfviewer/resources/rotate-left.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/rotate-left.svg" onTriggered: pageView.rotation -= 90 } } ToolButton { action: Action { shortcut: "Ctrl+R" - icon.source: "../../../../examples/pdf/pdfviewer/resources/rotate-right.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/rotate-right.svg" onTriggered: pageView.rotation += 90 } } ToolButton { action: Action { - icon.source: "../../../../examples/pdf/pdfviewer/resources/go-previous-view-page.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/go-previous-view-page.svg" enabled: pageView.backEnabled onTriggered: pageView.back() } @@ -115,7 +115,7 @@ ApplicationWindow { } ToolButton { action: Action { - icon.source: "../../../../examples/pdf/pdfviewer/resources/go-next-view-page.svg" + icon.source: "../../../../examples/pdf//resources/go-next-view-page.svg" enabled: pageView.forwardEnabled onTriggered: pageView.forward() } @@ -126,7 +126,7 @@ ApplicationWindow { ToolButton { action: Action { shortcut: StandardKey.Copy - icon.source: "../../../../examples/pdf/pdfviewer/resources/edit-copy.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/edit-copy.svg" enabled: pageView.selectedText !== "" onTriggered: pageView.copySelectionToClipboard() } @@ -246,7 +246,7 @@ ApplicationWindow { RowLayout { ToolButton { action: Action { - icon.source: "../../../../examples/pdf/pdfviewer/resources/go-up-search.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/go-up-search.svg" shortcut: StandardKey.FindPrevious onTriggered: pageView.searchBack() } @@ -261,7 +261,7 @@ ApplicationWindow { Layout.fillWidth: true Image { visible: searchField.text !== "" - source: "../../../../examples/pdf/pdfviewer/resources/edit-clear.svg" + source: "../../../../examples/pdf/singlepage/resources/edit-clear.svg" anchors { right: parent.right top: parent.top @@ -276,7 +276,7 @@ ApplicationWindow { } ToolButton { action: Action { - icon.source: "../../../../examples/pdf/pdfviewer/resources/go-down-search.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/go-down-search.svg" shortcut: StandardKey.FindNext onTriggered: pageView.searchForward() } diff --git a/tests/manual/quick/pdf/pessimizedListView.qml b/tests/manual/quick/pdf/pessimizedListView.qml index 1b514668e..6e1083fd3 100644 --- a/tests/manual/quick/pdf/pessimizedListView.qml +++ b/tests/manual/quick/pdf/pessimizedListView.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtQuick.Controls import QtQuick.Dialogs diff --git a/tests/manual/quick/pdf/simplest.qml b/tests/manual/quick/pdf/simplest.qml index 3f39bb213..0736f38ae 100644 --- a/tests/manual/quick/pdf/simplest.qml +++ b/tests/manual/quick/pdf/simplest.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick Image { diff --git a/tests/manual/quick/pdf/underscoredLinks.qml b/tests/manual/quick/pdf/underscoredLinks.qml index 514008ca2..f23d9a5a1 100644 --- a/tests/manual/quick/pdf/underscoredLinks.qml +++ b/tests/manual/quick/pdf/underscoredLinks.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtQuick.Controls import QtQuick.Dialogs diff --git a/tests/manual/quick/pdf/withdoc.qml b/tests/manual/quick/pdf/withdoc.qml index d4bf12e29..bcd1a9b58 100644 --- a/tests/manual/quick/pdf/withdoc.qml +++ b/tests/manual/quick/pdf/withdoc.qml @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtQuick.Controls import QtQuick.Dialogs @@ -19,6 +19,11 @@ Window { onPasswordRequired: function() { passwordDialog.open() } } + Component.onCompleted: { + if (Application.arguments.length > 2) + doc.source = Application.arguments[Application.arguments.length - 1] + } + FileDialog { id: fileDialog title: "Open a PDF file" @@ -85,6 +90,7 @@ Window { PdfPageImage { id: image document: doc + retainWhileLoading: true property real zoomFactor: Math.sqrt(2) @@ -120,6 +126,10 @@ Window { } } Shortcut { + sequence: StandardKey.SelectAll + onActivated: selection.selectAll() + } + Shortcut { sequence: "Ctrl+0" onActivated: image.sourceSize = undefined } diff --git a/tests/manual/quick/quick.pro b/tests/manual/quick/quick.pro deleted file mode 100644 index 8522763f8..000000000 --- a/tests/manual/quick/quick.pro +++ /dev/null @@ -1,5 +0,0 @@ -TEMPLATE = subdirs - -SUBDIRS += \ - faviconbrowser \ - touchbrowser diff --git a/tests/manual/quick/touchbrowser/AddressBar.qml b/tests/manual/quick/touchbrowser/AddressBar.qml index 6ccea3441..36a83d872 100644 --- a/tests/manual/quick/touchbrowser/AddressBar.qml +++ b/tests/manual/quick/touchbrowser/AddressBar.qml @@ -1,5 +1,5 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtQuick.Controls diff --git a/tests/manual/quick/touchbrowser/CMakeLists.txt b/tests/manual/quick/touchbrowser/CMakeLists.txt index d4064c05e..0d3275e58 100644 --- a/tests/manual/quick/touchbrowser/CMakeLists.txt +++ b/tests/manual/quick/touchbrowser/CMakeLists.txt @@ -2,40 +2,30 @@ # SPDX-License-Identifier: BSD-3-Clause if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) - cmake_minimum_required(VERSION 3.16) + cmake_minimum_required(VERSION 3.19) project(touchbrowser LANGUAGES CXX) find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) endif() -if(NOT CMAKE_CROSSCOMPILING) - add_definitions(-DDESKTOP_BUILD) - set(SOURCES "touchmockingapplication.cpp") -endif() +set(CMAKE_AUTORCC ON) +set(TOUCHMOCKING_DIR "../../touchmocking") + +include_directories(${TOUCHMOCKING_DIR}) +add_definitions(-DQUICK_TOUCHBROWSER) -qt_internal_add_manual_test(touchbrowser +qt_internal_add_manual_test(touchbrowser-quick GUI SOURCES main.cpp - utils.h - ${SOURCES} + resources.qrc + ${TOUCHMOCKING_DIR}/touchmockingapplication.cpp + ${TOUCHMOCKING_DIR}/touchmockingapplication.h + ${TOUCHMOCKING_DIR}/utils.h LIBRARIES - Qt::GuiPrivate + Qt::Core Qt::Quick Qt::WebEngineQuick ENABLE_AUTOGEN_TOOLS moc ) -set(touchbrowser_resource_files - "AddressBar.qml" - "main.qml" - "MockTouchPoint.qml" - "touchpoint.png" -) - -qt_add_resources(touchbrowser "touchbrowser" - PREFIX - "/" - FILES - ${touchbrowser_resource_files} -) diff --git a/tests/manual/quick/touchbrowser/MockTouchPoint.qml b/tests/manual/quick/touchbrowser/MockTouchPoint.qml index bdce0555c..8583d934f 100644 --- a/tests/manual/quick/touchbrowser/MockTouchPoint.qml +++ b/tests/manual/quick/touchbrowser/MockTouchPoint.qml @@ -1,6 +1,5 @@ -// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - import QtQuick Item { diff --git a/tests/manual/quick/touchbrowser/main.cpp b/tests/manual/quick/touchbrowser/main.cpp index b63f3b31c..7db3f40a1 100644 --- a/tests/manual/quick/touchbrowser/main.cpp +++ b/tests/manual/quick/touchbrowser/main.cpp @@ -1,23 +1,21 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only -#if defined(DESKTOP_BUILD) #include "touchmockingapplication.h" -#endif #include "utils.h" -#include <QtGui/QGuiApplication> -#include <QtQml/QQmlApplicationEngine> -#include <QtQml/QQmlContext> -#include <QtQuick/QQuickView> -#include <QtWebEngineQuick/qtwebenginequickglobal.h> +#include <QGuiApplication> +#include <QQmlApplicationEngine> +#include <QQmlContext> +#include <QQuickView> +#include <QtWebEngineQuick> static QUrl startupUrl() { QUrl ret; QStringList args(qApp->arguments()); args.takeFirst(); - for (const QString &arg : qAsConst(args)) { + for (const QString &arg : std::as_const(args)) { if (arg.startsWith(QLatin1Char('-'))) continue; ret = Utils::fromUserInput(arg); @@ -29,31 +27,10 @@ static QUrl startupUrl() int main(int argc, char **argv) { - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); - - // We use touch mocking on desktop and apply all the mobile switches. - QByteArrayList args = QByteArrayList() - << QByteArrayLiteral("--enable-embedded-switches") - << QByteArrayLiteral("--log-level=0"); - const int count = args.size() + argc; - QList<char*> qargv(count); - - qargv[0] = argv[0]; - for (int i = 0; i < args.size(); ++i) - qargv[i + 1] = args[i].data(); - for (int i = args.size() + 1; i < count; ++i) - qargv[i] = argv[i - args.size()]; - - int qAppArgCount = qargv.size(); - QtWebEngineQuick::initialize(); -#if defined(DESKTOP_BUILD) - TouchMockingApplication app(qAppArgCount, qargv.data()); -#else - QGuiApplication app(qAppArgCount, qargv.data()); -#endif + TouchMockingApplication app(argc, argv); + app.setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, true); QQuickView view; Utils utils; diff --git a/tests/manual/quick/touchbrowser/main.qml b/tests/manual/quick/touchbrowser/main.qml index 83ede7d75..ed60b36e5 100644 --- a/tests/manual/quick/touchbrowser/main.qml +++ b/tests/manual/quick/touchbrowser/main.qml @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtQuick.Layouts diff --git a/tests/manual/quick/touchbrowser/qml.qrc b/tests/manual/quick/touchbrowser/resources.qrc index 5c384502e..87d655a27 100644 --- a/tests/manual/quick/touchbrowser/qml.qrc +++ b/tests/manual/quick/touchbrowser/resources.qrc @@ -2,7 +2,6 @@ <qresource prefix="/"> <file>main.qml</file> <file>AddressBar.qml</file> - <file>MockTouchPoint.qml</file> - <file>touchpoint.png</file> + <file alias="touchpoint.png">../../touchmocking/touchpoint.png</file> </qresource> </RCC> diff --git a/tests/manual/quick/touchbrowser/touchbrowser.pro b/tests/manual/quick/touchbrowser/touchbrowser.pro index 92e4e6703..710584df8 100644 --- a/tests/manual/quick/touchbrowser/touchbrowser.pro +++ b/tests/manual/quick/touchbrowser/touchbrowser.pro @@ -1,19 +1,15 @@ TEMPLATE = app -QT += quick webenginequick -CONFIG += c++11 +DEFINES += QUICK_TOUCHBROWSER +QT += core gui quick webenginequick -SOURCES += \ - main.cpp +INCLUDEPATH += ../../touchmocking +SOURCES += \ + main.cpp \ + ../../touchmocking/touchmockingapplication.cpp HEADERS += \ - utils.h - -RESOURCES += qml.qrc + ../../touchmocking/touchmockingapplication.h \ + ../../touchmocking/utils.h -!cross_compile { - DEFINES += DESKTOP_BUILD - SOURCES += touchmockingapplication.cpp - HEADERS += touchmockingapplication.h - QT += gui-private -} +RESOURCES += resources.qrc diff --git a/tests/manual/quick/touchbrowser/touchmockingapplication.cpp b/tests/manual/quick/touchbrowser/touchmockingapplication.cpp deleted file mode 100644 index 4fad86d33..000000000 --- a/tests/manual/quick/touchbrowser/touchmockingapplication.cpp +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "touchmockingapplication.h" - -#include <qpa/qwindowsysteminterface.h> -#include <QtQuick/QQuickItem> -#include <QtQuick/QQuickView> -#include <QInputDevice> - -static inline bool isTouchEvent(const QEvent* event) -{ - switch (event->type()) { - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: - return true; - default: - return false; - } -} - -static inline bool isMouseEvent(const QEvent* event) -{ - switch (event->type()) { - case QEvent::MouseButtonPress: - case QEvent::MouseMove: - case QEvent::MouseButtonRelease: - case QEvent::MouseButtonDblClick: - return true; - default: - return false; - } -} - -TouchMockingApplication::TouchMockingApplication(int& argc, char** argv) - : QGuiApplication(argc, argv) - , m_realTouchEventReceived(false) - , m_pendingFakeTouchEventCount(0) - , m_holdingControl(false) -{ -} - -bool TouchMockingApplication::notify(QObject* target, QEvent* event) -{ - // We try to be smart, if we received real touch event, we are probably on a device - // with touch screen, and we should not have touch mocking. - - if (!event->spontaneous() || m_realTouchEventReceived) - return QGuiApplication::notify(target, event); - - if (isTouchEvent(event)) { - if (m_pendingFakeTouchEventCount) - --m_pendingFakeTouchEventCount; - else - m_realTouchEventReceived = true; - return QGuiApplication::notify(target, event); - } - - QQuickView* window = qobject_cast<QQuickView*>(target); - if (!window) - return QGuiApplication::notify(target, event); - - m_holdingControl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier); - - if (event->type() == QEvent::KeyRelease && static_cast<QKeyEvent*>(event)->key() == Qt::Key_Control) { - foreach (int id, m_heldTouchPoints) - if (m_touchPoints.contains(id) && !QGuiApplication::mouseButtons().testFlag(Qt::MouseButton(id))) { - QMutableEventPoint::setState(m_touchPoints[id], QEventPoint::Released); - m_heldTouchPoints.remove(id); - } else - QMutableEventPoint::setState(m_touchPoints[id], QEventPoint::Stationary); - - sendTouchEvent(window, m_heldTouchPoints.isEmpty() ? QEvent::TouchEnd : QEvent::TouchUpdate, static_cast<QKeyEvent*>(event)->timestamp()); - } - - if (isMouseEvent(event)) { - const QMouseEvent* const mouseEvent = static_cast<QMouseEvent*>(event); - - QEventPoint touchPoint; - QMutableEventPoint::setPressure(touchPoint, 1); - - QEvent::Type touchType = QEvent::None; - - switch (mouseEvent->type()) { - case QEvent::MouseButtonPress: - QMutableEventPoint::setId(touchPoint, mouseEvent->button()); - if (m_touchPoints.contains(touchPoint.id())) { - QMutableEventPoint::setState(touchPoint, QEventPoint::Updated); - touchType = QEvent::TouchUpdate; - } else { - QMutableEventPoint::setState(touchPoint, QEventPoint::Pressed); - // Check if more buttons are held down than just the event triggering one. - if (mouseEvent->buttons() > mouseEvent->button()) - touchType = QEvent::TouchUpdate; - else - touchType = QEvent::TouchBegin; - } - break; - case QEvent::MouseMove: - if (!mouseEvent->buttons()) { - // We have to swallow the event instead of propagating it, - // since we avoid sending the mouse release events and if the - // Flickable is the mouse grabber it would receive the event - // and would move the content. - event->accept(); - return true; - } - touchType = QEvent::TouchUpdate; - QMutableEventPoint::setId(touchPoint, mouseEvent->buttons()); - QMutableEventPoint::setState(touchPoint, QEventPoint::Updated); - break; - case QEvent::MouseButtonRelease: - // Check if any buttons are still held down after this event. - if (mouseEvent->buttons()) - touchType = QEvent::TouchUpdate; - else - touchType = QEvent::TouchEnd; - QMutableEventPoint::setId(touchPoint, mouseEvent->button()); - QMutableEventPoint::setState(touchPoint, QEventPoint::Released); - break; - case QEvent::MouseButtonDblClick: - // Eat double-clicks, their accompanying press event is all we need. - event->accept(); - return true; - default: - Q_ASSERT_X(false, "multi-touch mocking", "unhandled event type"); - } - - // A move can have resulted in multiple buttons, so we need check them individually. - if (touchPoint.id() & Qt::LeftButton) - updateTouchPoint(mouseEvent, touchPoint, Qt::LeftButton); - if (touchPoint.id() & Qt::MiddleButton) - updateTouchPoint(mouseEvent, touchPoint, Qt::MiddleButton); - if (touchPoint.id() & Qt::RightButton) - updateTouchPoint(mouseEvent, touchPoint, Qt::RightButton); - - if (m_holdingControl && touchPoint.state() == QEventPoint::Released) { - // We avoid sending the release event because the Flickable is - // listening to mouse events and would start a bounce-back - // animation if it received a mouse release. - event->accept(); - return true; - } - - // Update states for all other touch-points - for (QHash<int, QEventPoint>::iterator it = m_touchPoints.begin(), end = m_touchPoints.end(); it != end; ++it) { - if (!(it.value().id() & touchPoint.id())) - QMutableEventPoint::setState(it.value(), QEventPoint::Stationary); - } - - Q_ASSERT(touchType != QEvent::None); - - if (!sendTouchEvent(window, touchType, mouseEvent->timestamp())) - return QGuiApplication::notify(target, event); - - event->accept(); - return true; - } - - return QGuiApplication::notify(target, event); -} - -void TouchMockingApplication::updateTouchPoint(const QMouseEvent* mouseEvent, QEventPoint touchPoint, Qt::MouseButton mouseButton) -{ - // Ignore inserting additional touch points if Ctrl isn't held because it produces - // inconsistent touch events and results in assers in the gesture recognizers. - if (!m_holdingControl && m_touchPoints.size() && !m_touchPoints.contains(mouseButton)) - return; - - if (m_holdingControl && touchPoint.state() == QEventPoint::Released) { - m_heldTouchPoints.insert(mouseButton); - return; - } - - // Gesture recognition uses the screen position for the initial threshold - // but since the canvas translates touch events we actually need to pass - // the screen position as the scene position to deliver the appropriate - // coordinates to the target. - QMutableEventPoint::setPosition(touchPoint, mouseEvent->position()); - QMutableEventPoint::setScenePosition(touchPoint, mouseEvent->globalPosition()); - - if (touchPoint.state() == QEventPoint::Pressed) - QMutableEventPoint::setScenePosition(touchPoint, mouseEvent->scenePosition()); - else { - const QEventPoint& oldTouchPoint = m_touchPoints[mouseButton]; - QMutableEventPoint::setGlobalLastPosition(touchPoint, oldTouchPoint.globalPosition()); - } - - // Update current touch-point. - QMutableEventPoint::setId(touchPoint, mouseButton); - m_touchPoints.insert(mouseButton, touchPoint); -} - -bool TouchMockingApplication::sendTouchEvent(QQuickView* window, QEvent::Type type, ulong timestamp) -{ - static QPointingDevice* device = 0; - if (!device) { - device = new QPointingDevice(QStringLiteral("MockTouchDevice"), 1, - QPointingDevice::DeviceType::TouchScreen, - QPointingDevice::PointerType::AllPointerTypes, - QInputDevice::Capability::All, 3, 3, - QString(), QPointingDeviceUniqueId(), window->rootObject()); - - QWindowSystemInterface::registerInputDevice(device); - } - - m_pendingFakeTouchEventCount++; - - const QList<QEventPoint>& currentTouchPoints = m_touchPoints.values(); - QEventPoint::States touchPointStates = QEventPoint::States(); - foreach (const QEventPoint& touchPoint, currentTouchPoints) - touchPointStates |= touchPoint.state(); - - QTouchEvent event(type, device, Qt::NoModifier, currentTouchPoints); - event.setTimestamp(timestamp); - event.setAccepted(false); - - QGuiApplication::notify(window, &event); - - updateVisualMockTouchPoints(window, m_holdingControl ? currentTouchPoints : QList<QEventPoint>()); - - // Get rid of touch-points that are no longer valid - foreach (const QEventPoint& touchPoint, currentTouchPoints) { - if (touchPoint.state() == QEventPoint::Released) - m_touchPoints.remove(touchPoint.id()); - } - - return event.isAccepted(); -} - -void TouchMockingApplication::updateVisualMockTouchPoints(QQuickView* window,const QList<QEventPoint>& touchPoints) -{ - if (touchPoints.isEmpty()) { - // Hide all touch indicator items. - foreach (QQuickItem* item, m_activeMockComponents.values()) - item->setProperty("pressed", false); - - return; - } - - foreach (const QEventPoint& touchPoint, touchPoints) { - QQuickItem* mockTouchPointItem = m_activeMockComponents.value(touchPoint.id()); - - if (!mockTouchPointItem) { - QQmlComponent touchMockPointComponent(window->engine(), QUrl("qrc:///MockTouchPoint.qml")); - mockTouchPointItem = qobject_cast<QQuickItem*>(touchMockPointComponent.create()); - Q_ASSERT(mockTouchPointItem); - m_activeMockComponents.insert(touchPoint.id(), mockTouchPointItem); - mockTouchPointItem->setProperty("pointId", QVariant(touchPoint.id())); - mockTouchPointItem->setParent(window->rootObject()); - mockTouchPointItem->setParentItem(window->rootObject()); - } - - mockTouchPointItem->setX(touchPoint.position().x()); - mockTouchPointItem->setY(touchPoint.position().y()); - mockTouchPointItem->setWidth(touchPoint.ellipseDiameters().width()); - mockTouchPointItem->setHeight(touchPoint.ellipseDiameters().height()); - mockTouchPointItem->setProperty("pressed", QVariant(touchPoint.state() != QEventPoint::Released)); - } -} diff --git a/tests/manual/quick/touchbrowser/touchmockingapplication.h b/tests/manual/quick/touchbrowser/touchmockingapplication.h deleted file mode 100644 index 3264d5a0c..000000000 --- a/tests/manual/quick/touchbrowser/touchmockingapplication.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#ifndef TOUCHMOCKINGAPPLICATION_H -#define TOUCHMOCKINGAPPLICATION_H - -#include <QtGui/QGuiApplication> -#include <QtGui/private/qeventpoint_p.h> -#include <QtGui/QEventPoint> - -#include <private/qevent_p.h> - -QT_BEGIN_NAMESPACE -class QQuickView; -class QQuickItem; -QT_END_NAMESPACE - -class TouchMockingApplication : public QGuiApplication -{ - Q_OBJECT - -public: - TouchMockingApplication(int &argc, char **argv); - - virtual bool notify(QObject *, QEvent *) override; - -private: - void updateTouchPoint(const QMouseEvent *, QEventPoint, Qt::MouseButton); - bool sendTouchEvent(QQuickView *, QEvent::Type, ulong timestamp); - void updateVisualMockTouchPoints(QQuickView *,const QList<QEventPoint> &touchPoints); - -private: - bool m_realTouchEventReceived; - int m_pendingFakeTouchEventCount; - - QHash<int, QEventPoint> m_touchPoints; - QSet<int> m_heldTouchPoints; - QHash<int, QQuickItem*> m_activeMockComponents; - - bool m_holdingControl; -}; - -#endif // TOUCHMOCKINGAPPLICATION_H diff --git a/tests/manual/touchmocking/touchmockingapplication.cpp b/tests/manual/touchmocking/touchmockingapplication.cpp new file mode 100644 index 000000000..d15364662 --- /dev/null +++ b/tests/manual/touchmocking/touchmockingapplication.cpp @@ -0,0 +1,78 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "touchmockingapplication.h" + +#include <QCursor> +#include <QEvent> +#include <QPixmap> + +#if defined(QUICK_TOUCHBROWSER) +# include <QQuickView> +#endif + +#if defined(WIDGET_TOUCHBROWSER) +# include <QMainWindow> +#endif + +static inline bool isMouseEvent(QEvent *event) +{ + switch (event->type()) { + case QEvent::MouseMove: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + return true; + default: + return false; + } +} + +TouchMockingApplication::TouchMockingApplication(int &argc, char **argv) + : Application(argc, argv), m_touchPoint(new QCursor(QPixmap(":touchpoint.png"))) +{ +} + +TouchMockingApplication::~TouchMockingApplication() +{ + delete m_touchPoint; +} + +bool TouchMockingApplication::notify(QObject *target, QEvent *event) +{ + switch (event->type()) { + case QEvent::TouchBegin: + setOverrideCursor(*m_touchPoint); + break; + case QEvent::TouchEnd: + restoreCursor(); + break; + default: + break; + } + +// All mouse events that are not accepted by the application will be translated to touch events +// instead (see Qt::AA_SynthesizeTouchForUnhandledMouseEvents). +#if defined(QUICK_TOUCHBROWSER) + if (isMouseEvent(event) && qobject_cast<QQuickView *>(target)) { + event->ignore(); + return false; + } +#elif defined(WIDGET_TOUCHBROWSER) + // Popups ignore touch evenets so we send MouseEvents directly. + if (isMouseEvent(event)) { + if (activePopupWidget()) { + restoreCursor(); + } else { + event->ignore(); + return false; + } + } +#endif + return Application::notify(target, event); +} + +void TouchMockingApplication::restoreCursor() +{ + while (overrideCursor()) + restoreOverrideCursor(); +} diff --git a/tests/manual/touchmocking/touchmockingapplication.h b/tests/manual/touchmocking/touchmockingapplication.h new file mode 100644 index 000000000..e42159451 --- /dev/null +++ b/tests/manual/touchmocking/touchmockingapplication.h @@ -0,0 +1,35 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef TOUCHMOCKINGAPPLICATION_H +#define TOUCHMOCKINGAPPLICATION_H + +#if defined(QUICK_TOUCHBROWSER) +# include <QGuiApplication> +using Application = QGuiApplication; +#elif defined(WIDGET_TOUCHBROWSER) +# include <QApplication> +using Application = QApplication; +#endif + +QT_BEGIN_NAMESPACE +class QCursor; +QT_END_NAMESPACE + +class TouchMockingApplication : public Application +{ + Q_OBJECT + +public: + TouchMockingApplication(int &argc, char **argv); + ~TouchMockingApplication(); + + virtual bool notify(QObject *, QEvent *) override; + +private: + void restoreCursor(); + + QCursor *m_touchPoint; +}; + +#endif // TOUCHMOCKINGAPPLICATION_H diff --git a/tests/manual/quick/touchbrowser/touchpoint.png b/tests/manual/touchmocking/touchpoint.png Binary files differindex 7649ee991..7649ee991 100644 --- a/tests/manual/quick/touchbrowser/touchpoint.png +++ b/tests/manual/touchmocking/touchpoint.png diff --git a/tests/manual/quick/touchbrowser/utils.h b/tests/manual/touchmocking/utils.h index 605ebf23d..2e6fb1b05 100644 --- a/tests/manual/quick/touchbrowser/utils.h +++ b/tests/manual/touchmocking/utils.h @@ -1,19 +1,20 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef UTILS_H #define UTILS_H -#include <QtCore/QFileInfo> -#include <QtCore/QUrl> +#include <QFileInfo> +#include <QUrl> -class Utils : public QObject { +class Utils : public QObject +{ Q_OBJECT public: - Q_INVOKABLE static QUrl fromUserInput(const QString& userInput); + Q_INVOKABLE static QUrl fromUserInput(const QString &userInput); }; -inline QUrl Utils::fromUserInput(const QString& userInput) +inline QUrl Utils::fromUserInput(const QString &userInput) { QFileInfo fileInfo(userInput); if (fileInfo.exists()) diff --git a/tests/manual/widgets/CMakeLists.txt b/tests/manual/widgets/CMakeLists.txt index 4e619f066..7c19f9e43 100644 --- a/tests/manual/widgets/CMakeLists.txt +++ b/tests/manual/widgets/CMakeLists.txt @@ -1,2 +1,9 @@ add_subdirectory(inputmethods) -add_subdirectory(webgl) +add_subdirectory(geolocation) +add_subdirectory(touchbrowser) +if(QT_FEATURE_opengl) + add_subdirectory(webgl) +endif() +if(TARGET Qt6::HttpServer) + add_subdirectory(webrtc) +endif() diff --git a/tests/manual/widgets/geolocation/CMakeLists.txt b/tests/manual/widgets/geolocation/CMakeLists.txt new file mode 100644 index 000000000..2ca8c2f52 --- /dev/null +++ b/tests/manual/widgets/geolocation/CMakeLists.txt @@ -0,0 +1,55 @@ +# 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(geolocation LANGUAGES CXX) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_manual_test(geolocation + GUI + SOURCES + main.cpp + LIBRARIES + Qt::Core + Qt::Gui + Qt::Test + Qt::WebEngineWidgets + ENABLE_AUTOGEN_TOOLS + moc +) + +set_target_properties(geolocation PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_GUI_IDENTIFIER "io.qt.dev.webenginewidgets.geolocation" +) + +set(geolocation_resource_files + "geolocation.html" +) + +qt_add_resources(geolocation "geolocation" + PREFIX + "/" + FILES + ${geolocation_resource_files} +) + + foreach(permission_plugin IN LISTS QT_ALL_PLUGINS_FOUND_BY_FIND_PACKAGE_permissions) + set(permission_plugin "${QT_CMAKE_EXPORT_NAMESPACE}::${permission_plugin}") + qt6_import_plugins(geolocation INCLUDE ${permission_plugin}) + endforeach() + +if (APPLE) + set_target_properties(geolocation PROPERTIES + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist" + ) + + if (NOT CMAKE_GENERATOR STREQUAL "Xcode") + # Need to sign application for location permissions to work + add_custom_command(TARGET geolocation + POST_BUILD COMMAND codesign -s - geolocation.app) + endif() +endif() diff --git a/tests/manual/widgets/geolocation/Info.plist b/tests/manual/widgets/geolocation/Info.plist new file mode 100644 index 000000000..9853e1900 --- /dev/null +++ b/tests/manual/widgets/geolocation/Info.plist @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleName</key> + <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string> + <key>CFBundleIdentifier</key> + <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string> + <key>CFBundleExecutable</key> + <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string> + <key>CFBundleVersion</key> + <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string> + <key>CFBundleShortVersionString</key> + <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string> + <key>LSMinimumSystemVersion</key> + <string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string> + <key>NSHumanReadableCopyright</key> + <string>${MACOSX_BUNDLE_COPYRIGHT}</string> + <key>CFBundleIconFile</key> + <string>${MACOSX_BUNDLE_ICON_FILE}</string> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>NSSupportsAutomaticGraphicsSwitching</key> + <true/> + <key>NSLocationUsageDescription</key> + <string>Geolocation test would like to give web sites access to your location for demo purposes.</string> +</dict> +</plist> diff --git a/tests/manual/widgets/geolocation/geolocation.html b/tests/manual/widgets/geolocation/geolocation.html new file mode 100644 index 000000000..e8c54bc58 --- /dev/null +++ b/tests/manual/widgets/geolocation/geolocation.html @@ -0,0 +1,32 @@ +<html> +<head> +<title>Geolocation Permission API Test</title> +<script> + +var errorMessage; +var handled = false; + +function successHandler(location) { + var message = document.getElementById("message"); + message.innerHTML = "Latitude: " + location.coords.latitude + + "<br>Longitude: " + location.coords.longitude; + + errorMessage = ""; + handled = true; +} + +function errorHandler(error) { + errorMessage = error.message; + handled = true; +} + +<!-- One shot example --> +navigator.geolocation.getCurrentPosition(successHandler, errorHandler); + +</script> +</head> +<body> +<div id="message">Location unknown</div> +</body> +</html> + diff --git a/tests/manual/widgets/geolocation/main.cpp b/tests/manual/widgets/geolocation/main.cpp new file mode 100644 index 000000000..9e471650b --- /dev/null +++ b/tests/manual/widgets/geolocation/main.cpp @@ -0,0 +1,48 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QApplication> +#include <QFormLayout> +#include <QGroupBox> +#include <QHBoxLayout> +#include <QLabel> +#include <QMainWindow> +#include <QMessageBox> +#include <QVBoxLayout> +#include <QWebEnginePage> +#include <QWebEngineView> + +class GeoPermissionWebView : public QWebEngineView { + Q_OBJECT + +public slots: + void handlePermissionRequested(QWebEnginePermission permission) + { + qWarning("Feature Permission"); + QString title = tr("Permission Request"); + QString question = QLatin1String("Allow access to geolocation?"); + if (!question.isEmpty() && QMessageBox::question(window(), title, question) == QMessageBox::Yes) + permission.grant(); + else + permission.deny(); + } + +}; + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + QMainWindow w; + GeoPermissionWebView webview; + QWebEnginePage page; + QObject::connect(&page, &QWebEnginePage::permissionRequested, &webview, + &GeoPermissionWebView::handlePermissionRequested); + webview.setPage(&page); + page.load(QUrl("qrc:/geolocation.html")); + w.setCentralWidget(&webview); + w.show(); + + return a.exec(); +} + +#include "main.moc" diff --git a/tests/manual/widgets/inputmethods/colorpicker.cpp b/tests/manual/widgets/inputmethods/colorpicker.cpp index cc0840bcd..6ea8e8086 100644 --- a/tests/manual/widgets/inputmethods/colorpicker.cpp +++ b/tests/manual/widgets/inputmethods/colorpicker.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "colorpicker.h" diff --git a/tests/manual/widgets/inputmethods/colorpicker.h b/tests/manual/widgets/inputmethods/colorpicker.h index 171c0186b..719aa93eb 100644 --- a/tests/manual/widgets/inputmethods/colorpicker.h +++ b/tests/manual/widgets/inputmethods/colorpicker.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef COLORPICKER_H #define COLORPICKER_H @@ -7,8 +7,10 @@ #include <QColor> #include <QWidget> +QT_BEGIN_NAMESPACE class QLineEdit; class QPushButton; +QT_END_NAMESPACE class ColorPicker : public QWidget { diff --git a/tests/manual/widgets/inputmethods/controlview.cpp b/tests/manual/widgets/inputmethods/controlview.cpp index 86bf8cca9..85d7cfa0f 100644 --- a/tests/manual/widgets/inputmethods/controlview.cpp +++ b/tests/manual/widgets/inputmethods/controlview.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "controlview.h" diff --git a/tests/manual/widgets/inputmethods/controlview.h b/tests/manual/widgets/inputmethods/controlview.h index f6b3e7fe6..c686adbec 100644 --- a/tests/manual/widgets/inputmethods/controlview.h +++ b/tests/manual/widgets/inputmethods/controlview.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef CONTROLVIEW_H #define CONTROLVIEW_H @@ -8,12 +8,15 @@ #include <QTextCharFormat> #include <QWidget> -class ColorPicker; +QT_BEGIN_NAMESPACE class QComboBox; class QLabel; class QLineEdit; class QPushButton; class QSpinBox; +QT_END_NAMESPACE + +class ColorPicker; class ControlView : public QWidget { diff --git a/tests/manual/widgets/inputmethods/main.cpp b/tests/manual/widgets/inputmethods/main.cpp index 2378e95ae..4bba9f782 100644 --- a/tests/manual/widgets/inputmethods/main.cpp +++ b/tests/manual/widgets/inputmethods/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QApplication> #include <QFormLayout> diff --git a/tests/manual/widgets/inputmethods/referenceview.cpp b/tests/manual/widgets/inputmethods/referenceview.cpp index 27e784fbc..24957eaa9 100644 --- a/tests/manual/widgets/inputmethods/referenceview.cpp +++ b/tests/manual/widgets/inputmethods/referenceview.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "referenceview.h" diff --git a/tests/manual/widgets/inputmethods/referenceview.h b/tests/manual/widgets/inputmethods/referenceview.h index d943a93d0..62c43faeb 100644 --- a/tests/manual/widgets/inputmethods/referenceview.h +++ b/tests/manual/widgets/inputmethods/referenceview.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef REFERENCEVIEW_H #define REFERENCEVIEW_H diff --git a/tests/manual/widgets/inputmethods/testview.cpp b/tests/manual/widgets/inputmethods/testview.cpp index d57b22cc5..e0e84d72b 100644 --- a/tests/manual/widgets/inputmethods/testview.cpp +++ b/tests/manual/widgets/inputmethods/testview.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "testview.h" diff --git a/tests/manual/widgets/inputmethods/testview.h b/tests/manual/widgets/inputmethods/testview.h index feb512cde..5ba894bf3 100644 --- a/tests/manual/widgets/inputmethods/testview.h +++ b/tests/manual/widgets/inputmethods/testview.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TESTVIEW_H #define TESTVIEW_H @@ -7,8 +7,10 @@ #include <QTextCharFormat> #include <QWidget> +QT_BEGIN_NAMESPACE class QPushButton; class QTableView; +QT_END_NAMESPACE class TestView : public QWidget { diff --git a/tests/manual/widgets/inputmethods/webview.cpp b/tests/manual/widgets/inputmethods/webview.cpp index 915d73a7f..febd05049 100644 --- a/tests/manual/widgets/inputmethods/webview.cpp +++ b/tests/manual/widgets/inputmethods/webview.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "webview.h" #include <QWebEngineSettings> diff --git a/tests/manual/widgets/inputmethods/webview.h b/tests/manual/widgets/inputmethods/webview.h index a46dcb2f6..ec5a295d1 100644 --- a/tests/manual/widgets/inputmethods/webview.h +++ b/tests/manual/widgets/inputmethods/webview.h @@ -1,5 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef WEBVIEW_H #define WEBVIEW_H diff --git a/tests/manual/widgets/touchbrowser/CMakeLists.txt b/tests/manual/widgets/touchbrowser/CMakeLists.txt new file mode 100644 index 000000000..de60ad2b4 --- /dev/null +++ b/tests/manual/widgets/touchbrowser/CMakeLists.txt @@ -0,0 +1,30 @@ +# Copyright (C) 2022 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.19) + project(touchbrowser LANGUAGES CXX) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) +endif() + +set(CMAKE_AUTORCC ON) +set(TOUCHMOCKING_DIR "../../touchmocking") + +include_directories(${TOUCHMOCKING_DIR}) +add_definitions(-DWIDGET_TOUCHBROWSER) + +qt_internal_add_manual_test(touchbrowser-widget + GUI + SOURCES + main.cpp + resources.qrc + ${TOUCHMOCKING_DIR}/touchmockingapplication.cpp + ${TOUCHMOCKING_DIR}/touchmockingapplication.h + ${TOUCHMOCKING_DIR}/utils.h + LIBRARIES + Qt::Core + Qt::Gui + Qt::WebEngineWidgets + ENABLE_AUTOGEN_TOOLS + moc +) diff --git a/tests/manual/widgets/touchbrowser/main.cpp b/tests/manual/widgets/touchbrowser/main.cpp new file mode 100644 index 000000000..0aa3e7917 --- /dev/null +++ b/tests/manual/widgets/touchbrowser/main.cpp @@ -0,0 +1,62 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "touchmockingapplication.h" +#include "utils.h" + +#include <QApplication> +#include <QLineEdit> +#include <QMainWindow> +#include <QToolBar> +#include <QWebEngineView> + + +static QUrl startupUrl() +{ + QUrl ret; + QStringList args(qApp->arguments()); + args.takeFirst(); + for (const QString &arg : std::as_const(args)) { + if (arg.startsWith(QLatin1Char('-'))) + continue; + ret = Utils::fromUserInput(arg); + if (ret.isValid()) + return ret; + } + return QUrl(QStringLiteral("https://www.qt.io/")); +} + +int main(int argc, char **argv) +{ + TouchMockingApplication app(argc, argv); + app.setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, true); + + QMainWindow window; + QWebEngineView view(&window); + QToolBar addressBar("AddressBar", &window); + QLineEdit lineEdit(&addressBar); + + view.setAttribute(Qt::WA_AcceptTouchEvents, true); + view.setUrl(startupUrl()); + window.resize(1024, 750); + window.setCentralWidget(&view); + + addressBar.setAttribute(Qt::WA_AcceptTouchEvents, true); + addressBar.setMovable(false); + addressBar.toggleViewAction()->setEnabled(false); + + lineEdit.setAttribute(Qt::WA_AcceptTouchEvents, true); + lineEdit.setClearButtonEnabled(true); + + addressBar.addWidget(&lineEdit); + QObject::connect(&lineEdit, &QLineEdit::returnPressed, [&]() { + QUrl url = Utils::fromUserInput(lineEdit.text()); + lineEdit.setText(url.toDisplayString()); + view.setUrl(url); + }); + + window.addToolBar(&addressBar); + window.show(); + + return app.exec(); +} diff --git a/tests/manual/widgets/touchbrowser/resources.qrc b/tests/manual/widgets/touchbrowser/resources.qrc new file mode 100644 index 000000000..b621823ea --- /dev/null +++ b/tests/manual/widgets/touchbrowser/resources.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file alias="touchpoint.png">../../touchmocking/touchpoint.png</file> + </qresource> +</RCC> diff --git a/tests/manual/widgets/touchbrowser/touchbrowser.pro b/tests/manual/widgets/touchbrowser/touchbrowser.pro new file mode 100644 index 000000000..1587f390a --- /dev/null +++ b/tests/manual/widgets/touchbrowser/touchbrowser.pro @@ -0,0 +1,15 @@ +TEMPLATE = app + +DEFINES += WIDGET_TOUCHBROWSER +QT += core gui webenginewidgets + +INCLUDEPATH += ../../touchmocking + +SOURCES += \ + main.cpp \ + ../../touchmocking/touchmockingapplication.cpp +HEADERS += \ + ../../touchmocking/touchmockingapplication.h \ + ../../touchmocking/utils.h + +RESOURCES += resources.qrc diff --git a/tests/manual/widgets/webgl/main.cpp b/tests/manual/widgets/webgl/main.cpp index 7037c34db..e9b4c285a 100644 --- a/tests/manual/widgets/webgl/main.cpp +++ b/tests/manual/widgets/webgl/main.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/QDebug> #include <QtCore/QLoggingCategory> diff --git a/tests/manual/widgets/webrtc/CMakeLists.txt b/tests/manual/widgets/webrtc/CMakeLists.txt new file mode 100644 index 000000000..3f98f1fd6 --- /dev/null +++ b/tests/manual/widgets/webrtc/CMakeLists.txt @@ -0,0 +1,24 @@ +# 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.19) + project(webrtc LANGUAGES CXX) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) +endif() + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +qt_internal_add_manual_test(webrtc + GUI + SOURCES + main.cpp + mediaPicker.ui + qrc.qrc + LIBRARIES + Qt::Core + Qt::HttpServer + Qt::Gui + Qt::WebEngineWidgets +) diff --git a/tests/manual/widgets/webrtc/index.html b/tests/manual/widgets/webrtc/index.html new file mode 100644 index 000000000..433d643c3 --- /dev/null +++ b/tests/manual/widgets/webrtc/index.html @@ -0,0 +1,86 @@ +<!doctype html> +<html> + <head> + <style> + body { + display: flex; + justify-content: center; + align-items: center; + flex-wrap: wrap; + flex-flow: column; + } + buttons { + justify-content: space-around; + } + </style> + </head> + <body> + <div id="buttons" > + <input value ="getDisplayMedia" onclick="getDisplayMedia(true, true);" type="button"> + <input value = "chooseDesktopMedia" onclick="chooseDesktopMedia();" type="button"> + <input value ="Stop" onclick="stop();" type="button"> + </div> + <div id="content"></div> + </body> + <script> + const EXTENSION_ID = "nkeimhogjdpnpccoofpliimaahmaaome"; // hangout services extension + const content = document.getElementById("content"); + const video = document.createElement("video"); + video.setAttribute("width", 640); + video.setAttribute("height", 640); + video.setAttribute("style", "background-color: black;"); + content.appendChild(video); + + async function getDisplayMedia(v = true, a = true) { + stop(); + navigator.mediaDevices.getDisplayMedia({ video: v, audio: a }) + .then(stream => { + start(stream); + }, error => { + console.error(error); + }); + } + + function chooseDesktopMedia() { + stop(); + // Connect to the 'chooseDesktopMedia' listener within the hangout services extension. + let port = chrome.runtime.connect(EXTENSION_ID, {name: "chooseDesktopMedia"}) + + // The 'chooseDesktopMedia' api returns a streamId that + // identifies a media source in the constraints of 'getUserMedia' + // (see chromeMediaSourceId) + port.onMessage.addListener(result => { + navigator.mediaDevices.getUserMedia({ + video: { + mandatory: { + chromeMediaSource: "desktop", + chromeMediaSourceId: result.value.streamId + }, + } + }).then(stream => { + start(stream); + }, error => { + console.error(error); + }) + }) + + // Trigger the listener on the other side, + // we should see the picker dialog after this call. + port.postMessage({method: "chooseDesktopMedia"}) + } + + function stop() { + if (video.srcObject) + for (const track of video.srcObject.getTracks()) + track.stop() + video.srcObject = null; + video.setAttribute("style", "background-color: black;"); + } + + function start(stream) { + video.srcObject = stream; + video.play(); + } + + </script> +</html> diff --git a/tests/manual/widgets/webrtc/main.cpp b/tests/manual/widgets/webrtc/main.cpp new file mode 100644 index 000000000..c1742445a --- /dev/null +++ b/tests/manual/widgets/webrtc/main.cpp @@ -0,0 +1,112 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QApplication> +#include <QByteArray> +#include <QDialog> +#include <QFile> +#include <QHttpServer> +#include <QListView> +#include <QMessageBox> +#include <QWebEnginePage> +#include <QWebEngineProfile> +#include <QWebEngineSettings> +#include <QWebEngineView> + +#include "ui_mediaPicker.h" +#include <QWebEngineDesktopMediaRequest> + +// Test the screen/window selection and capturing APIs using QWebEngineDesktopMediaRequest, +// getDisplayMedia (js) and chooseDesktopMedia (hangouts) + +// Note: Wayland compositors require Pipewire support in QWE + +class Page : public QWebEnginePage +{ + Q_OBJECT + +public: + Page(QWebEngineProfile *profile, QObject *parent = nullptr); +private slots: + void handlePermissionRequest(const QUrl &origin, Feature feature); + void handleDesktopMediaRequest(const QWebEngineDesktopMediaRequest &request); +}; + +Page::Page(QWebEngineProfile *profile, QObject *parent) : QWebEnginePage(profile, parent) +{ + settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true); + connect(this, &QWebEnginePage::permissionRequested, this, + &Page::handlePermissionRequest); + connect(this, &QWebEnginePage::desktopMediaRequested, this, &Page::handleDesktopMediaRequest); +} + +void Page::handlePermissionRequest(QWebEnginePermission permission) +{ + if (QMessageBox::question(QApplication::activeWindow(), tr("Permission request"), + tr("allow access?")) + == QMessageBox::Yes) + permission.grant(); + else + permission.deny(); +} + +void Page::handleDesktopMediaRequest(const QWebEngineDesktopMediaRequest &request) +{ + Ui::MediaPickerDialog mediaPickerDialog; + QDialog dialog; + dialog.setModal(true); + mediaPickerDialog.setupUi(&dialog); + + auto *screensView = mediaPickerDialog.screensView; + auto *windowsView = mediaPickerDialog.windowsView; + auto *screensModel = request.screensModel(); + auto *windowsModel = request.windowsModel(); + + screensView->setModel(screensModel); + windowsView->setModel(windowsModel); + + if (dialog.exec() == QDialog::Accepted) { + if (mediaPickerDialog.tabWidget->currentIndex() == 0) + request.selectWindow(windowsView->selectionModel()->selectedIndexes().first()); + else + request.selectScreen(screensView->selectionModel()->selectedIndexes().first()); + } else { + request.cancel(); + } +} + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + QHttpServer server; + + QFile file(":index.html"); + + if (!file.open(QIODeviceBase::ReadOnly)) { + qWarning("failed to open file!"); + return 0; + } + + QByteArray data = file.readAll(); + if (data.isEmpty()) { + qWarning("failed to read file!"); + return 0; + } + + server.route("/index.html", [data]() { + return data; + }); + + server.listen(QHostAddress::Any, 3000); + + QWebEngineView view; + Page *page = new Page(QWebEngineProfile::defaultProfile(), &view); + view.setPage(page); + view.resize(1024, 750); + view.setUrl(QUrl("http://localhost:3000/index.html")); + view.show(); + return app.exec(); +} + +#include "main.moc" diff --git a/tests/manual/widgets/webrtc/mediaPicker.ui b/tests/manual/widgets/webrtc/mediaPicker.ui new file mode 100644 index 000000000..8bfab3f9b --- /dev/null +++ b/tests/manual/widgets/webrtc/mediaPicker.ui @@ -0,0 +1,118 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MediaPickerDialog</class> + <widget class="QDialog" name="MediaPickerDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>500</width> + <height>400</height> + </rect> + </property> + <property name="windowTitle"> + <string>Choose what to share</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QTabWidget" name="tabWidget"> + <property name="styleSheet"> + <string notr="true"/> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="windows"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="contextMenuPolicy"> + <enum>Qt::NoContextMenu</enum> + </property> + <property name="styleSheet"> + <string notr="true"/> + </property> + <attribute name="title"> + <string>Windows</string> + </attribute> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QListView" name="windowsView"/> + </item> + </layout> + </widget> + <widget class="QWidget" name="screens"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="contextMenuPolicy"> + <enum>Qt::NoContextMenu</enum> + </property> + <attribute name="title"> + <string>Screens</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QListView" name="screensView"/> + </item> + </layout> + </widget> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>MediaPickerDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>MediaPickerDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tests/manual/widgets/webrtc/qrc.qrc b/tests/manual/widgets/webrtc/qrc.qrc new file mode 100644 index 000000000..c3322b454 --- /dev/null +++ b/tests/manual/widgets/webrtc/qrc.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>index.html</file> + </qresource> +</RCC> diff --git a/tests/manual/widgets/widgets.pro b/tests/manual/widgets/widgets.pro deleted file mode 100644 index 34e88f0e3..000000000 --- a/tests/manual/widgets/widgets.pro +++ /dev/null @@ -1,5 +0,0 @@ -TEMPLATE= subdirs - -SUBDIRS += \ - inputmethods \ - webgl diff --git a/tests/quicktestbrowser/ApplicationRoot.qml b/tests/quicktestbrowser/ApplicationRoot.qml deleted file mode 100644 index 74e913ba5..000000000 --- a/tests/quicktestbrowser/ApplicationRoot.qml +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.1 -import QtWebEngine 1.1 - -QtObject { - id: root - - property bool thirdPartyCookiesEnabled: true - - property QtObject testProfile: WebEngineProfile { - storageName: "Test" - } - - property QtObject otrProfile: WebEngineProfile { - offTheRecord: true - } - - property Component browserWindowComponent: BrowserWindow { - applicationRoot: root - onClosing: destroy() - } - property Component browserDialogComponent: BrowserDialog { - onClosing: destroy() - } - function createWindow(profile) { - var newWindow = browserWindowComponent.createObject(root) - newWindow.currentWebView.profile = profile - profile.downloadRequested.connect(newWindow.onDownloadRequested) - profile.presentNotification.connect(newWindow.onPresentNotification) - return newWindow - } - function createDialog(profile) { - var newDialog = browserDialogComponent.createObject(root) - newDialog.currentWebView.profile = profile - return newDialog - } - function load(url) { - var browserWindow = createWindow(testProfile) - browserWindow.currentWebView.url = url - } -} diff --git a/tests/quicktestbrowser/BrowserDialog.qml b/tests/quicktestbrowser/BrowserDialog.qml deleted file mode 100644 index a896223dc..000000000 --- a/tests/quicktestbrowser/BrowserDialog.qml +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.1 -import QtQuick.Window 2.2 -import QtWebEngine 1.1 - -Window { - property alias currentWebView: webView - flags: Qt.Dialog - width: 800 - height: 600 - visible: true - onClosing: destroy() - WebEngineView { - id: webView - anchors.fill: parent - } -} diff --git a/tests/quicktestbrowser/BrowserWindow.qml b/tests/quicktestbrowser/BrowserWindow.qml deleted file mode 100644 index 699e877a9..000000000 --- a/tests/quicktestbrowser/BrowserWindow.qml +++ /dev/null @@ -1,507 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.1 -import QtWebEngine 1.2 - -import QtQuick.Controls 1.0 -import QtQuick.Controls.Styles 1.0 -import QtQuick.Layouts 1.0 -import QtQuick.Window 2.1 -import QtQuick.Controls.Private 1.0 -import Qt.labs.settings 1.0 -import QtQuick.Dialogs 1.2 - -ApplicationWindow { - id: browserWindow - property QtObject applicationRoot - property Item currentWebView: tabs.currentIndex < tabs.count ? tabs.getTab(tabs.currentIndex).item.webView : null - property int previousVisibility: Window.Windowed - - property bool isFullScreen: visibility == Window.FullScreen - onIsFullScreenChanged: { - // This is for the case where the system forces us to leave fullscreen. - if (currentWebView && !isFullScreen) { - currentWebView.state = "" - if (currentWebView.isFullScreen) { - currentWebView.fullScreenCancelled() - fullScreenNotification.hide() - } - } - } - - height: 600 - width: 800 - visible: true - title: currentWebView && currentWebView.title - - Settings { - id : appSettings - property alias autoLoadImages: loadImages.checked; - property alias javaScriptEnabled: javaScriptEnabled.checked; - property alias errorPageEnabled: errorPageEnabled.checked; - property alias pluginsEnabled: pluginsEnabled.checked; - property alias thirdPartyCookiesEnabled: thirdPartyCookiesEnabled.checked; - } - - // Make sure the Qt.WindowFullscreenButtonHint is set on OS X. - Component.onCompleted: flags = flags | Qt.WindowFullscreenButtonHint - - // Create a styleItem to determine the platform. - // When using style "mac", ToolButtons are not supposed to accept focus. - StyleItem { id: styleItem } - property bool platformIsMac: styleItem.style == "mac" - - Action { - shortcut: "Ctrl+D" - onTriggered: { - downloadView.visible = !downloadView.visible - } - } - - Action { - id: focus - shortcut: "Ctrl+L" - onTriggered: { - addressBar.forceActiveFocus(); - addressBar.selectAll(); - } - } - Action { - shortcut: "Ctrl+R" - onTriggered: { - if (currentWebView) - currentWebView.reload() - } - } - Action { - shortcut: "Ctrl+T" - onTriggered: { - tabs.createEmptyTab(currentWebView.profile) - tabs.currentIndex = tabs.count - 1 - addressBar.forceActiveFocus(); - addressBar.selectAll(); - } - } - Action { - shortcut: "Ctrl+W" - onTriggered: { - if (tabs.count == 1) - browserWindow.close() - else - tabs.removeTab(tabs.currentIndex) - } - } - - Action { - shortcut: "Escape" - onTriggered: { - if (browserWindow.isFullScreen) - browserWindow.visibility = browserWindow.previousVisibility - } - } - Action { - shortcut: "Ctrl+0" - onTriggered: zoomController.reset() - } - Action { - shortcut: "Ctrl+-" - onTriggered: zoomController.zoomOut() - } - Action { - shortcut: "Ctrl+=" - onTriggered: zoomController.zoomIn() - } - - Menu { - id: backHistoryMenu - - Instantiator { - model: currentWebView && currentWebView.history.backItems - MenuItem { - text: model.title - onTriggered: currentWebView.goBackOrForward(model.offset) - } - - onObjectAdded: backHistoryMenu.insertItem(index, object) - onObjectRemoved: backHistoryMenu.removeItem(object) - } - } - - Menu { - id: forwardHistoryMenu - - Instantiator { - model: currentWebView && currentWebView.history.forwardItems - MenuItem { - text: model.title - onTriggered: currentWebView.goBackOrForward(model.offset) - } - - onObjectAdded: forwardHistoryMenu.insertItem(index, object) - onObjectRemoved: forwardHistoryMenu.removeItem(object) - } - } - - toolBar: ToolBar { - id: navigationBar - RowLayout { - anchors.fill: parent; - ButtonWithMenu { - id: backButton - iconSource: "icons/go-previous.png" - enabled: currentWebView && currentWebView.canGoBack - activeFocusOnTab: !browserWindow.platformIsMac - onClicked: currentWebView.goBack() - longPressMenu: backHistoryMenu - } - ButtonWithMenu { - id: forwardButton - iconSource: "icons/go-next.png" - enabled: currentWebView && currentWebView.canGoForward - activeFocusOnTab: !browserWindow.platformIsMac - onClicked: currentWebView.goForward() - longPressMenu: forwardHistoryMenu - } - ToolButton { - id: reloadButton - iconSource: currentWebView && currentWebView.loading ? "icons/process-stop.png" : "icons/view-refresh.png" - onClicked: currentWebView && currentWebView.loading ? currentWebView.stop() : currentWebView.reload() - activeFocusOnTab: !browserWindow.platformIsMac - } - TextField { - id: addressBar - Image { - anchors.verticalCenter: addressBar.verticalCenter; - x: 5 - z: 2 - id: faviconImage - width: 16; height: 16 - source: currentWebView && currentWebView.icon - } - style: TextFieldStyle { - padding { - left: 26; - } - } - focus: true - Layout.fillWidth: true - text: currentWebView && currentWebView.url - onAccepted: currentWebView.url = utils.fromUserInput(text) - } - ToolButton { - id: settingsMenuButton - menu: Menu { - MenuItem { - id: loadImages - text: "Autoload images" - checkable: true - checked: true - } - MenuItem { - id: javaScriptEnabled - text: "JavaScript On" - checkable: true - checked: true - } - MenuItem { - id: errorPageEnabled - text: "ErrorPage On" - checkable: true - checked: true - } - MenuItem { - id: pluginsEnabled - text: "Plugins On" - checkable: true - checked: true - } - MenuItem { - id: thirdPartyCookiesEnabled - text: "Third party cookies enabled" - checkable: true - checked: true - onToggled: applicationRoot.thirdPartyCookiesEnabled = checked - } - MenuItem { - id: offTheRecordEnabled - text: "Off The Record" - checkable: true - checked: currentWebView.profile.offTheRecord - onToggled: currentWebView.profile = checked ? otrProfile : testProfile; - } - MenuItem { - id: httpDiskCacheEnabled - text: "HTTP Disk Cache" - checkable: !currentWebView.profile.offTheRecord - checked: (currentWebView.profile.httpCacheType == WebEngineProfile.DiskHttpCache) - onToggled: currentWebView.profile.httpCacheType = checked ? WebEngineProfile.DiskHttpCache : WebEngineProfile.MemoryHttpCache; - } - } - } - } - ProgressBar { - id: progressBar - height: 3 - anchors { - left: parent.left - top: parent.bottom - right: parent.right - leftMargin: -parent.leftMargin - rightMargin: -parent.rightMargin - } - style: ProgressBarStyle { - background: Item {} - } - z: -2; - minimumValue: 0 - maximumValue: 100 - value: (currentWebView && currentWebView.loadProgress < 100) ? currentWebView.loadProgress : 0 - } - } - - TabView { - id: tabs - function createEmptyTab(profile) { - var tab = addTab("", tabComponent) - // We must do this first to make sure that tab.active gets set so that tab.item gets instantiated immediately. - tab.active = true - tab.title = Qt.binding(function() { return tab.item.title }) - tab.item.webView.profile = profile - return tab - } - - anchors.fill: parent - Component.onCompleted: createEmptyTab(testProfile) - - Component { - id: tabComponent - Item { - property alias webView: webEngineView - property alias title: webEngineView.title - Action { - shortcut: "Ctrl+F" - onTriggered: { - findBar.visible = !findBar.visible - if (findBar.visible) { - findTextField.forceActiveFocus() - } - } - } - FeaturePermissionBar { - id: permBar - view: webEngineView - anchors { - left: parent.left - right: parent.right - top: parent.top - } - z: 3 - } - - WebEngineView { - id: webEngineView - - anchors { - fill: parent - top: permBar.bottom - } - - focus: true - - states: [ - State { - name: "FullScreen" - PropertyChanges { - target: tabs - frameVisible: false - tabsVisible: false - } - PropertyChanges { - target: navigationBar - visible: false - } - } - ] - settings.autoLoadImages: appSettings.autoLoadImages - settings.javascriptEnabled: appSettings.javaScriptEnabled - settings.errorPageEnabled: appSettings.errorPageEnabled - settings.pluginsEnabled: appSettings.pluginsEnabled - - onCertificateError: { - if (!acceptedCertificates.shouldAutoAccept(error)){ - error.defer() - sslDialog.enqueue(error) - } else{ - error.ignoreCertificateError() - } - } - - onNewWindowRequested: { - if (!request.userInitiated) - print("Warning: Blocked a popup window.") - else if (request.destination == WebEngineView.NewViewInTab) { - var tab = tabs.createEmptyTab(currentWebView.profile) - tabs.currentIndex = tabs.count - 1 - request.openIn(tab.item.webView) - } else if (request.destination == WebEngineView.NewViewInBackgroundTab) { - var tab = tabs.createEmptyTab(currentWebView.profile) - request.openIn(tab.item.webView) - } else if (request.destination == WebEngineView.NewViewInDialog) { - var dialog = applicationRoot.createDialog(currentWebView.profile) - request.openIn(dialog.currentWebView) - } else { - var window = applicationRoot.createWindow(currentWebView.profile) - request.openIn(window.currentWebView) - } - } - - onFullScreenRequested: { - if (request.toggleOn) { - webEngineView.state = "FullScreen" - browserWindow.previousVisibility = browserWindow.visibility - browserWindow.showFullScreen() - fullScreenNotification.show() - } else { - webEngineView.state = "" - browserWindow.visibility = browserWindow.previousVisibility - fullScreenNotification.hide() - } - request.accept() - } - - onFeaturePermissionRequested: { - permBar.securityOrigin = securityOrigin; - permBar.requestedFeature = feature; - permBar.visible = true; - } - } - - Rectangle { - id: findBar - anchors.top: webEngineView.top - anchors.right: webEngineView.right - width: 240 - height: 35 - border.color: "lightgray" - border.width: 1 - radius: 5 - visible: false - color: browserWindow.color - - RowLayout { - anchors.centerIn: findBar - TextField { - id: findTextField - onAccepted: { - webEngineView.findText(text) - } - } - ToolButton { - id: findBackwardButton - iconSource: "icons/go-previous.png" - onClicked: webEngineView.findText(findTextField.text, WebEngineView.FindBackward) - } - ToolButton { - id: findForwardButton - iconSource: "icons/go-next.png" - onClicked: webEngineView.findText(findTextField.text) - } - ToolButton { - id: findCancelButton - iconSource: "icons/process-stop.png" - onClicked: findBar.visible = false - } - } - } - } - } - } - - QtObject{ - id:acceptedCertificates - - property var acceptedUrls : [] - - function shouldAutoAccept(certificateError){ - var domain = utils.domainFromString(certificateError.url) - return acceptedUrls.indexOf(domain) >= 0 - } - } - - MessageDialog { - id: sslDialog - - property var certErrors: [] - icon: StandardIcon.Warning - standardButtons: StandardButton.No | StandardButton.Yes - title: "Server's certificate not trusted" - text: "Do you wish to continue?" - detailedText: "If you wish so, you may continue with an unverified certificate. " + - "Accepting an unverified certificate means " + - "you may not be connected with the host you tried to connect to.\n" + - "Do you wish to override the security check and continue?" - onYes: { - var cert = certErrors.shift() - var domain = utils.domainFromString(cert.url) - acceptedCertificates.acceptedUrls.push(domain) - cert.ignoreCertificateError() - presentError() - } - onNo: reject() - onRejected: reject() - - function reject(){ - certErrors.shift().rejectCertificate() - presentError() - } - function enqueue(error){ - certErrors.push(error) - presentError() - } - function presentError(){ - visible = certErrors.length > 0 - } - } - - FullScreenNotification { - id: fullScreenNotification - } - - DownloadView { - id: downloadView - visible: false - anchors.fill: parent - } - - function onDownloadRequested(download) { - downloadView.visible = true - downloadView.append(download) - download.accept() - } - - MessageDialog { - id: notificationDialog - width: 200 - standardButtons: StandardButton.Ok - } - - function onPresentNotification(notification) { - notificationDialog.title = notification.title - notificationDialog.text = notification.origin.toString() + '\n' + notification.message - notificationDialog.open() - } - - ZoomController { - id: zoomController - y: parent.mapFromItem(currentWebView, 0 , 0).y - 4 - anchors.right: parent.right - width: (parent.width > 800) ? parent.width * 0.25 : 220 - anchors.rightMargin: (parent.width > 400) ? 100 : 0 - } - Binding { - target: currentWebView - property: "zoomFactor" - value: zoomController.zoomFactor - } -} diff --git a/tests/quicktestbrowser/ButtonWithMenu.qml b/tests/quicktestbrowser/ButtonWithMenu.qml deleted file mode 100644 index 56a4d3533..000000000 --- a/tests/quicktestbrowser/ButtonWithMenu.qml +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.3 -import QtQuick.Controls 1.2 - -// The QtQuick controls guys are slackers, so we need to make our own stuff - -ToolButton { - id: root - property Menu longPressMenu - function showMenu() { - longPressMenu.__popup(Qt.rect(0, root.height, 0, 0), 0) - } - - Binding { - target: longPressMenu - property: "__visualItem" - value: root - } - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked: { - if (mouse.button == Qt.RightButton) - showMenu() - else - root.clicked() - } - onPressAndHold: showMenu() - } -} diff --git a/tests/quicktestbrowser/DownloadView.qml b/tests/quicktestbrowser/DownloadView.qml deleted file mode 100644 index f4834edad..000000000 --- a/tests/quicktestbrowser/DownloadView.qml +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.1 -import QtQuick.Controls 1.0 -import QtQuick.Controls.Styles 1.0 -import QtWebEngine 1.0 -import QtQuick.Layouts 1.0 - -Rectangle { - id: downloadView - color: "lightgray" - - ListModel { - id: downloadModel - property var downloads: [] - } - - function append(download) { - downloadModel.append(download) - downloadModel.downloads.push(download) - } - - Component { - id: downloadItemDelegate - - Rectangle { - width: listView.width - height: childrenRect.height - anchors.margins: 10 - radius: 3 - color: "transparent" - border.color: "black" - Rectangle { - id: progressBar - - property real progress: downloadModel.downloads[index] - ? downloadModel.downloads[index].receivedBytes / downloadModel.downloads[index].totalBytes : 0 - - radius: 3 - color: width == listView.width ? "green" : "#2b74c7" - width: listView.width * progress - height: cancelButton.height - - Behavior on width { - SmoothedAnimation { duration: 100 } - } - } - Rectangle { - anchors { - left: parent.left - right: parent.right - leftMargin: 20 - } - Label { - id: label - text: path - anchors { - verticalCenter: cancelButton.verticalCenter - left: parent.left - right: cancelButton.left - } - } - Button { - id: cancelButton - anchors.right: parent.right - iconSource: "icons/process-stop.png" - onClicked: { - var download = downloadModel.downloads[index] - - download.cancel() - - downloadModel.downloads = downloadModel.downloads.filter(function (el) { - return el.id !== download.id; - }); - downloadModel.remove(index) - } - } - } - } - - } - ListView { - id: listView - anchors { - topMargin: 10 - top: parent.top - bottom: parent.bottom - horizontalCenter: parent.horizontalCenter - } - width: parent.width - 20 - spacing: 5 - - model: downloadModel - delegate: downloadItemDelegate - - Text { - visible: !listView.count - horizontalAlignment: Text.AlignHCenter - height: 30 - anchors { - top: parent.top - left: parent.left - right: parent.right - } - font.pixelSize: 20 - text: "No active downloads." - } - - Rectangle { - color: "gray" - anchors { - bottom: parent.bottom - left: parent.left - right: parent.right - } - height: 30 - Button { - id: okButton - text: "OK" - anchors.centerIn: parent - onClicked: { - downloadView.visible = false - } - } - } - } -} diff --git a/tests/quicktestbrowser/FeaturePermissionBar.qml b/tests/quicktestbrowser/FeaturePermissionBar.qml deleted file mode 100644 index ca75875f5..000000000 --- a/tests/quicktestbrowser/FeaturePermissionBar.qml +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.1 -import QtQuick.Controls 1.0 -import QtWebEngine 1.1 -import QtQuick.Layouts 1.0 - -Rectangle { - property var requestedFeature; - property url securityOrigin; - property WebEngineView view; - - id: permissionBar - visible: false - height: acceptButton.height + 4 - - - function textForFeature(feature) { - switch (feature) { - case WebEngineView.Geolocation: return 'Allow %1 to access your location information?' - case WebEngineView.MediaAudioCapture: return 'Allow %1 to access your microphone?' - case WebEngineView.MediaVideoCapture: return 'Allow %1 to access your webcam?' - case WebEngineView.MediaAudioVideoCapture: return 'Allow %1 to access your microphone and webcam?' - case WebEngineView.DesktopVideoCapture: return 'Allow %1 to capture video of your desktop?' - case WebEngineView.DesktopAudioVideoCapture: return 'Allow %1 to capture audio and video of your desktop?' - case WebEngineView.Notifications: return 'Allow %1 to show notification on your desktop?' - default: break - } - return 'Grant permission for %1 to unknown or unsupported feature [' + feature + ']?' - } - - onRequestedFeatureChanged: { - message.text = textForFeature(requestedFeature).arg(securityOrigin); - } - - RowLayout { - anchors { - fill: permissionBar - leftMargin: 5 - rightMargin: 5 - } - Label { - id: message - Layout.fillWidth: true - } - - Button { - id: acceptButton - text: "Accept" - Layout.alignment: Qt.AlignRight - onClicked: { - view.grantFeaturePermission(securityOrigin, requestedFeature, true); - permissionBar.visible = false; - } - } - - Button { - text: "Deny" - Layout.alignment: Qt.AlignRight - onClicked: { - view.grantFeaturePermission(securityOrigin, requestedFeature, false); - permissionBar.visible = false - } - } - } -} diff --git a/tests/quicktestbrowser/FullScreenNotification.qml b/tests/quicktestbrowser/FullScreenNotification.qml deleted file mode 100644 index e48d6c646..000000000 --- a/tests/quicktestbrowser/FullScreenNotification.qml +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.5 - -Rectangle { - id: fullScreenNotification - width: 500 - height: 40 - color: "white" - radius: 7 - - visible: false - opacity: 0 - - function show() { - visible = true - opacity = 1 - reset.start() - } - - function hide() { - reset.stop() - opacity = 0 - } - - Behavior on opacity { - NumberAnimation { - duration: 750 - onStopped: { - if (opacity == 0) - visible = false - } - } - } - - Timer { - id: reset - interval: 5000 - onTriggered: hide() - } - - anchors.horizontalCenter: parent.horizontalCenter - y: 125 - - Text { - id: message - width: parent.width - - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - - wrapMode: Text.WordWrap - elide: Text.ElideNone - clip: true - - text: qsTr("You are now in fullscreen mode. Press ESC to quit!") - } -} diff --git a/tests/quicktestbrowser/ZoomController.qml b/tests/quicktestbrowser/ZoomController.qml deleted file mode 100644 index 2122c4365..000000000 --- a/tests/quicktestbrowser/ZoomController.qml +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick 2.3 -import QtQuick.Controls 1.2 -import QtQuick.Layouts 1.1 - -Rectangle { - property alias zoomFactor: slider.value ; - function zoomIn() { - visible = true - visibilityTimer.restart() - zoomFactor = zoomFactor + 0.25; - } - function zoomOut() { - visible = true - visibilityTimer.restart() - zoomFactor = zoomFactor - 0.25; - } - function reset() { zoomFactor = 1.0 } - - width: 220 - height: 30 - color: palette.window - visible: false - radius: 4 - - SystemPalette { - id: palette - } - Timer { - id: visibilityTimer - interval: 3000 - repeat: false - onTriggered: zoomController.visible = false - } - - RowLayout { - anchors.margins: 4 - anchors.fill: parent - ToolButton { - id: plusButton - text: '+' - onClicked: zoomIn() - } - ToolButton { - text: '\u2014' - id: minusButton - onClicked: zoomOut() - } - Slider { - id: slider - maximumValue: 5.0 - minimumValue: 0.25 - Layout.fillWidth: true; - stepSize: 0.05 - value: 1 - onValueChanged: visibilityTimer.restart() - } - Button { - text: "Reset" - onClicked: reset() - } - } -} diff --git a/tests/quicktestbrowser/main.cpp b/tests/quicktestbrowser/main.cpp deleted file mode 100644 index 2d4fa544e..000000000 --- a/tests/quicktestbrowser/main.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "utils.h" - -#ifndef QT_NO_WIDGETS -#include <QtWidgets/QApplication> -typedef QApplication Application; -#else -#include <QtGui/QGuiApplication> -typedef QGuiApplication Application; -#endif -#include <QtQml/QQmlApplicationEngine> -#include <QtQml/QQmlContext> -#include <QtQml/QQmlComponent> -#include <QtWebEngineQuick/qtwebenginequickglobal.h> -#include <QtWebEngineQuick/QQuickWebEngineProfile> -#include <QtWebEngineCore/qwebenginecookiestore.h> - -static QUrl startupUrl() -{ - QUrl ret; - QStringList args(qApp->arguments()); - args.takeFirst(); - for (const QString &arg : qAsConst(args)) { - if (arg.startsWith(QLatin1Char('-'))) - continue; - ret = Utils::fromUserInput(arg); - if (ret.isValid()) - return ret; - } - return QUrl(QStringLiteral("http://qt.io/")); -} - -int main(int argc, char **argv) -{ - Application app(argc, argv); - - // Enable dev tools by default for the test browser - if (qgetenv("QTWEBENGINE_REMOTE_DEBUGGING").isNull()) - qputenv("QTWEBENGINE_REMOTE_DEBUGGING", "1337"); - QtWebEngineQuick::initialize(); - - QQmlApplicationEngine appEngine; - Utils utils; - appEngine.rootContext()->setContextProperty("utils", &utils); - appEngine.load(QUrl("qrc:/ApplicationRoot.qml")); - - QObject *rootObject = appEngine.rootObjects().first(); - - QQuickWebEngineProfile *profile = new QQuickWebEngineProfile(rootObject); - - const QMetaObject *rootMeta = rootObject->metaObject(); - int index = rootMeta->indexOfProperty("thirdPartyCookiesEnabled"); - Q_ASSERT(index != -1); - QMetaProperty thirdPartyCookiesProperty = rootMeta->property(index); - profile->cookieStore()->setCookieFilter( - [rootObject,&thirdPartyCookiesProperty](const QWebEngineCookieStore::FilterRequest &request) - { - return !request.thirdParty || thirdPartyCookiesProperty.read(rootObject).toBool(); - }); - - index = rootMeta->indexOfProperty("testProfile"); - Q_ASSERT(index != -1); - QMetaProperty profileProperty = rootMeta->property(index); - profileProperty.write(rootObject, QVariant::fromValue(profile)); - - QMetaObject::invokeMethod(rootObject, "load", Q_ARG(QVariant, startupUrl())); - - return app.exec(); -} diff --git a/tests/quicktestbrowser/quicktestbrowser.pro b/tests/quicktestbrowser/quicktestbrowser.pro deleted file mode 100644 index 304135836..000000000 --- a/tests/quicktestbrowser/quicktestbrowser.pro +++ /dev/null @@ -1,26 +0,0 @@ -requires(qtConfig(accessibility)) - -TEMPLATE = app -TARGET = quicktestbrowser - -macx: CONFIG -= app_bundle - -HEADERS = utils.h -SOURCES = main.cpp - -OTHER_FILES += ApplicationRoot.qml \ - BrowserDialog.qml \ - BrowserWindow.qml \ - ButtonWithMenu.qml \ - ContextMenuExtras.qml \ - DownloadView.qml \ - FeaturePermissionBar.qml \ - FullScreenNotification.qml - -RESOURCES += resources.qrc - -QT += qml quick webenginequick - -qtHaveModule(widgets) { - QT += widgets # QApplication is required to get native styling with QtQuickControls -} diff --git a/tests/quicktestbrowser/resources.qrc b/tests/quicktestbrowser/resources.qrc deleted file mode 100644 index 8fbe286fb..000000000 --- a/tests/quicktestbrowser/resources.qrc +++ /dev/null @@ -1,19 +0,0 @@ -<!DOCTYPE RCC><RCC version="1.0"> - <qresource prefix="/"> - <file>ApplicationRoot.qml</file> - <file>BrowserDialog.qml</file> - <file>BrowserWindow.qml</file> - <file>FeaturePermissionBar.qml</file> - <file>FullScreenNotification.qml</file> - <file>ButtonWithMenu.qml</file> - <file>DownloadView.qml</file> - <file>ZoomController.qml</file> - </qresource> - <qresource prefix="icons"> - <!-- To the risk of this breaking more often, do not duplicate the resources since this application won't be deployed --> - <file alias="go-next.png">../../examples/webenginequick/quicknanobrowser/icons/3rdparty/go-next.png</file> - <file alias="go-previous.png">../../examples/webenginequick/quicknanobrowser/icons/3rdparty/go-previous.png</file> - <file alias="process-stop.png">../../examples/webenginequick/quicknanobrowser/icons/3rdparty/process-stop.png</file> - <file alias="view-refresh.png">../../examples/webenginequick/quicknanobrowser/icons/3rdparty/view-refresh.png</file> - </qresource> -</RCC> diff --git a/tests/quicktestbrowser/utils.h b/tests/quicktestbrowser/utils.h deleted file mode 100644 index 8521e9bf5..000000000 --- a/tests/quicktestbrowser/utils.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#ifndef UTILS_H -#define UTILS_H - -#include <QtCore/QFileInfo> -#include <QtCore/QUrl> - -class Utils : public QObject { - Q_OBJECT -public: - Q_INVOKABLE static QUrl fromUserInput(const QString& userInput); - Q_INVOKABLE static QString domainFromString(const QString& urlString); -}; - -inline QUrl Utils::fromUserInput(const QString& userInput) -{ - QFileInfo fileInfo(userInput); - if (fileInfo.exists()) - return QUrl::fromLocalFile(fileInfo.absoluteFilePath()); - return QUrl::fromUserInput(userInput); -} - -inline QString Utils::domainFromString(const QString& urlString) -{ - return QUrl::fromUserInput(urlString).host(); -} - -#endif // UTILS_H |