diff options
Diffstat (limited to 'tests')
445 files changed, 15201 insertions, 8092 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8d1f1a002..a8f031a5d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,27 +1 @@ -if(NOT QT_BUILD_STANDALONE_TESTS) - cmake_minimum_required(VERSION 3.19) - - include(${CMAKE_CURRENT_LIST_DIR}/../.cmake.conf) - - project(QtWebEngineTests - VERSION "${QT_REPO_MODULE_VERSION}" - DESCRIPTION "Qt WebEngine Tests" - HOMEPAGE_URL "https://qt.io/" - LANGUAGES CXX C - ) - - # Make sure we use the fixed BASE argument of qt_add_resource. - set(QT_USE_FIXED_QT_ADD_RESOURCE_BASE TRUE) -endif() - -find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS BuildInternals Core) -find_package(Qt6 ${PROJECT_VERSION} CONFIG OPTIONAL_COMPONENTS Gui Widgets - WebEngineCore WebEngineWidgets WebEngineQuick QuickWidgets Test QuickTest WebSockets) - -if(NOT QT_BUILD_STANDALONE_TESTS) - qt_build_repo_begin() - qt_build_tests() - qt_build_repo_end() -else() - qt_build_tests() -endif() +qt_build_tests() diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt index 2227e6e46..1b0ff3e9d 100644 --- a/tests/auto/CMakeLists.txt +++ b/tests/auto/CMakeLists.txt @@ -1,17 +1,20 @@ -if(QT_FEATURE_qtwebengine_build - OR (QT_BUILD_STANDALONE_TESTS AND TARGET Qt::WebEngineCore)) - add_subdirectory(core) +# 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) + add_subdirectory(core) endif() -if(QT_FEATURE_qtwebengine_quick_build - OR (QT_BUILD_STANDALONE_TESTS AND TARGET Qt::WebEngineQuick)) +if(TARGET Qt::WebEngineQuick) add_subdirectory(quick) endif() -if(QT_FEATURE_qtwebengine_widgets_build - OR (QT_BUILD_STANDALONE_TESTS AND TARGET Qt::WebEngineWidgets)) +if(TARGET Qt::WebEngineWidgets) add_subdirectory(widgets) endif() -#if(QT_FEATURE_build_qtpdf AND QT_FEATURE_webengine_qtpdf_support) -# add_subdirectory(pdf) -#endif() +if(TARGET Qt::Pdf) + add_subdirectory(pdf) +endif() +if(TARGET Qt::PdfQuick) + add_subdirectory(pdfquick) +endif() diff --git a/tests/auto/Info.plist.in b/tests/auto/Info.plist.in deleted file mode 100644 index e7f314042..000000000 --- a/tests/auto/Info.plist.in +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version=\"1.0\" encoding=\"UTF-8\"?> -<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"> -<plist version=\"1.0\"> -<dict> - <key>CFBundlePackageType</key> - <string>APPL</string> - <key>CFBundleSignature</key> - <string>????</string> - <key>CFBundleExecutable</key> - <string>$${TARGET}</string> - <key>CFBundleIdentifier</key> - <string>org.qt-project.qt.tests.$${TARGET_HYPHENATED}</string> - <key>CFBundleName</key> - <string>$${TARGET}</string> - <key>LSUIElement</key> - <string>0</string> -</dict> -</plist> diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro deleted file mode 100644 index 257c7b387..000000000 --- a/tests/auto/auto.pro +++ /dev/null @@ -1,26 +0,0 @@ - -include($$QTWEBENGINE_OUT_ROOT/src/buildtools/qtbuildtools-config.pri) -include($$QTWEBENGINE_OUT_ROOT/src/webenginequick/qtwebenginequick-config.pri) -include($$QTWEBENGINE_OUT_ROOT/src/webenginewidgets/qtwebenginewidgets-config.pri) -include($$QTWEBENGINE_OUT_ROOT/src/pdf/qtpdf-config.pri) -include($$QTWEBENGINE_OUT_ROOT/src/pdfwidgets/qtpdfwidgets-config.pri) - -QT_FOR_CONFIG += \ - buildtools-private \ - webenginecore-private \ - webenginequick-private \ - webenginewidgets-private \ - pdf-private \ - pdfwidgets-private - -TEMPLATE = subdirs - -qtConfig(build-qtwebengine-core):qtConfig(webengine-core-support) { - qtConfig(webengine-quick): SUBDIRS += quick - qtConfig(webengine-widgets): SUBDIRS += core widgets -} - -qtConfig(build-qtpdf):qtConfig(webengine-qtpdf-support) { - SUBDIRS += pdf -} - diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt index d3c0651d1..2fa1f915a 100644 --- a/tests/auto/cmake/CMakeLists.txt +++ b/tests/auto/cmake/CMakeLists.txt @@ -1,16 +1,30 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause -cmake_minimum_required(VERSION 2.8) - -project(qmake_cmake_files) +cmake_minimum_required(VERSION 3.16) +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/cmake/cmake.pro b/tests/auto/cmake/cmake.pro deleted file mode 100644 index 51d30da67..000000000 --- a/tests/auto/cmake/cmake.pro +++ /dev/null @@ -1,8 +0,0 @@ - -# Cause make to do nothing. -TEMPLATE = subdirs - -CMAKE_QT_MODULES_UNDER_TEST = webengine -qtHaveModule(widgets): CMAKE_QT_MODULES_UNDER_TEST += webenginewidgets - -CONFIG += ctest_testcase diff --git a/tests/auto/core/CMakeLists.txt b/tests/auto/core/CMakeLists.txt index ecb3b2cf9..5908756b4 100644 --- a/tests/auto/core/CMakeLists.txt +++ b/tests/auto/core/CMakeLists.txt @@ -1,10 +1,25 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + add_subdirectory(qwebenginecookiestore) +add_subdirectory(qwebengineloadinginfo) add_subdirectory(qwebenginesettings) +if(QT_FEATURE_ssl) + # only tests doh, and requires ssl + add_subdirectory(qwebengineglobalsettings) +endif() add_subdirectory(qwebengineurlrequestinterceptor) +add_subdirectory(qwebengineurlrequestjob) add_subdirectory(origins) add_subdirectory(devtools) +add_subdirectory(getdomainandregistry) +add_subdirectory(qtversion) if(QT_FEATURE_ssl) add_subdirectory(qwebengineclientcertificatestore) add_subdirectory(certificateerror) endif() + +if(QT_FEATURE_webenginedriver) + add_subdirectory(webenginedriver) +endif() diff --git a/tests/auto/core/certificateerror/BLACKLIST b/tests/auto/core/certificateerror/BLACKLIST new file mode 100644 index 000000000..a8fd16bf3 --- /dev/null +++ b/tests/auto/core/certificateerror/BLACKLIST @@ -0,0 +1,2 @@ +[fatalError] +* diff --git a/tests/auto/core/certificateerror/CMakeLists.txt b/tests/auto/core/certificateerror/CMakeLists.txt index 57801e195..6223ca25c 100644 --- a/tests/auto/core/certificateerror/CMakeLists.txt +++ b/tests/auto/core/certificateerror/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../httpserver/httpserver.cmake) include(../../util/util.cmake) diff --git a/tests/auto/core/certificateerror/certificateerror.pro b/tests/auto/core/certificateerror/certificateerror.pro deleted file mode 100644 index 73ba7515b..000000000 --- a/tests/auto/core/certificateerror/certificateerror.pro +++ /dev/null @@ -1,3 +0,0 @@ -include(../tests.pri) -include(../../shared/https.pri) -QT *= core-private diff --git a/tests/auto/core/certificateerror/tst_certificateerror.cpp b/tests/auto/core/certificateerror/tst_certificateerror.cpp index a2b15a1ae..67e2d8ae4 100644 --- a/tests/auto/core/certificateerror/tst_certificateerror.cpp +++ b/tests/auto/core/certificateerror/tst_certificateerror.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include <httpsserver.h> #include <util.h> @@ -43,6 +18,7 @@ public: private Q_SLOTS: void handleError_data(); void handleError(); + void fatalError(); }; struct PageWithCertificateErrorHandler : QWebEnginePage @@ -91,8 +67,8 @@ void tst_CertificateError::handleError_data() void tst_CertificateError::handleError() { - HttpsServer server(":/resources/server.pem",":/resources/server.key"); - server.setExpectError(true); + HttpsServer server(":/resources/server.pem", ":/resources/server.key", ""); + server.setExpectError(false); QVERIFY(server.start()); connect(&server, &HttpsServer::newRequest, [&] (HttpReqRep *rr) { @@ -116,7 +92,7 @@ void tst_CertificateError::handleError() QCOMPARE(chain[1].serialNumber(), "3c:16:83:83:59:c4:2a:65:8f:7a:b2:07:10:14:4e:2d:70:9a:3e:23"); if (deferError) { - QCOMPARE(page.loadSpy.count(), 0); + QCOMPARE(page.loadSpy.size(), 0); QCOMPARE(toPlainTextSync(&page), QString()); if (acceptCertificate) @@ -126,9 +102,32 @@ 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() +{ + PageWithCertificateErrorHandler page(false, false); + page.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + QSignalSpy loadFinishedSpy(&page, &QWebEnginePage::loadFinished); + + page.setUrl(QUrl("https://revoked.badssl.com")); + if (!loadFinishedSpy.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."); + } + + // revoked certificate might not be reported as invalid by chromium and the load will silently succeed + bool failed = !loadFinishedSpy.first().first().toBool(), hasError = bool(page.error); + QCOMPARE(failed, hasError); + if (hasError) { + QVERIFY(!page.error->isOverridable()); + // Fatal certificate errors are implicitly rejected. But second call should not cause crash. + page.error->rejectCertificate(); + } } QTEST_MAIN(tst_CertificateError) diff --git a/tests/auto/core/core.pro b/tests/auto/core/core.pro deleted file mode 100644 index 23b25bd97..000000000 --- a/tests/auto/core/core.pro +++ /dev/null @@ -1,12 +0,0 @@ -TEMPLATE = subdirs -QT_FOR_CONFIG += network-private - -SUBDIRS += \ - qwebenginecookiestore \ - qwebenginesettings \ - qwebengineurlrequestinterceptor \ - devtools \ - origins - -qtConfig(ssl): SUBDIRS += certificateerror qwebengineclientcertificatestore - diff --git a/tests/auto/core/devtools/CMakeLists.txt b/tests/auto/core/devtools/CMakeLists.txt index fd8d850f0..efde75240 100644 --- a/tests/auto/core/devtools/CMakeLists.txt +++ b/tests/auto/core/devtools/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + qt_internal_add_test(tst_devtools SOURCES tst_devtools.cpp diff --git a/tests/auto/core/devtools/devtools.pro b/tests/auto/core/devtools/devtools.pro deleted file mode 100644 index e99c7f493..000000000 --- a/tests/auto/core/devtools/devtools.pro +++ /dev/null @@ -1 +0,0 @@ -include(../tests.pri) diff --git a/tests/auto/core/devtools/tst_devtools.cpp b/tests/auto/core/devtools/tst_devtools.cpp index 3026b3931..57a2b83a3 100644 --- a/tests/auto/core/devtools/tst_devtools.cpp +++ b/tests/auto/core/devtools/tst_devtools.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 <QtTest/QtTest> @@ -46,7 +21,10 @@ 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); inspector->setInspectedPage(page); page->triggerAction(QWebEnginePage::InspectElement); @@ -62,12 +40,16 @@ void tst_DevTools::attachAndDestroyInspectorFirst() { // External inspector + manual destruction of inspector first QWebEnginePage* page = new QWebEnginePage(); + + // shouldn't do anything until page is set + page->triggerAction(QWebEnginePage::InspectElement); + QWebEnginePage* inspector = new QWebEnginePage(); inspector->setInspectedPage(page); 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..bed27c812 --- /dev/null +++ b/tests/auto/core/getdomainandregistry/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +include(../../util/util.cmake) + +qt_internal_add_test(tst_getdomainandregistry + SOURCES + tst_getdomainandregistry.cpp + LIBRARIES + Qt::WebEngineCore + Test::Util +) diff --git a/tests/auto/core/getdomainandregistry/tst_getdomainandregistry.cpp b/tests/auto/core/getdomainandregistry/tst_getdomainandregistry.cpp new file mode 100644 index 000000000..e9e0bf105 --- /dev/null +++ b/tests/auto/core/getdomainandregistry/tst_getdomainandregistry.cpp @@ -0,0 +1,30 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <QtTest/QtTest> +#include <QtWebEngineCore/qtwebenginecoreglobal.h> + +class tst_GetDomainAndRegistry final : public QObject { + Q_OBJECT + +private Q_SLOTS: + void getDomainAndRegistry(); +}; + +void tst_GetDomainAndRegistry::getDomainAndRegistry() { + QCOMPARE(qWebEngineGetDomainAndRegistry({"http://www.google.com/"}), QString("google.com")); + QCOMPARE(qWebEngineGetDomainAndRegistry({"http://www.google.co.uk/"}), QString("google.co.uk")); + QCOMPARE(qWebEngineGetDomainAndRegistry({"http://127.0.0.1/"}), QString()); + QCOMPARE(qWebEngineGetDomainAndRegistry({"https://qt.io/"}), QString("qt.io")); + QCOMPARE(qWebEngineGetDomainAndRegistry({"https://download.qt.io/"}), QString("qt.io")); + QCOMPARE(qWebEngineGetDomainAndRegistry({"https://foo.fr/"}), QString("foo.fr")); + QCOMPARE(qWebEngineGetDomainAndRegistry({"https://foo.gouv.fr/"}), QString("foo.gouv.fr")); + QCOMPARE(qWebEngineGetDomainAndRegistry({"https://bar.foo.gouv.fr/"}), QString("foo.gouv.fr")); + QCOMPARE(qWebEngineGetDomainAndRegistry({"https://fr.wikipedia.org/wiki/%C3%89l%C3%A9phant"}), QString("wikipedia.org")); + QCOMPARE(qWebEngineGetDomainAndRegistry({"https://foo.günstigbestellen.de/"}), QString("foo.xn--gnstigbestellen-zvb.de")); + QCOMPARE(qWebEngineGetDomainAndRegistry({"https://foo.g\u00fcnstigbestellen.de/"}), QString("foo.xn--gnstigbestellen-zvb.de")); +} + + +QTEST_MAIN(tst_GetDomainAndRegistry) +#include "tst_getdomainandregistry.moc" diff --git a/tests/auto/core/origins/CMakeLists.txt b/tests/auto/core/origins/CMakeLists.txt index 79b8278a7..306074994 100644 --- a/tests/auto/core/origins/CMakeLists.txt +++ b/tests/auto/core/origins/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../httpserver/httpserver.cmake) include(../../util/util.cmake) @@ -6,6 +9,7 @@ qt_internal_add_test(tst_origins tst_origins.cpp LIBRARIES Qt::WebEngineCore + Qt::WebEngineWidgets Test::HttpServer Test::Util ) @@ -29,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/origins.pro b/tests/auto/core/origins/origins.pro deleted file mode 100644 index 6cf0b2b92..000000000 --- a/tests/auto/core/origins/origins.pro +++ /dev/null @@ -1,8 +0,0 @@ -include(../tests.pri) -include(../../shared/http.pri) - -qtConfig(webengine-webchannel):qtHaveModule(websockets) { - QT += websockets - DEFINES += WEBSOCKETS -} - 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/media.html b/tests/auto/core/origins/resources/media.html new file mode 100644 index 000000000..091485b61 --- /dev/null +++ b/tests/auto/core/origins/resources/media.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> + <head> + <title>Media</title> + <script> + function addAudio(src) { + let aud = document.createElement('audio') + aud.src = src + document.getElementsByTagName("body")[0].appendChild(aud) + } + </script> + </head> + <body> + </body> +</html> diff --git a/tests/auto/core/origins/resources/mixedSchemes.html b/tests/auto/core/origins/resources/mixedSchemes.html index c73e9ecdc..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"; }, 500); + 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 00c20ba37..9499caa1f 100644 --- a/tests/auto/core/origins/resources/mixedSchemes_frame.html +++ b/tests/auto/core/origins/resources/mixedSchemes_frame.html @@ -3,8 +3,12 @@ <head> <title>Mixed - Frame</title> <script> - 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/resources/redirect.css b/tests/auto/core/origins/resources/redirect.css index 41d7560cc..a6a03d8f5 100644 --- a/tests/auto/core/origins/resources/redirect.css +++ b/tests/auto/core/origins/resources/redirect.css @@ -1,8 +1,3 @@ -@font-face { - font-family: 'MyWebFont'; - src: url('redirect1:/resources/Akronim-Regular.woff2') format('woff2'); -} - body { - font-family: 'MyWebFont', Fallback, sans-serif; + font-family: serif; } diff --git a/tests/auto/core/origins/resources/redirect.html b/tests/auto/core/origins/resources/redirect.html index 04948e14b..603cb76f0 100644 --- a/tests/auto/core/origins/resources/redirect.html +++ b/tests/auto/core/origins/resources/redirect.html @@ -2,7 +2,14 @@ <html> <head> <title>redirect</title> - <link rel="stylesheet" href="redirect1:/resources/redirect.css"> + <script> + function addStylesheetLink(src) { + let link = document.createElement('link'); + link.rel = 'stylesheet'; + link.href = src; + document.getElementsByTagName("head")[0].appendChild(link); + } + </script> </head> <body> Text diff --git a/tests/auto/core/origins/tst_origins.cpp b/tests/auto/core/origins/tst_origins.cpp index 1aeb3628a..81385701f 100644 --- a/tests/auto/core/origins/tst_origins.cpp +++ b/tests/auto/core/origins/tst_origins.cpp @@ -1,42 +1,19 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 <util.h> #include "httpserver.h" #include <QtCore/qfile.h> #include <QtTest/QtTest> +#include <QtWebEngineCore/qwebengineurlrequestinterceptor.h> #include <QtWebEngineCore/qwebengineurlrequestjob.h> #include <QtWebEngineCore/qwebengineurlscheme.h> #include <QtWebEngineCore/qwebengineurlschemehandler.h> #include <QtWebEngineCore/qwebenginesettings.h> #include <QtWebEngineCore/qwebengineprofile.h> #include <QtWebEngineCore/qwebenginepage.h> +#include <QtWebEngineWidgets/qwebengineview.h> #if defined(WEBSOCKETS) #include <QtWebSockets/qwebsocket.h> @@ -48,6 +25,8 @@ #define QSL QStringLiteral #define QBAL QByteArrayLiteral +Q_LOGGING_CATEGORY(lc, "qt.webengine.tests") + void registerSchemes() { { @@ -125,14 +104,20 @@ void registerSchemes() } { - QWebEngineUrlScheme scheme(QBAL("redirect1")); + QWebEngineUrlScheme scheme(QBAL("redirect")); scheme.setFlags(QWebEngineUrlScheme::CorsEnabled); QWebEngineUrlScheme::registerScheme(scheme); } { - QWebEngineUrlScheme scheme(QBAL("redirect2")); - scheme.setFlags(QWebEngineUrlScheme::CorsEnabled); + QWebEngineUrlScheme scheme(QBAL("redirect-secure")); + scheme.setFlags(QWebEngineUrlScheme::SecureScheme); + QWebEngineUrlScheme::registerScheme(scheme); + } + + { + QWebEngineUrlScheme scheme(QBAL("redirect-local")); + scheme.setFlags(QWebEngineUrlScheme::LocalScheme | QWebEngineUrlScheme::LocalAccessAllowed); QWebEngineUrlScheme::registerScheme(scheme); } @@ -141,7 +126,40 @@ void registerSchemes() scheme.setFlags(QWebEngineUrlScheme::CorsEnabled); QWebEngineUrlScheme::registerScheme(scheme); } - + { + QWebEngineUrlScheme scheme(QBAL("secure-cors")); + scheme.setFlags(QWebEngineUrlScheme::SecureScheme | QWebEngineUrlScheme::CorsEnabled); + QWebEngineUrlScheme::registerScheme(scheme); + } + { + QWebEngineUrlScheme scheme(QBAL("localaccess")); + scheme.setFlags(QWebEngineUrlScheme::LocalAccessAllowed); + QWebEngineUrlScheme::registerScheme(scheme); + } + { + QWebEngineUrlScheme scheme(QBAL("local")); + scheme.setFlags(QWebEngineUrlScheme::LocalScheme); + QWebEngineUrlScheme::registerScheme(scheme); + } + { + QWebEngineUrlScheme scheme(QBAL("local-localaccess")); + scheme.setFlags(QWebEngineUrlScheme::LocalScheme | QWebEngineUrlScheme::LocalAccessAllowed); + QWebEngineUrlScheme::registerScheme(scheme); + } + { + QWebEngineUrlScheme scheme(QBAL("local-cors")); + 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) @@ -165,9 +183,15 @@ public: profile->installUrlSchemeHandler(QBAL("HostSyntax-ContentSecurityPolicyIgnored"), this); profile->installUrlSchemeHandler(QBAL("HostAndPortSyntax"), this); profile->installUrlSchemeHandler(QBAL("HostPortAndUserInformationSyntax"), this); - profile->installUrlSchemeHandler(QBAL("redirect1"), this); - profile->installUrlSchemeHandler(QBAL("redirect2"), this); + profile->installUrlSchemeHandler(QBAL("redirect"), this); + profile->installUrlSchemeHandler(QBAL("redirect-secure"), this); + profile->installUrlSchemeHandler(QBAL("redirect-local"), this); profile->installUrlSchemeHandler(QBAL("cors"), this); + profile->installUrlSchemeHandler(QBAL("secure-cors"), this); + profile->installUrlSchemeHandler(QBAL("localaccess"), this); + profile->installUrlSchemeHandler(QBAL("local"), this); + profile->installUrlSchemeHandler(QBAL("local-localaccess"), this); + profile->installUrlSchemeHandler(QBAL("local-cors"), this); } QList<QUrl> &requests() { return m_requests; } @@ -178,18 +202,24 @@ private: QUrl url = job->requestUrl(); m_requests << url; - if (url.scheme() == QBAL("redirect1")) { - url.setScheme(QBAL("redirect2")); - job->redirect(url); - return; + if (url.scheme().startsWith("redirect")) { + QString path = url.path(); + int idx = path.indexOf(QChar('/')); + if (idx > 0) { + url.setScheme(path.first(idx)); + url.setPath(path.mid(idx, -1)); + job->redirect(url); + return; + } } QString pathPrefix = QDir(QT_TESTCASE_SOURCEDIR).canonicalPath(); if (url.path().startsWith("/qtwebchannel/")) pathPrefix = QSL(":"); QString pathSuffix = url.path(); - QFile *file = new QFile(pathPrefix + pathSuffix, job); + auto file = std::make_unique<QFile>(pathPrefix + pathSuffix, job); if (!file->open(QIODevice::ReadOnly)) { + qWarning() << "Failed to read data for:" << url << file->errorString(); job->fail(QWebEngineUrlRequestJob::RequestFailed); return; } @@ -198,12 +228,69 @@ private: mimeType = QBAL("application/javascript"); else if (pathSuffix.endsWith(QSL(".css"))) mimeType = QBAL("text/css"); - job->reply(mimeType, file); + job->reply(mimeType, file.release()); } QList<QUrl> m_requests; }; +class TestRequestInterceptor : public QWebEngineUrlRequestInterceptor +{ +public: + TestRequestInterceptor() = default; + void interceptRequest(QWebEngineUrlRequestInfo &info) override + { + qCDebug(lc) << this << "Type:" << info.resourceType() << info.requestMethod() << "Navigation:" << info.navigationType() + << info.requestUrl() << "Initiator:" << info.initiator(); + + QUrl url = info.requestUrl(); + requests << url; + if (url.scheme().startsWith("redirect")) { + QString path = url.path(); + int idx = path.indexOf(QChar('/')); + if (idx > 0) { + url.setScheme(path.first(idx)); + url.setPath(path.mid(idx, -1)); + info.redirect(url); + } + } + } + QList<QUrl> requests; +}; + +class TestPage : public QWebEnginePage +{ +public: + TestPage(QWebEngineProfile *profile) : QWebEnginePage(profile, nullptr) + { + } + void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel, + const QString &message, int, + const QString &) override + { + 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; +}; + class tst_Origins final : public QObject { Q_OBJECT @@ -220,10 +307,17 @@ 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(); void mixedXHR(); + void mixedContent_data(); + void mixedContent(); + void localMediaBlock_data(); + void localMediaBlock(); #if defined(WEBSOCKETS) void webSocket(); #endif @@ -232,7 +326,17 @@ private Q_SLOTS: void serviceWorker(); void viewSource(); void createObjectURL(); - void redirect(); + void redirectScheme(); + void redirectSchemeLocal(); + void redirectSchemeSecure(); + void redirectInterceptor(); + void redirectInterceptorLocal(); + void redirectInterceptorSecure(); + void redirectInterceptorFile(); + void redirectInterceptorHttp(); + void fetchApiCustomUrl_data(); + void fetchApiCustomUrl(); + void fetchApiHttpUrl(); private: bool verifyLoad(const QUrl &url) @@ -249,7 +353,7 @@ private: } QWebEngineProfile m_profile; - QWebEnginePage *m_page = nullptr; + TestPage *m_page = nullptr; TstUrlSchemeHandler *m_handler = nullptr; }; @@ -270,7 +374,7 @@ void tst_Origins::cleanupTestCase() void tst_Origins::init() { - m_page = new QWebEnginePage(&m_profile, nullptr); + m_page = new TestPage(&m_profile); } void tst_Origins::cleanup() @@ -370,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"))); @@ -488,8 +592,6 @@ void tst_Origins::subdirWithoutAccess() { ScopedAttribute sa(m_page->settings(), QWebEngineSettings::LocalContentCanAccessFileUrls, false); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError"))); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError"))); QVERIFY(verifyLoad("file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + "/resources/subdir/index.html")); QCOMPARE(eval(QSL("msg[0]")), QVariant()); @@ -507,27 +609,112 @@ 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" }); QVERIFY(server.start()); - ScopedAttribute sa(m_page->settings(), QWebEngineSettings::LocalContentCanAccessRemoteUrls, EnableAccess); - if (!EnableAccess) - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("blocked by CORS policy"))); + 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(); - eval("sendXHR('" + server.url("/mixedXHR.txt").toString() + "')"); - QTRY_COMPARE(eval("result"), (EnableAccess ? QString("ok") : QString("error"))); + 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 + + 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. @@ -538,105 +725,149 @@ 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() { - 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"))); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError"))); - eval(QSL("setIFrameUrl('qrc:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError"))); - eval(QSL("setIFrameUrl('tst:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); - - QVERIFY(verifyLoad(QSL("qrc:/resources/mixedSchemes.html"))); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError"))); - eval("setIFrameUrl('file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() - + "/resources/mixedSchemes_frame.html')"); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); - eval(QSL("setIFrameUrl('qrc:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess"))); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError"))); - eval(QSL("setIFrameUrl('tst:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); - - QVERIFY(verifyLoad(QSL("tst:/resources/mixedSchemes.html"))); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Not allowed to load local resource"))); - eval("setIFrameUrl('file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() - + "/resources/mixedSchemes_frame.html')"); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("cannotLoad"))); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError"))); - 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"))); - - QVERIFY(verifyLoad(QSL("PathSyntax:/resources/mixedSchemes.html"))); - eval(QSL("setIFrameUrl('PathSyntax:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess"))); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Not allowed to load local resource"))); - 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')")); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError"))); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError"))); - eval(QSL("setIFrameUrl('PathSyntax-NoAccessAllowed:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); + QTest::addColumn<QString>("schemeFrom"); + QTest::addColumn<QVariantMap>("testPairs"); + + 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 }, + } }, + }; + + 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-LocalAccessAllowed:/resources/mixedSchemes.html"))); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError"))); - eval(QSL("setIFrameUrl('PathSyntax:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError"))); - 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"))); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError"))); - 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-NoAccessAllowed:/resources/mixedSchemes.html"))); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError"))); - eval(QSL("setIFrameUrl('PathSyntax:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Not allowed to load local resource"))); - eval(QSL("setIFrameUrl('PathSyntax-Local:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("cannotLoad"))); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError"))); - eval(QSL("setIFrameUrl('PathSyntax-LocalAccessAllowed:/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError"))); - 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); + + 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)); + + QStringList schemesTo, expected, results; + for (auto it = testPairs.begin(), end = testPairs.end(); it != end; ++it) { + + 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]); + + 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)); + } - 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"))); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError"))); - eval(QSL("setIFrameUrl('HostSyntax://b/resources/mixedSchemes_frame.html')")); - QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); + 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. void tst_Origins::mixedSchemesWithCsp() { QVERIFY(verifyLoad(QSL("HostSyntax://a/resources/mixedSchemesWithCsp.html"))); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("violates the following Content Security Policy"))); eval(QSL("setIFrameUrl('HostSyntax://a/resources/mixedSchemes_frame.html')")); QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("violates the following Content Security Policy"))); eval(QSL("setIFrameUrl('HostSyntax://b/resources/mixedSchemes_frame.html')")); QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); QVERIFY(verifyLoad(QSL("HostSyntax-ContentSecurityPolicyIgnored://a/resources/mixedSchemesWithCsp.html"))); eval(QSL("setIFrameUrl('HostSyntax-ContentSecurityPolicyIgnored://a/resources/mixedSchemes_frame.html')")); QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadAndAccess"))); - QTest::ignoreMessage(QtSystemMsg, QRegularExpression(QSL("Uncaught SecurityError"))); eval(QSL("setIFrameUrl('HostSyntax-ContentSecurityPolicyIgnored://b/resources/mixedSchemes_frame.html')")); QTRY_COMPARE(eval(QSL("result")), QVariant(QSL("canLoadButNotAccess"))); } @@ -649,79 +880,325 @@ void tst_Origins::mixedSchemesWithCsp() // schemes with the CorsEnabled flag. void tst_Origins::mixedXHR_data() { - QTest::addColumn<QString>("url"); - QTest::addColumn<QString>("command"); - QTest::addColumn<QVariant>("result"); - QTest::newRow("file->file") << QString("file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() - + "/resources/mixedXHR.html") - << QString("sendXHR('file:" - + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() - + "/resources/mixedXHR.txt')") - << QVariant(QString("ok")); - QTest::newRow("file->qrc") << QString("file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() - + "/resources/mixedXHR.html") - << QString("sendXHR('qrc:/resources/mixedXHR.txt')") - << QVariant(QString("error")); - QTest::newRow("file->tst") << QString("file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() - + "/resources/mixedXHR.html") - << QString("sendXHR('tst:/resources/mixedXHR.txt')") - << QVariant(QString("error")); - QTest::newRow("file->data") << QString("file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() - + "/resources/mixedXHR.html") - << QString("sendXHR('data:,ok')") << QVariant(QString("ok")); - QTest::newRow("file->cors") << QString("file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() - + "/resources/mixedXHR.html") - << QString("sendXHR('cors:/resources/mixedXHR.txt')") - << QVariant(QString("ok")); - - QTest::newRow("qrc->file") << QString("qrc:/resources/mixedXHR.html") - << QString("sendXHR('file:" - + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() - + "/resources/mixedXHR.txt')") - << QVariant(QString("error")); - QTest::newRow("qrc->qrc") << QString("qrc:/resources/mixedXHR.html") - << QString("sendXHR('qrc:/resources/mixedXHR.txt')") - << QVariant(QString("ok")); - QTest::newRow("qrc->tst") << QString("qrc:/resources/mixedXHR.html") - << QString("sendXHR('tst:/resources/mixedXHR.txt')") - << QVariant(QString("error")); - QTest::newRow("qrc->data") << QString("qrc:/resources/mixedXHR.html") - << QString("sendXHR('data:,ok')") - << QVariant(QString("ok")); - QTest::newRow("qrc->cors") << QString("qrc:/resources/mixedXHR.html") - << QString("sendXHR('cors:/resources/mixedXHR.txt')") - << QVariant(QString("ok")); - - QTest::newRow("tst->file") << QString("tst:/resources/mixedXHR.html") - << QString("sendXHR('file:" - + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() - + "/resources/mixedXHR.txt')") - << QVariant(QString("error")); - QTest::newRow("tst->qrc") << QString("tst:/resources/mixedXHR.html") - << QString("sendXHR('qrc:/resources/mixedXHR.txt')") - << QVariant(QString("error")); - QTest::newRow("tst->tst") << QString("tst:/resources/mixedXHR.html") - << QString("sendXHR('tst:/resources/mixedXHR.txt')") - << QVariant(QString("ok")); - QTest::newRow("tst->data") << QString("tst:/resources/mixedXHR.html") - << QString("sendXHR('data:,ok')") - << QVariant(QString("ok")); - QTest::newRow("tst->cors") << QString("tst:/resources/mixedXHR.html") - << QString("sendXHR('cors:/resources/mixedXHR.txt')") - << QVariant(QString("ok")); - + QTest::addColumn<QString>("schemeFrom"); + QTest::addColumn<bool>("canAccessFileUrls"); + QTest::addColumn<bool>("canAccessRemoteUrl"); + QTest::addColumn<QVariantMap>("testPairs"); + + bool defaultFileAccess = QWebEnginePage().settings()->testAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls); + bool defaultRemoteAccess = QWebEnginePage().settings()->testAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls); + Q_ASSERT(defaultFileAccess); + Q_ASSERT(!defaultRemoteAccess); + std::vector<std::pair<bool, bool>> settingCombinations = { + { defaultFileAccess, defaultRemoteAccess }, // tag: *schemeFrom*_local_noremote + { defaultFileAccess, !defaultRemoteAccess }, // tag: *schemeFrom*_local_remote + { !defaultFileAccess, defaultRemoteAccess }, // tag: *schemeFrom*_nolocal_noremote + { !defaultFileAccess, !defaultRemoteAccess } // tag: *schemeFrom*_nolocal_remote + }; + + QVariant OK = QString("ok"), ERR = QString("error"); + std::vector< + std::pair<const char *, std::vector< + std::pair<const char *, std::vector<QVariant>>>>> data = { + { "file", { + { "file", { OK, OK, ERR, ERR } }, + { "qrc", { ERR, ERR, ERR, ERR } }, + { "tst", { ERR, ERR, ERR, ERR } }, + { "data", { OK, OK, OK, OK } }, + { "cors", { ERR, OK, ERR, OK } }, + { "local-localaccess", { OK, OK, ERR, ERR } }, + { "local-cors", { OK, OK, ERR, ERR } }, } }, + + { "qrc", { + { "file", { ERR, ERR, ERR, ERR } }, + { "qrc", { OK, OK, OK, OK } }, + { "tst", { ERR, ERR, ERR, ERR } }, + { "data", { OK, OK, OK, OK } }, + { "cors", { OK, OK, OK, OK } }, + { "local-localaccess", { ERR, ERR, ERR, ERR } }, + { "local-cors", { ERR, ERR, ERR, ERR } }, } }, + + { "tst", { + { "file", { ERR, ERR, ERR, ERR } }, + { "qrc", { ERR, ERR, ERR, ERR } }, + { "tst", { OK, OK, OK, OK } }, + { "data", { OK, OK, OK, OK } }, + { "cors", { OK, OK, OK, OK } }, + { "local-localaccess", { ERR, ERR, ERR, ERR } }, + { "local-cors", { ERR, ERR, ERR, ERR } }, } }, + + { "cors", { // -local +cors -local-access + { "file", { ERR, ERR, ERR, ERR } }, + { "qrc", { ERR, ERR, ERR, ERR } }, + { "tst", { ERR, ERR, ERR, ERR } }, + { "data", { OK, OK, OK, OK } }, + { "cors", { OK, OK, OK, OK } }, + { "local-localaccess", { ERR, ERR, ERR, ERR } }, + { "local-cors", { ERR, ERR, ERR, ERR } }, } }, + + { "local", { // +local -cors -local-access + { "file", { OK, OK, ERR, ERR } }, + { "qrc", { ERR, ERR, ERR, ERR } }, + { "tst", { ERR, ERR, ERR, ERR } }, + { "data", { OK, OK, OK, OK } }, + { "cors", { ERR, OK, ERR, OK } }, + { "local-localaccess", { OK, OK, ERR, ERR } }, + { "local-cors", { OK, OK, ERR, ERR } }, } }, + + { "local-cors", { // +local +cors -local-access + { "file", { OK, OK, ERR, ERR } }, + { "qrc", { ERR, ERR, ERR, ERR } }, + { "tst", { ERR, ERR, ERR, ERR } }, + { "data", { OK, OK, OK, OK } }, + { "cors", { ERR, OK, ERR, OK } }, + { "local-localaccess", { OK, OK, ERR, ERR } }, + { "local-cors", { OK, OK, ERR, ERR } }, } }, + + { "local-localaccess", { // +local -cors +local-access + { "file", { OK, OK, OK, OK } }, + { "qrc", { ERR, ERR, ERR, ERR } }, + { "tst", { ERR, ERR, ERR, ERR } }, + { "data", { OK, OK, OK, OK } }, + { "cors", { ERR, OK, ERR, OK } }, + { "local-localaccess", { OK, OK, OK, OK } }, + { "local-cors", { OK, OK, OK, OK } }, } }, + + { "localaccess", { // -local -cors +local-access + { "file", { OK, OK, OK, OK } }, + { "qrc", { ERR, ERR, ERR, ERR } }, + { "tst", { ERR, ERR, ERR, ERR } }, + { "data", { OK, OK, OK, OK } }, + { "cors", { OK, OK, OK, OK } }, + { "local-localaccess", { OK, OK, OK, OK } }, + { "local-cors", { OK, OK, OK, OK } }, } }, + }; + + for (auto &&d : data) { + auto schemeFrom = d.first; + + for (int i = 0; i < 4; ++i) { + const auto &it = settingCombinations[i]; + bool canAccessFileUrls = it.first, canAccessRemoteUrl = it.second; + + QVariantMap testPairs; + for (auto &&destSchemes : d.second) { + auto &&destScheme = destSchemes.first; + auto &&expectedResults = destSchemes.second; + testPairs[destScheme] = expectedResults[i]; + } + + QTest::addRow("%s_%s_%s", schemeFrom, (canAccessFileUrls ? "local" : "nolocal"), (canAccessRemoteUrl ? "remote" : "noremote")) + << schemeFrom << canAccessFileUrls << canAccessRemoteUrl << testPairs; + } + } } - void tst_Origins::mixedXHR() { - QFETCH(QString, url); - QFETCH(QString, command); - QFETCH(QVariant, result); + QFETCH(QString, schemeFrom); + QFETCH(bool, canAccessFileUrls); + QFETCH(bool, canAccessRemoteUrl); + QFETCH(QVariantMap, testPairs); + + QString srcDir(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath()); + auto loadUrl = QString("%1:%2/resources/mixedXHR.html").arg(schemeFrom).arg(schemeFrom == "file" ? srcDir : ""); + auto sendXHR = [&] (const QString &scheme) { + if (scheme == "data") + return QString("sendXHR('data:,ok')"); + return QString("sendXHR('%1:%2/resources/mixedXHR.txt')").arg(scheme).arg(scheme == "file" ? srcDir : ""); + }; + + QCOMPARE(testPairs.size(), 7); + ScopedAttribute sa0(m_page->settings(), QWebEngineSettings::LocalContentCanAccessFileUrls, canAccessFileUrls); + ScopedAttribute sa1(m_page->settings(), QWebEngineSettings::LocalContentCanAccessRemoteUrls, canAccessRemoteUrl); + QVERIFY(verifyLoad(loadUrl)); + + QStringList schemesTo, expected, results; + for (auto it = testPairs.begin(), end = testPairs.end(); it != end; ++it) { + auto schemeTo = it.key(); + auto expectedResult = it.value().toString(); + auto command = sendXHR(schemeTo); + + eval(command); + + QTRY_COMPARE(eval(QSL("result !== undefined")), QVariant(true)); + auto result = eval(QSL("result")).toString(); + + schemesTo.append(schemeTo.rightJustified(10)); + results.append(result.rightJustified(10)); + expected.append(expectedResult.rightJustified(10)); + } + QVERIFY2(results == expected, + qPrintable(QString("From '%1' to:\n\tScheme: %2\n\tActual: %3\n\tExpect: %4") + .arg(schemeFrom).arg(schemesTo.join(' ')).arg(results.join(' ')).arg(expected.join(' ')))); +} + +// Load the main page over one scheme, then load an iframe over a different scheme. This load is not considered CORS. +void tst_Origins::mixedContent_data() +{ + QTest::addColumn<QString>("schemeFrom"); + QTest::addColumn<bool>("canAccessFileUrls"); + QTest::addColumn<bool>("canAccessRemoteUrl"); + QTest::addColumn<QVariantMap>("testPairs"); + + bool defaultFileAccess = true; + bool defaultRemoteAccess = false; + std::vector<std::pair<bool, bool>> settingCombinations = { + { defaultFileAccess, defaultRemoteAccess }, // tag: *schemeFrom*_local_noremote + { defaultFileAccess, !defaultRemoteAccess }, // tag: *schemeFrom*_local_remote + { !defaultFileAccess, defaultRemoteAccess }, // tag: *schemeFrom*_nolocal_noremote + { !defaultFileAccess, !defaultRemoteAccess } // tag: *schemeFrom*_nolocal_remote + }; + + 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 *, std::vector<QVariant>>>>> data = { + { "file", { + { "file", { SLF, SLF, ERR, ERR } }, + { "qrc", { OK, OK, OK, OK } }, + { "tst", { ERR, OK, ERR, OK } }, + { "data", { OK, OK, OK, OK } }, + { "cors", { ERR, OK, ERR, OK } }, + { "local-localaccess", { OK, OK, ERR, ERR } }, + { "local-cors", { OK, OK, ERR, ERR } }, + } }, + + { "qrc", { + { "file", { ERR, ERR, ERR, ERR } }, + { "qrc", { SLF, SLF, SLF, SLF } }, + { "tst", { OK, OK, OK, OK } }, + { "data", { OK, OK, OK, OK } }, + { "cors", { OK, OK, OK, OK } }, + { "local-localaccess", { ERR, ERR, ERR, ERR } }, + { "local-cors", { ERR, ERR, ERR, ERR } }, } }, + + { "tst", { + { "file", { ERR, ERR, ERR, ERR } }, + { "qrc", { OK, OK, OK, OK } }, + { "tst", { SLF, SLF, SLF, SLF } }, + { "data", { OK, OK, OK, OK } }, + { "cors", { OK, OK, OK, OK } }, + { "local-localaccess", { ERR, ERR, ERR, ERR } }, + { "local-cors", { ERR, ERR, ERR, ERR } }, } }, + + { "cors", { // -local +cors -local-access + { "file", { ERR, ERR, ERR, ERR } }, + { "qrc", { OK, OK, OK, OK } }, + { "tst", { OK, OK, OK, OK } }, + { "data", { OK, OK, OK, OK } }, + { "cors", { SLF, SLF, SLF, SLF } }, + { "local-localaccess", { ERR, ERR, ERR, ERR } }, + { "local-cors", { ERR, ERR, ERR, ERR } }, } }, + + { "local", { // +local -cors -local-access + { "file", { OK, OK, ERR, ERR } }, + { "qrc", { OK, OK, OK, OK } }, + { "tst", { ERR, OK, ERR, OK } }, + { "data", { OK, OK, OK, OK } }, + { "cors", { ERR, OK, ERR, OK } }, + { "local-localaccess", { OK, OK, ERR, ERR } }, + { "local-cors", { OK, OK, ERR, ERR } }, + } }, + + { "local-cors", { // +local +cors -local-access + { "file", { OK, OK, ERR, ERR } }, + { "qrc", { OK, OK, OK, OK } }, + { "tst", { ERR, OK, ERR, OK } }, + { "data", { OK, OK, OK, OK } }, + { "cors", { ERR, OK, ERR, OK } }, + { "local-localaccess", { OK, OK, ERR, ERR } }, + { "local-cors", { SLF, SLF, ERR, ERR } }, + } }, + + { "local-localaccess", { // +local -cors + OK-access + { "file", { OK, OK, OK, OK } }, + { "qrc", { OK, OK, OK, OK } }, + { "tst", { ERR, OK, ERR, OK } }, + { "data", { OK, OK, OK, OK } }, + { "cors", { ERR, OK, ERR, OK } }, + { "local-localaccess", { SLF, SLF, OK, OK } }, // ### should probably be: SLF, SLF, SLF, SLF + { "local-cors", { OK, OK, OK, OK } }, + } }, + + { "localaccess", { // -local -cors +local-access + { "file", { OK, OK, OK, OK } }, + { "qrc", { OK, OK, OK, OK } }, + { "tst", { OK, OK, OK, OK } }, + { "data", { OK, OK, OK, OK } }, + { "cors", { OK, OK, OK, OK } }, + { "local-localaccess", { OK, OK, OK, OK } }, + { "local-cors", { OK, OK, OK, OK } }, } }, + }; + + for (auto &&d : data) { + auto schemeFrom = d.first; + + for (int i = 0; i < 4; ++i) { + const auto &it = settingCombinations[i]; + bool canAccessFileUrls = it.first, canAccessRemoteUrl = it.second; + + QVariantMap testPairs; + for (auto &&destSchemes : d.second) { + auto &&destScheme = destSchemes.first; + auto &&expectedResults = destSchemes.second; + testPairs[destScheme] = expectedResults[i]; + } + + QTest::addRow("%s_%s_%s", schemeFrom, (canAccessFileUrls ? "local" : "nolocal"), (canAccessRemoteUrl ? "remote" : "noremote")) + << schemeFrom << canAccessFileUrls << canAccessRemoteUrl << testPairs; + } + } +} - QVERIFY(verifyLoad(url)); - eval(command); - QTRY_COMPARE(eval(QString("result")), result); +void tst_Origins::mixedContent() +{ + QFETCH(QString, schemeFrom); + QFETCH(bool, canAccessFileUrls); + QFETCH(bool, canAccessRemoteUrl); + QFETCH(QVariantMap, testPairs); + + QString srcDir(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath()); + auto loadUrl = QString("%1:%2/resources/mixedSchemes.html").arg(schemeFrom).arg(schemeFrom == "file" ? srcDir : ""); + + QCOMPARE(testPairs.size(), 7); + ScopedAttribute sa2(m_page->settings(), QWebEngineSettings::ErrorPageEnabled, false); + ScopedAttribute sa0(m_page->settings(), QWebEngineSettings::LocalContentCanAccessFileUrls, canAccessFileUrls); + ScopedAttribute sa1(m_page->settings(), QWebEngineSettings::LocalContentCanAccessRemoteUrls, canAccessRemoteUrl); + QVERIFY(verifyLoad(loadUrl)); + + auto setIFrameUrl = [&] (const QString &scheme) { + if (scheme == "data") + 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 : ""); + 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->clearLog(); + QStringList schemesTo, expected, results; + for (auto it = testPairs.begin(), end = testPairs.end(); it != end; ++it) { + + auto schemeTo = it.key(); + auto expectedResult = it.value().toString(); + + eval(setIFrameUrl(schemeTo)); + + // 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(' ')))); } #if defined(WEBSOCKETS) @@ -877,12 +1354,12 @@ void tst_Origins::serviceWorker() QVERIFY(verifyLoad(QSL("tst:/resources/serviceWorker.html"))); QTRY_VERIFY(eval(QSL("done")).toBool()); QVERIFY(eval(QSL("error")).toString() - .contains(QSL("Cannot read property 'register' of undefined"))); + .contains(QSL("Cannot read properties of undefined"))); QVERIFY(verifyLoad(QSL("PathSyntax:/resources/serviceWorker.html"))); QTRY_VERIFY(eval(QSL("done")).toBool()); QVERIFY(eval(QSL("error")).toString() - .contains(QSL("Cannot read property 'register' of undefined"))); + .contains(QSL("Cannot read properties of undefined"))); QVERIFY(verifyLoad(QSL("PathSyntax-Secure:/resources/serviceWorker.html"))); QTRY_VERIFY(eval(QSL("done")).toBool()); @@ -892,7 +1369,7 @@ void tst_Origins::serviceWorker() QVERIFY(verifyLoad(QSL("PathSyntax-ServiceWorkersAllowed:/resources/serviceWorker.html"))); QTRY_VERIFY(eval(QSL("done")).toBool()); QVERIFY(eval(QSL("error")).toString() - .contains(QSL("Cannot read property 'register' of undefined"))); + .contains(QSL("Cannot read properties of undefined"))); QVERIFY(verifyLoad(QSL("PathSyntax-Secure-ServiceWorkersAllowed:/resources/serviceWorker.html"))); QTRY_VERIFY(eval(QSL("done")).toBool()); @@ -901,7 +1378,7 @@ void tst_Origins::serviceWorker() QVERIFY(verifyLoad(QSL("PathSyntax-NoAccessAllowed:/resources/serviceWorker.html"))); QTRY_VERIFY(eval(QSL("done")).toBool()); QVERIFY(eval(QSL("error")).toString() - .contains(QSL("Cannot read property 'register' of undefined"))); + .contains(QSL("Cannot read properties of undefined"))); } // Support for view-source must be enabled explicitly. @@ -943,17 +1420,310 @@ void tst_Origins::createObjectURL() QVERIFY(eval(QSL("result")).toString().startsWith(QSL("blob:tst:"))); } -void tst_Origins::redirect() +void tst_Origins::redirectScheme() { - QVERIFY(verifyLoad(QSL("redirect1:/resources/redirect.html"))); - QTRY_COMPARE(m_handler->requests().size(), 7); - QCOMPARE(m_handler->requests()[0], QUrl(QStringLiteral("redirect1:/resources/redirect.html"))); - QCOMPARE(m_handler->requests()[1], QUrl(QStringLiteral("redirect2:/resources/redirect.html"))); - QCOMPARE(m_handler->requests()[2], QUrl(QStringLiteral("redirect1:/resources/redirect.css"))); - QCOMPARE(m_handler->requests()[3], QUrl(QStringLiteral("redirect2:/resources/redirect.css"))); - QCOMPARE(m_handler->requests()[4], QUrl(QStringLiteral("redirect1:/resources/Akronim-Regular.woff2"))); - QCOMPARE(m_handler->requests()[5], QUrl(QStringLiteral("redirect1:/resources/Akronim-Regular.woff2"))); - QCOMPARE(m_handler->requests()[6], QUrl(QStringLiteral("redirect2:/resources/Akronim-Regular.woff2"))); + QVERIFY(verifyLoad(QSL("redirect:cors/resources/redirect.html"))); + eval("addStylesheetLink('redirect:cors/resources/redirect.css')"); + QTRY_COMPARE(m_handler->requests().size(), 4); + QCOMPARE(m_handler->requests()[0], QUrl(QStringLiteral("redirect:cors/resources/redirect.html"))); + QCOMPARE(m_handler->requests()[1], QUrl(QStringLiteral("cors:/resources/redirect.html"))); + QCOMPARE(m_handler->requests()[2], QUrl(QStringLiteral("redirect:cors/resources/redirect.css"))); + QCOMPARE(m_handler->requests()[3], QUrl(QStringLiteral("cors:/resources/redirect.css"))); + + QVERIFY(!verifyLoad(QSL("redirect:file/resources/redirect.html"))); + QVERIFY(!verifyLoad(QSL("redirect:local/resources/redirect.html"))); + QVERIFY(!verifyLoad(QSL("redirect:local-cors/resources/redirect.html"))); +} + +void tst_Origins::redirectSchemeLocal() +{ + QVERIFY(verifyLoad(QSL("redirect-local:local/resources/redirect.html"))); + eval("addStylesheetLink('redirect-local:local/resources/redirect.css')"); + QTRY_COMPARE(m_handler->requests().size(), 4); + QCOMPARE(m_handler->requests()[0], QUrl(QStringLiteral("redirect-local:local/resources/redirect.html"))); + QCOMPARE(m_handler->requests()[1], QUrl(QStringLiteral("local:/resources/redirect.html"))); + QCOMPARE(m_handler->requests()[2], QUrl(QStringLiteral("redirect-local:local/resources/redirect.css"))); + QCOMPARE(m_handler->requests()[3], QUrl(QStringLiteral("local:/resources/redirect.css"))); +} + +void tst_Origins::redirectSchemeSecure() +{ + QVERIFY(verifyLoad(QSL("redirect-secure:secure-cors/resources/redirect.html"))); + eval("addStylesheetLink('redirect-secure:secure-cors/resources/redirect.css')"); + QTRY_COMPARE(m_handler->requests().size(), 4); + QCOMPARE(m_handler->requests()[0], QUrl(QStringLiteral("redirect-secure:secure-cors/resources/redirect.html"))); + QCOMPARE(m_handler->requests()[1], QUrl(QStringLiteral("secure-cors:/resources/redirect.html"))); + QCOMPARE(m_handler->requests()[2], QUrl(QStringLiteral("redirect-secure:secure-cors/resources/redirect.css"))); + QCOMPARE(m_handler->requests()[3], QUrl(QStringLiteral("secure-cors:/resources/redirect.css"))); +} + +void tst_Origins::redirectInterceptor() +{ + TestRequestInterceptor interceptor; + m_profile.setUrlRequestInterceptor(&interceptor); + + QVERIFY(verifyLoad(QSL("redirect:cors/resources/redirect.html"))); + eval("addStylesheetLink('redirect:cors/resources/redirect.css')"); + + QTRY_COMPARE(interceptor.requests.size(), 4); + QTRY_COMPARE(m_handler->requests().size(), 2); + QCOMPARE(m_handler->requests()[0], QUrl(QStringLiteral("cors:/resources/redirect.html"))); + QCOMPARE(m_handler->requests()[1], QUrl(QStringLiteral("cors:/resources/redirect.css"))); + + QCOMPARE(interceptor.requests[0], QUrl(QStringLiteral("redirect:cors/resources/redirect.html"))); + QCOMPARE(interceptor.requests[1], QUrl(QStringLiteral("cors:/resources/redirect.html"))); + QCOMPARE(interceptor.requests[2], QUrl(QStringLiteral("redirect:cors/resources/redirect.css"))); + QCOMPARE(interceptor.requests[3], QUrl(QStringLiteral("cors:/resources/redirect.css"))); + + QVERIFY(!verifyLoad(QSL("redirect:file/resources/redirect.html"))); + QVERIFY(!verifyLoad(QSL("redirect:local/resources/redirect.html"))); + QVERIFY(!verifyLoad(QSL("redirect:local-cors/resources/redirect.html"))); +} + +void tst_Origins::redirectInterceptorLocal() +{ + TestRequestInterceptor interceptor; + m_profile.setUrlRequestInterceptor(&interceptor); + + QVERIFY(verifyLoad(QSL("redirect-local:local/resources/redirect.html"))); + eval("addStylesheetLink('redirect-local:local/resources/redirect.css')"); + + QTRY_COMPARE(interceptor.requests.size(), 4); + QTRY_COMPARE(m_handler->requests().size(), 2); + QCOMPARE(m_handler->requests()[0], QUrl(QStringLiteral("local:/resources/redirect.html"))); + QCOMPARE(m_handler->requests()[1], QUrl(QStringLiteral("local:/resources/redirect.css"))); + + QCOMPARE(interceptor.requests[0], QUrl(QStringLiteral("redirect-local:local/resources/redirect.html"))); + QCOMPARE(interceptor.requests[1], QUrl(QStringLiteral("local:/resources/redirect.html"))); + QCOMPARE(interceptor.requests[2], QUrl(QStringLiteral("redirect-local:local/resources/redirect.css"))); + QCOMPARE(interceptor.requests[3], QUrl(QStringLiteral("local:/resources/redirect.css"))); +} + +void tst_Origins::redirectInterceptorSecure() +{ + TestRequestInterceptor interceptor; + m_profile.setUrlRequestInterceptor(&interceptor); + + QVERIFY(verifyLoad(QSL("redirect-secure:secure-cors/resources/redirect.html"))); + eval("addStylesheetLink('redirect-secure:secure-cors/resources/redirect.css')"); + + QTRY_COMPARE(interceptor.requests.size(), 4); + QTRY_COMPARE(m_handler->requests().size(), 2); + QCOMPARE(m_handler->requests()[0], QUrl(QStringLiteral("secure-cors:/resources/redirect.html"))); + QCOMPARE(m_handler->requests()[1], QUrl(QStringLiteral("secure-cors:/resources/redirect.css"))); + + QCOMPARE(interceptor.requests[0], QUrl(QStringLiteral("redirect-secure:secure-cors/resources/redirect.html"))); + QCOMPARE(interceptor.requests[1], QUrl(QStringLiteral("secure-cors:/resources/redirect.html"))); + QCOMPARE(interceptor.requests[2], QUrl(QStringLiteral("redirect-secure:secure-cors/resources/redirect.css"))); + QCOMPARE(interceptor.requests[3], QUrl(QStringLiteral("secure-cors:/resources/redirect.css"))); +} + +class TestRedirectInterceptor : public QWebEngineUrlRequestInterceptor +{ +public: + TestRedirectInterceptor() = default; + void interceptRequest(QWebEngineUrlRequestInfo &info) override + { + qCDebug(lc) << this << "Type:" << info.resourceType() << info.requestMethod() << "Navigation:" << info.navigationType() + << info.requestUrl() << "Initiator:" << info.initiator(); + + QUrl url = info.requestUrl(); + requests << url; + if (url.path().startsWith("/redirect")) { + QString path = url.path(); + int idx = path.indexOf(QChar('/'), 10); + if (idx > 0) { + url.setScheme(path.mid(10, idx - 10)); + url.setPath(path.mid(idx, -1)); + url.setHost({}); + info.redirect(url); + } + } + } + QList<QUrl> requests; +}; + +void tst_Origins::redirectInterceptorFile() +{ + TestRedirectInterceptor interceptor; + m_profile.setUrlRequestInterceptor(&interceptor); + + QVERIFY(verifyLoad(QSL("file:///redirect/local-cors/resources/redirect.html"))); + eval("addStylesheetLink('file:///redirect/local-cors/resources/redirect.css')"); + + QTRY_COMPARE(interceptor.requests.size(), 4); + QTRY_COMPARE(m_handler->requests().size(), 2); + QCOMPARE(m_handler->requests()[0], QUrl(QStringLiteral("local-cors:/resources/redirect.html"))); + QCOMPARE(m_handler->requests()[1], QUrl(QStringLiteral("local-cors:/resources/redirect.css"))); + + QCOMPARE(interceptor.requests[0], QUrl(QStringLiteral("file:///redirect/local-cors/resources/redirect.html"))); + QCOMPARE(interceptor.requests[1], QUrl(QStringLiteral("local-cors:/resources/redirect.html"))); + QCOMPARE(interceptor.requests[2], QUrl(QStringLiteral("file:///redirect/local-cors/resources/redirect.css"))); + QCOMPARE(interceptor.requests[3], QUrl(QStringLiteral("local-cors:/resources/redirect.css"))); +} + +void tst_Origins::redirectInterceptorHttp() +{ + TestRedirectInterceptor interceptor; + m_profile.setUrlRequestInterceptor(&interceptor); + + QVERIFY(verifyLoad(QSL("http://hallo/redirect/cors/resources/redirect.html"))); + eval("addStylesheetLink('http://hallo/redirect/cors/resources/redirect.css')"); + + QTRY_COMPARE(interceptor.requests.size(), 4); + QTRY_COMPARE(m_handler->requests().size(), 2); + QCOMPARE(m_handler->requests()[0], QUrl(QStringLiteral("cors:/resources/redirect.html"))); + QCOMPARE(m_handler->requests()[1], QUrl(QStringLiteral("cors:/resources/redirect.css"))); + + QCOMPARE(interceptor.requests[0], QUrl(QStringLiteral("http://hallo/redirect/cors/resources/redirect.html"))); + QCOMPARE(interceptor.requests[1], QUrl(QStringLiteral("cors:/resources/redirect.html"))); + QCOMPARE(interceptor.requests[2], QUrl(QStringLiteral("http://hallo/redirect/cors/resources/redirect.css"))); + QCOMPARE(interceptor.requests[3], QUrl(QStringLiteral("cors:/resources/redirect.css"))); +} + +void tst_Origins::localMediaBlock_data() +{ + QTest::addColumn<bool>("enableAccess"); + QTest::addRow("enabled") << true; + QTest::addRow("disabled") << false; +} + +void tst_Origins::localMediaBlock() +{ + QFETCH(bool, enableAccess); + + std::atomic<bool> accessed = false; + HttpServer server; + server.setResourceDirs({ QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + "/resources" }); + connect(&server, &HttpServer::newRequest, [&](HttpReqRep *) { accessed.store(true); }); + QVERIFY(server.start()); + + ScopedAttribute sa1(m_page->settings(), QWebEngineSettings::LocalContentCanAccessRemoteUrls, enableAccess); + + QVERIFY(verifyLoad("file:" + QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + "/resources/media.html")); + eval("addAudio('" + server.url("/mixedXHR.txt").toString() + "')"); + + // Give it a chance to avoid a false positive on the default value of accessed. + if (!enableAccess) + QTest::qSleep(500); + QTRY_COMPARE(accessed.load(), enableAccess); + +} + +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) diff --git a/tests/auto/core/origins/tst_origins.qrc b/tests/auto/core/origins/tst_origins.qrc deleted file mode 100644 index ae5245378..000000000 --- a/tests/auto/core/origins/tst_origins.qrc +++ /dev/null @@ -1,23 +0,0 @@ -<!DOCTYPE RCC> -<RCC version="1.0"> -<qresource> - <file>resources/createObjectURL.html</file> - <file>resources/dedicatedWorker.html</file> - <file>resources/dedicatedWorker.js</file> - <file>resources/mixedSchemes.html</file> - <file>resources/mixedSchemesWithCsp.html</file> - <file>resources/mixedSchemes_frame.html</file> - <file>resources/mixedXHR.html</file> - <file>resources/mixedXHR.txt</file> - <file>resources/serviceWorker.html</file> - <file>resources/serviceWorker.js</file> - <file>resources/sharedWorker.html</file> - <file>resources/sharedWorker.js</file> - <file>resources/subdir/frame2.html</file> - <file>resources/subdir/index.html</file> - <file>resources/subdir_frame1.html</file> - <file>resources/viewSource.html</file> - <file>resources/websocket.html</file> - <file>resources/websocket2.html</file> -</qresource> -</RCC> diff --git a/tests/auto/core/qtversion/CMakeLists.txt b/tests/auto/core/qtversion/CMakeLists.txt new file mode 100644 index 000000000..9a5e89266 --- /dev/null +++ b/tests/auto/core/qtversion/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_test(tst_qtversion + SOURCES + tst_qtversion.cpp + LIBRARIES + Qt::WebEngineCore +) + diff --git a/tests/auto/core/qtversion/tst_qtversion.cpp b/tests/auto/core/qtversion/tst_qtversion.cpp new file mode 100644 index 000000000..44c2d4e5c --- /dev/null +++ b/tests/auto/core/qtversion/tst_qtversion.cpp @@ -0,0 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <QtTest/QtTest> +#include <QtWebEngineCore/qwebenginepage.h> + +class tst_QtVersion : public QObject +{ + Q_OBJECT +signals: + void done(); +private Q_SLOTS: + void checkVersion(); +}; + +void tst_QtVersion::checkVersion() +{ + QWebEnginePage page; + QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); + QSignalSpy doneSpy(this, &tst_QtVersion::done); + page.load(QUrl("chrome://qt")); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 12000); + page.toPlainText([this](const QString &result) { + QVERIFY(result.contains(qWebEngineVersion())); + QVERIFY(result.contains(qWebEngineChromiumVersion())); + QVERIFY(result.contains(qWebEngineChromiumSecurityPatchVersion())); + emit done(); + }); + QTRY_VERIFY(doneSpy.size()); +} + +QTEST_MAIN(tst_QtVersion) + +#include "tst_qtversion.moc" diff --git a/tests/auto/core/qwebengineclientcertificatestore/CMakeLists.txt b/tests/auto/core/qwebengineclientcertificatestore/CMakeLists.txt index fa2d5e538..8cee7f630 100644 --- a/tests/auto/core/qwebengineclientcertificatestore/CMakeLists.txt +++ b/tests/auto/core/qwebengineclientcertificatestore/CMakeLists.txt @@ -1,8 +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 @@ -10,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" @@ -19,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/qwebengineclientcertificatestore.pro b/tests/auto/core/qwebengineclientcertificatestore/qwebengineclientcertificatestore.pro deleted file mode 100644 index e99c7f493..000000000 --- a/tests/auto/core/qwebengineclientcertificatestore/qwebengineclientcertificatestore.pro +++ /dev/null @@ -1 +0,0 @@ -include(../tests.pri) 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 f288a2c75..7d82a5640 100644 --- a/tests/auto/core/qwebengineclientcertificatestore/tst_qwebengineclientcertificatestore.cpp +++ b/tests/auto/core/qwebengineclientcertificatestore/tst_qwebengineclientcertificatestore.cpp @@ -1,34 +1,14 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include <httpsserver.h> +#include <util.h> #include <QtTest/QtTest> #include <QtWebEngineCore/qwebengineclientcertificatestore.h> +#include <QtWebEngineCore/qwebenginepage.h> #include <QtWebEngineCore/qwebengineprofile.h> +#include <QtWebEngineCore/qwebenginecertificateerror.h> +#include <QtWebEngineCore/qwebenginesettings.h> class tst_QWebEngineClientCertificateStore : public QObject { @@ -39,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() @@ -51,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 @@ -77,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/qwebengineclientcertificatestore/tst_qwebengineclientcertificatestore.qrc b/tests/auto/core/qwebengineclientcertificatestore/tst_qwebengineclientcertificatestore.qrc deleted file mode 100644 index db481fef6..000000000 --- a/tests/auto/core/qwebengineclientcertificatestore/tst_qwebengineclientcertificatestore.qrc +++ /dev/null @@ -1,8 +0,0 @@ -<RCC> - <qresource prefix="/"> - <file>resources/certificate.crt</file> - <file>resources/privatekey.key</file> - <file>resources/certificate1.crt</file> - <file>resources/privatekey1.key</file> - </qresource> -</RCC> diff --git a/tests/auto/core/qwebenginecookiestore/CMakeLists.txt b/tests/auto/core/qwebenginecookiestore/CMakeLists.txt index 33ba5ff1a..cc14940f1 100644 --- a/tests/auto/core/qwebenginecookiestore/CMakeLists.txt +++ b/tests/auto/core/qwebenginecookiestore/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../httpserver/httpserver.cmake) include(../../util/util.cmake) diff --git a/tests/auto/core/qwebenginecookiestore/qwebenginecookiestore.pro b/tests/auto/core/qwebenginecookiestore/qwebenginecookiestore.pro deleted file mode 100644 index 9c239f1a7..000000000 --- a/tests/auto/core/qwebenginecookiestore/qwebenginecookiestore.pro +++ /dev/null @@ -1,2 +0,0 @@ -include(../tests.pri) -include(../../shared/http.pri) diff --git a/tests/auto/core/qwebenginecookiestore/tst_qwebenginecookiestore.cpp b/tests/auto/core/qwebenginecookiestore/tst_qwebenginecookiestore.cpp index ef3b84691..3fff2cd45 100644 --- a/tests/auto/core/qwebenginecookiestore/tst_qwebenginecookiestore.cpp +++ b/tests/auto/core/qwebenginecookiestore/tst_qwebenginecookiestore.cpp @@ -1,34 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include <util.h> #include <QtTest/QtTest> -#include <QtWebEngineCore/qwebenginecallback.h> #include <QtWebEngineCore/qwebenginecookiestore.h> #include <QtWebEngineCore/qwebengineprofile.h> #include <QtWebEngineCore/qwebenginepage.h> @@ -36,6 +10,10 @@ #include "httpserver.h" #include "httpreqrep.h" +// locally overwrite the default timeout of QTY_(COMPARE|VERIFY) +#define QWE_TRY_COMPARE(x, y) QTRY_COMPARE_WITH_TIMEOUT(x, y, 30000) +#define QWE_TRY_VERIFY(x) QTRY_VERIFY_WITH_TIMEOUT(x, 30000) + class tst_QWebEngineCookieStore : public QObject { Q_OBJECT @@ -56,6 +34,7 @@ private Q_SLOTS: // as it checks storage manipulation without navigation void setAndDeleteCookie(); + void setInvalidCookie(); void cookieSignals(); void batchCookieTasks(); void basicFilter(); @@ -105,22 +84,22 @@ void tst_QWebEngineCookieStore::cookieSignals() page.load(QUrl("qrc:///resources/index.html")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 30000); + QWE_TRY_COMPARE(loadSpy.size(), 1); QVariant success = loadSpy.takeFirst().takeFirst(); QVERIFY(success.toBool()); - QTRY_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")); - QTRY_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")); - QTRY_COMPARE(cookieRemovedSpy.count(), 1); + QWE_TRY_COMPARE(cookieRemovedSpy.size(), 1); } void tst_QWebEngineCookieStore::setAndDeleteCookie() @@ -141,33 +120,64 @@ void tst_QWebEngineCookieStore::setAndDeleteCookie() client->loadAllCookies(); // /* FIXME remove 'blank' navigation once loadAllCookies api is fixed page.load(QUrl("about:blank")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 30000); + QWE_TRY_COMPARE(loadSpy.size(), 1); // */ // check if pending cookies are set and removed client->setCookie(cookie1); client->setCookie(cookie2); - QTRY_COMPARE(cookieAddedSpy.count(), 2); + QWE_TRY_COMPARE(cookieAddedSpy.size(), 2); client->deleteCookie(cookie1); - QTRY_COMPARE(cookieRemovedSpy.count(), 1); + QWE_TRY_COMPARE(cookieRemovedSpy.size(), 1); page.load(QUrl("qrc:///resources/content.html")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 2, 30000); + QWE_TRY_COMPARE(loadSpy.size(), 2); QVariant success = loadSpy.takeFirst().takeFirst(); QVERIFY(success.toBool()); - QTRY_COMPARE(cookieAddedSpy.count(), 2); - QTRY_COMPARE(cookieRemovedSpy.count(), 1); + QWE_TRY_COMPARE(cookieAddedSpy.size(), 2); + QWE_TRY_COMPARE(cookieRemovedSpy.size(), 1); cookieAddedSpy.clear(); cookieRemovedSpy.clear(); client->setCookie(cookie3); - QTRY_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); - QTRY_COMPARE(cookieAddedSpy.count(), 1); - QTRY_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() @@ -186,29 +196,29 @@ void tst_QWebEngineCookieStore::batchCookieTasks() client->loadAllCookies(); // /* FIXME remove 'blank' navigation once loadAllCookies api is fixed page.load(QUrl("about:blank")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 30000); + QWE_TRY_COMPARE(loadSpy.size(), 1); // */ client->setCookie(cookie1); client->setCookie(cookie2); - QTRY_COMPARE(cookieAddedSpy.count(), 2); + QWE_TRY_COMPARE(cookieAddedSpy.size(), 2); page.load(QUrl("qrc:///resources/index.html")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 2, 30000); + QWE_TRY_COMPARE(loadSpy.size(), 2); QVariant success = loadSpy.takeFirst().takeFirst(); QVERIFY(success.toBool()); - QTRY_COMPARE(cookieAddedSpy.count(), 4); - QTRY_COMPARE(cookieRemovedSpy.count(), 0); + QWE_TRY_COMPARE(cookieAddedSpy.size(), 4); + QWE_TRY_COMPARE(cookieRemovedSpy.size(), 0); cookieAddedSpy.clear(); cookieRemovedSpy.clear(); client->deleteSessionCookies(); - QTRY_COMPARE(cookieRemovedSpy.count(), 3); + QWE_TRY_COMPARE(cookieRemovedSpy.size(), 3); client->deleteAllCookies(); - QTRY_COMPARE(cookieRemovedSpy.count(), 4); + QWE_TRY_COMPARE(cookieRemovedSpy.size(), 4); } void tst_QWebEngineCookieStore::basicFilter() @@ -225,22 +235,22 @@ void tst_QWebEngineCookieStore::basicFilter() page.load(QUrl("qrc:///resources/index.html")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 30000); + QWE_TRY_COMPARE(loadSpy.size(), 1); QVERIFY(loadSpy.takeFirst().takeFirst().toBool()); - QTRY_COMPARE(cookieAddedSpy.count(), 2); - QTRY_COMPARE(accessTested.loadAcquire(), 2); // FIXME? + QWE_TRY_COMPARE(cookieAddedSpy.size(), 2); + QWE_TRY_COMPARE(accessTested.loadAcquire(), 2); // FIXME? client->deleteAllCookies(); - QTRY_COMPARE(cookieRemovedSpy.count(), 2); + QWE_TRY_COMPARE(cookieRemovedSpy.size(), 2); client->setCookieFilter([&](const QWebEngineCookieStore::FilterRequest &){ ++accessTested; return false; }); page.triggerAction(QWebEnginePage::ReloadAndBypassCache); - QTRY_COMPARE(loadSpy.count(), 1); + QWE_TRY_COMPARE(loadSpy.size(), 1); QVERIFY(loadSpy.takeFirst().takeFirst().toBool()); - QTRY_COMPARE(accessTested.loadAcquire(), 4); // FIXME? + 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() @@ -281,25 +291,25 @@ void tst_QWebEngineCookieStore::basicFilterOverHTTP() QUrl firstPartyUrl = httpServer.url("/test.html"); page.load(firstPartyUrl); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 30000); + QWE_TRY_COMPARE(loadSpy.size(), 1); QVERIFY(loadSpy.takeFirst().takeFirst().toBool()); - QTRY_COMPARE(cookieAddedSpy.count(), 1); - QTRY_COMPARE(accessTested.loadAcquire(), 4); + QWE_TRY_COMPARE(cookieAddedSpy.size(), 1); + QWE_TRY_COMPARE(accessTested.loadAcquire(), 4); QVERIFY(cookieRequestHeader.isEmpty()); - QTRY_COMPARE(serverSpy.count(), 3); + QWE_TRY_COMPARE(serverSpy.size(), 3); page.triggerAction(QWebEnginePage::Reload); - QTRY_COMPARE(loadSpy.count(), 1); + QWE_TRY_COMPARE(loadSpy.size(), 1); QVERIFY(loadSpy.takeFirst().takeFirst().toBool()); QVERIFY(!cookieRequestHeader.isEmpty()); - QTRY_COMPARE(cookieAddedSpy.count(), 1); - QTRY_COMPARE(accessTested.loadAcquire(), 6); + QWE_TRY_COMPARE(cookieAddedSpy.size(), 1); + QWE_TRY_COMPARE(accessTested.loadAcquire(), 6); - QTRY_COMPARE(serverSpy.count(), 5); + QWE_TRY_COMPARE(serverSpy.size(), 5); client->deleteAllCookies(); - QTRY_COMPARE(cookieRemovedSpy.count(), 1); + QWE_TRY_COMPARE(cookieRemovedSpy.size(), 1); client->setCookieFilter([&](const QWebEngineCookieStore::FilterRequest &request) { resourceFirstParty.append(qMakePair(request.origin, request.firstPartyUrl)); @@ -307,28 +317,28 @@ void tst_QWebEngineCookieStore::basicFilterOverHTTP() return false; }); page.triggerAction(QWebEnginePage::ReloadAndBypassCache); - QTRY_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); - QTRY_COMPARE(accessTested.loadAcquire(), 9); + QCOMPARE(cookieAddedSpy.size(), 1); + QWE_TRY_COMPARE(accessTested.loadAcquire(), 9); - QTRY_COMPARE(serverSpy.count(), 7); + QWE_TRY_COMPARE(serverSpy.size(), 7); page.triggerAction(QWebEnginePage::Reload); - QTRY_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 - QTRY_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()))); } @@ -345,17 +355,17 @@ void tst_QWebEngineCookieStore::html5featureFilter() page.load(QUrl("qrc:///resources/content.html")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 30000); + QWE_TRY_COMPARE(loadSpy.size(), 1); QVERIFY(loadSpy.takeFirst().takeFirst().toBool()); QCOMPARE(accessTested.loadAcquire(), 0); // FIXME? QTest::ignoreMessage(QtCriticalMsg, QRegularExpression(".*Uncaught SecurityError.*sessionStorage.*")); page.runJavaScript("sessionStorage.test = 5;"); - QTRY_COMPARE(accessTested.loadAcquire(), 1); + QWE_TRY_COMPARE(accessTested.loadAcquire(), 1); QTest::ignoreMessage(QtCriticalMsg, QRegularExpression(".*Uncaught SecurityError.*sessionStorage.*")); QAtomicInt callbackTriggered = 0; page.runJavaScript("sessionStorage.test", [&](const QVariant &v) { QVERIFY(!v.isValid()); callbackTriggered = 1; }); - QTRY_VERIFY(callbackTriggered); + QWE_TRY_VERIFY(callbackTriggered); } QTEST_MAIN(tst_QWebEngineCookieStore) diff --git a/tests/auto/core/qwebenginecookiestore/tst_qwebenginecookiestore.qrc b/tests/auto/core/qwebenginecookiestore/tst_qwebenginecookiestore.qrc deleted file mode 100644 index afeae268b..000000000 --- a/tests/auto/core/qwebenginecookiestore/tst_qwebenginecookiestore.qrc +++ /dev/null @@ -1,6 +0,0 @@ -<!DOCTYPE RCC><RCC version="1.0"> -<qresource> - <file>resources/index.html</file> - <file>resources/content.html</file> -</qresource> -</RCC> diff --git a/tests/auto/core/qwebengineglobalsettings/CMakeLists.txt b/tests/auto/core/qwebengineglobalsettings/CMakeLists.txt new file mode 100644 index 000000000..fa81ba8df --- /dev/null +++ b/tests/auto/core/qwebengineglobalsettings/CMakeLists.txt @@ -0,0 +1,29 @@ +# 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::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..0af85a711 --- /dev/null +++ b/tests/auto/core/qwebengineglobalsettings/tst_qwebengineglobalsettings.cpp @@ -0,0 +1,121 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <QtTest> +#include <widgetutil.h> +#include <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() +{ + 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(QUrl("https://google.com/")); + if (!loadSpy.wait(20000)) { + QSKIP("Couldn't load page from network, skipping test."); + } + + QTRY_COMPARE(isMockDnsServerCalled, isMockDnsServerCalledExpected); + QCOMPARE(isLoadSuccessful, isDnsResolutionSuccessExpected); + QVERIFY(httpsServer.stop()); +} + +static QByteArrayList params = QByteArrayList() << "--ignore-certificate-errors"; + +W_QTEST_MAIN(tst_QWebEngineGlobalSettings, params) +#include "tst_qwebengineglobalsettings.moc" diff --git a/tests/auto/core/qwebengineloadinginfo/CMakeLists.txt b/tests/auto/core/qwebengineloadinginfo/CMakeLists.txt new file mode 100644 index 000000000..09d9c30f5 --- /dev/null +++ b/tests/auto/core/qwebengineloadinginfo/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_test(tst_qwebengineloadinginfo + SOURCES + tst_qwebengineloadinginfo.cpp + LIBRARIES + Qt::WebEngineCore + Test::HttpServer +) diff --git a/tests/auto/core/qwebengineloadinginfo/tst_qwebengineloadinginfo.cpp b/tests/auto/core/qwebengineloadinginfo/tst_qwebengineloadinginfo.cpp new file mode 100644 index 000000000..ccae7436b --- /dev/null +++ b/tests/auto/core/qwebengineloadinginfo/tst_qwebengineloadinginfo.cpp @@ -0,0 +1,93 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <QtTest/QtTest> +#include <QtWebEngineCore/qwebengineprofile.h> +#include <QtWebEngineCore/qwebenginepage.h> +#include <QtWebEngineCore/qwebengineloadinginfo.h> +#include <QtWebEngineCore/qwebenginehttprequest.h> +#include <QtWebEngineCore/qwebenginesettings.h> + +#include <httpserver.h> +#include <httpreqrep.h> + +typedef QMultiMap<QByteArray, QByteArray> Map; + +class tst_QWebEngineLoadingInfo : public QObject +{ + Q_OBJECT + +public: + tst_QWebEngineLoadingInfo() { } + +public slots: + void loadingInfoChanged(QWebEngineLoadingInfo loadingInfo) + { + const auto responseHeaders = loadingInfo.responseHeaders(); + QFETCH(Map, expected); + + if (loadingInfo.status() == QWebEngineLoadingInfo::LoadSucceededStatus + || loadingInfo.status() == QWebEngineLoadingInfo::LoadFailedStatus) { + if (!expected.empty()) + QCOMPARE(responseHeaders, expected); + } else { + QVERIFY(responseHeaders.size() == 0); + } + } + +private Q_SLOTS: + void responseHeaders_data() + { + QTest::addColumn<int>("responseCode"); + QTest::addColumn<Map>("input"); + QTest::addColumn<Map>("expected"); + + const Map empty; + const Map input { + std::make_pair("header1", "value1"), + std::make_pair("header2", "value2") + }; + const Map expected { + std::make_pair("header1", "value1"), + std::make_pair("header2", "value2"), + std::make_pair("Connection", "close") + }; + + + QTest::newRow("with headers HTTP 200") << 200 << input << expected; + QTest::newRow("with headers HTTP 500") << 500 << input << expected; + QTest::newRow("without headers HTTP 200") << 200 << empty << empty; + QTest::newRow("without headers HTTP 500") << 500 << empty << empty; + } + + void responseHeaders() + { + HttpServer httpServer; + + QFETCH(Map, input); + QFETCH(int, responseCode); + QObject::connect(&httpServer, &HttpServer::newRequest, this, [&](HttpReqRep *rr) { + for (auto it = input.cbegin(); it != input.cend(); ++it) + rr->setResponseHeader(it.key(), it.value()); + + rr->sendResponse(responseCode); + }); + QVERIFY(httpServer.start()); + + QWebEngineProfile profile; + QWebEnginePage page(&profile); + page.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + QObject::connect(&page, &QWebEnginePage::loadingChanged, this, &tst_QWebEngineLoadingInfo::loadingInfoChanged); + + + QWebEngineHttpRequest request(httpServer.url("/somepage.html")); + page.load(request); + + QTRY_VERIFY(spy.count() > 0); + QVERIFY(httpServer.stop()); + } +}; + +QTEST_MAIN(tst_QWebEngineLoadingInfo) +#include "tst_qwebengineloadinginfo.moc" diff --git a/tests/auto/core/qwebenginesettings/CMakeLists.txt b/tests/auto/core/qwebenginesettings/CMakeLists.txt index 7f8b49d1b..756b99bbb 100644 --- a/tests/auto/core/qwebenginesettings/CMakeLists.txt +++ b/tests/auto/core/qwebenginesettings/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../util/util.cmake) qt_internal_add_test(tst_qwebenginesettings @@ -5,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/qwebenginesettings.pro b/tests/auto/core/qwebenginesettings/qwebenginesettings.pro deleted file mode 100644 index 70786e70f..000000000 --- a/tests/auto/core/qwebenginesettings/qwebenginesettings.pro +++ /dev/null @@ -1,2 +0,0 @@ -include(../tests.pri) -QT *= core-private gui-private 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 0f8d90a08..c12c0c45c 100644 --- a/tests/auto/core/qwebengineurlrequestinterceptor/CMakeLists.txt +++ b/tests/auto/core/qwebengineurlrequestinterceptor/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../util/util.cmake) include(../../httpserver/httpserver.cmake) @@ -6,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" @@ -31,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/qwebengineurlrequestinterceptor.pro b/tests/auto/core/qwebengineurlrequestinterceptor/qwebengineurlrequestinterceptor.pro deleted file mode 100644 index 9c239f1a7..000000000 --- a/tests/auto/core/qwebengineurlrequestinterceptor/qwebengineurlrequestinterceptor.pro +++ /dev/null @@ -1,2 +0,0 @@ -include(../tests.pri) -include(../../shared/http.pri) 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/resources/sw.html b/tests/auto/core/qwebengineurlrequestinterceptor/resources/sw.html index af44b45a2..fc3d9ded4 100644 --- a/tests/auto/core/qwebengineurlrequestinterceptor/resources/sw.html +++ b/tests/auto/core/qwebengineurlrequestinterceptor/resources/sw.html @@ -2,13 +2,40 @@ <html> <body> <script> + function logState(state) { + console.log("Service worker " + state) + } + + const registerServiceWorker = async () => { + try { + var serviceWorker; + const registration = await navigator.serviceWorker.register('/sw.js'); + if (registration.installing) { + serviceWorker = registration.installing; + } else if (registration.waiting) { + serviceWorker = registration.waiting; + } else if (registration.active) { + serviceWorker = registration.active; + } + } catch (error) { + console.error("Service worker registration error: ${error}"); + } + if (serviceWorker) { + logState(serviceWorker.state); + serviceWorker.addEventListener('statechange', function(e) { + logState(e.target.state); + }); + } + }; if ('serviceWorker' in navigator) { - window.addEventListener('load', function() { - navigator.serviceWorker.register('/sw.js').then(function(registration) { - console.log('ServiceWorker registration successful with scope: ', registration.scope); - }, function(err) { - console.error('ServiceWorker registration failed: ', err); - }); + registerServiceWorker(); + navigator.serviceWorker.ready.then((registration) => { + navigator.serviceWorker.onmessage = (event) => { + if (event.data && event.data.type === 'PONG') { + console.log("Service worker done"); + } + }; + registration.active.postMessage({type: 'PING'}); }); } </script> diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/resources/sw.js b/tests/auto/core/qwebengineurlrequestinterceptor/resources/sw.js index 2216e2a07..196a9ad67 100644 --- a/tests/auto/core/qwebengineurlrequestinterceptor/resources/sw.js +++ b/tests/auto/core/qwebengineurlrequestinterceptor/resources/sw.js @@ -1,3 +1,16 @@ self.addEventListener('install', function(event) { - console.log('ServiceWorker installed'); + event.waitUntil(self.skipWaiting()); +}); + +self.addEventListener('activate', function(event) { + event.waitUntil(self.clients.claim()); +}); +self.addEventListener('message', (event) => { + if (event.data && event.data.type === 'PING') { + self.clients.matchAll({includeUncontrolled: true, type: 'window'}).then((clients) => { + if (clients && clients.length) { + clients[0].postMessage({type: 'PONG'}); + } + }); + } }); diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp index 94669c4ca..7cea14c0c 100644 --- a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp +++ b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp @@ -1,38 +1,17 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses #include <util.h> #include <QtTest/QtTest> #include <QtWebEngineCore/qwebengineurlrequestinfo.h> +#include <QtWebEngineCore/private/qwebengineurlrequestinfo_p.h> #include <QtWebEngineCore/qwebengineurlrequestinterceptor.h> #include <QtWebEngineCore/qwebenginesettings.h> #include <QtWebEngineCore/qwebengineprofile.h> #include <QtWebEngineCore/qwebenginepage.h> +#include <QtWebEngineCore/qwebenginehttprequest.h> #include <httpserver.h> #include <httpreqrep.h> @@ -64,12 +43,18 @@ private Q_SLOTS: void requestInterceptorByResourceType_data(); void requestInterceptorByResourceType(); void firstPartyUrlHttp(); + void headers(); void customHeaders(); void initiator(); void jsServiceWorker(); void replaceInterceptor_data(); void replaceInterceptor(); void replaceOnIntercept(); + void multipleRedirects(); + void postWithBody_data(); + void postWithBody(); + void profilePreventsPageInterception_data(); + void profilePreventsPageInterception(); }; tst_QWebEngineUrlRequestInterceptor::tst_QWebEngineUrlRequestInterceptor() @@ -102,12 +87,14 @@ struct RequestInfo { , firstPartyUrl(info.firstPartyUrl()) , initiator(info.initiator()) , resourceType(info.resourceType()) + , headers(info.httpHeaders()) {} QUrl requestUrl; QUrl firstPartyUrl; QUrl initiator; int resourceType; + QHash<QByteArray, QByteArray> headers; }; static const QUrl kRedirectUrl = QUrl("qrc:///resources/content.html"); @@ -154,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) @@ -202,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: @@ -231,7 +241,7 @@ void tst_QWebEngineUrlRequestInterceptor::interceptRequest() QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); page.load(QUrl("qrc:///resources/index.html")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); QVariant success = loadSpy.takeFirst().takeFirst(); QVERIFY(success.toBool()); loadSpy.clear(); @@ -239,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()); @@ -247,22 +257,22 @@ void tst_QWebEngineUrlRequestInterceptor::interceptRequest() interceptor.shouldRedirect = true; page.load(QUrl("qrc:///resources/__placeholder__")); - QTRY_COMPARE(loadSpy.count(), 1); + 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(loadSpy.count(), 1); + 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 @@ -295,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")); } @@ -331,8 +341,8 @@ void tst_QWebEngineUrlRequestInterceptor::requestedUrl() page.setUrl(QUrl("qrc:///resources/__placeholder__")); QVERIFY(spy.wait()); - QTRY_COMPARE(spy.count(), 1); - 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")); @@ -340,15 +350,15 @@ void tst_QWebEngineUrlRequestInterceptor::requestedUrl() interceptor.shouldRedirect = false; page.setUrl(QUrl("qrc:/non-existent.html")); - QTRY_COMPARE(spy.count(), 2); - 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, 15000); - 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")); @@ -376,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() @@ -406,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() @@ -444,21 +454,21 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrlNestedIframes() QWebEnginePage page(&profile); QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); page.setUrl(requestUrl); - QTRY_COMPARE(loadSpy.count(), 1); + 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); @@ -534,11 +544,11 @@ void tst_QWebEngineUrlRequestInterceptor::requestInterceptorByResourceType() QWebEnginePage page(&profile); QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); page.setUrl(firstPartyUrl); - QTRY_COMPARE(loadSpy.count(), 1); + 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); @@ -602,6 +612,40 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrlHttp() QCOMPARE(info.firstPartyUrl, firstPartyUrl); } +void tst_QWebEngineUrlRequestInterceptor::headers() +{ + HttpServer httpServer; + httpServer.setResourceDirs({ QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + "/resources" }); + QVERIFY(httpServer.start()); + QWebEngineProfile profile; + TestRequestInterceptor interceptor(false); + profile.setUrlRequestInterceptor(&interceptor); + + QWebEnginePage page(&profile); + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + + QWebEngineHttpRequest request(httpServer.url("/content.html")); + request.setHeader("X-HEADERNAME", "HEADERVALUE"); + page.load(request); + QVERIFY(spy.wait()); + QVERIFY(interceptor.requestInfos.last().headers.contains("X-HEADERNAME")); + QCOMPARE(interceptor.requestInfos.last().headers.value("X-HEADERNAME"), + QByteArray("HEADERVALUE")); + + bool jsFinished = false; + + page.runJavaScript(R"( +var request = new XMLHttpRequest(); +request.open('GET', 'resource.html', /* async = */ false); +request.setRequestHeader('X-FOO', 'BAR'); +request.send(); +)", + [&](const QVariant &) { jsFinished = true; }); + QTRY_VERIFY(jsFinished); + QVERIFY(interceptor.requestInfos.last().headers.contains("X-FOO")); + QCOMPARE(interceptor.requestInfos.last().headers.value("X-FOO"), QByteArray("BAR")); +} + void tst_QWebEngineUrlRequestInterceptor::customHeaders() { // Create HTTP Server to parse the request. @@ -726,8 +770,7 @@ void tst_QWebEngineUrlRequestInterceptor::jsServiceWorker() HttpServer server; server.setResourceDirs({ QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + "/resources" }); QVERIFY(server.start()); - - QWebEngineProfile profile(QStringLiteral("Test")); + QWebEngineProfile profile; std::unique_ptr<ConsolePage> page; page.reset(new ConsolePage(&profile)); TestRequestInterceptor interceptor(/* intercept */ false); @@ -735,10 +778,17 @@ void tst_QWebEngineUrlRequestInterceptor::jsServiceWorker() QVERIFY(loadSync(page.get(), server.url("/sw.html"))); // We expect only one message here, because logging of services workers is not exposed in our API. - QTRY_COMPARE(page->messages.count(), 1); - QCOMPARE(page->levels.at(0), QWebEnginePage::InfoMessageLevel); + // 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.size(), 5, 20000); - QUrl firstPartyUrl = QUrl(server.url().toString() + "sw.js"); + QCOMPARE(page->levels.at(0), QWebEnginePage::InfoMessageLevel); + QCOMPARE(page->messages.at(0),QLatin1String("Service worker installing")); + QCOMPARE(page->messages.at(1),QLatin1String("Service worker installed")); + QCOMPARE(page->messages.at(2),QLatin1String("Service worker activating")); + QCOMPARE(page->messages.at(3),QLatin1String("Service worker activated")); + QCOMPARE(page->messages.at(4),QLatin1String("Service worker done")); + QUrl firstPartyUrl = QUrl(server.url().toString() + "sw.html"); QList<RequestInfo> infos; // Service Worker QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeServiceWorker)); @@ -804,7 +854,7 @@ void tst_QWebEngineUrlRequestInterceptor::replaceInterceptor() }); page.setUrl(server.url("/favicon.html")); - QTRY_COMPARE(spy.count(), 2); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 2, 20000); QTRY_VERIFY(fetchFinished); QString s; QDebug d(&s); @@ -848,7 +898,7 @@ void tst_QWebEngineUrlRequestInterceptor::replaceOnIntercept() }; page.setUrl(server.url("/favicon.html")); - QTRY_COMPARE(spy.count(), 1); + 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 @@ -869,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/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.qrc b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.qrc deleted file mode 100644 index 6a34635f7..000000000 --- a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.qrc +++ /dev/null @@ -1,24 +0,0 @@ -<!DOCTYPE RCC><RCC version="1.0"> - <qresource prefix="/"> - <file>resources/content.html</file> - <file>resources/favicon.html</file> - <file>resources/firstparty.html</file> - <file>resources/fontawesome.woff</file> - <file>resources/iframe.html</file> - <file>resources/iframe2.html</file> - <file>resources/iframe3.html</file> - <file>resources/image.html</file> - <file>resources/image_in_iframe.html</file> - <file>resources/index.html</file> - <file>resources/media.html</file> - <file>resources/media.mp4</file> - <file>resources/media_in_iframe.html</file> - <file>resources/resource.html</file> - <file>resources/resource_in_iframe.html</file> - <file>resources/script.js</file> - <file>resources/style.css</file> - <file>resources/sw.html</file> - <file>resources/sw.js</file> - <file>resources/icons/favicon.png</file> - </qresource> -</RCC> diff --git a/tests/auto/core/qwebengineurlrequestjob/CMakeLists.txt b/tests/auto/core/qwebengineurlrequestjob/CMakeLists.txt new file mode 100644 index 000000000..02b668313 --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestjob/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_test(tst_qwebengineurlrequestjob + SOURCES + tst_qwebengineurlrequestjob.cpp + LIBRARIES + Qt::WebEngineCore +) + +# Resources: +set(tst_qwebengineurlrequestjob_resource_files + "additionalResponseHeadersScript.html" + "requestBodyScript.html" +) + +qt_add_resources(tst_qwebengineurlrequestjob "tst_qwebengineurlrequestjob" + PREFIX + "/" + FILES + ${tst_qwebengineurlrequestjob_resource_files} +) diff --git a/tests/auto/core/qwebengineurlrequestjob/additionalResponseHeadersScript.html b/tests/auto/core/qwebengineurlrequestjob/additionalResponseHeadersScript.html new file mode 100644 index 000000000..a1b8a92d3 --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestjob/additionalResponseHeadersScript.html @@ -0,0 +1,12 @@ +<!doctype html> +<html> +<body> + <script type="text/javascript"> + var request = new XMLHttpRequest(); + request.open('GET', 'additionalresponseheadershandler:about', /* async = */ false); + request.send(); + var headers = request.getAllResponseHeaders(); + console.log("TST_ADDITIONALRESPONSEHEADERS;" + headers); + </script> +</body> +</html> diff --git a/tests/auto/core/qwebengineurlrequestjob/requestBodyScript.html b/tests/auto/core/qwebengineurlrequestjob/requestBodyScript.html new file mode 100644 index 000000000..95b7ff1bf --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestjob/requestBodyScript.html @@ -0,0 +1,12 @@ +<!doctype html> +<html> +<body> + <script type="text/javascript"> + var request = new XMLHttpRequest(); + request.open('POST', 'requestbodyhandler:about', /* async = */ false); + request.setRequestHeader('Content-Type', 'text/plain'); + request.send('reading request body successful'); + console.log("TST_REQUESTBODY;" + request.response); + </script> +</body> +</html> diff --git a/tests/auto/core/qwebengineurlrequestjob/tst_qwebengineurlrequestjob.cpp b/tests/auto/core/qwebengineurlrequestjob/tst_qwebengineurlrequestjob.cpp new file mode 100644 index 000000000..d48da2c44 --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestjob/tst_qwebengineurlrequestjob.cpp @@ -0,0 +1,187 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <QtTest/QtTest> +#include <QtWebEngineCore/qwebengineurlschemehandler.h> +#include <QtWebEngineCore/qwebengineurlscheme.h> +#include <QtWebEngineCore/qwebengineurlrequestjob.h> +#include <QtWebEngineCore/qwebengineprofile.h> +#include <QtWebEngineCore/qwebenginepage.h> + +class CustomPage : public QWebEnginePage +{ + Q_OBJECT + +public: + CustomPage(QWebEngineProfile *profile, QString compareStringPrefix, QString compareStringSuffix, + QObject *parent = nullptr) + : QWebEnginePage(profile, parent) + , m_compareStringPrefix(compareStringPrefix) + , m_compareStringSuffix(compareStringSuffix) + , m_comparedMessageCount(0) + { + } + + int comparedMessageCount() const { return m_comparedMessageCount; } + +signals: + void receivedMessage(); + +protected: + void javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel level, + const QString &message, int lineNumber, + const QString &sourceID) override + { + Q_UNUSED(level); + Q_UNUSED(lineNumber); + Q_UNUSED(sourceID); + + auto splitMessage = message.split(";"); + if (splitMessage[0] == m_compareStringPrefix) { + QCOMPARE(splitMessage[1], m_compareStringSuffix); + m_comparedMessageCount++; + emit receivedMessage(); + } + } + +private: + QString m_compareStringPrefix; + QString m_compareStringSuffix; + int m_comparedMessageCount; +}; + +class AdditionalResponseHeadersHandler : public QWebEngineUrlSchemeHandler +{ + Q_OBJECT +public: + AdditionalResponseHeadersHandler(bool addAdditionalResponseHeaders, QObject *parent = nullptr) + : QWebEngineUrlSchemeHandler(parent) + , m_addAdditionalResponseHeaders(addAdditionalResponseHeaders) + { + } + + void requestStarted(QWebEngineUrlRequestJob *job) override + { + QCOMPARE(job->requestUrl(), QUrl(schemeName + ":about")); + + QMultiMap<QByteArray, QByteArray> additionalResponseHeaders; + if (m_addAdditionalResponseHeaders) { + additionalResponseHeaders.insert(QByteArray::fromStdString("test1"), + QByteArray::fromStdString("test1VALUE")); + additionalResponseHeaders.insert(QByteArray::fromStdString("test2"), + QByteArray::fromStdString("test2VALUE")); + } + job->setAdditionalResponseHeaders(additionalResponseHeaders); + + QFile *file = new QFile(QStringLiteral(":additionalResponseHeadersScript.html"), job); + file->open(QIODevice::ReadOnly); + + job->reply(QByteArrayLiteral("text/html"), file); + } + + static void registerUrlScheme() + { + QWebEngineUrlScheme webUiScheme(schemeName); + webUiScheme.setFlags(QWebEngineUrlScheme::CorsEnabled); + QWebEngineUrlScheme::registerScheme(webUiScheme); + } + + const static inline QByteArray schemeName = + QByteArrayLiteral("additionalresponseheadershandler"); + +private: + bool m_addAdditionalResponseHeaders; +}; + +class RequestBodyHandler : public QWebEngineUrlSchemeHandler +{ + Q_OBJECT +public: + void requestStarted(QWebEngineUrlRequestJob *job) override + { + QCOMPARE(job->requestUrl(), QUrl(schemeName + ":about")); + QCOMPARE(job->requestMethod(), QByteArrayLiteral("POST")); + + QIODevice *requestBodyDevice = job->requestBody(); + requestBodyDevice->open(QIODevice::ReadOnly); + QByteArray requestBody = requestBodyDevice->readAll(); + requestBodyDevice->close(); + + QBuffer *buf = new QBuffer(job); + buf->open(QBuffer::ReadWrite); + buf->write(requestBody); + job->reply(QByteArrayLiteral("text/plain"), buf); + buf->close(); + } + + static void registerUrlScheme() + { + QWebEngineUrlScheme webUiScheme(schemeName); + webUiScheme.setFlags(QWebEngineUrlScheme::CorsEnabled + | QWebEngineUrlScheme::FetchApiAllowed); + QWebEngineUrlScheme::registerScheme(webUiScheme); + } + + const static inline QByteArray schemeName = QByteArrayLiteral("requestbodyhandler"); +}; + +class tst_QWebEngineUrlRequestJob : public QObject +{ + Q_OBJECT + +public: + tst_QWebEngineUrlRequestJob() { } + +private Q_SLOTS: + void initTestCase() + { + AdditionalResponseHeadersHandler::registerUrlScheme(); + RequestBodyHandler::registerUrlScheme(); + } + + void withAdditionalResponseHeaders_data() + { + QTest::addColumn<bool>("withHeaders"); + QTest::addColumn<QString>("expectedHeaders"); + QTest::newRow("headers enabled") + << true << "content-type: text/html\r\ntest1: test1value\r\ntest2: test2value\r\n"; + QTest::newRow("headers disabled") << false << "content-type: text/html\r\n"; + } + + void withAdditionalResponseHeaders() + { + QFETCH(bool, withHeaders); + QFETCH(QString, expectedHeaders); + + QWebEngineProfile profile; + + AdditionalResponseHeadersHandler handler(withHeaders); + profile.installUrlSchemeHandler(AdditionalResponseHeadersHandler::schemeName, &handler); + + CustomPage page(&profile, "TST_ADDITIONALRESPONSEHEADERS", expectedHeaders); + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + + page.load(QUrl("qrc:///additionalResponseHeadersScript.html")); + QVERIFY(spy.wait()); + QCOMPARE(page.comparedMessageCount(), 1); + } + + void requestBody() + { + QWebEngineProfile profile; + + RequestBodyHandler handler; + profile.installUrlSchemeHandler(RequestBodyHandler::schemeName, &handler); + + const QString expected = "reading request body successful"; + CustomPage page(&profile, "TST_REQUESTBODY", expected); + QSignalSpy spy(&page, SIGNAL(receivedMessage())); + + page.load(QUrl("qrc:///requestBodyScript.html")); + QVERIFY(spy.wait()); + QCOMPARE(page.comparedMessageCount(), 1); + } +}; + +QTEST_MAIN(tst_QWebEngineUrlRequestJob) +#include "tst_qwebengineurlrequestjob.moc" diff --git a/tests/auto/core/tests.pri b/tests/auto/core/tests.pri deleted file mode 100644 index 2f34f1735..000000000 --- a/tests/auto/core/tests.pri +++ /dev/null @@ -1,17 +0,0 @@ -TEMPLATE = app - -CONFIG += testcase - -VPATH += $$_PRO_FILE_PWD_ -TARGET = tst_$$TARGET - -SOURCES += $${TARGET}.cpp -INCLUDEPATH += $$PWD - -exists($$_PRO_FILE_PWD_/$${TARGET}.qrc): RESOURCES += $${TARGET}.qrc - -QT += testlib network webenginecore - -# This define is used by some tests to look up resources in the source tree -DEFINES += TESTS_SOURCE_DIR=\\\"$$PWD/\\\" -include(../embed_info_plist.pri) diff --git a/tests/auto/core/webenginedriver/CMakeLists.txt b/tests/auto/core/webenginedriver/CMakeLists.txt new file mode 100644 index 000000000..c8cf8b3ab --- /dev/null +++ b/tests/auto/core/webenginedriver/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_webenginedriver LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +add_subdirectory(test) +add_subdirectory(browser) + +add_dependencies(tst_webenginedriver + testbrowser +) diff --git a/tests/auto/core/webenginedriver/browser/CMakeLists.txt b/tests/auto/core/webenginedriver/browser/CMakeLists.txt new file mode 100644 index 000000000..25e162e7b --- /dev/null +++ b/tests/auto/core/webenginedriver/browser/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_executable(testbrowser + SOURCES + main.cpp + LIBRARIES + Qt::Core + Qt::Gui + Qt::WebEngineWidgets + OUTPUT_DIRECTORY + ${CMAKE_CURRENT_BINARY_DIR}/.. +) diff --git a/tests/auto/core/webenginedriver/browser/main.cpp b/tests/auto/core/webenginedriver/browser/main.cpp new file mode 100644 index 000000000..4b8f3513f --- /dev/null +++ b/tests/auto/core/webenginedriver/browser/main.cpp @@ -0,0 +1,21 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWebEngineCore/qwebenginepage.h> +#include <QtWebEngineWidgets/qwebengineview.h> +#include <QtWidgets/qapplication.h> + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + QWebEngineView view; + QObject::connect(view.page(), &QWebEnginePage::windowCloseRequested, &app, &QApplication::quit); + QObject::connect(&app, &QApplication::aboutToQuit, + []() { fprintf(stderr, "Test browser is about to quit.\n"); }); + + view.resize(100, 100); + view.show(); + + return app.exec(); +} diff --git a/tests/auto/core/webenginedriver/resources/input.html b/tests/auto/core/webenginedriver/resources/input.html new file mode 100644 index 000000000..c21458350 --- /dev/null +++ b/tests/auto/core/webenginedriver/resources/input.html @@ -0,0 +1,5 @@ +<html> +<body> + <input type="text" id="text_input"> +</body> +</html> diff --git a/tests/auto/core/webenginedriver/test/CMakeLists.txt b/tests/auto/core/webenginedriver/test/CMakeLists.txt new file mode 100644 index 000000000..041bf955b --- /dev/null +++ b/tests/auto/core/webenginedriver/test/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +include(../../../util/util.cmake) + +qt_internal_add_test(tst_webenginedriver + SOURCES + ../tst_webenginedriver.cpp + LIBRARIES + Qt::Network + Qt::WebEngineCore + Qt::WebEngineWidgets + Test::Util + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.." +) + +set(tst_webenginedriver_resource_files + "../resources/input.html" +) + +qt_internal_add_resource(tst_webenginedriver "tst_webenginedriver" + PREFIX + "/" + FILES + ${tst_webenginedriver_resource_files} +) diff --git a/tests/auto/core/webenginedriver/tst_webenginedriver.cpp b/tests/auto/core/webenginedriver/tst_webenginedriver.cpp new file mode 100644 index 000000000..cd3098b25 --- /dev/null +++ b/tests/auto/core/webenginedriver/tst_webenginedriver.cpp @@ -0,0 +1,631 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <QtTest/QtTest> + +#include <QtCore/qjsondocument.h> +#include <QtCore/qlibraryinfo.h> +#include <QtCore/qobject.h> +#include <QtCore/qprocess.h> +#include <QtGui/qimage.h> +#include <QtNetwork/qnetworkaccessmanager.h> +#include <QtNetwork/qnetworkreply.h> +#include <QtNetwork/qnetworkrequest.h> +#include <QtWebEngineCore/qtwebenginecoreglobal.h> +#include <QtWebEngineCore/qwebenginepage.h> +#include <QtWebEngineCore/qwebengineprofile.h> +#include <QtWebEngineWidgets/qwebengineview.h> + +#include <widgetutil.h> + +#define REMOTE_DEBUGGING_PORT 12345 +#define WEBENGINEDRIVER_PORT 9515 + +class DriverServer : public QObject +{ + Q_OBJECT + +public: + DriverServer(QProcessEnvironment processEnvironment = {}, QStringList processArguments = {}) + : m_serverURL(QUrl( + QLatin1String("http://localhost:%1").arg(QString::number(WEBENGINEDRIVER_PORT)))) + { + QString driverPath = QLibraryInfo::path(QLibraryInfo::LibraryExecutablesPath) + + QLatin1String("/webenginedriver"); +#if defined(Q_OS_WIN) + driverPath += QLatin1String(".exe"); +#endif + m_process.setProgram(driverPath); + + if (processArguments.isEmpty()) + processArguments + << QLatin1String("--port=%1").arg(QString::number(WEBENGINEDRIVER_PORT)); + m_process.setArguments(processArguments); + + if (!processEnvironment.isEmpty()) + m_process.setProcessEnvironment(processEnvironment); + + connect(&m_process, &QProcess::errorOccurred, [this](QProcess::ProcessError error) { + qWarning() << "WebEngineDriver error occurred:" << error; + dumpConsoleMessages(); + }); + + connect(&m_process, &QProcess::readyReadStandardError, [this]() { + QProcess::ProcessChannel tmp = m_process.readChannel(); + m_process.setReadChannel(QProcess::StandardError); + while (m_process.canReadLine()) { + QString line = QString::fromUtf8(m_process.readLine()); + if (line.endsWith(QLatin1Char('\n'))) + line.truncate(line.size() - 1); + m_stderr << line; + } + m_process.setReadChannel(tmp); + }); + + connect(&m_process, &QProcess::readyReadStandardOutput, [this]() { + QProcess::ProcessChannel tmp = m_process.readChannel(); + m_process.setReadChannel(QProcess::StandardOutput); + while (m_process.canReadLine()) { + QString line = QString::fromUtf8(m_process.readLine()); + if (line.endsWith(QLatin1Char('\n'))) + line.truncate(line.size() - 1); + m_stdout << line; + } + m_process.setReadChannel(tmp); + }); + } + + ~DriverServer() + { + if (m_process.state() != QProcess::Running) + return; + + if (!m_sessionId.isEmpty()) + deleteSession(); + + shutdown(); + } + + bool start() + { + if (!QFileInfo::exists(m_process.program())) { + qWarning() << "WebEngineDriver executable not found:" << m_process.program(); + return false; + } + + connect(&m_process, &QProcess::finished, + [this](int exitCode, QProcess::ExitStatus exitStatus) { + qWarning().nospace() + << "WebEngineDriver exited unexpectedly (exitCode: " << exitCode + << " exitStatus: " << exitStatus << "):"; + dumpConsoleMessages(); + }); + + m_process.start(); + + bool started = m_process.waitForStarted(); + if (!started) { + qWarning() << "Failed to start WebEngineDriver:" << m_process.errorString(); + return false; + } + + bool ready = QTest::qWaitFor( + [this]() { + if (m_process.state() != QProcess::Running) + return true; + + for (QString line : m_stdout) { + if (line.contains( + QLatin1String("WebEngineDriver was started successfully."))) + return true; + } + + return false; + }, + 10000); + + if (ready && m_process.state() != QProcess::Running) { + // Warning is already reported by handler of QProcess::finished() signal. + return false; + } + + if (!ready) { + if (m_stdout.empty()) + qWarning("Starting WebEngineDriver timed out."); + else + qWarning("Something went wrong while starting WebEngineDriver:"); + + dumpConsoleMessages(); + return false; + } + + return true; + } + + bool shutdown() + { + // Do not warn about unexpected exit. + disconnect(&m_process, &QProcess::finished, nullptr, nullptr); + + bool sent = sendCommand(QLatin1String("/shutdown")); + + bool finished = (m_process.state() == QProcess::NotRunning) || m_process.waitForFinished(); + if (!finished || !sent) + qWarning() << "Failed to properly shutdown WebEngineDriver:" << m_process.errorString(); + + return finished; + } + + bool sendCommand(QString command, const QJsonDocument ¶ms = {}, + QJsonDocument *result = nullptr) + { + if (command.contains(QLatin1String(":sessionId"))) { + if (m_sessionId.isEmpty()) { + qWarning("Unable to execute session command without session."); + return false; + } + + QStringList commandList = command.split(QLatin1Char('/')); + for (int i = 0; i < commandList.size(); ++i) { + if (commandList[i] == QLatin1String(":sessionId")) { + commandList[i] = m_sessionId; + break; + } + } + + command = commandList.join(QLatin1Char('/')); + } + + QNetworkReply::NetworkError replyError = QNetworkReply::NoError; + QString replyString; + + connect( + &m_qnam, &QNetworkAccessManager::finished, this, + [&replyError, &replyString](QNetworkReply *reply) { + replyError = reply->error(); + replyString = QString::fromUtf8(reply->readAll()); + }, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection)); + + QNetworkRequest request; + QUrl requestURL = m_serverURL; + + requestURL.setPath(command); + request.setUrl(requestURL); + + if (params.isEmpty()) { + m_qnam.get(request); + } else { + request.setHeader(QNetworkRequest::ContentTypeHeader, + QVariant::fromValue(QStringLiteral("application/json"))); + m_qnam.post(request, params.toJson(QJsonDocument::Compact)); + } + + bool ready = QTest::qWaitFor( + [&replyError, &replyString]() { + return replyError != QNetworkReply::NoError || !replyString.isEmpty(); + }, + 10000); + + if (!ready) { + qWarning() << "Command" << command << "timed out."; + dumpConsoleMessages(); + return false; + } + + if (replyError != QNetworkReply::NoError) { + qWarning() << "Network error:" << replyError; + if (!replyString.isEmpty()) { + QJsonDocument errorReply = QJsonDocument::fromJson(replyString.toLatin1()); + if (!errorReply.isNull()) { + QString error = errorReply["value"]["error"].toString(); + QString message = errorReply["value"]["message"].toString(); + if (!error.isEmpty() || message.isEmpty()) { + qWarning() << "error:" << error; + qWarning() << "message:" << message; + return false; + } + } + + qWarning() << replyString; + return false; + } + + dumpConsoleMessages(); + return false; + } + + if (result) { + if (replyString.isEmpty()) { + qWarning("Network reply is empty."); + return false; + } + + QJsonParseError jsonError; + *result = QJsonDocument::fromJson(replyString.toLatin1(), &jsonError); + + if (jsonError.error != QJsonParseError::NoError) { + qWarning() << "Unable to parse reply:" << jsonError.errorString(); + return false; + } + } + + return true; + } + + bool createSession(QJsonObject chromeOptions = {}, QString *sessionId = nullptr) + { + if (!m_sessionId.isEmpty()) { + qWarning("A session already exists."); + return false; + } + + QJsonObject root; + + if (chromeOptions.isEmpty()) { + // Connect to the test by default. + chromeOptions.insert( + QLatin1String("debuggerAddress"), + QLatin1String("localhost:%1").arg(QString::number(REMOTE_DEBUGGING_PORT))); + chromeOptions.insert(QLatin1String("w3c"), true); + } + + QJsonObject alwaysMatch; + alwaysMatch.insert(QLatin1String("goog:chromeOptions"), chromeOptions); + + QJsonObject seOptions; + seOptions.insert(QLatin1String("loggingPrefs"), QJsonObject()); + alwaysMatch.insert(QLatin1String("se:options"), seOptions); + + QJsonObject capabilities; + capabilities.insert(QLatin1String("alwaysMatch"), alwaysMatch); + root.insert(QLatin1String("capabilities"), capabilities); + + QJsonDocument params; + params.setObject(root); + + QJsonDocument sessionReply; + bool sent = sendCommand(QLatin1String("/session"), params, &sessionReply); + if (sent) { + m_sessionId = sessionReply["value"]["sessionId"].toString(); + if (sessionId) + *sessionId = m_sessionId; + } + + return sent; + } + + bool deleteSession() + { + if (m_sessionId.isEmpty()) { + qWarning("There is no active session."); + return false; + } + + QNetworkReply::NetworkError replyError = QNetworkReply::NoError; + QString replyString; + + connect( + &m_qnam, &QNetworkAccessManager::finished, this, + [&replyError, &replyString](QNetworkReply *reply) { + replyError = reply->error(); + replyString = QString::fromUtf8(reply->readAll()); + }, + static_cast<Qt::ConnectionType>(Qt::SingleShotConnection)); + + QNetworkRequest request; + QUrl requestURL = m_serverURL; + requestURL.setPath(QLatin1String("/session/%1").arg(m_sessionId)); + request.setUrl(requestURL); + m_qnam.deleteResource(request); + + bool ready = QTest::qWaitFor( + [&replyError, &replyString]() { + return replyError != QNetworkReply::NoError || !replyString.isEmpty(); + }, + 10000); + + if (!ready) { + qWarning("Deleting session timed out."); + return false; + } + + if (replyError != QNetworkReply::NoError) { + qWarning() << "Network error:" << replyError; + return false; + } + + return true; + } + + QStringList stderrLines() const { return m_stderr; } + + bool waitForMessageOnStderr(const QRegularExpression &re, QString *message = nullptr) const + { + return QTest::qWaitFor( + [this, &re, message]() { + for (QString line : m_stderr) { + if (line.contains(re)) { + if (message) + *message = line; + return true; + } + } + + return false; + }, + 10000); + } + +private: + void dumpConsoleMessages() + { + auto dumpLines = [](QStringList *lines) { + if (lines->empty()) + return; + + for (QString line : *lines) + qWarning() << qPrintable(line); + + lines->clear(); + }; + + dumpLines(&m_stdout); + dumpLines(&m_stderr); + } + + QProcess m_process; + QStringList m_stdout; + QStringList m_stderr; + + QUrl m_serverURL; + QString m_sessionId; + + QNetworkAccessManager m_qnam; +}; + +class tst_WebEngineDriver : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void status(); + void startBrowser(); + void navigate(); + void typeElement(); + void executeScript(); + void screenshot(); +}; + +void tst_WebEngineDriver::status() +{ + DriverServer driverServer; + QVERIFY(driverServer.start()); + + QJsonDocument statusReply; + QVERIFY(driverServer.sendCommand(QLatin1String("/status"), {}, &statusReply)); + + QString versionString = statusReply["value"]["build"]["version"].toString(); + QCOMPARE(qWebEngineChromiumVersion(), versionString.split(QLatin1Char(' '))[0]); + + bool ready = statusReply["value"]["ready"].toBool(); + QVERIFY(ready); +} + +void tst_WebEngineDriver::startBrowser() +{ + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(QLatin1String("QTWEBENGINE_REMOTE_DEBUGGING"), + QString::number(REMOTE_DEBUGGING_PORT)); + + QStringList args; + args << QLatin1String("--port=%1").arg(QString::number(WEBENGINEDRIVER_PORT)); + args << QLatin1String("--log-level=ALL"); + + DriverServer driverServer(env, args); + QVERIFY(driverServer.start()); + + QString testBrowserPath = + QCoreApplication::applicationDirPath() + QLatin1String("/testbrowser"); +#if defined(Q_OS_WIN) + testBrowserPath += QLatin1String(".exe"); +#endif + + QJsonArray chromeArgs; + chromeArgs.append(QLatin1String("enable-logging=stderr")); + chromeArgs.append(QLatin1String("v=1")); + // To force graceful shutdown. + chromeArgs.append(QLatin1String("log-net-log")); + + QJsonObject chromeOptions; + chromeOptions.insert(QLatin1String("binary"), testBrowserPath); + chromeOptions.insert(QLatin1String("args"), chromeArgs); + chromeOptions.insert(QLatin1String("w3c"), true); + QString sessionId; + QVERIFY(driverServer.createSession(chromeOptions, &sessionId)); + + // Test if the browser is started. + QVERIFY(driverServer.waitForMessageOnStderr(QRegularExpression( + QLatin1String("^DevTools listening on ws://127.0.0.1:%1/devtools/browser/") + .arg(QString::number(REMOTE_DEBUGGING_PORT))))); + QVERIFY(driverServer.waitForMessageOnStderr(QRegularExpression( + QLatin1String("^Remote debugging server started successfully. " + "Try pointing a Chromium-based browser to http://127.0.0.1:%1") + .arg(QString::number(REMOTE_DEBUGGING_PORT))))); + + // Check custom chromeArgs via logging. + QVERIFY(driverServer.waitForMessageOnStderr(QRegularExpression(QLatin1String("VERBOSE1")))); + + QJsonDocument statusReply; + QVERIFY(driverServer.sendCommand(QLatin1String("/status"), {}, &statusReply)); + bool ready = statusReply["value"]["ready"].toBool(); + QVERIFY(ready); + + QVERIFY(driverServer.deleteSession()); + + // Test if the browser is stopped. + QVERIFY(driverServer.waitForMessageOnStderr( + QRegularExpression(QLatin1String("Test browser is about to quit.")))); + QVERIFY(driverServer.waitForMessageOnStderr( + QRegularExpression(QLatin1String("\\[%1\\] RESPONSE Quit").arg(sessionId)))); +} + +void tst_WebEngineDriver::navigate() +{ + DriverServer driverServer; + QVERIFY(driverServer.start()); + + QWebEnginePage page; + QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); + + page.load(QUrl(QLatin1String("about:blank"))); + QTRY_COMPARE(loadSpy.size(), 1); + + QVERIFY(driverServer.createSession()); + + QUrl url = QUrl(QLatin1String("qrc:///resources/input.html")); + QString paramsString = QLatin1String("{\"url\": \"%1\"}").arg(url.toString()); + QJsonDocument params = QJsonDocument::fromJson(paramsString.toUtf8()); + QVERIFY(driverServer.sendCommand(QLatin1String("/session/:sessionId/url"), params)); + QTRY_COMPARE(loadSpy.size(), 2); + QVERIFY(loadSpy.at(1).at(0).toBool()); + QCOMPARE(url, page.url()); + + QVERIFY(driverServer.deleteSession()); +} + +void tst_WebEngineDriver::typeElement() +{ + DriverServer driverServer; + QVERIFY(driverServer.start()); + + QWebEngineView view; + view.resize(300, 100); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QUrl url = QUrl(QLatin1String("qrc:///resources/input.html")); + QSignalSpy loadSpy(view.page(), &QWebEnginePage::loadFinished); + view.load(url); + QTRY_COMPARE(loadSpy.size(), 1); + + QVERIFY(driverServer.createSession()); + + // Find <input id="text_input"> element and extract its id from the reply. + QString textInputId; + { + QString paramsString = QLatin1String( + "{\"using\": \"css selector\", \"value\": \"[id=\\\"text_input\\\"]\"}"); + QJsonDocument params = QJsonDocument::fromJson(paramsString.toUtf8()); + QJsonDocument elementReply; + QVERIFY(driverServer.sendCommand(QLatin1String("/session/:sessionId/element"), params, + &elementReply)); + + QVERIFY(!elementReply.isEmpty()); + QJsonObject value = elementReply["value"].toObject(); + QVERIFY(!value.isEmpty()); + QStringList keys = value.keys(); + QCOMPARE(keys.size(), 1); + textInputId = value[keys[0]].toString(); + QVERIFY(!textInputId.isEmpty()); + } + + // Type text into the input field. + QString inputText = QLatin1String("WebEngineDriver"); + { + QString command = QLatin1String("/session/:sessionId/element/%1/value").arg(textInputId); + + QJsonObject root; + root.insert(QLatin1String("text"), inputText); + QJsonArray value; + for (QChar ch : inputText) { + value.append(QJsonValue(ch)); + } + root.insert(QLatin1String("value"), value); + root.insert(QLatin1String("id"), textInputId); + QJsonDocument params; + params.setObject(root); + + QVERIFY(driverServer.sendCommand(command, params)); + + QTRY_COMPARE( + evaluateJavaScriptSync(view.page(), "document.getElementById('text_input').value") + .toString(), + inputText); + } + + QVERIFY(driverServer.deleteSession()); +} + +void tst_WebEngineDriver::executeScript() +{ + DriverServer driverServer; + QVERIFY(driverServer.start()); + + QUrl url = QUrl(QLatin1String("qrc:///resources/input.html")); + QWebEnginePage page; + QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); + + page.load(url); + QTRY_COMPARE(loadSpy.size(), 1); + QCOMPARE(page.title(), url.toString()); + QSignalSpy titleSpy(&page, &QWebEnginePage::titleChanged); + + QString newTitle = QLatin1String("WebEngineDriver Test Page"); + QString script = QLatin1String("document.title = '%1';" + "return document.title;") + .arg(newTitle); + + QVERIFY(driverServer.createSession()); + QString paramsString = QLatin1String("{\"script\": \"%1\", \"args\": []}").arg(script); + QJsonDocument params = QJsonDocument::fromJson(paramsString.toUtf8()); + QJsonDocument executeReply; + QVERIFY(driverServer.sendCommand(QLatin1String("/session/:sessionId/execute/sync"), params, + &executeReply)); + + QTRY_COMPARE(titleSpy.size(), 1); + QCOMPARE(executeReply["value"].toString(), newTitle); + QCOMPARE(page.title(), newTitle); + + QVERIFY(driverServer.deleteSession()); +} + +void tst_WebEngineDriver::screenshot() +{ + DriverServer driverServer; + QVERIFY(driverServer.start()); + + QWebEngineView view; + view.resize(300, 100); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QSignalSpy loadSpy(view.page(), &QWebEnginePage::loadFinished); + + view.setHtml(QLatin1String("<html><head><style>" + "html {background-color:red;}" + "</style></head><body></body></html>")); + QTRY_COMPARE(loadSpy.size(), 1); + + QVERIFY(driverServer.createSession()); + QJsonDocument screenshotReply; + QVERIFY(driverServer.sendCommand(QLatin1String("/session/:sessionId/screenshot"), {}, + &screenshotReply)); + + QByteArray base64 = screenshotReply["value"].toString().toLocal8Bit(); + QVERIFY(!base64.isEmpty()); + QImage screenshot; + screenshot.loadFromData(QByteArray::fromBase64(base64)); + QVERIFY(!screenshot.isNull()); + QCOMPARE(screenshot.pixel(screenshot.width() / 2, screenshot.height() / 2), 0xFFFF0000); + + QVERIFY(driverServer.deleteSession()); +} + +#define STRINGIFY_LITERAL(x) #x +#define STRINGIFY_EXPANDED(x) STRINGIFY_LITERAL(x) +static QByteArrayList params = QByteArrayList() + << "--remote-debugging-port=" STRINGIFY_EXPANDED(REMOTE_DEBUGGING_PORT); +W_QTEST_MAIN(tst_WebEngineDriver, params) + +#include "tst_webenginedriver.moc" diff --git a/tests/auto/embed_info_plist.pri b/tests/auto/embed_info_plist.pri deleted file mode 100644 index cca93c35e..000000000 --- a/tests/auto/embed_info_plist.pri +++ /dev/null @@ -1,13 +0,0 @@ -macos { - CONFIG -= app_bundle - - # QTBUG-57354 embed Info.plist so that certain fonts can be found in non-bundle apps - out_info = $$OUT_PWD/Info.plist - embed_info_plist.input = $$PWD/Info.plist.in - embed_info_plist.output = $$out_info - TARGET_HYPHENATED = $$replace(TARGET, [^a-zA-Z0-9-.], -) - QMAKE_SUBSTITUTES += embed_info_plist - QMAKE_LFLAGS += -Wl,-sectcreate,__TEXT,__info_plist,$$shell_quote($$out_info) - PRE_TARGETDEPS += $$out_info - QMAKE_DISTCLEAN += $$out_info -} diff --git a/tests/auto/httpserver/CMakeLists.txt b/tests/auto/httpserver/CMakeLists.txt index 7d4ddd030..0a1f881b9 100644 --- a/tests/auto/httpserver/CMakeLists.txt +++ b/tests/auto/httpserver/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + cmake_minimum_required(VERSION 3.18) project(minimal LANGUAGES CXX) diff --git a/tests/auto/httpserver/httpreqrep.cpp b/tests/auto/httpserver/httpreqrep.cpp index ee9dbbaa9..8b338ce4e 100644 --- a/tests/auto/httpserver/httpreqrep.cpp +++ b/tests/auto/httpserver/httpreqrep.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "httpreqrep.h" HttpReqRep::HttpReqRep(QTcpSocket *socket, QObject *parent) @@ -82,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()) { @@ -141,3 +121,5 @@ void HttpReqRep::handleDisconnected() m_state = State::DISCONNECTED; Q_EMIT closed(); } + +#include "moc_httpreqrep.cpp" diff --git a/tests/auto/httpserver/httpreqrep.h b/tests/auto/httpserver/httpreqrep.h index e1979e050..774c08eb1 100644 --- a/tests/auto/httpserver/httpreqrep.h +++ b/tests/auto/httpserver/httpreqrep.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef HTTPREQREP_H #define HTTPREQREP_H @@ -50,6 +25,7 @@ public: QByteArray requestMethod() const { return m_requestMethod; } QByteArray requestPath() const { return m_requestPath; } QByteArray requestHeader(const QByteArray &key) const; + bool hasRequestHeader(const QByteArray &key) const; // Response parameters (can be set until sendResponse()/close()). diff --git a/tests/auto/httpserver/httpserver.cmake b/tests/auto/httpserver/httpserver.cmake index e10c52b76..f98434e1a 100644 --- a/tests/auto/httpserver/httpserver.cmake +++ b/tests/auto/httpserver/httpserver.cmake @@ -1,8 +1,7 @@ -if (NOT TARGET Test::HttpServer) +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause - set(CMAKE_AUTOMOC ON) - set(CMAKE_AUTORCC ON) - set(CMAKE_AUTOUIC ON) +if (NOT TARGET Test::HttpServer) add_library(httpserver STATIC ${CMAKE_CURRENT_LIST_DIR}/httpreqrep.cpp @@ -13,6 +12,9 @@ if (NOT TARGET Test::HttpServer) ${CMAKE_CURRENT_LIST_DIR}/proxy_server.cpp ) + # moc binary might not exist in case of top level build + qt_autogen_tools(httpserver ENABLE_AUTOGEN_TOOLS "moc") + if(QT_FEATURE_ssl) target_sources(httpserver INTERFACE ${CMAKE_CURRENT_LIST_DIR}/httpsserver.h) endif() diff --git a/tests/auto/httpserver/httpserver.cpp b/tests/auto/httpserver/httpserver.cpp index 10147ae6c..e08af77e7 100644 --- a/tests/auto/httpserver/httpserver.cpp +++ b/tests/auto/httpserver/httpserver.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "httpserver.h" #include <QFile> @@ -49,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() @@ -104,12 +80,13 @@ 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)) { QMimeType mime = QMimeDatabase().mimeTypeForFileNameAndData(f.fileName(), &f); rr->setResponseHeader(QByteArrayLiteral("Content-Type"), mime.name().toUtf8()); + rr->setResponseHeader(QByteArrayLiteral("Access-Control-Allow-Origin"), QByteArrayLiteral("*")); rr->setResponseBody(f.readAll()); rr->sendResponse(); } else { @@ -146,3 +123,5 @@ QString HttpServer::sharedDataDir() const { return SERVER_SOURCE_DIR + QLatin1String("/data"); } + +#include "moc_httpserver.cpp" diff --git a/tests/auto/httpserver/httpserver.h b/tests/auto/httpserver/httpserver.h index acc742775..201eef4c6 100644 --- a/tests/auto/httpserver/httpserver.h +++ b/tests/auto/httpserver/httpserver.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef HTTPSERVER_H #define HTTPSERVER_H @@ -84,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 b257e69a7..d029851aa 100644 --- a/tests/auto/httpserver/httpsserver.h +++ b/tests/auto/httpserver/httpsserver.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef HTTPSSERVER_H #define HTTPSSERVER_H @@ -32,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 3c5588603..338415311 100644 --- a/tests/auto/httpserver/proxy_server.cpp +++ b/tests/auto/httpserver/proxy_server.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "proxy_server.h" #include <QDataStream> @@ -106,3 +81,5 @@ void ProxyServer::handleReadReady() m_data.clear(); emit requestReceived(); } + +#include "moc_proxy_server.cpp" diff --git a/tests/auto/httpserver/proxy_server.h b/tests/auto/httpserver/proxy_server.h index 57e69efe7..6be0c4e1a 100644 --- a/tests/auto/httpserver/proxy_server.h +++ b/tests/auto/httpserver/proxy_server.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #ifndef PROXY_SERVER_H #define PROXY_SERVER_H diff --git a/tests/auto/pdf/CMakeLists.txt b/tests/auto/pdf/CMakeLists.txt new file mode 100644 index 000000000..205bd24d0 --- /dev/null +++ b/tests/auto/pdf/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +add_subdirectory(qpdfbookmarkmodel) +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/pdf.pro b/tests/auto/pdf/pdf.pro deleted file mode 100644 index a2b3fcff2..000000000 --- a/tests/auto/pdf/pdf.pro +++ /dev/null @@ -1,8 +0,0 @@ -TEMPLATE = subdirs - -SUBDIRS = \ - qpdfbookmarkmodel \ - qpdfpagenavigation \ - qpdfpagerenderer - -qtHaveModule(printsupport): SUBDIRS += qpdfdocument diff --git a/tests/auto/pdf/qpdfbookmarkmodel/CMakeLists.txt b/tests/auto/pdf/qpdfbookmarkmodel/CMakeLists.txt new file mode 100644 index 000000000..729bc9138 --- /dev/null +++ b/tests/auto/pdf/qpdfbookmarkmodel/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_test(tst_qpdfbookmarkmodel + SOURCES + tst_qpdfbookmarkmodel.cpp + LIBRARIES + Qt::Gui + Qt::Network + Qt::Pdf + TESTDATA + pdf-sample.bookmarks.pdf + pdf-sample.bookmarks_pages.pdf +) + diff --git a/tests/auto/pdf/qpdfbookmarkmodel/qpdfbookmarkmodel.pro b/tests/auto/pdf/qpdfbookmarkmodel/qpdfbookmarkmodel.pro deleted file mode 100644 index 11a010637..000000000 --- a/tests/auto/pdf/qpdfbookmarkmodel/qpdfbookmarkmodel.pro +++ /dev/null @@ -1,5 +0,0 @@ -CONFIG += testcase -TARGET = tst_qpdfbookmarkmodel -QT += pdf testlib network -macos:CONFIG -= app_bundle -SOURCES += tst_qpdfbookmarkmodel.cpp diff --git a/tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp b/tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp index fddc98011..a1804e179 100644 --- a/tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp +++ b/tests/auto/pdf/qpdfbookmarkmodel/tst_qpdfbookmarkmodel.cpp @@ -1,38 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtTest/QtTest> @@ -57,8 +24,8 @@ private slots: void setLoadedDocument(); void unloadDocument(); void testTreeStructure(); - void testListStructure(); void testPageNumberRole(); + void testLocationAndZoomRoles(); }; void tst_QPdfBookmarkModel::emptyModel() @@ -66,7 +33,6 @@ void tst_QPdfBookmarkModel::emptyModel() QPdfBookmarkModel model; QVERIFY(!model.document()); - QCOMPARE(model.structureMode(), QPdfBookmarkModel::TreeMode); QCOMPARE(model.rowCount(), 0); QCOMPARE(model.columnCount(), 1); QCOMPARE(model.index(0, 0).isValid(), false); @@ -80,7 +46,6 @@ void tst_QPdfBookmarkModel::setEmptyDocument() model.setDocument(&document); QCOMPARE(model.document(), &document); - QCOMPARE(model.structureMode(), QPdfBookmarkModel::TreeMode); QCOMPARE(model.rowCount(), 0); QCOMPARE(model.columnCount(), 1); QCOMPARE(model.index(0, 0).isValid(), false); @@ -96,10 +61,10 @@ void tst_QPdfBookmarkModel::setEmptyDocumentAndLoad() QSignalSpy modelAboutToBeResetSpy(&model, SIGNAL(modelAboutToBeReset())); QSignalSpy modelResetSpy(&model, SIGNAL(modelReset())); - QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks.pdf")), QPdfDocument::NoError); + 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); } @@ -107,7 +72,7 @@ void tst_QPdfBookmarkModel::setEmptyDocumentAndLoad() void tst_QPdfBookmarkModel::setLoadedDocument() { QPdfDocument document; - QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks.pdf")), QPdfDocument::NoError); + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks.pdf")), QPdfDocument::Error::None); QPdfBookmarkModel model; @@ -116,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); } @@ -125,7 +90,7 @@ void tst_QPdfBookmarkModel::setLoadedDocument() void tst_QPdfBookmarkModel::unloadDocument() { QPdfDocument document; - QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks.pdf")), QPdfDocument::NoError); + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks.pdf")), QPdfDocument::Error::None); QPdfBookmarkModel model; model.setDocument(&document); @@ -137,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); } @@ -146,7 +111,7 @@ void tst_QPdfBookmarkModel::unloadDocument() void tst_QPdfBookmarkModel::testTreeStructure() { QPdfDocument document; - QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks.pdf")), QPdfDocument::NoError); + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks.pdf")), QPdfDocument::Error::None); QPdfBookmarkModel model; model.setDocument(&document); @@ -154,115 +119,76 @@ void tst_QPdfBookmarkModel::testTreeStructure() QCOMPARE(model.rowCount(), 3); const QModelIndex index1 = model.index(0, 0); - QCOMPARE(index1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 1")); - QCOMPARE(index1.data(QPdfBookmarkModel::LevelRole).toInt(), 0); + QCOMPARE(index1.data(int(QPdfBookmarkModel::Role::Title)).toString(), QLatin1String("Section 1")); + QCOMPARE(index1.data(int(QPdfBookmarkModel::Role::Level)).toInt(), 0); QCOMPARE(model.rowCount(index1), 2); const QModelIndex index1_1 = model.index(0, 0, index1); - QCOMPARE(index1_1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 1.1")); - QCOMPARE(index1_1.data(QPdfBookmarkModel::LevelRole).toInt(), 1); + QCOMPARE(index1_1.data(int(QPdfBookmarkModel::Role::Title)).toString(), QLatin1String("Section 1.1")); + QCOMPARE(index1_1.data(int(QPdfBookmarkModel::Role::Level)).toInt(), 1); QCOMPARE(model.rowCount(index1_1), 0); const QModelIndex index1_2 = model.index(1, 0, index1); - QCOMPARE(index1_2.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 1.2")); - QCOMPARE(index1_2.data(QPdfBookmarkModel::LevelRole).toInt(), 1); + QCOMPARE(index1_2.data(int(QPdfBookmarkModel::Role::Title)).toString(), QLatin1String("Section 1.2")); + QCOMPARE(index1_2.data(int(QPdfBookmarkModel::Role::Level)).toInt(), 1); QCOMPARE(model.rowCount(index1_2), 0); const QModelIndex index2 = model.index(1, 0); - QCOMPARE(index2.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2")); - QCOMPARE(index2.data(QPdfBookmarkModel::LevelRole).toInt(), 0); + QCOMPARE(index2.data(int(QPdfBookmarkModel::Role::Title)).toString(), QLatin1String("Section 2")); + QCOMPARE(index2.data(int(QPdfBookmarkModel::Role::Level)).toInt(), 0); QCOMPARE(model.rowCount(index2), 2); const QModelIndex index2_1 = model.index(0, 0, index2); - QCOMPARE(index2_1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2.1")); - QCOMPARE(index2_1.data(QPdfBookmarkModel::LevelRole).toInt(), 1); + QCOMPARE(index2_1.data(int(QPdfBookmarkModel::Role::Title)).toString(), QLatin1String("Section 2.1")); + QCOMPARE(index2_1.data(int(QPdfBookmarkModel::Role::Level)).toInt(), 1); QCOMPARE(model.rowCount(index2_1), 1); const QModelIndex index2_1_1 = model.index(0, 0, index2_1); - QCOMPARE(index2_1_1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2.1.1")); - QCOMPARE(index2_1_1.data(QPdfBookmarkModel::LevelRole).toInt(), 2); + QCOMPARE(index2_1_1.data(int(QPdfBookmarkModel::Role::Title)).toString(), QLatin1String("Section 2.1.1")); + QCOMPARE(index2_1_1.data(int(QPdfBookmarkModel::Role::Level)).toInt(), 2); QCOMPARE(model.rowCount(index2_1_1), 0); const QModelIndex index2_2 = model.index(1, 0, index2); - QCOMPARE(index2_2.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2.2")); - QCOMPARE(index2_2.data(QPdfBookmarkModel::LevelRole).toInt(), 1); + QCOMPARE(index2_2.data(int(QPdfBookmarkModel::Role::Title)).toString(), QLatin1String("Section 2.2")); + QCOMPARE(index2_2.data(int(QPdfBookmarkModel::Role::Level)).toInt(), 1); QCOMPARE(model.rowCount(index2_2), 0); const QModelIndex index3 = model.index(2, 0); - QCOMPARE(index3.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 3")); - QCOMPARE(index3.data(QPdfBookmarkModel::LevelRole).toInt(), 0); + QCOMPARE(index3.data(int(QPdfBookmarkModel::Role::Title)).toString(), QLatin1String("Section 3")); + QCOMPARE(index3.data(int(QPdfBookmarkModel::Role::Level)).toInt(), 0); QCOMPARE(model.rowCount(index3), 0); const QModelIndex index4 = model.index(3, 0); QCOMPARE(index4, QModelIndex()); } -void tst_QPdfBookmarkModel::testListStructure() +void tst_QPdfBookmarkModel::testPageNumberRole() { QPdfDocument document; - QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks.pdf")), QPdfDocument::NoError); + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks_pages.pdf")), QPdfDocument::Error::None); QPdfBookmarkModel model; model.setDocument(&document); - QSignalSpy modelAboutToBeResetSpy(&model, SIGNAL(modelAboutToBeReset())); - QSignalSpy modelResetSpy(&model, SIGNAL(modelReset())); - - model.setStructureMode(QPdfBookmarkModel::ListMode); - - QCOMPARE(modelAboutToBeResetSpy.count(), 1); - QCOMPARE(modelResetSpy.count(), 1); - - QCOMPARE(model.rowCount(), 8); + QCOMPARE(model.rowCount(), 3); const QModelIndex index1 = model.index(0, 0); - QCOMPARE(index1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 1")); - QCOMPARE(index1.data(QPdfBookmarkModel::LevelRole).toInt(), 0); - QCOMPARE(model.rowCount(index1), 0); - - const QModelIndex index1_1 = model.index(1, 0); - QCOMPARE(index1_1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 1.1")); - QCOMPARE(index1_1.data(QPdfBookmarkModel::LevelRole).toInt(), 1); - QCOMPARE(model.rowCount(index1_1), 0); - - const QModelIndex index1_2 = model.index(2, 0); - QCOMPARE(index1_2.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 1.2")); - QCOMPARE(index1_2.data(QPdfBookmarkModel::LevelRole).toInt(), 1); - QCOMPARE(model.rowCount(index1_2), 0); + QCOMPARE(index1.data(int(QPdfBookmarkModel::Role::Page)).toInt(), 0); - const QModelIndex index2 = model.index(3, 0); - QCOMPARE(index2.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2")); - QCOMPARE(index2.data(QPdfBookmarkModel::LevelRole).toInt(), 0); - QCOMPARE(model.rowCount(index2), 0); - - const QModelIndex index2_1 = model.index(4, 0); - QCOMPARE(index2_1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2.1")); - QCOMPARE(index2_1.data(QPdfBookmarkModel::LevelRole).toInt(), 1); - QCOMPARE(model.rowCount(index2_1), 0); - - const QModelIndex index2_1_1 = model.index(5, 0); - QCOMPARE(index2_1_1.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2.1.1")); - QCOMPARE(index2_1_1.data(QPdfBookmarkModel::LevelRole).toInt(), 2); - QCOMPARE(model.rowCount(index2_1_1), 0); - - const QModelIndex index2_2 = model.index(6, 0); - QCOMPARE(index2_2.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 2.2")); - QCOMPARE(index2_2.data(QPdfBookmarkModel::LevelRole).toInt(), 1); - QCOMPARE(model.rowCount(index2_2), 0); + const QModelIndex index2 = model.index(1, 0); + QCOMPARE(index2.data(int(QPdfBookmarkModel::Role::Page)).toInt(), 1); - const QModelIndex index3 = model.index(7, 0); - QCOMPARE(index3.data(QPdfBookmarkModel::TitleRole).toString(), QLatin1String("Section 3")); - QCOMPARE(index3.data(QPdfBookmarkModel::LevelRole).toInt(), 0); - QCOMPARE(model.rowCount(index3), 0); + const QModelIndex index2_1 = model.index(0, 0, index2); + QCOMPARE(index2_1.data(int(QPdfBookmarkModel::Role::Page)).toInt(), 1); - const QModelIndex index4 = model.index(8, 0); - QCOMPARE(index4, QModelIndex()); + const QModelIndex index3 = model.index(2, 0); + QCOMPARE(index3.data(int(QPdfBookmarkModel::Role::Page)).toInt(), 2); } -void tst_QPdfBookmarkModel::testPageNumberRole() +void tst_QPdfBookmarkModel::testLocationAndZoomRoles() { QPdfDocument document; - QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks_pages.pdf")), QPdfDocument::NoError); + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks_pages.pdf")), QPdfDocument::Error::None); QPdfBookmarkModel model; model.setDocument(&document); @@ -270,16 +196,20 @@ void tst_QPdfBookmarkModel::testPageNumberRole() QCOMPARE(model.rowCount(), 3); const QModelIndex index1 = model.index(0, 0); - QCOMPARE(index1.data(QPdfBookmarkModel::PageNumberRole).toInt(), 0); + QCOMPARE(index1.data(int(QPdfBookmarkModel::Role::Location)).toPoint(), QPoint(57, 69)); + QCOMPARE(index1.data(int(QPdfBookmarkModel::Role::Zoom)).toInt(), 0); const QModelIndex index2 = model.index(1, 0); - QCOMPARE(index2.data(QPdfBookmarkModel::PageNumberRole).toInt(), 1); + QCOMPARE(index2.data(int(QPdfBookmarkModel::Role::Location)).toPoint(), QPoint(57, 57)); + QCOMPARE(index2.data(int(QPdfBookmarkModel::Role::Zoom)).toInt(), 0); const QModelIndex index2_1 = model.index(0, 0, index2); - QCOMPARE(index2_1.data(QPdfBookmarkModel::PageNumberRole).toInt(), 1); + QCOMPARE(index2_1.data(int(QPdfBookmarkModel::Role::Location)).toPoint(), QPoint(57, 526)); + QCOMPARE(index2_1.data(int(QPdfBookmarkModel::Role::Zoom)).toInt(), 0); const QModelIndex index3 = model.index(2, 0); - QCOMPARE(index3.data(QPdfBookmarkModel::PageNumberRole).toInt(), 2); + QCOMPARE(index3.data(int(QPdfBookmarkModel::Role::Location)).toPoint(), QPoint(57, 402)); + QCOMPARE(index3.data(int(QPdfBookmarkModel::Role::Zoom)).toInt(), 0); } QTEST_MAIN(tst_QPdfBookmarkModel) diff --git a/tests/auto/pdf/qpdfdocument/BLACKLIST b/tests/auto/pdf/qpdfdocument/BLACKLIST deleted file mode 100644 index b8db556d6..000000000 --- a/tests/auto/pdf/qpdfdocument/BLACKLIST +++ /dev/null @@ -1,6 +0,0 @@ -[password] -* - -[passwordClearedOnClose] -* - diff --git a/tests/auto/pdf/qpdfdocument/CMakeLists.txt b/tests/auto/pdf/qpdfdocument/CMakeLists.txt new file mode 100644 index 000000000..b8300ef27 --- /dev/null +++ b/tests/auto/pdf/qpdfdocument/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_test(tst_qpdfdocument + SOURCES + tst_qpdfdocument.cpp + LIBRARIES + Qt::Gui + 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/qpdfdocument.pro b/tests/auto/pdf/qpdfdocument/qpdfdocument.pro deleted file mode 100644 index 8382a25e3..000000000 --- a/tests/auto/pdf/qpdfdocument/qpdfdocument.pro +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG += testcase -TARGET = tst_qpdfdocument -QT += pdf printsupport testlib network -macx:CONFIG -= app_bundle -SOURCES += tst_qpdfdocument.cpp - 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/test.pdf b/tests/auto/pdf/qpdfdocument/test.pdf Binary files differnew file mode 100644 index 000000000..0832dfbed --- /dev/null +++ b/tests/auto/pdf/qpdfdocument/test.pdf diff --git a/tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp b/tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp index 29b85fc89..d222bff0c 100644 --- a/tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp +++ b/tests/auto/pdf/qpdfdocument/tst_qpdfdocument.cpp @@ -1,38 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <QtTest/QtTest> @@ -40,7 +7,9 @@ #include <QPainter> #include <QPdfDocument> #include <QPrinter> +#include <QDateTime> #include <QTemporaryFile> +#include <QTimeZone> #include <QNetworkAccessManager> #include <QNetworkRequest> #include <QNetworkReply> @@ -66,16 +35,32 @@ private slots: void status(); void passwordClearedOnClose(); void metaData(); + void pageLabels(); + void getSelection_data(); + void getSelection(); + void getSelectionAtIndex_data(); + void getSelectionAtIndex(); + +private: + void consistencyCheck(QPdfDocument &doc) const; }; struct TemporaryPdf: public QTemporaryFile { TemporaryPdf(); QPageLayout pageLayout; + + static QString pageText(int page) { + switch (page) { + case 0: return QStringLiteral("Hello Page 1"); + case 1: return QStringLiteral("Hello Page 2"); + default: return {}; + } + } }; -TemporaryPdf::TemporaryPdf() +TemporaryPdf::TemporaryPdf():QTemporaryFile(QStringLiteral("qpdfdocument")) { open(); pageLayout = QPageLayout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF()); @@ -88,9 +73,9 @@ TemporaryPdf::TemporaryPdf() { QPainter painter(&printer); - painter.drawText(100, 100, QStringLiteral("Hello Page 1")); + painter.drawText(100, 100, pageText(0)); printer.newPage(); - painter.drawText(100, 100, QStringLiteral("Hello Page 2")); + painter.drawText(100, 100, pageText(1)); } } @@ -105,12 +90,12 @@ void tst_QPdfDocument::pageCount() QSignalSpy pageCountChangedSpy(&doc, SIGNAL(pageCountChanged(int))); QCOMPARE(doc.pageCount(), 0); - QCOMPARE(doc.load(tempPdf.fileName()), QPdfDocument::NoError); + 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.pageSize(0).toSize(), tempPdf.pageLayout.fullRectPoints().size()); + QCOMPARE(doc.pagePointSize(0).toSize(), tempPdf.pageLayout.fullRectPoints().size()); } void tst_QPdfDocument::loadFromIODevice() @@ -120,13 +105,26 @@ 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[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading); - QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Ready); - QCOMPARE(doc.error(), QPdfDocument::NoError); + 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); +} + +void tst_QPdfDocument::consistencyCheck(QPdfDocument &doc) const +{ + for (int i = 0; i < doc.pageCount(); ++i) { + const QString expected = TemporaryPdf::pageText(i); + QPdfSelection page = doc.getAllText(i); + QCOMPARE(page.text(), expected); + auto pageMoved = std::move(page); + QCOMPARE(pageMoved.text(), expected); + } } void tst_QPdfDocument::loadAsync() @@ -144,12 +142,14 @@ void tst_QPdfDocument::loadAsync() doc.load(reply.data()); - QCOMPARE(statusChangedSpy.count(), 2); - QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading); - QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Ready); + 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); } void tst_QPdfDocument::password() @@ -158,15 +158,15 @@ void tst_QPdfDocument::password() QSignalSpy passwordChangedSpy(&doc, SIGNAL(passwordChanged())); QCOMPARE(doc.pageCount(), 0); - QCOMPARE(doc.load(QFINDTESTDATA("pdf-sample.protected.pdf")), QPdfDocument::IncorrectPasswordError); - QCOMPARE(passwordChangedSpy.count(), 0); + QCOMPARE(doc.load(QFINDTESTDATA("pdf-sample.protected.pdf")), QPdfDocument::Error::IncorrectPassword); + QCOMPARE(passwordChangedSpy.size(), 0); doc.setPassword(QStringLiteral("WrongPassword")); - QCOMPARE(passwordChangedSpy.count(), 1); - QCOMPARE(doc.load(QFINDTESTDATA("pdf-sample.protected.pdf")), QPdfDocument::IncorrectPasswordError); - QCOMPARE(doc.status(), QPdfDocument::Error); + 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(doc.load(QFINDTESTDATA("pdf-sample.protected.pdf")), QPdfDocument::NoError); + QCOMPARE(passwordChangedSpy.size(), 2); + QCOMPARE(doc.load(QFINDTESTDATA("pdf-sample.protected.pdf")), QPdfDocument::Error::None); QCOMPARE(doc.pageCount(), 1); } @@ -180,21 +180,25 @@ void tst_QPdfDocument::close() doc.load(&tempPdf); - QCOMPARE(statusChangedSpy.count(), 2); - QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading); - QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Ready); - QCOMPARE(pageCountChangedSpy.count(), 1); + 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.size(), 1); QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount()); statusChangedSpy.clear(); pageCountChangedSpy.clear(); + consistencyCheck(doc); + if (QTest::currentTestFailed()) + return; + doc.close(); - QCOMPARE(statusChangedSpy.count(), 2); - QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Unloading); - QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Null); + 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()); } @@ -207,31 +211,33 @@ void tst_QPdfDocument::loadAfterClose() QSignalSpy pageCountChangedSpy(&doc, SIGNAL(pageCountChanged(int))); doc.load(&tempPdf); - QCOMPARE(statusChangedSpy.count(), 2); - QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading); - QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Ready); - QCOMPARE(pageCountChangedSpy.count(), 1); + 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.size(), 1); QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount()); statusChangedSpy.clear(); pageCountChangedSpy.clear(); doc.close(); - QCOMPARE(statusChangedSpy.count(), 2); - QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Unloading); - QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Null); - QCOMPARE(pageCountChangedSpy.count(), 1); + 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.size(), 1); QCOMPARE(pageCountChangedSpy[0][0].toInt(), doc.pageCount()); statusChangedSpy.clear(); pageCountChangedSpy.clear(); doc.load(&tempPdf); - QCOMPARE(statusChangedSpy.count(), 2); - QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading); - QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Ready); - QCOMPARE(doc.error(), QPdfDocument::NoError); + 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); } void tst_QPdfDocument::closeOnDestroy() @@ -249,10 +255,10 @@ void tst_QPdfDocument::closeOnDestroy() delete doc; - QCOMPARE(statusChangedSpy.count(), 2); - QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Unloading); - QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Null); - QCOMPARE(pageCountChangedSpy.count(), 1); + 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.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); } } @@ -277,35 +283,35 @@ void tst_QPdfDocument::status() TemporaryPdf tempPdf; QPdfDocument doc; - QCOMPARE(doc.status(), QPdfDocument::Null); + QCOMPARE(doc.status(), QPdfDocument::Status::Null); QSignalSpy statusChangedSpy(&doc, SIGNAL(statusChanged(QPdfDocument::Status))); // open existing document doc.load(&tempPdf); - QCOMPARE(statusChangedSpy.count(), 2); - QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading); - QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Ready); + 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(); - QCOMPARE(doc.status(), QPdfDocument::Ready); + QCOMPARE(doc.status(), QPdfDocument::Status::Ready); // close document doc.close(); - QCOMPARE(statusChangedSpy.count(), 2); - QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Unloading); - QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Null); + 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(); - QCOMPARE(doc.status(), QPdfDocument::Null); + QCOMPARE(doc.status(), QPdfDocument::Status::Null); // try to open non-existing document doc.load(QFINDTESTDATA("does-not-exist.pdf")); - QCOMPARE(statusChangedSpy.count(), 2); - QCOMPARE(statusChangedSpy[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading); - QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Error); - QCOMPARE(doc.status(), QPdfDocument::Error); + 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); statusChangedSpy.clear(); // try to open non-existing document asynchronously @@ -320,15 +326,15 @@ 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[0][0].value<QPdfDocument::Status>(), QPdfDocument::Loading); - QCOMPARE(statusChangedSpy[1][0].value<QPdfDocument::Status>(), QPdfDocument::Error); + 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(doc.load(QFINDTESTDATA("pdf-sample.protected.pdf")), QPdfDocument::NoError); + 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() @@ -358,26 +364,118 @@ void tst_QPdfDocument::metaData() QPdfDocument doc; // a closed document does not return any meta data - QCOMPARE(doc.metaData(QPdfDocument::Title).toString(), QString()); - QCOMPARE(doc.metaData(QPdfDocument::Subject).toString(), QString()); - QCOMPARE(doc.metaData(QPdfDocument::Author).toString(), QString()); - QCOMPARE(doc.metaData(QPdfDocument::Keywords).toString(), QString()); - QCOMPARE(doc.metaData(QPdfDocument::Producer).toString(), QString()); - QCOMPARE(doc.metaData(QPdfDocument::Creator).toString(), QString()); - QCOMPARE(doc.metaData(QPdfDocument::CreationDate).toDateTime(), QDateTime()); - QCOMPARE(doc.metaData(QPdfDocument::ModificationDate).toDateTime(), QDateTime()); + QCOMPARE(doc.metaData(QPdfDocument::MetaDataField::Title).toString(), QString()); + QCOMPARE(doc.metaData(QPdfDocument::MetaDataField::Subject).toString(), QString()); + QCOMPARE(doc.metaData(QPdfDocument::MetaDataField::Author).toString(), QString()); + QCOMPARE(doc.metaData(QPdfDocument::MetaDataField::Keywords).toString(), QString()); + QCOMPARE(doc.metaData(QPdfDocument::MetaDataField::Producer).toString(), QString()); + QCOMPARE(doc.metaData(QPdfDocument::MetaDataField::Creator).toString(), QString()); + QCOMPARE(doc.metaData(QPdfDocument::MetaDataField::CreationDate).toDateTime(), QDateTime()); + QCOMPARE(doc.metaData(QPdfDocument::MetaDataField::ModificationDate).toDateTime(), QDateTime()); - QCOMPARE(doc.load(QFINDTESTDATA("pdf-sample.metadata.pdf")), QPdfDocument::NoError); + QCOMPARE(doc.load(QFINDTESTDATA("pdf-sample.metadata.pdf")), QPdfDocument::Error::None); // check for proper meta data from sample document - QCOMPARE(doc.metaData(QPdfDocument::Title).toString(), QString::fromLatin1("Qt PDF Unit Test Document")); - QCOMPARE(doc.metaData(QPdfDocument::Subject).toString(), QString::fromLatin1("A test for meta data access")); - QCOMPARE(doc.metaData(QPdfDocument::Author).toString(), QString::fromLatin1("John Doe")); - QCOMPARE(doc.metaData(QPdfDocument::Keywords).toString(), QString::fromLatin1("meta data keywords")); - QCOMPARE(doc.metaData(QPdfDocument::Producer).toString(), QString::fromLatin1("LibreOffice 5.1")); - QCOMPARE(doc.metaData(QPdfDocument::Creator).toString(), QString::fromLatin1("Writer")); - QCOMPARE(doc.metaData(QPdfDocument::CreationDate).toDateTime(), QDateTime(QDate(2016, 8, 7), QTime(7, 3, 6), Qt::UTC)); - QCOMPARE(doc.metaData(QPdfDocument::ModificationDate).toDateTime(), QDateTime(QDate(2016, 8, 8), QTime(8, 3, 6), Qt::UTC)); + QCOMPARE(doc.metaData(QPdfDocument::MetaDataField::Title).toString(), QString::fromLatin1("Qt PDF Unit Test Document")); + QCOMPARE(doc.metaData(QPdfDocument::MetaDataField::Subject).toString(), QString::fromLatin1("A test for meta data access")); + QCOMPARE(doc.metaData(QPdfDocument::MetaDataField::Author).toString(), QString::fromLatin1("John Doe")); + 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), QTimeZone::UTC)); + QCOMPARE(doc.metaData(QPdfDocument::MetaDataField::ModificationDate).toDateTime(), + QDateTime(QDate(2016, 8, 8), QTime(8, 3, 6), QTimeZone::UTC)); +} + +void tst_QPdfDocument::pageLabels() +{ + QPdfDocument doc; + QCOMPARE(doc.load(QFINDTESTDATA("test.pdf")), QPdfDocument::Error::None); + QCOMPARE(doc.pageCount(), 3); + QCOMPARE(doc.pageLabel(0), "Qt"); + QCOMPARE(doc.pageLabel(1), "1"); + 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) diff --git a/tests/auto/pdf/qpdfpagenavigation/qpdfpagenavigation.pro b/tests/auto/pdf/qpdfpagenavigation/qpdfpagenavigation.pro deleted file mode 100644 index 8de99543f..000000000 --- a/tests/auto/pdf/qpdfpagenavigation/qpdfpagenavigation.pro +++ /dev/null @@ -1,5 +0,0 @@ -CONFIG += testcase -TARGET = tst_qpdfpagenavigation -QT += pdf testlib network -macos:CONFIG -= app_bundle -SOURCES += tst_qpdfpagenavigation.cpp diff --git a/tests/auto/pdf/qpdfpagenavigation/tst_qpdfpagenavigation.cpp b/tests/auto/pdf/qpdfpagenavigation/tst_qpdfpagenavigation.cpp deleted file mode 100644 index ff6a02750..000000000 --- a/tests/auto/pdf/qpdfpagenavigation/tst_qpdfpagenavigation.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include <QtTest/QtTest> - -#include <QPdfDocument> -#include <QPdfPageNavigation> - -class tst_QPdfPageNavigation: public QObject -{ - Q_OBJECT - -private slots: - void defaultValues(); - void setEmptyDocument(); - void setEmptyDocumentAndLoad(); - void setLoadedDocument(); - void unloadDocument(); - void navigate(); -}; - -void tst_QPdfPageNavigation::defaultValues() -{ - QPdfPageNavigation pageNavigation; - - QCOMPARE(pageNavigation.document(), nullptr); - QCOMPARE(pageNavigation.currentPage(), 0); - QCOMPARE(pageNavigation.pageCount(), 0); - QCOMPARE(pageNavigation.canGoToPreviousPage(), false); - QCOMPARE(pageNavigation.canGoToNextPage(), false); -} - -void tst_QPdfPageNavigation::setEmptyDocument() -{ - QPdfDocument document; - QPdfPageNavigation pageNavigation; - - pageNavigation.setDocument(&document); - - QCOMPARE(pageNavigation.document(), &document); - QCOMPARE(pageNavigation.currentPage(), 0); - QCOMPARE(pageNavigation.pageCount(), 0); - QCOMPARE(pageNavigation.canGoToPreviousPage(), false); - QCOMPARE(pageNavigation.canGoToNextPage(), false); -} - -void tst_QPdfPageNavigation::setEmptyDocumentAndLoad() -{ - QPdfDocument document; - QPdfPageNavigation pageNavigation; - - pageNavigation.setDocument(&document); - - QSignalSpy currentPageChangedSpy(&pageNavigation, &QPdfPageNavigation::currentPageChanged); - QSignalSpy pageCountChangedSpy(&pageNavigation, &QPdfPageNavigation::pageCountChanged); - QSignalSpy canGoToPreviousPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToPreviousPageChanged); - QSignalSpy canGoToNextPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToNextPageChanged); - - QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagenavigation.pdf")), QPdfDocument::NoError); - - QCOMPARE(currentPageChangedSpy.count(), 0); // current page stays '0' - QCOMPARE(pageCountChangedSpy.count(), 1); - QCOMPARE(pageCountChangedSpy[0][0].toInt(), 3); - QCOMPARE(canGoToPreviousPageChangedSpy.count(), 0); // still no previous page available - QCOMPARE(canGoToNextPageChangedSpy.count(), 1); - QCOMPARE(canGoToNextPageChangedSpy[0][0].toBool(), true); -} - -void tst_QPdfPageNavigation::setLoadedDocument() -{ - QPdfDocument document; - QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagenavigation.pdf")), QPdfDocument::NoError); - - QPdfPageNavigation pageNavigation; - - QSignalSpy currentPageChangedSpy(&pageNavigation, &QPdfPageNavigation::currentPageChanged); - QSignalSpy pageCountChangedSpy(&pageNavigation, &QPdfPageNavigation::pageCountChanged); - QSignalSpy canGoToPreviousPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToPreviousPageChanged); - QSignalSpy canGoToNextPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToNextPageChanged); - - pageNavigation.setDocument(&document); - - QCOMPARE(currentPageChangedSpy.count(), 0); // current page stays '0' - QCOMPARE(pageCountChangedSpy.count(), 1); - QCOMPARE(pageCountChangedSpy[0][0].toInt(), 3); - QCOMPARE(canGoToPreviousPageChangedSpy.count(), 0); // still no previous page available - QCOMPARE(canGoToNextPageChangedSpy.count(), 1); - QCOMPARE(canGoToNextPageChangedSpy[0][0].toBool(), true); -} - -void tst_QPdfPageNavigation::unloadDocument() -{ - QPdfDocument document; - QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagenavigation.pdf")), QPdfDocument::NoError); - - QPdfPageNavigation pageNavigation; - pageNavigation.setDocument(&document); - - QSignalSpy currentPageChangedSpy(&pageNavigation, &QPdfPageNavigation::currentPageChanged); - QSignalSpy pageCountChangedSpy(&pageNavigation, &QPdfPageNavigation::pageCountChanged); - QSignalSpy canGoToPreviousPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToPreviousPageChanged); - QSignalSpy canGoToNextPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToNextPageChanged); - - document.close(); - - QCOMPARE(currentPageChangedSpy.count(), 0); // current page stays '0' - QCOMPARE(pageCountChangedSpy.count(), 1); - QCOMPARE(pageCountChangedSpy[0][0].toInt(), 0); - QCOMPARE(canGoToPreviousPageChangedSpy.count(), 0); // still no previous page available - QCOMPARE(canGoToNextPageChangedSpy.count(), 1); - QCOMPARE(canGoToNextPageChangedSpy[0][0].toBool(), false); -} - -void tst_QPdfPageNavigation::navigate() -{ - QPdfDocument document; - QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagenavigation.pdf")), QPdfDocument::NoError); - - QPdfPageNavigation pageNavigation; - pageNavigation.setDocument(&document); - - QSignalSpy currentPageChangedSpy(&pageNavigation, &QPdfPageNavigation::currentPageChanged); - QSignalSpy canGoToPreviousPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToPreviousPageChanged); - QSignalSpy canGoToNextPageChangedSpy(&pageNavigation, &QPdfPageNavigation::canGoToNextPageChanged); - - QCOMPARE(pageNavigation.currentPage(), 0); - - // try to go to previous page while there is none - QCOMPARE(pageNavigation.canGoToPreviousPage(), false); - pageNavigation.goToPreviousPage(); - QCOMPARE(canGoToPreviousPageChangedSpy.count(), 0); - QCOMPARE(pageNavigation.currentPage(), 0); - QCOMPARE(pageNavigation.canGoToPreviousPage(), false); - - // try to go to next page - QCOMPARE(pageNavigation.canGoToNextPage(), true); - pageNavigation.goToNextPage(); - QCOMPARE(canGoToPreviousPageChangedSpy.count(), 1); - QCOMPARE(canGoToNextPageChangedSpy.count(), 0); - QCOMPARE(currentPageChangedSpy.count(), 1); - QCOMPARE(pageNavigation.currentPage(), 1); - QCOMPARE(pageNavigation.canGoToPreviousPage(), true); - - currentPageChangedSpy.clear(); - canGoToPreviousPageChangedSpy.clear(); - canGoToNextPageChangedSpy.clear(); - - // try to go to last page - pageNavigation.setCurrentPage(2); - QCOMPARE(canGoToPreviousPageChangedSpy.count(), 0); - QCOMPARE(canGoToNextPageChangedSpy.count(), 1); - QCOMPARE(currentPageChangedSpy.count(), 1); - QCOMPARE(pageNavigation.currentPage(), 2); - QCOMPARE(pageNavigation.canGoToNextPage(), false); - - // check that invalid requests are ignored - pageNavigation.setCurrentPage(-1); - QCOMPARE(pageNavigation.currentPage(), 2); - - pageNavigation.setCurrentPage(3); - QCOMPARE(pageNavigation.currentPage(), 2); -} - -QTEST_MAIN(tst_QPdfPageNavigation) - -#include "tst_qpdfpagenavigation.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/qpdfpagenavigation/pdf-sample.pagenavigation.pdf b/tests/auto/pdf/qpdfpagenavigator/pdf-sample.bookmarks_pages.pdf Binary files differindex c4e1aa36e..c4e1aa36e 100644 --- a/tests/auto/pdf/qpdfpagenavigation/pdf-sample.pagenavigation.pdf +++ b/tests/auto/pdf/qpdfpagenavigator/pdf-sample.bookmarks_pages.pdf diff --git a/tests/auto/pdf/qpdfpagenavigator/tst_qpdfpagenavigator.cpp b/tests/auto/pdf/qpdfpagenavigator/tst_qpdfpagenavigator.cpp new file mode 100644 index 000000000..327a9f36a --- /dev/null +++ b/tests/auto/pdf/qpdfpagenavigator/tst_qpdfpagenavigator.cpp @@ -0,0 +1,70 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + + +#include <QtTest/QtTest> + +#include <QPdfDocument> +#include <QPdfView> +#include <QPdfPageNavigator> + +class tst_QPdfPageNavigator: public QObject +{ + Q_OBJECT + +private slots: + void offScreenSignals(); +}; + +void tst_QPdfPageNavigator::offScreenSignals() +{ + QPdfDocument document; + QPdfView pdfView; + QPdfPageNavigator *navigator = pdfView.pageNavigator(); + + QSignalSpy currentPageChanged(navigator, &QPdfPageNavigator::currentPageChanged); + QSignalSpy currentLocationChanged(navigator, &QPdfPageNavigator::currentLocationChanged); + QSignalSpy backAvailableChanged(navigator, &QPdfPageNavigator::backAvailableChanged); + QSignalSpy forwardAvailableChanged(navigator, &QPdfPageNavigator::forwardAvailableChanged); + QSignalSpy jumped(navigator, &QPdfPageNavigator::jumped); + + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.bookmarks_pages.pdf")), QPdfDocument::Error::None); + QVERIFY2(document.pageCount() == 3, "Test document has changed! 3 pages expected."); + pdfView.setDocument(&document); + + // Start with a clean history + QCOMPARE(forwardAvailableChanged.count(), 0); + QCOMPARE(backAvailableChanged.count(), 0); + + navigator->jump(3, QPoint()); + QCOMPARE(forwardAvailableChanged.count(), 0); + QCOMPARE(backAvailableChanged.count(), 1); + QCOMPARE(currentPageChanged.count(), 1); + QCOMPARE(currentLocationChanged.count(), 0); + QCOMPARE(jumped.count(), 1); + + navigator->jump(1, QPoint()); + QCOMPARE(forwardAvailableChanged.count(), 0); + QCOMPARE(backAvailableChanged.count(), 1); + QCOMPARE(currentPageChanged.count(), 2); + QCOMPARE(currentLocationChanged.count(), 0); + QCOMPARE(jumped.count(), 2); + + navigator->back(); + QCOMPARE(forwardAvailableChanged.count(), 1); + QCOMPARE(backAvailableChanged.count(), 1); + QCOMPARE(currentPageChanged.count(), 3); + QCOMPARE(currentLocationChanged.count(), 0); + QCOMPARE(jumped.count(), 3); + + navigator->forward(); + QCOMPARE(forwardAvailableChanged.count(), 2); + QCOMPARE(backAvailableChanged.count(), 1); + QCOMPARE(currentPageChanged.count(), 4); + QCOMPARE(currentLocationChanged.count(), 0); + QCOMPARE(jumped.count(), 4); +} + +QTEST_MAIN(tst_QPdfPageNavigator) + +#include "tst_qpdfpagenavigator.moc" diff --git a/tests/auto/pdf/qpdfpagerenderer/CMakeLists.txt b/tests/auto/pdf/qpdfpagerenderer/CMakeLists.txt new file mode 100644 index 000000000..53a68fe59 --- /dev/null +++ b/tests/auto/pdf/qpdfpagerenderer/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_test(tst_qpdfpagerenderer + SOURCES + tst_qpdfpagerenderer.cpp + LIBRARIES + Qt::Gui + Qt::Network + Qt::Pdf + TESTDATA + pdf-sample.pagerenderer.pdf +) + diff --git a/tests/auto/pdf/qpdfpagerenderer/qpdfpagerenderer.pro b/tests/auto/pdf/qpdfpagerenderer/qpdfpagerenderer.pro deleted file mode 100644 index 9ccb4e82c..000000000 --- a/tests/auto/pdf/qpdfpagerenderer/qpdfpagerenderer.pro +++ /dev/null @@ -1,5 +0,0 @@ -CONFIG += testcase -TARGET = tst_qpdfpagerenderer -QT += pdf testlib network -macos:CONFIG -= app_bundle -SOURCES += tst_qpdfpagerenderer.cpp diff --git a/tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp b/tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp index 534fbd9ce..39d32df0b 100644 --- a/tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp +++ b/tests/auto/pdf/qpdfpagerenderer/tst_qpdfpagerenderer.cpp @@ -1,38 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias König <tobias.koenig@kdab.com> -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the QtPDF module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 #include <QPdfDocument> #include <QPdfPageRenderer> @@ -89,7 +56,7 @@ void tst_QPdfPageRenderer::withLoadedDocumentSingleThreaded() QPdfPageRenderer pageRenderer; pageRenderer.setDocument(&document); - QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagerenderer.pdf")), QPdfDocument::NoError); + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagerenderer.pdf")), QPdfDocument::Error::None); QSignalSpy pageRenderedSpy(&pageRenderer, &QPdfPageRenderer::pageRendered); @@ -97,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); @@ -112,7 +79,7 @@ void tst_QPdfPageRenderer::withLoadedDocumentMultiThreaded() pageRenderer.setDocument(&document); pageRenderer.setRenderMode(QPdfPageRenderer::RenderMode::MultiThreaded); - QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagerenderer.pdf")), QPdfDocument::NoError); + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagerenderer.pdf")), QPdfDocument::Error::None); QSignalSpy pageRenderedSpy(&pageRenderer, &QPdfPageRenderer::pageRendered); @@ -120,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); @@ -133,7 +100,7 @@ void tst_QPdfPageRenderer::switchingRenderMode() QPdfPageRenderer pageRenderer; pageRenderer.setDocument(&document); - QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagerenderer.pdf")), QPdfDocument::NoError); + QCOMPARE(document.load(QFINDTESTDATA("pdf-sample.pagerenderer.pdf")), QPdfDocument::Error::None); QSignalSpy pageRenderedSpy(&pageRenderer, &QPdfPageRenderer::pageRendered); @@ -141,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); @@ -157,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); @@ -171,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 new file mode 100644 index 000000000..668d1ea36 --- /dev/null +++ b/tests/auto/pdf/qpdfsearchmodel/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_test(tst_qpdfsearchmodel + SOURCES + tst_qpdfsearchmodel.cpp + LIBRARIES + Qt::Gui + Qt::Network + Qt::Pdf + TESTDATA + rotated_text.pdf + tagged_mcr_multipage.pdf + test.pdf +) diff --git a/tests/auto/pdf/qpdfsearchmodel/qpdfsearchmodel.pro b/tests/auto/pdf/qpdfsearchmodel/qpdfsearchmodel.pro deleted file mode 100644 index 205fef175..000000000 --- a/tests/auto/pdf/qpdfsearchmodel/qpdfsearchmodel.pro +++ /dev/null @@ -1,5 +0,0 @@ -CONFIG += testcase -TARGET = tst_qpdfsearchmodel -QT += pdf testlib network -macos:CONFIG -= app_bundle -SOURCES += tst_qpdfsearchmodel.cpp 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/test.pdf b/tests/auto/pdf/qpdfsearchmodel/test.pdf Binary files differindex a9dc1bc29..0832dfbed 100644 --- a/tests/auto/pdf/qpdfsearchmodel/test.pdf +++ b/tests/auto/pdf/qpdfsearchmodel/test.pdf diff --git a/tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp b/tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp index c0706faaf..cf71b148e 100644 --- a/tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp +++ b/tests/auto/pdf/qpdfsearchmodel/tst_qpdfsearchmodel.cpp @@ -1,38 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL3$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 #include <QtTest/QtTest> @@ -40,6 +7,8 @@ #include <QPdfDocument> #include <QPdfSearchModel> +Q_LOGGING_CATEGORY(lcTests, "qt.pdf.tests") + class tst_QPdfSearchModel: public QObject { Q_OBJECT @@ -48,20 +17,51 @@ public: tst_QPdfSearchModel() {} private slots: + void findText_data(); void findText(); }; +void tst_QPdfSearchModel::findText_data() +{ + QTest::addColumn<QString>("pdfPath"); + QTest::addColumn<QString>("searchString"); + QTest::addColumn<int>("expectedMatchCount"); + QTest::addColumn<int>("matchIndexToCheck"); + QTest::addColumn<int>("expectedRectangleCount"); + QTest::addColumn<int>("rectIndexToCheck"); + QTest::addColumn<QRect>("expectedMatchBounds"); + + QTest::newRow("the search for ai") << QFINDTESTDATA("test.pdf") + << "ai" << 3 << 0 << 1 << 0 << QRect(321, 202, 9, 11); + QTest::newRow("rotated text") << QFINDTESTDATA("rotated_text.pdf") + << "world!" << 2 << 0 << 1 << 0 << QRect(76, 102, 26, 28); + QTest::newRow("displaced text") << QFINDTESTDATA("tagged_mcr_multipage.pdf") + << "1" << 1 << 0 << 1 << 0 << QRect(34, 22, 3, 8); +} + void tst_QPdfSearchModel::findText() { + QFETCH(QString, pdfPath); + QFETCH(QString, searchString); + QFETCH(int, expectedMatchCount); + QFETCH(int, matchIndexToCheck); + QFETCH(int, expectedRectangleCount); + QFETCH(int, rectIndexToCheck); + QFETCH(QRect, expectedMatchBounds); + QPdfDocument document; - QCOMPARE(document.load(QFINDTESTDATA("test.pdf")), QPdfDocument::NoError); + QCOMPARE(document.load(pdfPath), QPdfDocument::Error::None); QPdfSearchModel model; model.setDocument(&document); - QList<QRectF> matches = model.matches(1, "ai"); + model.setSearchString(searchString); - qDebug() << matches; - QCOMPARE(matches.count(), 3); + QTRY_COMPARE(model.count(), expectedMatchCount); // wait for the timer + QPdfLink match = model.resultAtIndex(matchIndexToCheck); + qCDebug(lcTests) << match; + QList<QRectF> rects = match.rectangles(); + QCOMPARE(rects.size(), expectedRectangleCount); + QCOMPARE(rects.at(rectIndexToCheck).toRect(), expectedMatchBounds); } QTEST_MAIN(tst_QPdfSearchModel) diff --git a/tests/auto/pdfquick/CMakeLists.txt b/tests/auto/pdfquick/CMakeLists.txt new file mode 100644 index 000000000..e6a3a460c --- /dev/null +++ b/tests/auto/pdfquick/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(multipageview) +add_subdirectory(pdfpageimage) diff --git a/tests/auto/pdfquick/multipageview/BLACKLIST b/tests/auto/pdfquick/multipageview/BLACKLIST new file mode 100644 index 000000000..9012902f6 --- /dev/null +++ b/tests/auto/pdfquick/multipageview/BLACKLIST @@ -0,0 +1,7 @@ +# QTBUG-111306 +[navigation:click links and go back, twice] +android + +# QTBUG-111306 +[navigation:click two links in series and then go back] +android diff --git a/tests/auto/pdfquick/multipageview/CMakeLists.txt b/tests/auto/pdfquick/multipageview/CMakeLists.txt new file mode 100644 index 000000000..50f7d7d8f --- /dev/null +++ b/tests/auto/pdfquick/multipageview/CMakeLists.txt @@ -0,0 +1,30 @@ +# Collect test data +file(GLOB_RECURSE test_data_glob + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + data/*) +list(APPEND test_data ${test_data_glob}) + +qt_internal_add_test(tst_multipageview + SOURCES + tst_multipageview.cpp + ../shared/util.cpp ../shared/util.h + LIBRARIES + Qt::Gui + Qt::Quick + Qt::PdfQuickPrivate + TESTDATA ${test_data} +) + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_multipageview CONDITION ANDROID OR IOS + DEFINES + QT_QMLTEST_DATADIR=":/data" +) + +qt_internal_extend_target(tst_multipageview CONDITION NOT ANDROID AND NOT IOS + DEFINES + QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" +) + diff --git a/tests/auto/pdfquick/multipageview/data/bookmarksAndLinks.pdf b/tests/auto/pdfquick/multipageview/data/bookmarksAndLinks.pdf new file mode 100644 index 000000000..aa0b99039 --- /dev/null +++ b/tests/auto/pdfquick/multipageview/data/bookmarksAndLinks.pdf @@ -0,0 +1,317 @@ +%PDF-1.7
+
+1 0 obj
+<<
+ /Type /Catalog
+ /PageMode /FullScreen
+ /Outlines 6 0 R
+ /Pages 2 0 R
+ /Names 50 0 R
+ /PageLabels 23 0 R
+ /ViewerPreferences<</NonFullScreenPageMode (UseThumbs)>>
+>>
+endobj
+
+50 0 obj
+<<
+ /Dests <</Names [ (ToTest2) [4 0 R /XYZ 300 300 1] (ToTest3) [5 0 R /XYZ 290 10 0.5] (ToTest1) [3 0 R /XYZ 600 800 1] ]>>
+>>
+endobj
+
+23 0 obj
+<<
+ /Nums [0 <</S /D /P(test )>> 3 <</S /A >> 4<</S /R/St >> 5<</S /r/St >> ]
+ /Limits [0 5]
+>>
+endobj
+
+2 0 obj
+<<
+ /Type /Pages
+ /Kids [3 0 R 4 0 R 5 0 R]
+ /Count 3
+>>
+endobj
+
+3 0 obj
+<<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 10 600 800]
+ /Annots [24 0 R 25 0 R]
+ /Contents 16 0 R
+ /Resources <<
+ /Font <</F1 18 0 R>>
+ >>
+>>
+endobj
+
+24 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest2)
+ /A << /Type /Action
+ /S /GoTo
+ /D [5 0 R /FitR ¨C4 399 199 533]
+ >>
+ /Rect [10 690 150 720]
+
+>>
+endobj
+
+25 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest3)
+ /Rect [10 630 150 650]
+>>
+endobj
+
+
+16 0 obj
+<< /Length 0 >>
+ stream
+ BT
+ /F1 72 Tf
+ 200 200 TD
+ 0 0 1 RG
+ 5 Tr
+ (Test_1) Tj
+ 0 800 m
+ 600 0 l S
+ /F1 30 Tf
+ 0 1 0 RG
+ 1 Tr
+ -190 490 TD
+ (GO Test_2) Tj
+ 0 -50 TD
+ 5 w
+ 2 Tr
+ 1 0 0 RG
+ (GO Test_3) Tj
+ ET
+ endstream
+endobj
+
+
+endobj
+
+18 0 obj
+<<
+ /Type /Font
+ /Subtype /Type1
+ /Name /F1
+ /BaseFont /Helvetica
+>>
+endobj
+
+4 0 obj
+<<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [10 0 500 700]
+ /Annots [60 0 R]
+ /Contents 19 0 R
+ /Resources <<
+ /Font <</F2 20 0 R>>
+ >>
+>>
+endobj
+
+19 0 obj
+<< /Length 0 >>
+stream
+BT
+ 1 -0.7 0 1 30 100 cm
+ /F2 50 Tf
+ 10 50 TD
+ (TEST_2) Tj
+
+ 1 0.7 0 1 -30 -100 cm
+ /F2 25 Tf
+ 1 0 1 RG
+ 7 w
+ 100 60 TD
+
+ (GO Test_1) Tj
+ 100 100 140 40 re S f
+ET
+endstream
+endobj
+
+20 0 obj
+<<
+ /Type /Font
+ /Subtype /TrueType
+ /Name /F2
+ /BaseFont /NewYork , Bold
+ /FirstChar 0
+ /LastChar 255
+ /Widths 23 0 R
+ /FontDescriptor 7 0 R
+ /Encoding /MacRomanEncoding
+>>
+endobj
+
+60 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest1)
+ /Rect [110 110 230 150]
+>>
+endobj
+
+5 0 obj
+<<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [-10 -10 400 600]
+ /Annots [61 0 R]
+ /Contents 21 0 R
+ /Resources << /Font <</F3 22 0 R>> >>
+>>
+endobj
+
+21 0 obj
+<< /Length 0 >>
+stream
+BT
+ /F3 30 Tf
+ 290 10 TD
+ (TEST_3) Tj
+ -50 90 TD
+ (GO Test_2)Tj
+ET
+endstream
+endobj
+
+22 0 obj
+<<
+ /Type /Font
+ /Subtype /Type1
+ /Name /F3
+ /BaseFont /Courier-Bold
+>>
+endobj
+
+61 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest2)
+ /Rect [240 90 400 130]
+>>
+
+6 0 obj
+<<
+ /Type /Outlines
+ /First 7 0 R
+ /Last 11 0 R
+ /Count 4 0 R
+>>
+endobj
+
+7 0 obj
+<<
+ /Title (First)
+ /Parent 6 0 R
+ /Next 8 0 R
+ /C [1 0 0]
+ /Dest [ 3 0 R /XYZ 600 800 0.5 ]
+>>
+endobj
+
+8 0 obj
+<<
+ /Title (Second)
+ /Parent 6 0 R
+ /Prev 7 0 R
+ /Next 9 0 R
+ /C [0 1 0]
+ % /Dest [ 4 0 R /XYZ 500 700 null ]
+/Dest (ToTest2)
+>>
+endobj
+
+9 0 obj
+<<
+ /Title (Third)
+ /Parent 6 0 R
+ /Prev 8 0 R
+ /Next 10 0 R
+ /C [0 0 1]
+ /Dest [ 5 0 R /XYZ 400 600 0.8 ]
+>>
+endobj
+
+10 0 obj
+<<
+ /Title (Fourth)
+ /Parent 6 0 R
+ /Prev 9 0 R
+ /Next 11 0 R
+>>
+endobj
+
+11 0 obj
+<<
+ /Title (Fivth)
+ /Parent 6 0 R
+ /Prev 10 0 R
+ /First 12 0 R
+ /Last 15 0 R
+ /Count 4
+>>
+endobj
+
+12 0 obj
+<<
+ /Title (Fivth_1)
+ /Parent 11 0 R
+ /Next 13 0 R
+>>
+endobj
+
+13 0 obj
+<<
+ /Title (Fivth_2)
+ /Parent 11 0 R
+ /Prev 12 0 R
+ /Next 14 0 R
+>>
+endobj
+
+14 0 obj
+<<
+ /Title (Fivth_3)
+ /Parent 11 0 R
+ /Prev 13 0 R
+ /Next 15 0 R
+>>
+endobj
+
+15 0 obj
+<<
+ /Title (Fivth_4)
+ /Parent 11 0 R
+ /Prev 14 0 R
+>>
+endobj
+
+
+
+
+xref
+0000000000 65536 f
+
+trailer
+<<
+ /Size 0
+ /Root 1 0 R
+>>
+startxref
+0
+%%EOF
diff --git a/tests/auto/pdfquick/multipageview/data/jumpOnDocumentReady.qml b/tests/auto/pdfquick/multipageview/data/jumpOnDocumentReady.qml new file mode 100644 index 000000000..ce74f5ed8 --- /dev/null +++ b/tests/auto/pdfquick/multipageview/data/jumpOnDocumentReady.qml @@ -0,0 +1,18 @@ +import QtQuick +import QtQuick.Pdf + +PdfMultiPageView { + id: view + width: 480 + height: 480 + + document: PdfDocument { + id: document + onStatusChanged: { + if(status === PdfDocument.Ready) + view.goToPage(2) + } + } + + Component.onCompleted: document.source = "bookmarksAndLinks.pdf" +} diff --git a/tests/auto/pdfquick/multipageview/data/multiPageView.qml b/tests/auto/pdfquick/multipageview/data/multiPageView.qml new file mode 100644 index 000000000..bf88180ce --- /dev/null +++ b/tests/auto/pdfquick/multipageview/data/multiPageView.qml @@ -0,0 +1,8 @@ +import QtQuick +import QtQuick.Pdf + +PdfMultiPageView { + width: 480; height: 480 + property alias source: document.source + document: PdfDocument { id: document } +} diff --git a/tests/auto/pdfquick/multipageview/data/multiPageViewWithFeedback.qml b/tests/auto/pdfquick/multipageview/data/multiPageViewWithFeedback.qml new file mode 100644 index 000000000..93a556c97 --- /dev/null +++ b/tests/auto/pdfquick/multipageview/data/multiPageViewWithFeedback.qml @@ -0,0 +1,18 @@ +import QtQuick +import QtQuick.Pdf + +PdfMultiPageView { + id: view + property point hoverPos: hover.point.position + width: 640; height: 480 + document: PdfDocument { } + + // mouse hover feedback for test development + Rectangle { + width: 200 + height: hoverPosLabel.implicitHeight + 12 + color: "beige" + Text { id: hoverPosLabel; x: 6; y: 6; text: view.hoverPos.x + ", " + view.hoverPos.y } + } + HoverHandler { id: hover } +} diff --git a/tests/auto/pdfquick/multipageview/data/pdf-sample.protected.pdf b/tests/auto/pdfquick/multipageview/data/pdf-sample.protected.pdf Binary files differnew file mode 100644 index 000000000..d76fdd1a6 --- /dev/null +++ b/tests/auto/pdfquick/multipageview/data/pdf-sample.protected.pdf diff --git a/tests/auto/pdfquick/multipageview/data/qpdfwriter.pdf b/tests/auto/pdfquick/multipageview/data/qpdfwriter.pdf Binary files differnew file mode 100644 index 000000000..4abc76f6d --- /dev/null +++ b/tests/auto/pdfquick/multipageview/data/qpdfwriter.pdf diff --git a/tests/auto/pdfquick/multipageview/tst_multipageview.cpp b/tests/auto/pdfquick/multipageview/tst_multipageview.cpp new file mode 100644 index 000000000..c5e0b30db --- /dev/null +++ b/tests/auto/pdfquick/multipageview/tst_multipageview.cpp @@ -0,0 +1,446 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QSignalSpy> +#include <QTest> +#include <QtCore/QLoggingCategory> +#include <QtGui/QClipboard> +#include <QtGui/QPointingDevice> +#include <QtGui/QStyleHints> +#include <QtQuick/QQuickView> +#include <QtPdfQuick/private/qquickpdflinkmodel_p.h> +#include <QtPdfQuick/private/qquickpdfsearchmodel_p.h> +#include <QtPdfQuick/private/qquickpdfpageimage_p.h> +#include "../shared/util.h" + +using namespace Qt::StringLiterals; + +Q_LOGGING_CATEGORY(lcTests, "qt.pdf.tests") + +class tst_MultiPageView : public QQuickDataTest +{ + Q_OBJECT + +private Q_SLOTS: + void internalLink_data(); + void internalLink(); + void navigation_data(); + void navigation(); + void password(); + void selectionAndClipboard(); + void search(); + void pinchDragPinch(); + void jumpOnDocumentReady(); + +public: + enum NavigationAction { + Back, + Forward, + GotoPage, + GotoLocation, + ClickLink + }; + Q_ENUM(NavigationAction) + + struct NavigationCommand { + NavigationAction action; + int index; + QPointF location; + qreal zoom; + QPointF expectedContentPos; + int expectedCurrentPage; + }; + +private: + QScopedPointer<QPointingDevice> touchscreen = QScopedPointer<QPointingDevice>(QTest::createTouchDevice()); +}; + +void tst_MultiPageView::internalLink_data() +{ + QTest::addColumn<int>("linkIndex"); + QTest::addColumn<int>("expectedPage"); + QTest::addColumn<qreal>("expectedZoom"); + QTest::addColumn<QPoint>("expectedScroll"); + + QTest::newRow("first link") << 0 << 1 << qreal(1) << QPoint(134, 1286); + // TODO fails because it zooms out, and the view leaves gaps between pages currently +// QTest::newRow("second link") << 1 << 2 << qreal(0.5) << QPoint(0, 717); +} + +void tst_MultiPageView::internalLink() +{ + QFETCH(int, linkIndex); + QFETCH(int, expectedPage); + QFETCH(qreal, expectedZoom); + QFETCH(QPoint, expectedScroll); + + QQuickView window; + QVERIFY(showView(window, testFileUrl("multiPageView.qml"))); + QQuickItem *pdfView = window.rootObject(); + QVERIFY(pdfView); + pdfView->setProperty("source", testFileUrl("bookmarksAndLinks.pdf")); + QTRY_COMPARE(pdfView->property("currentPageRenderingStatus").toInt(), QQuickPdfPageImage::Ready); + + QQuickItem *table = static_cast<QQuickItem *>(findFirstChild(pdfView, "QQuickTableView")); + QVERIFY(table); + QQuickItem *firstPage = tableViewItemAtCell(table, 0, 0); + QVERIFY(firstPage); + QQuickPdfLinkModel *linkModel = firstPage->findChild<QQuickPdfLinkModel*>(); + QVERIFY(linkModel); + QQuickItem *repeater = qobject_cast<QQuickItem *>(linkModel->parent()); + QVERIFY(repeater); + QVERIFY(repeater->property("count").toInt() > linkIndex); + + QCOMPARE(pdfView->property("backEnabled").toBool(), false); + QCOMPARE(pdfView->property("forwardEnabled").toBool(), false); + + // get the PdfLinkDelegate instance, which has a TapHandler declared inside + QQuickItem *linkDelegate = repeaterItemAt(repeater, linkIndex); + QVERIFY(linkDelegate); + const auto modelIdx = linkModel->index(linkIndex); + const int linkPage = linkModel->data(modelIdx, int(QPdfLinkModel::Role::Page)).toInt(); + QVERIFY(linkPage >= 0); + const QPointF linkLocation = linkModel->data(modelIdx, int(QPdfLinkModel::Role::Location)).toPointF(); + const qreal linkZoom = linkModel->data(modelIdx, int(QPdfLinkModel::Role::Zoom)).toReal(); + + // click on it, and check whether it went to the right place + const auto point = linkDelegate->position().toPoint() + QPoint(15, 15); + QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, point); + QTRY_COMPARE(tableViewContentPos(table).y(), expectedScroll.y()); + const auto linkScrollPos = tableViewContentPos(table); + qCDebug(lcTests, "clicked link @ %d, %d and expected scrolling to %d, %d; actually scrolled to %d, %d", + point.x(), point.y(), expectedScroll.x(), expectedScroll.y(), linkScrollPos.x(), linkScrollPos.y()); + QVERIFY(qAbs(linkScrollPos.x() - expectedScroll.x()) < 15); + QTRY_COMPARE(pdfView->property("currentPageRenderingStatus").toInt(), QQuickPdfPageImage::Ready); + QCOMPARE(pdfView->property("currentPage").toInt(), linkPage); + QCOMPARE(linkPage, expectedPage); + QCOMPARE(pdfView->property("renderScale").toReal(), linkZoom); + QCOMPARE(linkZoom, expectedZoom); + qCDebug(lcTests, "link %d goes to page %d location {%lf,%lf} zoom %lf scroll to {%lf,%lf}", + linkIndex, linkPage, linkLocation.x(), linkLocation.y(), linkZoom, + table->property("contentX").toReal(), table->property("contentY").toReal()); + + // check that we can go back to where we came from + QCOMPARE(pdfView->property("backEnabled").toBool(), true); + QCOMPARE(pdfView->property("forwardEnabled").toBool(), false); + QVERIFY(QMetaObject::invokeMethod(pdfView, "back")); + QTRY_COMPARE(tableViewContentPos(table), QPoint(0, 0)); + QCOMPARE(pdfView->property("currentPage").toInt(), 0); + QCOMPARE(pdfView->property("renderScale").toReal(), qreal(1)); + + // and then forward again + QCOMPARE(pdfView->property("backEnabled").toBool(), false); + QCOMPARE(pdfView->property("forwardEnabled").toBool(), true); + QVERIFY(QMetaObject::invokeMethod(pdfView, "forward")); + QTRY_COMPARE(tableViewContentPos(table), linkScrollPos); + QCOMPARE(pdfView->property("currentPage").toInt(), linkPage); + QCOMPARE(pdfView->property("renderScale").toReal(), linkZoom); +} + +void tst_MultiPageView::navigation_data() +{ + QTest::addColumn<QList<NavigationCommand>>("actions"); + const int totalPageSpacing = 832; // 826 points + 6 px (rowSpacing) + + QList<NavigationCommand> actions; + actions << NavigationCommand {NavigationAction::GotoPage, 2, {}, 0, {0, 1664}, 2} + << NavigationCommand {NavigationAction::GotoPage, 3, {}, 0, {0, 2496}, 3} + << NavigationCommand {NavigationAction::Back, 0, {}, 0, {0, 1664}, 2} + << NavigationCommand {NavigationAction::Back, 0, {}, 0, {0, 0}, 0}; + QTest::newRow("goto and back") << actions; + + actions.clear(); + actions // first link is "More..." going to page 0, location 8, 740 + << NavigationCommand {NavigationAction::ClickLink, 0, {465, 65}, 0, {0, 740}, 0} + << NavigationCommand {NavigationAction::Back, 0, {}, 0, {0, 0}, 0} + // link "setPdfVersion()" going to page 3, location 8, 295 + << NavigationCommand {NavigationAction::ClickLink, 0, {255, 455}, 0, {0, totalPageSpacing * 3 + 295}, 3} + << NavigationCommand {NavigationAction::Back, 0, {}, 0, {0, 0}, 0}; + QTest::newRow("click links and go back, twice") << actions; + + actions.clear(); + actions // first link is "More..." going to page 0, location 8, 740 + << NavigationCommand {NavigationAction::ClickLink, 0, {465, 65}, 0, {0, 740}, 0} + // link "newPage()" going to page 1, location 8, 290 + << NavigationCommand {NavigationAction::ClickLink, 0, {480, 40}, 0, {0, totalPageSpacing + 290}, 1} // fails, goes back to page 0 + << NavigationCommand {NavigationAction::Back, 0, {}, 0, {8, 740}, 0} + << NavigationCommand {NavigationAction::Back, 0, {}, 0, {0, 0}, 0}; + QTest::newRow("click two links in series and then go back") << actions; +} + +void tst_MultiPageView::navigation() +{ + QFETCH(QList<NavigationCommand>, actions); + + QQuickView window; + window.setColor(Qt::gray); + window.setSource(testFileUrl("multiPageViewWithFeedback.qml")); + QTRY_COMPARE(window.status(), QQuickView::Ready); + QQuickItem *pdfView = window.rootObject(); + QVERIFY(pdfView); + QObject *doc = pdfView->property("document").value<QObject *>(); + QVERIFY(doc); + doc->setProperty("source", testFileUrl("qpdfwriter.pdf")); + QQuickItem *table = static_cast<QQuickItem *>(findFirstChild(pdfView, "QQuickTableView")); + QVERIFY(table); + // Expect that contentY == destination y after a jump, for ease of comparison. + // 0.01 is close enough to 0 that we can compare int positions accurately, + // but nonzero so that QRectF::isValid() is true in tableView.positionViewAtCell() + table->setProperty("jumpLocationMargin", QPointF(0.01, 0.01)); + + window.show(); + window.requestActivate(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + QTRY_COMPARE(table->property("contentHeight").toInt(), 3322); + QCOMPARE(table->property("contentY").toInt(), 0); + + for (const NavigationCommand &nav : actions) { + switch (nav.action) { + case NavigationAction::Back: + QVERIFY(QMetaObject::invokeMethod(pdfView, "back")); + QCOMPARE(pdfView->property("forwardEnabled").toBool(), true); + break; + case NavigationAction::Forward: + QVERIFY(QMetaObject::invokeMethod(pdfView, "forward")); + QCOMPARE(pdfView->property("backEnabled").toBool(), true); + break; + case NavigationAction::GotoPage: + QVERIFY(QMetaObject::invokeMethod(pdfView, "goToPage", + Q_ARG(QVariant, QVariant(nav.index)))); + QCOMPARE(pdfView->property("backEnabled").toBool(), true); + break; + case NavigationAction::GotoLocation: + QVERIFY(QMetaObject::invokeMethod(pdfView, "goToLocation", + Q_ARG(QVariant, QVariant(nav.index)), + Q_ARG(QVariant, QVariant(nav.location)), + Q_ARG(QVariant, QVariant(nav.zoom)) )); + break; + case NavigationAction::ClickLink: + // Link delegates don't exist until page rendering is done + QTRY_VERIFY(pdfView->property("currentPageRenderingStatus").toInt() == 1); // QQuickImage::Status::Ready + QTest::mouseClick(&window, Qt::LeftButton, Qt::NoModifier, nav.location.toPoint()); + // Wait for the destination page to be rendered + QTRY_VERIFY(pdfView->property("currentPageRenderingStatus").toInt() == 1); // QQuickImage::Status::Ready + break; + } + qCDebug(lcTests) << "action" << nav.action << "index" << nav.index + << "contentX,Y" << table->property("contentX").toInt() << table->property("contentY").toInt() + << "expected" << nav.expectedContentPos; + QTRY_COMPARE(table->property("contentY").toInt(), nav.expectedContentPos.y()); + // some minor side-to-side scrolling happens, in practice + QVERIFY(qAbs(table->property("contentX").toInt() - nav.expectedContentPos.x()) < 10); + QCOMPARE(pdfView->property("currentPage").toInt(), nav.expectedCurrentPage); + } + + QCOMPARE(pdfView->property("backEnabled").toBool(), false); +} + +void tst_MultiPageView::password() +{ + QQuickView window; + QVERIFY(showView(window, testFileUrl("multiPageView.qml"))); + QQuickItem *pdfView = window.rootObject(); + QVERIFY(pdfView); + QQuickPdfDocument *doc = pdfView->property("document").value<QQuickPdfDocument*>(); + QVERIFY(doc); + QPdfDocument *cppDoc = static_cast<QPdfDocument *>(qmlExtendedObject(doc)); + QVERIFY(cppDoc); + QSignalSpy passwordRequiredSpy(doc, SIGNAL(passwordRequired())); + // actually QPdfDocument::passwordRequired, but QML_EXTENDED gives us this signal virtually in QQuickPdfDocument + QVERIFY(passwordRequiredSpy.isValid()); + QSignalSpy passwordChangedSpy(doc, SIGNAL(passwordChanged())); + // actually QPdfDocument::passwordChanged, but QML_EXTENDED gives us this signal virtually in QQuickPdfDocument + QVERIFY(passwordChangedSpy.isValid()); + QSignalSpy statusChangedSpy(doc, SIGNAL(statusChanged(QPdfDocument::Status))); + // actually QPdfDocument::statusChanged, but QML_EXTENDED gives us this signal virtually in QQuickPdfDocument + QVERIFY(statusChangedSpy.isValid()); + QSignalSpy pageCountChangedSpy(doc, SIGNAL(pageCountChanged(int))); + // QPdfDocument::pageCountChanged(int), but QML_EXTENDED gives us this signal virtually in QQuickPdfDocument + QVERIFY(pageCountChangedSpy.isValid()); + QSignalSpy extPageCountChangedSpy(cppDoc, &QPdfDocument::pageCountChanged); + // actual QPdfDocument::pageCountChanged(int), for comparison with the illusory QQuickPdfDocument::pageCountChanged + QVERIFY(extPageCountChangedSpy.isValid()); + + QVERIFY(pdfView->setProperty("source", testFileUrl(u"pdf-sample.protected.pdf"_s))); + + QTRY_COMPARE(passwordRequiredSpy.size(), 1); + qCDebug(lcTests) << "error while awaiting password" << doc->error() + << "passwordRequired count" << passwordRequiredSpy.size() + << "statusChanged count" << statusChangedSpy.size(); + QCOMPARE(doc->property("status").toInt(), int(QPdfDocument::Status::Error)); + QCOMPARE(pageCountChangedSpy.size(), 0); + QCOMPARE(extPageCountChangedSpy.size(), 0); + QCOMPARE(statusChangedSpy.size(), 2); // Loading and then Error + statusChangedSpy.clear(); + QVERIFY(doc->setProperty("password", u"Qt"_s)); + QCOMPARE(passwordChangedSpy.size(), 1); + QTRY_COMPARE(doc->property("status").toInt(), int(QPdfDocument::Status::Ready)); + qCDebug(lcTests) << "after setPassword" << doc->error() + << "passwordChanged count" << passwordChangedSpy.size() + << "statusChanged count" << statusChangedSpy.size() + << "pageCountChanged count" << pageCountChangedSpy.size(); + QCOMPARE(statusChangedSpy.size(), 2); // Loading and then Ready + QCOMPARE(pageCountChangedSpy.size(), 1); + QCOMPARE(extPageCountChangedSpy.size(), pageCountChangedSpy.size()); +} + +void tst_MultiPageView::selectionAndClipboard() +{ + QQuickView window; + QVERIFY(showView(window, testFileUrl("multiPageView.qml"))); + QQuickItem *pdfView = window.rootObject(); + QVERIFY(pdfView); + QQuickPdfDocument *doc = pdfView->property("document").value<QQuickPdfDocument*>(); + QVERIFY(doc); + QVERIFY(doc->setProperty("password", u"Qt"_s)); + QVERIFY(pdfView->setProperty("source", testFileUrl((u"pdf-sample.protected.pdf"_s)))); + QTRY_COMPARE(pdfView->property("currentPageRenderingStatus").toInt(), QQuickPdfPageImage::Ready); + + QVERIFY(QMetaObject::invokeMethod(pdfView, "selectAll")); + QString sel = pdfView->property("selectedText").toString(); + QCOMPARE(sel.size(), 1073); + +#if QT_CONFIG(clipboard) + QClipboard *clip = qApp->clipboard(); + if (clip->supportsSelection()) + QCOMPARE(clip->text(QClipboard::Selection), sel); + QVERIFY(QMetaObject::invokeMethod(pdfView, "copySelectionToClipboard")); + QCOMPARE(clip->text(QClipboard::Clipboard), sel); +#endif // clipboard +} + +void tst_MultiPageView::search() +{ + QQuickView window; + QVERIFY(showView(window, testFileUrl("multiPageView.qml"))); + window.setResizeMode(QQuickView::SizeRootObjectToView); + window.resize(200, 200); + QQuickItem *pdfView = window.rootObject(); + QVERIFY(pdfView); + QTRY_COMPARE(pdfView->width(), 200); + QQuickPdfDocument *doc = pdfView->property("document").value<QQuickPdfDocument*>(); + QVERIFY(doc); + QVERIFY(doc->setProperty("password", u"Qt"_s)); + QVERIFY(pdfView->setProperty("source", testFileUrl(u"pdf-sample.protected.pdf"_s))); + QTRY_COMPARE(pdfView->property("currentPageRenderingStatus").toInt(), QQuickPdfPageImage::Ready); + QPdfSearchModel *searchModel = pdfView->property("searchModel").value<QPdfSearchModel*>(); + QVERIFY(searchModel); + QQuickItem *table = static_cast<QQuickItem *>(findFirstChild(pdfView, "QQuickTableView")); + QVERIFY(table); + QQuickItem *firstPage = tableViewItemAtCell(table, 0, 0); + QVERIFY(firstPage); + QObject *multiline = findFirstChild(firstPage, "QQuickPathMultiline"); + QVERIFY(multiline); + + pdfView->setProperty("searchString", u"PDF"_s); + QTRY_COMPARE(searchModel->rowCount(QModelIndex()), 7); // occurrences of the word "PDF" in this file + const int count = searchModel->rowCount(QModelIndex()); + QList<QList<QPointF>> resultOutlines = multiline->property("paths").value<QList<QList<QPointF>>>(); + QCOMPARE(resultOutlines.size(), 7); + QPoint contentPos = tableViewContentPos(table); + int movements = 0; + for (int i = 0; i < count; ++i) { + // only one page, so IndexOnPage data is the same as overall index + QCOMPARE(i, searchModel->data(searchModel->index(i), int(QPdfSearchModel::Role::IndexOnPage)).toInt()); + QCOMPARE(resultOutlines.at(i).size(), 5); // 5-point polygon is a rectangle (including drawing back to the start, to close it) + QCOMPARE(resultOutlines.at(i).first(), searchModel->data(searchModel->index(i), int(QPdfSearchModel::Role::Location)).toPointF()); + + QVERIFY(QMetaObject::invokeMethod(pdfView, "searchForward")); + QTest::qWait(500); // animation time; but it doesn't always need to move + // TODO maybe: if movement starts, wait for it to stop somehow? + qCDebug(lcTests) << i << resultOutlines.at(i) << "scrolled to" << tableViewContentPos(table); + if (tableViewContentPos(table) != contentPos) + ++movements; + contentPos = tableViewContentPos(table); + } + qCDebug(lcTests) << "total movements" << movements; + QVERIFY(movements > 4); +} + +void tst_MultiPageView::pinchDragPinch() +{ + qputenv("QML_NO_TOUCH_COMPRESSION", "1"); + QQuickView window; + QVERIFY(showView(window, testFileUrl("multiPageView.qml"))); + QQuickItem *pdfView = window.rootObject(); + QVERIFY(pdfView); + pdfView->setProperty("source", testFileUrl("bookmarksAndLinks.pdf")); + QTRY_COMPARE(pdfView->property("currentPageRenderingStatus").toInt(), QQuickPdfPageImage::Ready); + QQuickItem *table = static_cast<QQuickItem *>(findFirstChild(pdfView, "QQuickTableView")); + QVERIFY(table); + QQuickItem *firstPage = tableViewItemAtCell(table, 0, 0); + QVERIFY(firstPage); + QQuickItem *paper = firstPage->childAt(10, 10); + QVERIFY(paper); + QQuickPdfPageImage *image = firstPage->findChild<QQuickPdfPageImage *>(); + QVERIFY(image); + + auto pinch = [&window, paper, this]() { + const int threshold = QGuiApplication::styleHints()->startDragDistance(); + const int movement = 100; + QCOMPARE_GT(movement, threshold); + const qreal initialScale = paper->scale(); + QPoint p0(100, 200); + QPoint p1(200, 200); + QTest::QTouchEventSequence seq = QTest::touchEvent(&window, touchscreen.get()); + seq.press(0, p0, &window).commit(); + seq.stationary(0).press(1, p1, &window).commit(); + p1.setX(p1.x() + movement); + QSignalSpy frameSwappedSpy(&window, &QQuickWindow::frameSwapped); + seq.stationary(0).move(1, p1, &window).commit(); + // after a frame is rendered, the PinchHandler ought to be active + // (but verifying it would require private API) + QTRY_VERIFY(frameSwappedSpy.size() > 0); + QTRY_COMPARE(paper->scale(), initialScale); + + for (int i = 1; i <= 2; ++i) { + p1.setX(p1.x() + movement); + seq.stationary(0).move(1, p1, &window).commit(); + QTRY_COMPARE(paper->scale(), initialScale + i * 0.5); + } + seq.release(0, p0, &window).release(1, p1, &window).commit(); + }; + + auto drag = [&window, table, this]() { + const int movement = 100; + QPoint p0(200, 200); + QTest::QTouchEventSequence seq = QTest::touchEvent(&window, touchscreen.get()); + seq.press(0, p0, &window).commit(); + p0.setY(p0.y() + movement); + seq.move(0, p0, &window).commit(); + p0.setY(p0.y() + movement); + seq.move(0, p0, &window).commit(); + seq.release(0, p0, &window).commit(); + QTRY_COMPARE(table->property("moving"), false); + }; + + pinch(); + qCDebug(lcTests) << "new scale" << pdfView->property("renderScale").toReal(); + QTRY_COMPARE(pdfView->property("renderScale").toReal(), 2); + + drag(); + QCOMPARE(pdfView->property("renderScale").toReal(), 2); + + pinch(); + qCDebug(lcTests) << "new scale" << pdfView->property("renderScale").toReal(); + QTRY_COMPARE(pdfView->property("renderScale").toReal(), 4); + + // wait for rendering to be done before we exit: if we delete the document + // prematurely, QPdfIOHandler might access a dangling pointer + QTRY_COMPARE(image->status(), QQuickPdfPageImage::Ready); +} + +void tst_MultiPageView::jumpOnDocumentReady() // QTBUG-119416 +{ + QQuickView window; + QVERIFY(showView(window, testFileUrl("jumpOnDocumentReady.qml"))); + QQuickItem *pdfView = window.rootObject(); + QVERIFY(pdfView); + + // QML calls view.goToPage(2): verify that it eventually happens + QTRY_COMPARE(pdfView->property("currentPage").toInt(), 2); +} + +QTEST_MAIN(tst_MultiPageView) +#include "tst_multipageview.moc" diff --git a/tests/auto/pdfquick/pdfpageimage/CMakeLists.txt b/tests/auto/pdfquick/pdfpageimage/CMakeLists.txt new file mode 100644 index 000000000..da67d8721 --- /dev/null +++ b/tests/auto/pdfquick/pdfpageimage/CMakeLists.txt @@ -0,0 +1,30 @@ +# Collect test data +file(GLOB_RECURSE test_data_glob + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + data/*) +list(APPEND test_data ${test_data_glob}) + +qt_internal_add_test(tst_pdfpageimage + SOURCES + tst_pdfpageimage.cpp + ../shared/util.cpp ../shared/util.h + LIBRARIES + Qt::Gui + Qt::Quick + Qt::PdfQuickPrivate + TESTDATA ${test_data} +) + +## Scopes: +##################################################################### + +qt_internal_extend_target(tst_pdfpageimage CONDITION ANDROID OR IOS + DEFINES + QT_QMLTEST_DATADIR=":/data" +) + +qt_internal_extend_target(tst_pdfpageimage CONDITION NOT ANDROID AND NOT IOS + DEFINES + QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data" +) + diff --git a/tests/auto/pdfquick/pdfpageimage/data/bookmarksAndLinks.pdf b/tests/auto/pdfquick/pdfpageimage/data/bookmarksAndLinks.pdf new file mode 100644 index 000000000..aa0b99039 --- /dev/null +++ b/tests/auto/pdfquick/pdfpageimage/data/bookmarksAndLinks.pdf @@ -0,0 +1,317 @@ +%PDF-1.7
+
+1 0 obj
+<<
+ /Type /Catalog
+ /PageMode /FullScreen
+ /Outlines 6 0 R
+ /Pages 2 0 R
+ /Names 50 0 R
+ /PageLabels 23 0 R
+ /ViewerPreferences<</NonFullScreenPageMode (UseThumbs)>>
+>>
+endobj
+
+50 0 obj
+<<
+ /Dests <</Names [ (ToTest2) [4 0 R /XYZ 300 300 1] (ToTest3) [5 0 R /XYZ 290 10 0.5] (ToTest1) [3 0 R /XYZ 600 800 1] ]>>
+>>
+endobj
+
+23 0 obj
+<<
+ /Nums [0 <</S /D /P(test )>> 3 <</S /A >> 4<</S /R/St >> 5<</S /r/St >> ]
+ /Limits [0 5]
+>>
+endobj
+
+2 0 obj
+<<
+ /Type /Pages
+ /Kids [3 0 R 4 0 R 5 0 R]
+ /Count 3
+>>
+endobj
+
+3 0 obj
+<<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 10 600 800]
+ /Annots [24 0 R 25 0 R]
+ /Contents 16 0 R
+ /Resources <<
+ /Font <</F1 18 0 R>>
+ >>
+>>
+endobj
+
+24 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest2)
+ /A << /Type /Action
+ /S /GoTo
+ /D [5 0 R /FitR ¨C4 399 199 533]
+ >>
+ /Rect [10 690 150 720]
+
+>>
+endobj
+
+25 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest3)
+ /Rect [10 630 150 650]
+>>
+endobj
+
+
+16 0 obj
+<< /Length 0 >>
+ stream
+ BT
+ /F1 72 Tf
+ 200 200 TD
+ 0 0 1 RG
+ 5 Tr
+ (Test_1) Tj
+ 0 800 m
+ 600 0 l S
+ /F1 30 Tf
+ 0 1 0 RG
+ 1 Tr
+ -190 490 TD
+ (GO Test_2) Tj
+ 0 -50 TD
+ 5 w
+ 2 Tr
+ 1 0 0 RG
+ (GO Test_3) Tj
+ ET
+ endstream
+endobj
+
+
+endobj
+
+18 0 obj
+<<
+ /Type /Font
+ /Subtype /Type1
+ /Name /F1
+ /BaseFont /Helvetica
+>>
+endobj
+
+4 0 obj
+<<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [10 0 500 700]
+ /Annots [60 0 R]
+ /Contents 19 0 R
+ /Resources <<
+ /Font <</F2 20 0 R>>
+ >>
+>>
+endobj
+
+19 0 obj
+<< /Length 0 >>
+stream
+BT
+ 1 -0.7 0 1 30 100 cm
+ /F2 50 Tf
+ 10 50 TD
+ (TEST_2) Tj
+
+ 1 0.7 0 1 -30 -100 cm
+ /F2 25 Tf
+ 1 0 1 RG
+ 7 w
+ 100 60 TD
+
+ (GO Test_1) Tj
+ 100 100 140 40 re S f
+ET
+endstream
+endobj
+
+20 0 obj
+<<
+ /Type /Font
+ /Subtype /TrueType
+ /Name /F2
+ /BaseFont /NewYork , Bold
+ /FirstChar 0
+ /LastChar 255
+ /Widths 23 0 R
+ /FontDescriptor 7 0 R
+ /Encoding /MacRomanEncoding
+>>
+endobj
+
+60 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest1)
+ /Rect [110 110 230 150]
+>>
+endobj
+
+5 0 obj
+<<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [-10 -10 400 600]
+ /Annots [61 0 R]
+ /Contents 21 0 R
+ /Resources << /Font <</F3 22 0 R>> >>
+>>
+endobj
+
+21 0 obj
+<< /Length 0 >>
+stream
+BT
+ /F3 30 Tf
+ 290 10 TD
+ (TEST_3) Tj
+ -50 90 TD
+ (GO Test_2)Tj
+ET
+endstream
+endobj
+
+22 0 obj
+<<
+ /Type /Font
+ /Subtype /Type1
+ /Name /F3
+ /BaseFont /Courier-Bold
+>>
+endobj
+
+61 0 obj
+<<
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest (ToTest2)
+ /Rect [240 90 400 130]
+>>
+
+6 0 obj
+<<
+ /Type /Outlines
+ /First 7 0 R
+ /Last 11 0 R
+ /Count 4 0 R
+>>
+endobj
+
+7 0 obj
+<<
+ /Title (First)
+ /Parent 6 0 R
+ /Next 8 0 R
+ /C [1 0 0]
+ /Dest [ 3 0 R /XYZ 600 800 0.5 ]
+>>
+endobj
+
+8 0 obj
+<<
+ /Title (Second)
+ /Parent 6 0 R
+ /Prev 7 0 R
+ /Next 9 0 R
+ /C [0 1 0]
+ % /Dest [ 4 0 R /XYZ 500 700 null ]
+/Dest (ToTest2)
+>>
+endobj
+
+9 0 obj
+<<
+ /Title (Third)
+ /Parent 6 0 R
+ /Prev 8 0 R
+ /Next 10 0 R
+ /C [0 0 1]
+ /Dest [ 5 0 R /XYZ 400 600 0.8 ]
+>>
+endobj
+
+10 0 obj
+<<
+ /Title (Fourth)
+ /Parent 6 0 R
+ /Prev 9 0 R
+ /Next 11 0 R
+>>
+endobj
+
+11 0 obj
+<<
+ /Title (Fivth)
+ /Parent 6 0 R
+ /Prev 10 0 R
+ /First 12 0 R
+ /Last 15 0 R
+ /Count 4
+>>
+endobj
+
+12 0 obj
+<<
+ /Title (Fivth_1)
+ /Parent 11 0 R
+ /Next 13 0 R
+>>
+endobj
+
+13 0 obj
+<<
+ /Title (Fivth_2)
+ /Parent 11 0 R
+ /Prev 12 0 R
+ /Next 14 0 R
+>>
+endobj
+
+14 0 obj
+<<
+ /Title (Fivth_3)
+ /Parent 11 0 R
+ /Prev 13 0 R
+ /Next 15 0 R
+>>
+endobj
+
+15 0 obj
+<<
+ /Title (Fivth_4)
+ /Parent 11 0 R
+ /Prev 14 0 R
+>>
+endobj
+
+
+
+
+xref
+0000000000 65536 f
+
+trailer
+<<
+ /Size 0
+ /Root 1 0 R
+>>
+startxref
+0
+%%EOF
diff --git a/tests/auto/pdfquick/pdfpageimage/data/pdfPageImage.qml b/tests/auto/pdfquick/pdfpageimage/data/pdfPageImage.qml new file mode 100644 index 000000000..a268bf14b --- /dev/null +++ b/tests/auto/pdfquick/pdfpageimage/data/pdfPageImage.qml @@ -0,0 +1,16 @@ +import QtQuick +import QtQuick.Pdf + +Item { + width: 320 + height: 320 + + PdfDocument { + id: doc + source: "bookmarksAndLinks.pdf" + } + + PdfPageImage { + anchors.centerIn: parent + } +} diff --git a/tests/auto/pdfquick/pdfpageimage/tst_pdfpageimage.cpp b/tests/auto/pdfquick/pdfpageimage/tst_pdfpageimage.cpp new file mode 100644 index 000000000..d2c9c8709 --- /dev/null +++ b/tests/auto/pdfquick/pdfpageimage/tst_pdfpageimage.cpp @@ -0,0 +1,131 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <QRegularExpression> +#include <QSignalSpy> +#include <QTest> +#include <QtQuick/QQuickView> +#include <QtPdfQuick/private/qquickpdfdocument_p.h> +#include <QtPdfQuick/private/qquickpdfpageimage_p.h> +#include "../shared/util.h" + +using namespace Qt::StringLiterals; + +Q_LOGGING_CATEGORY(lcTests, "qt.pdf.tests") + +// #define DEBUG_WRITE_OUTPUT + +class tst_PdfPageImage : public QQuickDataTest +{ + Q_OBJECT + +private Q_SLOTS: + void settableProperties_data(); + void settableProperties(); + +public: + enum Property { + Source = 0x01, + Document = 0x02, + SourceSize = 0x04, + SourceClipRect = 0x08, + MipMap = 0x10, + AutoTransform = 0x20, + Asynchronous = 0x40, + NoCache = 0x80, + Mirror = 0x100, + MirrorVertically = 0x200, + ColorSpace = 0x400, + }; + Q_DECLARE_FLAGS(Properties, Property) + Q_FLAG(Properties) + +private: +#ifdef DEBUG_WRITE_OUTPUT + QTemporaryDir m_tmpDir; +#endif +}; + +void tst_PdfPageImage::settableProperties_data() +{ + QTest::addColumn<tst_PdfPageImage::Properties>("toSet"); + QTest::addColumn<QSize>("expectedSize"); + QTest::addColumn<QRegularExpression>("expectedWarning"); + + const QRegularExpression NoWarning; + const qreal dpr = qGuiApp->devicePixelRatio(); + + QTest::newRow("source") << Properties(Source) << (QSizeF(600, 790) * dpr).toSize() + << QRegularExpression("document property not set: falling back to inefficient loading"); // QTBUG-104767 + QTest::newRow("document") << Properties(Document) << QSize(600, 790) << NoWarning; + QTest::newRow("source and document") << Properties(Source | Document) << QSize(600, 790) + << QRegularExpression("document and source properties in conflict"); + QTest::newRow("document and sourceSize") << Properties(Document | SourceSize) << QSize(100, 100) << NoWarning; + QTest::newRow("document and sourceClipRect") << Properties(Document | SourceClipRect) << QSize(100, 100) << NoWarning; + QTest::newRow("document and autoTransform") << Properties(Document | AutoTransform) << QSize(600, 790) << NoWarning; + QTest::newRow("document and async") << Properties(Document | Asynchronous) << QSize(600, 790) << NoWarning; + QTest::newRow("document and nocache") << Properties(Document | NoCache) << QSize(600, 790) << NoWarning; + QTest::newRow("document and mirror") << Properties(Document | Mirror) << QSize(600, 790) << NoWarning; + QTest::newRow("document and mirrorVertically") << Properties(Document | MirrorVertically) << QSize(600, 790) << NoWarning; + QTest::newRow("document and colorSpace") << Properties(Document | ColorSpace) << QSize(600, 790) << NoWarning; +} + +void tst_PdfPageImage::settableProperties() +{ + QFETCH(tst_PdfPageImage::Properties, toSet); + QFETCH(QSize, expectedSize); + QFETCH(QRegularExpression, expectedWarning); + + QQuickView window; + if (!expectedWarning.pattern().isEmpty()) + QTest::ignoreMessage(QtWarningMsg, expectedWarning); + QVERIFY(showView(window, testFileUrl("pdfPageImage.qml"))); + QQuickPdfPageImage *pdfImage = window.rootObject()->findChild<QQuickPdfPageImage *>(); + QVERIFY(pdfImage); + QQuickPdfDocument *doc = window.rootObject()->findChild<QQuickPdfDocument *>(); + QVERIFY(doc); + if (toSet.testFlag(Document)) + pdfImage->setDocument(doc); + if (toSet.testFlag(Source)) + pdfImage->setSource(doc->source()); + if (toSet.testFlag(SourceSize)) + pdfImage->setSourceSize({100, 100}); + if (toSet.testFlag(SourceClipRect)) + pdfImage->setSourceClipRect({100, 100, 100, 100}); + if (toSet.testFlag(MipMap)) + pdfImage->setMipmap(true); + if (toSet.testFlag(AutoTransform)) + pdfImage->setAutoTransform(true); + if (toSet.testFlag(Asynchronous)) { + QCOMPARE(pdfImage->asynchronous(), false); + // test the opposite of the default + pdfImage->setAsynchronous(true); + } + if (toSet.testFlag(NoCache)) { + QCOMPARE(pdfImage->cache(), true); + // test the opposite of the default + pdfImage->setCache(false); + } + if (toSet.testFlag(Mirror)) { + QCOMPARE(pdfImage->mirror(), false); + pdfImage->setMirror(true); + } + if (toSet.testFlag(MirrorVertically)) { + QCOMPARE(pdfImage->mirrorVertically(), false); + pdfImage->setMirrorVertically(true); + } + if (toSet.testFlag(ColorSpace)) + pdfImage->setColorSpace(QColorSpace::ProPhotoRgb); + QTRY_COMPARE(pdfImage->status(), QQuickPdfPageImage::Ready); + const QImage img = pdfImage->image(); + QCOMPARE(img.size(), expectedSize); +#ifdef DEBUG_WRITE_OUTPUT + m_tmpDir.setAutoRemove(false); + const auto path = m_tmpDir.filePath(QString::fromLocal8Bit(QTest::currentDataTag()) + ".png"); + qCDebug(lcTests) << "saving to" << path; + img.save(path); +#endif +} + +QTEST_MAIN(tst_PdfPageImage) +#include "tst_pdfpageimage.moc" diff --git a/tests/auto/pdfquick/shared/util.cpp b/tests/auto/pdfquick/shared/util.cpp new file mode 100644 index 000000000..c540ebfa6 --- /dev/null +++ b/tests/auto/pdfquick/shared/util.cpp @@ -0,0 +1,110 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "util.h" +#include <QtQuick/QQuickItem> + +QQuickDataTest::QQuickDataTest() : + m_initialized(false), +#ifdef QT_TESTCASE_BUILDDIR + m_dataDirectory(QTest::qFindTestData("data", QT_QMLTEST_DATADIR, 0, QT_TESTCASE_BUILDDIR)), +#else + m_dataDirectory(QTest::qFindTestData("data", QT_QMLTEST_DATADIR, 0)), +#endif + + m_dataDirectoryUrl(m_dataDirectory.startsWith(QLatin1Char(':')) + ? QUrl(QLatin1String("qrc") + m_dataDirectory) + : QUrl::fromLocalFile(m_dataDirectory + QLatin1Char('/'))) +{ +} + +QQuickDataTest::~QQuickDataTest() +{ +} + +void QQuickDataTest::initTestCase() +{ + QVERIFY2(!m_dataDirectory.isEmpty(), "'data' directory not found"); + m_directory = QFileInfo(m_dataDirectory).absolutePath(); + if (m_dataDirectoryUrl.scheme() != QLatin1String("qrc")) + QVERIFY2(QDir::setCurrent(m_directory), qPrintable(QLatin1String("Could not chdir to ") + m_directory)); + + if (QGuiApplication::platformName() == QLatin1String("offscreen") + || QGuiApplication::platformName() == QLatin1String("minimal")) + { + QSKIP("Skipping visual tests due to running with offscreen/minimal"); + } + + m_initialized = true; +} + +void QQuickDataTest::cleanupTestCase() +{ + m_initialized = false; +} + +QString QQuickDataTest::testFile(const QString &fileName) const +{ + if (m_directory.isEmpty()) + qFatal("QQuickDataTest::initTestCase() not called."); + QString result = m_dataDirectory; + result += QLatin1Char('/'); + result += fileName; + return result; +} + +QObject *QQuickDataTest::findFirstChild(QObject *parent, const char *className) +{ + const auto children = parent->findChildren<QObject*>(); + for (QObject *child : children) { + if (child->inherits(className)) + return child; + } + return nullptr; +} + +bool QQuickDataTest::showView(QQuickView &view, const QUrl &url) +{ + view.setSource(url); + while (view.status() == QQuickView::Loading) + QTest::qWait(10); + if (view.status() != QQuickView::Ready) + return false; + const QRect screenGeometry = view.screen()->availableGeometry(); + const QSize size = view.size(); + const QPoint offset = QPoint(size.width() / 2, size.height() / 2); + view.setFramePosition(screenGeometry.center() - offset); +#if QT_CONFIG(cursor) // Get the cursor out of the way. + QCursor::setPos(view.geometry().topRight() + QPoint(100, 100)); +#endif + view.show(); + if (!QTest::qWaitForWindowExposed(&view)) + return false; + if (!view.rootObject()) + return false; + return true; +} + +QQuickItem *QQuickDataTest::repeaterItemAt(QQuickItem *repeater, int i) +{ + static const QMetaMethod itemAtMethod = repeater->metaObject()->method( + repeater->metaObject()->indexOfMethod("itemAt(int)")); + QQuickItem *ret = nullptr; + itemAtMethod.invoke(repeater, Qt::DirectConnection, Q_RETURN_ARG(QQuickItem*, ret), Q_ARG(int, i)); + return ret; +} + +QQuickItem *QQuickDataTest::tableViewItemAtCell(QQuickItem *table, int col, int row) +{ + static const QMetaMethod itemAtCellMethod = table->metaObject()->method( + table->metaObject()->indexOfMethod("itemAtCell(int,int)")); + QQuickItem *ret = nullptr; + itemAtCellMethod.invoke(table, Qt::DirectConnection, + Q_RETURN_ARG(QQuickItem*, ret), Q_ARG(int, col), Q_ARG(int, row)); + return ret; +} + +QPoint QQuickDataTest::tableViewContentPos(QQuickItem *table) +{ + return QPoint(table->property("contentX").toInt(), table->property("contentY").toInt()); +} diff --git a/tests/auto/pdfquick/shared/util.h b/tests/auto/pdfquick/shared/util.h new file mode 100644 index 000000000..9ceb711af --- /dev/null +++ b/tests/auto/pdfquick/shared/util.h @@ -0,0 +1,58 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QUICK_VISUAL_TEST_UTIL_H +#define QUICK_VISUAL_TEST_UTIL_H + +#include <QtCore/QUrl> +#include <QtQuick/QQuickView> +#include <QtTest/QTest> + +/*! \internal + Base class for tests with data that are located in a "data" subfolder. +*/ +class QQuickDataTest : public QObject +{ + Q_OBJECT +public: + QQuickDataTest(); + ~QQuickDataTest(); + + bool initialized() const { return m_initialized; } + + bool showView(QQuickView &view, const QUrl &url); + + QString testFile(const QString &fileName) const; + inline QString testFile(const char *fileName) const + { return testFile(QLatin1String(fileName)); } + inline QUrl testFileUrl(const QString &fileName) const + { + const QString fn = testFile(fileName); + return fn.startsWith(QLatin1Char(':')) + ? QUrl(QLatin1String("qrc") + fn) + : QUrl::fromLocalFile(fn); + } + inline QUrl testFileUrl(const char *fileName) const + { return testFileUrl(QLatin1String(fileName)); } + + inline QString dataDirectory() const { return m_dataDirectory; } + inline QUrl dataDirectoryUrl() const { return m_dataDirectoryUrl; } + inline QString directory() const { return m_directory; } + + QObject *findFirstChild(QObject *parent, const char *className); + QQuickItem *repeaterItemAt(QQuickItem *repeater, int i); + QQuickItem *tableViewItemAtCell(QQuickItem *table, int col, int row); + QPoint tableViewContentPos(QQuickItem *table); + +public slots: + virtual void initTestCase(); + virtual void cleanupTestCase(); + +private: + bool m_initialized; + QString m_dataDirectory; + QUrl m_dataDirectoryUrl; + QString m_directory; +}; + +#endif diff --git a/tests/auto/quick/CMakeLists.txt b/tests/auto/quick/CMakeLists.txt index d44d67d38..d2cf7c3b3 100644 --- a/tests/auto/quick/CMakeLists.txt +++ b/tests/auto/quick/CMakeLists.txt @@ -1,8 +1,14 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + add_subdirectory(dialogs) add_subdirectory(publicapi) add_subdirectory(qquickwebenginedefaultsurfaceformat) add_subdirectory(qtbug-70248) -add_subdirectory(uidelegates) +# Re-enable if QTBUG-101744 and QTBUG-103354 have been fixed. +if(NOT MACOS) + add_subdirectory(uidelegates) +endif() add_subdirectory(inspectorserver) add_subdirectory(qmltests) add_subdirectory(qquickwebengineview) diff --git a/tests/auto/quick/certificateerror/CMakeLists.txt b/tests/auto/quick/certificateerror/CMakeLists.txt deleted file mode 100644 index df40edd86..000000000 --- a/tests/auto/quick/certificateerror/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -include(../../httpserver/httpserver.cmake) -include(../../util/util.cmake) - -qt_internal_add_test(tst_certificateerror_quick - SOURCES - testhandler.cpp testhandler.h - tst_certificateerror.cpp - LIBRARIES - Qt::CorePrivate - Qt::WebEngineQuickPrivate - Test::HttpServer - Test::Util -) - -set(certificateerror_resource_files - "resources/server.pem" - "resources/server.key" - "WebView.qml" -) - -qt_internal_add_resource(tst_certificateerror_quick "certificateerror" - PREFIX - "/" - FILES - ${certificateerror_resource_files} -) diff --git a/tests/auto/quick/certificateerror/resources/server.key b/tests/auto/quick/certificateerror/resources/server.key deleted file mode 100644 index 9bf87aee3..000000000 --- a/tests/auto/quick/certificateerror/resources/server.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpQIBAAKCAQEAqAygFPG5ILLb3G51D0OIN4Kpm5t3Oh1nByTnvi1kMz+sCBBd -CSugt4NnKkB6kiGtMEsrEm1/xg8Bkfbpet3v3+jAidRpjvCISqy3Z9D1cgCFM46h -iob/AvLZpqITiAgsU4fJ4auuIKhFplIGrIKMv2gK8haoBGBoRUD1RM+irwjEr6TN -XTQt2Ap+Ouxs53NLPhAOgumpfzzRR8/Umbhen+G5MhH+XTzzreiClz2V6A79ePJj -y1uQ8NJ79feOOWBDRizRDWwxsnNd24GjkpvcaTwafiK6Vdqeub+XTtiB5RPal2on -Cj0TQDcnaacecl/zmUWsIFNkNJWDcd3/vEdyOQIDAQABAoIBAQCW93icOCdim6tu -FIDu7HEjxSsPUpPCToWu4lWaAHcinxGx0NlzkpD4K4DzcSdrvfszBmQ0UtBVokd7 -1IAdU+HZmePWLk+CDM2zoAPHrO3Cs3r2PS0cIHhZMsearcG0E/uWMseHB08PoXuo -lcnPEhzVGueyYe4guGcTx+5PGeUBLf+fJcEc3rIQnT2LYulM2aqBZSQM3jRUaPYs -F0awDpCNwajW/Bt2VB14Pr+H5MJ+WSznFCqW7SolBkqDGfKckXPSHgX6xZ0y7VCI -MM8vwlVI4mPkaHvSQMSI8vS4Qh+SGQCSs/AuuNLjjPoz1YotV3Ih4YbLj6BjFP2g -CrqzT6VNAoGBANOHmsqE0nRkLzonTDrMdla5b0TjTxwtNM5DjLgJa6UBBqPe+1Lv -JFoBP9bIfYDRWZOZrxXItfMmM43nK/ST6Xqgx1IpHUCLKVr2pA9RXrP+m4oawfgn -frW212fHibeOYiLy+DaQXQ0VRFxsc/VbwKVyVlMEcNg3N93x2E67M7vjAoGBAMtg -7wDa+5gjwuyNr7LKkp5VDTmtKQhoDtg4sw6MSQSMF6fJT9Z4kGTZ23+G85/LsM/k -iXbceabGJ0CQJvGn6oW4dI2Ut2c2nCNVbQCxJ6Nyn/yW7bRLShMnwXvbGAVxVUax -5ohJPZGJ8ar2CP76A0bkvm2Nwylq2gp6Y8h7+iwzAoGBAKizwfQ6sk45iKDsrpNG -dir8gY2DbJigRTksDpLIkJ1skAspz295YpiV3oBCLjYKwVJCg6zwAo0FrqBB+oB5 -ZwByMgWI3NeZJUZy5q2Ay/Lp4MroRELR3PC3/lu6fE90szgEZ4m84TmJ+Jdtt527 -q41H/yj+pbELePb95vIDw2LZAoGBAJBZ+MmupCzUFSI5Xp+UUIS48W4ijaE92mt1 -swF8aMcleBTLOjOL11D9oGHfs0OUG6czGq6WxnGs62dT6ZBUEo1e4rsq9xH3HNOn -anq3Qt8sGIn7xjPVzHnUGeyDEYWrb0+CLZJGCcEnG7SwdKolYfYLnW281Oysvp35 -SKGf/W0pAoGAa2+sZmhb1mpGAf6Bi4z+uym/6qOJmG6CnrBSM9e/r8nujwFVkCYF -3iz48qx3GbuliO6za8aM1drX2u8KWp1uP5KzwYvtW5SfpQ1eusFblHEYQQNRcKLT -j/wZBXnU961eMKkkTe2XsPirO8rVhVmxuFLqT/aEPffcragQFFIGOEQ= ------END RSA PRIVATE KEY----- diff --git a/tests/auto/quick/certificateerror/resources/server.pem b/tests/auto/quick/certificateerror/resources/server.pem deleted file mode 100644 index a201ed08e..000000000 --- a/tests/auto/quick/certificateerror/resources/server.pem +++ /dev/null @@ -1,41 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDezCCAmOgAwIBAgIUFZEIIzeR7lEA10rb14w7MfhP87MwDQYJKoZIhvcNAQEL -BQAwWjELMAkGA1UEBhMCREUxDzANBgNVBAgMBkJlcmxpbjEPMA0GA1UEBwwGQmVy -bGluMRUwEwYDVQQKDAxUaGVRdENvbXBhbnkxEjAQBgNVBAsMCXdlYmVuZ2luZTAe -Fw0yMTA1MTAyMTM1MTJaFw0yMjA1MTAyMTM1MTJaMGAxCzAJBgNVBAYTAkRFMQ8w -DQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJlcmxpbjEVMBMGA1UECgwMVGhlUXRD -b21wYW55MRgwFgYDVQQDDA93ZWJlbmdpbmUucXQuaW8wggEiMA0GCSqGSIb3DQEB -AQUAA4IBDwAwggEKAoIBAQCoDKAU8bkgstvcbnUPQ4g3gqmbm3c6HWcHJOe+LWQz -P6wIEF0JK6C3g2cqQHqSIa0wSysSbX/GDwGR9ul63e/f6MCJ1GmO8IhKrLdn0PVy -AIUzjqGKhv8C8tmmohOICCxTh8nhq64gqEWmUgasgoy/aAryFqgEYGhFQPVEz6Kv -CMSvpM1dNC3YCn467Gznc0s+EA6C6al/PNFHz9SZuF6f4bkyEf5dPPOt6IKXPZXo -Dv148mPLW5Dw0nv19445YENGLNENbDGyc13bgaOSm9xpPBp+IrpV2p65v5dO2IHl -E9qXaicKPRNANydppx5yX/OZRawgU2Q0lYNx3f+8R3I5AgMBAAGjMzAxMBoGA1Ud -EQQTMBGCD3dlYmVuZ2luZS5xdC5pbzATBgNVHSUEDDAKBggrBgEFBQcDATANBgkq -hkiG9w0BAQsFAAOCAQEAjThKpP0sBv1vEmaqBc1wTu//7RHmFcoStTt3scADzb2C -9gjOVC4NzxBneLkv01444Z1p/Iiu/ZZ+VKu7aJElJgnBWEisYwJ09t3cdZRA0UY7 -XRvTVAqV0OlsB1Jn0afE+aTLGjWo+jSYzua0O+NK74e23p9jkdSmXxH9w0FB/oyM -FGIOFnnfP0+QR4ZVvAGk2H60tBHQKmCM6b87TiD4GQIfOghCQWH+qJYSuyGu4hkE -uis+n1KHHhed3GIJOHpm7gt1C9qtjcp1nOpv0ycQjfc9CGvr02BcQjhMeO65hX0A -TvCgKN9/XMFv5jwwjjPCL12GBhwnN2k9hM/tEYpe2A== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDOzCCAiMCFDwWg4NZxCplj3qyBxAUTi1wmj4jMA0GCSqGSIb3DQEBCwUAMFox -CzAJBgNVBAYTAkRFMQ8wDQYDVQQIDAZCZXJsaW4xDzANBgNVBAcMBkJlcmxpbjEV -MBMGA1UECgwMVGhlUXRDb21wYW55MRIwEAYDVQQLDAl3ZWJlbmdpbmUwHhcNMjEw -NTEwMjEzMTE4WhcNMjIwNTEwMjEzMTE4WjBaMQswCQYDVQQGEwJERTEPMA0GA1UE -CAwGQmVybGluMQ8wDQYDVQQHDAZCZXJsaW4xFTATBgNVBAoMDFRoZVF0Q29tcGFu -eTESMBAGA1UECwwJd2ViZW5naW5lMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAuc/8xVrfSzOsI6kYul+o1QIPBh1I86eQm1PhTBDMAAPHuzyPaEMgBkn2 -XAUmvkynGpNioaJDU2ndV2fBHvsoeQCdNNmjFTe1rKYjrN6U2X5KoYSzN93TOYzK -aR38fEFx+w4qV76nnxSjYtGNe9z74GrfWFMdDQ0NJKzvaO4gaZ+OOg0OzWy4MJQ0 -aINo3UV55Y7Nt92AxFweiuHucKu+rjf3BX7n0Af/Tcs2c84f0R3HA7euReSibVvX -f33eHLRKwu2bvDjXiUzOdkxBn9GTo6Q09LyY6wDG0ZdWnyCKj3NBQKBVrq+bs3Q0 -ATsWhj/PvYlZhhZh4EOlqYOhCpwv4wIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCC -pLSFGJcG0zhHW+2A6ogmpn2tA8gKUZx7f0J1nwgPEoAXQqWQv/299ZtmWfMKHUkk -ygG4u80C87wWPH42XWXo/KDrP9iYzoqAvtqbRuPG9PAxefQ/JUSnuhikA51g9+Mu -IDKKKSI+y/JW9u0Qo77fp/5n2DaFn5B+pBYvn/xLfaEa9bRdJMTEMsElGbPBzMZd -I/7X6B78X6Ow5TuRKSeZA7E1AZ/+e5A4Hj65bLAugoSKz3zaS0dV26LwAo18c2zP -TqtwHyIVj4QCoI6Z694q9KH4Pkml3fz8VSkk+MvZMWapvUhHu/DneTgqGbp9POYg -nx6oWME6idhnvN6DljxB ------END CERTIFICATE----- diff --git a/tests/auto/quick/dialogs/CMakeLists.txt b/tests/auto/quick/dialogs/CMakeLists.txt index b7f088f0d..4d8dc853b 100644 --- a/tests/auto/quick/dialogs/CMakeLists.txt +++ b/tests/auto/quick/dialogs/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../httpserver/httpserver.cmake) include(../../util/util.cmake) diff --git a/tests/auto/quick/dialogs/WebView.qml b/tests/auto/quick/dialogs/WebView.qml index 01f4ac297..45fafb42d 100644 --- a/tests/auto/quick/dialogs/WebView.qml +++ b/tests/auto/quick/dialogs/WebView.qml @@ -1,36 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.0 -import QtWebEngine 1.4 -import QtQuick.Window 2.0 -import QtTest 1.0 -import io.qt.tester 1.0 +import QtQuick +import QtWebEngine +import QtQuick.Window +import QtTest +import io.qt.tester Window { width: 50 diff --git a/tests/auto/quick/dialogs/dialogs.pro b/tests/auto/quick/dialogs/dialogs.pro deleted file mode 100644 index 79952d8ee..000000000 --- a/tests/auto/quick/dialogs/dialogs.pro +++ /dev/null @@ -1,13 +0,0 @@ -include(../tests.pri) -QT += core-private webenginequick webenginequick-private - -HEADERS += \ - server.h \ - testhandler.h - -SOURCES += \ - server.cpp \ - testhandler.cpp - -RESOURCES += \ - dialogs.qrc diff --git a/tests/auto/quick/dialogs/testhandler.cpp b/tests/auto/quick/dialogs/testhandler.cpp index 78a944cc6..f45852630 100644 --- a/tests/auto/quick/dialogs/testhandler.cpp +++ b/tests/auto/quick/dialogs/testhandler.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 "testhandler.h" diff --git a/tests/auto/quick/dialogs/testhandler.h b/tests/auto/quick/dialogs/testhandler.h index 93ecfcdcb..c72e81841 100644 --- a/tests/auto/quick/dialogs/testhandler.h +++ b/tests/auto/quick/dialogs/testhandler.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 TESTHANDLER_H #define TESTHANDLER_H diff --git a/tests/auto/quick/dialogs/tst_dialogs.cpp b/tests/auto/quick/dialogs/tst_dialogs.cpp index 8543c47da..2b861efa6 100644 --- a/tests/auto/quick/dialogs/tst_dialogs.cpp +++ b/tests/auto/quick/dialogs/tst_dialogs.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 "testhandler.h" #include <quickutil.h> @@ -96,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; } @@ -121,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"); } @@ -178,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(); @@ -222,7 +197,7 @@ void tst_Dialogs::javaScriptDialogRequested() QSignalSpy dialogSpy(m_listener, &TestHandler::requestChanged); m_listener->runJavaScript(script); - QTRY_COMPARE(dialogSpy.count(), 1); + QTRY_COMPARE(dialogSpy.size(), 1); auto *dialog = qobject_cast<QQuickWebEngineJavaScriptDialogRequest*>(m_listener->request()); QVERIFY2(dialog, "Incorrect dialog requested"); dialog->dialogReject(); diff --git a/tests/auto/quick/inspectorserver/CMakeLists.txt b/tests/auto/quick/inspectorserver/CMakeLists.txt index e2c3bb2ab..d890581b8 100644 --- a/tests/auto/quick/inspectorserver/CMakeLists.txt +++ b/tests/auto/quick/inspectorserver/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + qt_internal_add_test(tst_inspectorserver SOURCES tst_inspectorserver.cpp diff --git a/tests/auto/quick/inspectorserver/inspectorserver.pro b/tests/auto/quick/inspectorserver/inspectorserver.pro deleted file mode 100644 index 5110a3ae9..000000000 --- a/tests/auto/quick/inspectorserver/inspectorserver.pro +++ /dev/null @@ -1,4 +0,0 @@ -include(../tests.pri) -QT += webenginequick -QT_PRIVATE += core-private webenginequick-private webenginecore-private -DEFINES += IMPORT_DIR=\"\\\"$${ROOT_BUILD_DIR}$${QMAKE_DIR_SEP}imports\\\"\" diff --git a/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp b/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp index 2879895ca..a9638bee4 100644 --- a/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp +++ b/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 <QNetworkAccessManager> #include <QNetworkReply> @@ -34,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); @@ -47,6 +23,7 @@ private Q_SLOTS: void init(); void cleanup(); + void testDevToolsId(); void testPageList(); void testRemoteDebuggingMessage(); void openRemoteDebuggingSession(); @@ -61,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); @@ -72,8 +50,8 @@ void tst_InspectorServer::prepareWebViewComponent() static QQmlEngine* engine = new QQmlEngine(this); m_component.reset(new QQmlComponent(engine, this)); - m_component->setData(QByteArrayLiteral("import QtQuick 2.0\n" - "import QtWebEngine 1.2\n" + m_component->setData(QByteArrayLiteral("import QtQuick\n" + "import QtWebEngine\n" "WebEngineView { }") , QUrl()); } @@ -103,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(); @@ -115,13 +93,28 @@ 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() + QLatin1String("/html/basic_page.html")); QSignalSpy loadSpy(webView(), SIGNAL(loadingChanged(QWebEngineLoadingInfo))); webView()->setUrl(testPageUrl); - QTRY_VERIFY(loadSpy.size() && !webView()->isLoading()); + QTRY_VERIFY_WITH_TIMEOUT(loadSpy.size() && !webView()->isLoading(), 10000); // Our page has developerExtrasEnabled and should be the only one in the list. QJsonArray pageList = fetchPageList(); @@ -135,7 +128,7 @@ void tst_InspectorServer::testRemoteDebuggingMessage() + QLatin1String("/html/basic_page.html")); QSignalSpy loadSpy(webView(), SIGNAL(loadingChanged(QWebEngineLoadingInfo))); webView()->setUrl(testPageUrl); - QTRY_VERIFY(loadSpy.size() && !webView()->isLoading()); + QTRY_VERIFY_WITH_TIMEOUT(loadSpy.size() && !webView()->isLoading(), 10000); QJsonArray pageList = fetchPageList(); QCOMPARE(pageList.size(), 1); @@ -161,7 +154,7 @@ void tst_InspectorServer::testRemoteDebuggingMessage() .arg(pageList.at(0).toObject().value("webSocketDebuggerUrl").toString()) .arg(jsExpression)); - QTRY_COMPARE(webSocketQueryWebView->title(), jsExpressionResult); + QTRY_COMPARE_WITH_TIMEOUT(webSocketQueryWebView->title(), jsExpressionResult, 10000); } void tst_InspectorServer::openRemoteDebuggingSession() @@ -170,7 +163,7 @@ void tst_InspectorServer::openRemoteDebuggingSession() + QLatin1String("/html/basic_page.html")); QSignalSpy loadSpy(webView(), SIGNAL(loadingChanged(QWebEngineLoadingInfo))); webView()->setUrl(testPageUrl); - QTRY_VERIFY(loadSpy.size() && !webView()->isLoading()); + QTRY_VERIFY_WITH_TIMEOUT(loadSpy.size() && !webView()->isLoading(), 10000); QJsonArray pageList = fetchPageList(); QCOMPARE(pageList.size(), 1); @@ -185,7 +178,7 @@ void tst_InspectorServer::openRemoteDebuggingSession() // - The page list didn't return a valid inspector URL // - Or the front-end couldn't be loaded through the inspector HTTP server // - Or the web socket connection couldn't be established between the front-end and the page through the inspector server - QTRY_VERIFY_WITH_TIMEOUT(inspectorWebView->title().startsWith("DevTools -"), 30000); + QTRY_VERIFY_WITH_TIMEOUT(inspectorWebView->title().startsWith("DevTools -"), 60000); } QTEST_MAIN(tst_InspectorServer) diff --git a/tests/auto/quick/publicapi/CMakeLists.txt b/tests/auto/quick/publicapi/CMakeLists.txt index abed54a10..e345a076a 100644 --- a/tests/auto/quick/publicapi/CMakeLists.txt +++ b/tests/auto/quick/publicapi/CMakeLists.txt @@ -1,8 +1,10 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + qt_internal_add_test(tst_publicapi SOURCES tst_publicapi.cpp LIBRARIES Qt::CorePrivate Qt::WebEngineQuickPrivate - Qt::WebEngineWidgetsPrivate ) diff --git a/tests/auto/quick/publicapi/publicapi.pro b/tests/auto/quick/publicapi/publicapi.pro deleted file mode 100644 index bb0e03f65..000000000 --- a/tests/auto/quick/publicapi/publicapi.pro +++ /dev/null @@ -1,3 +0,0 @@ -include(../tests.pri) -QT += webenginequick -QT_PRIVATE += core-private webenginequick-private webenginecore-private diff --git a/tests/auto/quick/publicapi/tst_publicapi.cpp b/tests/auto/quick/publicapi/tst_publicapi.cpp index fe0b9d2b4..cfa75f0bf 100644 --- a/tests/auto/quick/publicapi/tst_publicapi.cpp +++ b/tests/auto/quick/publicapi/tst_publicapi.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 <QMetaEnum> #include <QMetaMethod> @@ -35,6 +10,8 @@ #include <QtTest/QtTest> #include <QtWebEngineQuick/QQuickWebEngineProfile> #include <QtWebEngineCore/QWebEngineCertificateError> +#include <QtWebEngineCore/QWebEngineDesktopMediaRequest> +#include <QtWebEngineCore/QWebEngineFileSystemAccessRequest> #include <QtWebEngineCore/QWebEngineFindTextResult> #include <QtWebEngineCore/QWebEngineFullScreenRequest> #include <QtWebEngineCore/QWebEngineHistory> @@ -47,14 +24,16 @@ #include <QtWebEngineCore/QWebEngineDownloadRequest> #include <QtWebEngineCore/QWebEngineScript> #include <QtWebEngineCore/QWebEngineLoadingInfo> +#include <QtWebEngineCore/QWebEngineWebAuthUxRequest> #include <private/qquickwebengineview_p.h> #include <private/qquickwebengineaction_p.h> #include <private/qquickwebengineclientcertificateselection_p.h> #include <private/qquickwebenginedialogrequests_p.h> #include <private/qquickwebenginedownloadrequest_p.h> -#include <private/qquickwebenginenewviewrequest_p.h> +#include <private/qquickwebenginenewwindowrequest_p.h> #include <private/qquickwebenginesettings_p.h> #include <private/qquickwebenginesingleton_p.h> +#include <private/qquickwebenginetouchselectionmenurequest_p.h> class tst_publicapi : public QObject { Q_OBJECT @@ -80,20 +59,28 @@ static const QList<const QMetaObject *> typesToCheck = QList<const QMetaObject * << &QQuickWebEngineJavaScriptDialogRequest::staticMetaObject << &QQuickWebEngineColorDialogRequest::staticMetaObject << &QQuickWebEngineFileDialogRequest::staticMetaObject - << &QQuickWebEngineNewViewRequest::staticMetaObject + << &QQuickWebEngineNewWindowRequest::staticMetaObject << &QQuickWebEngineTooltipRequest::staticMetaObject << &QWebEngineContextMenuRequest::staticMetaObject << &QWebEngineCertificateError::staticMetaObject + << &QWebEngineDesktopMediaRequest::staticMetaObject + << &QWebEngineFileSystemAccessRequest::staticMetaObject << &QWebEngineFindTextResult::staticMetaObject << &QWebEngineLoadingInfo::staticMetaObject + << &QAbstractListModel::staticMetaObject << &QWebEngineNavigationRequest::staticMetaObject << &QWebEngineNewWindowRequest::staticMetaObject << &QWebEngineNotification::staticMetaObject << &QWebEngineQuotaRequest::staticMetaObject << &QWebEngineRegisterProtocolHandlerRequest::staticMetaObject + << &QQuickWebEngineTouchSelectionMenuRequest::staticMetaObject + << &QWebEngineWebAuthUxRequest::staticMetaObject + << &QWebEngineWebAuthPinRequest::staticMetaObject ; -static QList<QMetaEnum> knownEnumNames = QList<QMetaEnum>(); +static QList<QMetaEnum> knownEnumNames = QList<QMetaEnum>() + << QWebEngineDownloadRequest::staticMetaObject.enumerator(QWebEngineDownloadRequest::staticMetaObject.indexOfEnumerator("SavePageFormat")) + ; static const QStringList hardcodedTypes = QStringList() << "QJSValue" @@ -104,7 +91,9 @@ static const QStringList hardcodedTypes = QStringList() << "const QQuickWebEngineContextMenuData*" << "QWebEngineCookieStore*" << "Qt::LayoutDirection" - << "QQuickWebEngineScriptCollection*"; + << "QQuickWebEngineScriptCollection*" + << "QQmlComponent*" + << "QMultiMap<QByteArray,QByteArray>"; static const QStringList expectedAPI = QStringList() << "QQuickWebEngineAction.text --> QString" @@ -281,10 +270,25 @@ 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" << "QWebEngineFullScreenRequest.toggleOn --> bool" + << "QWebEngineFileSystemAccessRequest.File --> HandleType" + << "QWebEngineFileSystemAccessRequest.Directory --> HandleType" + << "QWebEngineFileSystemAccessRequest.Read --> AccessFlags" + << "QWebEngineFileSystemAccessRequest.Write --> AccessFlags" + << "QWebEngineFileSystemAccessRequest.origin --> QUrl" + << "QWebEngineFileSystemAccessRequest.filePath --> QUrl" + << "QWebEngineFileSystemAccessRequest.handleType --> QWebEngineFileSystemAccessRequest::HandleType" + << "QWebEngineFileSystemAccessRequest.accessFlags --> QFlags<QWebEngineFileSystemAccessRequest::AccessFlag>" + << "QWebEngineFileSystemAccessRequest.accept() --> void" + << "QWebEngineFileSystemAccessRequest.reject() --> void" << "QWebEngineHistory.backItems --> QWebEngineHistoryModel*" << "QWebEngineHistory.clear() --> void" << "QWebEngineHistory.forwardItems --> QWebEngineHistoryModel*" @@ -303,6 +307,7 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineJavaScriptDialogRequest.title --> QString" << "QQuickWebEngineJavaScriptDialogRequest.type --> QQuickWebEngineJavaScriptDialogRequest::DialogType" << "QWebEngineLoadingInfo.errorCode --> int" + << "QWebEngineLoadingInfo.responseHeaders --> QMultiMap<QByteArray,QByteArray>" << "QWebEngineLoadingInfo.errorDomain --> QWebEngineLoadingInfo::ErrorDomain" << "QWebEngineLoadingInfo.errorString --> QString" << "QWebEngineLoadingInfo.status --> QWebEngineLoadingInfo::LoadStatus" @@ -312,6 +317,7 @@ static const QStringList expectedAPI = QStringList() << "QWebEngineLoadingInfo.LoadStartedStatus --> LoadStatus" << "QWebEngineLoadingInfo.LoadStoppedStatus --> LoadStatus" << "QWebEngineLoadingInfo.LoadSucceededStatus --> LoadStatus" + << "QWebEngineLoadingInfo.HttpStatusCodeDomain --> ErrorDomain" << "QWebEngineLoadingInfo.CertificateErrorDomain --> ErrorDomain" << "QWebEngineLoadingInfo.ConnectionErrorDomain --> ErrorDomain" << "QWebEngineLoadingInfo.DnsErrorDomain --> ErrorDomain" @@ -343,7 +349,7 @@ static const QStringList expectedAPI = QStringList() << "QWebEngineNewWindowRequest.InNewDialog --> DestinationType" << "QWebEngineNewWindowRequest.InNewTab --> DestinationType" << "QWebEngineNewWindowRequest.InNewWindow --> DestinationType" - << "QQuickWebEngineNewViewRequest.openIn(QQuickWebEngineView*) --> void" + << "QQuickWebEngineNewWindowRequest.openIn(QQuickWebEngineView*) --> void" << "QQuickWebEngineProfile.AllowPersistentCookies --> PersistentCookiesPolicy" << "QQuickWebEngineProfile.DiskHttpCache --> HttpCacheType" << "QQuickWebEngineProfile.ForcePersistentCookies --> PersistentCookiesPolicy" @@ -353,6 +359,7 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineProfile.cachePath --> QString" << "QQuickWebEngineProfile.cachePathChanged() --> void" << "QQuickWebEngineProfile.clearHttpCache() --> void" + << "QQuickWebEngineProfile.clearHttpCacheCompleted() --> void" << "QQuickWebEngineProfile.downloadFinished(QQuickWebEngineDownloadRequest*) --> void" << "QQuickWebEngineProfile.downloadRequested(QQuickWebEngineDownloadRequest*) --> void" << "QQuickWebEngineProfile.downloadPath --> QString" @@ -372,6 +379,8 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineProfile.persistentCookiesPolicyChanged() --> void" << "QQuickWebEngineProfile.persistentStoragePath --> QString" << "QQuickWebEngineProfile.persistentStoragePathChanged() --> void" + << "QQuickWebEngineProfile.isPushServiceEnabled --> bool" + << "QQuickWebEngineProfile.pushServiceEnabledChanged() --> void" << "QQuickWebEngineProfile.spellCheckEnabled --> bool" << "QQuickWebEngineProfile.spellCheckEnabledChanged() --> void" << "QQuickWebEngineProfile.spellCheckLanguages --> QStringList" @@ -400,6 +409,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" @@ -422,6 +435,8 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineSettings.localContentCanAccessRemoteUrlsChanged() --> void" << "QQuickWebEngineSettings.localStorageEnabled --> bool" << "QQuickWebEngineSettings.localStorageEnabledChanged() --> void" + << "QQuickWebEngineSettings.navigateOnDropEnabled --> bool" + << "QQuickWebEngineSettings.navigateOnDropEnabledChanged() --> void" << "QQuickWebEngineSettings.pdfViewerEnabled --> bool" << "QQuickWebEngineSettings.pdfViewerEnabledChanged() --> void" << "QQuickWebEngineSettings.playbackRequiresUserGesture --> bool" @@ -444,9 +459,17 @@ 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" + << "QQuickWebEngineTouchSelectionMenuRequest.accepted --> bool" + << "QQuickWebEngineTouchSelectionMenuRequest.Cut --> TouchSelectionCommandFlags" + << "QQuickWebEngineTouchSelectionMenuRequest.Copy --> TouchSelectionCommandFlags" + << "QQuickWebEngineTouchSelectionMenuRequest.Paste --> TouchSelectionCommandFlags" + << "QQuickWebEngineTouchSelectionMenuRequest.selectionBounds --> QRect" + << "QQuickWebEngineTouchSelectionMenuRequest.touchSelectionCommandFlags --> QFlags<QQuickWebEngineTouchSelectionMenuRequest::TouchSelectionCommandFlag>" << "QWebEngineScript.ApplicationWorld --> ScriptWorldId" << "QWebEngineScript.Deferred --> InjectionPoint" << "QWebEngineScript.DocumentCreation --> InjectionPoint" @@ -462,7 +485,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" @@ -476,6 +498,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" @@ -493,7 +516,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" @@ -503,9 +525,13 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.B7 --> PrintedPageSizeId" << "QQuickWebEngineView.B8 --> PrintedPageSizeId" << "QQuickWebEngineView.B9 --> PrintedPageSizeId" + << "QQuickWebEngineView.B10 --> PrintedPageSizeId" << "QQuickWebEngineView.Back --> WebAction" << "QQuickWebEngineView.C5E --> PrintedPageSizeId" << "QQuickWebEngineView.CertificateErrorDomain --> ErrorDomain" + << "QQuickWebEngineView.ChangeTextDirectionLTR --> WebAction" + << "QQuickWebEngineView.ChangeTextDirectionRTL --> WebAction" + << "QQuickWebEngineView.ClipboardReadWrite --> Feature" << "QQuickWebEngineView.Comm10E --> PrintedPageSizeId" << "QQuickWebEngineView.ConnectionErrorDomain --> ErrorDomain" << "QQuickWebEngineView.Copy --> WebAction" @@ -618,11 +644,10 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.LoadStartedStatus --> LoadStatus" << "QQuickWebEngineView.LoadStoppedStatus --> LoadStatus" << "QQuickWebEngineView.LoadSucceededStatus --> LoadStatus" + << "QQuickWebEngineView.LocalFontsAccess --> Feature" << "QQuickWebEngineView.MediaAudioCapture --> Feature" << "QQuickWebEngineView.MediaAudioVideoCapture --> Feature" << "QQuickWebEngineView.MediaVideoCapture --> Feature" - << "QQuickWebEngineView.NPageSize --> PrintedPageSizeId" - << "QQuickWebEngineView.NPaperSize --> PrintedPageSizeId" << "QQuickWebEngineView.NoErrorDomain --> ErrorDomain" << "QQuickWebEngineView.Notifications --> Feature" << "QQuickWebEngineView.NoWebAction --> WebAction" @@ -662,6 +687,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" @@ -681,10 +707,13 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.contentsSize --> QSizeF" << "QQuickWebEngineView.contentsSizeChanged(QSizeF) --> void" << "QQuickWebEngineView.contextMenuRequested(QWebEngineContextMenuRequest*) --> void" + << "QQuickWebEngineView.desktopMediaRequested(QWebEngineDesktopMediaRequest) --> void" + << "QQuickWebEngineView.devToolsId --> QString" << "QQuickWebEngineView.devToolsView --> QQuickWebEngineView*" << "QQuickWebEngineView.devToolsViewChanged() --> void" - << "QQuickWebEngineView.featurePermissionRequested(QUrl,Feature) --> void" + << "QQuickWebEngineView.featurePermissionRequested(QUrl,QQuickWebEngineView::Feature) --> void" << "QQuickWebEngineView.fileDialogRequested(QQuickWebEngineFileDialogRequest*) --> void" + << "QQuickWebEngineView.fileSystemAccessRequested(QWebEngineFileSystemAccessRequest) --> void" << "QQuickWebEngineView.findText(QString) --> void" << "QQuickWebEngineView.findText(QString,FindFlags) --> void" << "QQuickWebEngineView.findText(QString,FindFlags,QJSValue) --> void" @@ -695,7 +724,7 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.goBack() --> void" << "QQuickWebEngineView.goBackOrForward(int) --> void" << "QQuickWebEngineView.goForward() --> void" - << "QQuickWebEngineView.grantFeaturePermission(QUrl,Feature,bool) --> void" + << "QQuickWebEngineView.grantFeaturePermission(QUrl,QQuickWebEngineView::Feature,bool) --> void" << "QQuickWebEngineView.history --> QWebEngineHistory*" << "QQuickWebEngineView.icon --> QUrl" << "QQuickWebEngineView.iconChanged() --> void" @@ -703,10 +732,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" @@ -715,7 +744,20 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.loading --> bool" << "QQuickWebEngineView.loadingChanged(QWebEngineLoadingInfo) --> void" << "QQuickWebEngineView.navigationRequested(QWebEngineNavigationRequest*) --> void" - << "QQuickWebEngineView.newViewRequested(QQuickWebEngineNewViewRequest*) --> void" + << "QQuickWebEngineView.newWindowRequested(QQuickWebEngineNewWindowRequest*) --> void" + << "QQuickWebEngineView.AcceptRequest --> NavigationRequestAction" + << "QQuickWebEngineView.IgnoreRequest --> NavigationRequestAction" + << "QQuickWebEngineView.BackForwardNavigation --> NavigationType" + << "QQuickWebEngineView.FormSubmittedNavigation --> NavigationType" + << "QQuickWebEngineView.LinkClickedNavigation --> NavigationType" + << "QQuickWebEngineView.OtherNavigation --> NavigationType" + << "QQuickWebEngineView.RedirectNavigation --> NavigationType" + << "QQuickWebEngineView.ReloadNavigation --> NavigationType" + << "QQuickWebEngineView.TypedNavigation --> NavigationType" + << "QQuickWebEngineView.NewViewInBackgroundTab --> NewViewDestination" + << "QQuickWebEngineView.NewViewInDialog --> NewViewDestination" + << "QQuickWebEngineView.NewViewInTab --> NewViewDestination" + << "QQuickWebEngineView.NewViewInWindow --> NewViewDestination" << "QQuickWebEngineView.pdfPrintingFinished(QString,bool) --> void" << "QQuickWebEngineView.printRequested() --> void" << "QQuickWebEngineView.printToPdf(QJSValue) --> void" @@ -732,11 +774,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" @@ -751,6 +793,9 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.title --> QString" << "QQuickWebEngineView.titleChanged() --> void" << "QQuickWebEngineView.tooltipRequested(QQuickWebEngineTooltipRequest*) --> void" + << "QQuickWebEngineView.touchHandleDelegate --> QQmlComponent*" + << "QQuickWebEngineView.touchHandleDelegateChanged() --> void" + << "QQuickWebEngineView.touchSelectionMenuRequested(QQuickWebEngineTouchSelectionMenuRequest*) --> void" << "QQuickWebEngineView.triggerWebAction(WebAction) --> void" << "QQuickWebEngineView.url --> QUrl" << "QQuickWebEngineView.urlChanged() --> void" @@ -764,7 +809,9 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.windowCloseRequested() --> void" << "QQuickWebEngineView.zoomFactor --> double" << "QQuickWebEngineView.zoomFactorChanged(double) --> void" - << "QQuickWebEngineView.acceptAsNewView(QWebEngineNewWindowRequest*) --> void" + << "QQuickWebEngineView.acceptAsNewWindow(QWebEngineNewWindowRequest*) --> void" + << "QQuickWebEngineView.save(QString) --> void" + << "QQuickWebEngineView.save(QString,QWebEngineDownloadRequest::SavePageFormat) --> void" << "QWebEngineQuotaRequest.accept() --> void" << "QWebEngineQuotaRequest.origin --> QUrl" << "QWebEngineQuotaRequest.reject() --> void" @@ -783,6 +830,55 @@ static const QStringList expectedAPI = QStringList() << "QWebEngineNotification.click() --> void" << "QWebEngineNotification.close() --> void" << "QWebEngineNotification.closed() --> void" + << "QQuickWebEngineView.webAuthUxRequested(QWebEngineWebAuthUxRequest*) --> void" + << "QWebEngineWebAuthUxRequest.WebAuthUxState.NotStarted --> WebAuthUxState" + << "QWebEngineWebAuthUxRequest.WebAuthUxState.SelectAccount --> WebAuthUxState" + << "QWebEngineWebAuthUxRequest.WebAuthUxState.CollectPin --> WebAuthUxState" + << "QWebEngineWebAuthUxRequest.WebAuthUxState.FinishTokenCollection --> WebAuthUxState" + << "QWebEngineWebAuthUxRequest.WebAuthUxState.RequestFailed --> WebAuthUxState" + << "QWebEngineWebAuthUxRequest.WebAuthUxState.Cancelled --> WebAuthUxState" + << "QWebEngineWebAuthUxRequest.WebAuthUxState.Completed --> WebAuthUxState" + << "QWebEngineWebAuthUxRequest.PinEntryReason.Set --> PinEntryReason" + << "QWebEngineWebAuthUxRequest.PinEntryReason.Change --> PinEntryReason" + << "QWebEngineWebAuthUxRequest.PinEntryReason.Challenge --> PinEntryReason" + << "QWebEngineWebAuthUxRequest.PinEntryError.NoError --> PinEntryError" + << "QWebEngineWebAuthUxRequest.PinEntryError.InternalUvLocked --> PinEntryError" + << "QWebEngineWebAuthUxRequest.PinEntryError.WrongPin --> PinEntryError" + << "QWebEngineWebAuthUxRequest.PinEntryError.TooShort --> PinEntryError" + << "QWebEngineWebAuthUxRequest.PinEntryError.InvalidCharacters --> PinEntryError" + << "QWebEngineWebAuthUxRequest.PinEntryError.SameAsCurrentPin --> PinEntryError" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.Timeout --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.KeyNotRegistered --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.KeyAlreadyRegistered --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.SoftPinBlock --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.HardPinBlock --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.AuthenticatorRemovedDuringPinEntry --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.AuthenticatorMissingResidentKeys --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.AuthenticatorMissingUserVerification --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.AuthenticatorMissingLargeBlob --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.NoCommonAlgorithms --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.StorageFull --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.UserConsentDenied --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.RequestFailureReason.WinUserCancelled --> RequestFailureReason" + << "QWebEngineWebAuthUxRequest.userNames --> QStringList" + << "QWebEngineWebAuthUxRequest.state --> QWebEngineWebAuthUxRequest::WebAuthUxState" + << "QWebEngineWebAuthUxRequest.relyingPartyId --> QString" + << "QWebEngineWebAuthUxRequest.pinRequest --> QWebEngineWebAuthPinRequest" + << "QWebEngineWebAuthUxRequest.requestFailureReason --> QWebEngineWebAuthUxRequest::RequestFailureReason" + << "QWebEngineWebAuthUxRequest.stateChanged(QWebEngineWebAuthUxRequest::WebAuthUxState) --> void" + << "QWebEngineWebAuthUxRequest.cancel() --> void" + << "QWebEngineWebAuthUxRequest.retry() --> void" + << "QWebEngineWebAuthUxRequest.setSelectedAccount(QString) --> void" + << "QWebEngineWebAuthUxRequest.setPin(QString) --> void" + << "QWebEngineWebAuthPinRequest.reason --> QWebEngineWebAuthUxRequest::PinEntryReason" + << "QWebEngineWebAuthPinRequest.error --> QWebEngineWebAuthUxRequest::PinEntryError" + << "QWebEngineWebAuthPinRequest.minPinLength --> int" + << "QWebEngineWebAuthPinRequest.remainingAttempts --> int" + << "QQuickWebEngineSettings.AllowImageAnimation --> ImageAnimationPolicy" + << "QQuickWebEngineSettings.AnimateImageOnce --> ImageAnimationPolicy" + << "QQuickWebEngineSettings.DisallowImageAnimation --> ImageAnimationPolicy" + << "QQuickWebEngineSettings.imageAnimationPolicy --> QQuickWebEngineSettings::ImageAnimationPolicy" + << "QQuickWebEngineSettings.imageAnimationPolicyChanged() --> void" ; static bool isCheckedEnum(QMetaType t) @@ -876,14 +972,14 @@ 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)) { - QWARN(qPrintable("Expected list is not up-to-date: " + actual)); + qWarning("Expected list is not up-to-date: %ls", qUtf16Printable(actual)); apiMatch = false; } } @@ -891,7 +987,7 @@ void tst_publicapi::publicAPI() for (const QString &expected : expectedAPI) { if (!actualAPI.contains(expected)) { apiMatch = false; - QWARN(qPrintable("Not implemented: " + expected)); + qWarning("Not implemented: %ls", qUtf16Printable(expected)); } } diff --git a/tests/auto/quick/qmltests/BLACKLIST b/tests/auto/quick/qmltests/BLACKLIST index f4d7d98df..fc8f9f0d8 100644 --- a/tests/auto/quick/qmltests/BLACKLIST +++ b/tests/auto/quick/qmltests/BLACKLIST @@ -1,5 +1,12 @@ -[NewViewRequest::test_loadNewViewRequest] +[NewWindowRequest::test_loadNewWindowRequest] macos [WebEngineViewContextMenu::test_contextMenuLinkAndSelectedText] macos + +[CertificateError::test_fatalError] +* + +[CertificateError::test_error] +* + diff --git a/tests/auto/quick/qmltests/CMakeLists.txt b/tests/auto/quick/qmltests/CMakeLists.txt index 455c4c746..daae6d60d 100644 --- a/tests/auto/quick/qmltests/CMakeLists.txt +++ b/tests/auto/quick/qmltests/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../httpserver/httpserver.cmake) include(../../util/util.cmake) @@ -18,17 +21,21 @@ set(testList tst_activeFocusOnPress.qml tst_audioMuted.qml tst_contextMenu.qml + tst_basicProfiles.qml + 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 @@ -51,7 +58,9 @@ set(testList tst_titleChanged.qml tst_unhandledKeyEventPropagation.qml tst_userScripts.qml + tst_userScriptCollection.qml tst_viewSource.qml + tst_save.qml ) if(QT_FEATURE_webengine_webchannel) @@ -62,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 aa9d67d99..415985471 100644 --- a/tests/auto/quick/qmltests/data/TestWebEngineView.qml +++ b/tests/auto/quick/qmltests/data/TestWebEngineView.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 import QtTest @@ -35,19 +10,29 @@ WebEngineView { property bool windowCloseRequestedSignalEmitted: false settings.focusOnNavigationEnabled: true + function loadSucceeded() { return loadStatus == WebEngineView.LoadSucceededStatus } + function loadFailed() { return loadStatus == WebEngineView.LoadFailedStatus } + function loadStopped() { return loadStatus == WebEngineView.LoadStoppedStatus } + + function waitForLoadResult(timeout) { + loadStatus = null + var r = _waitFor(function() { return loadStatus != null && loadStatus != WebEngineView.LoadStartedStatus }, timeout) + return r + } + function waitForLoadSucceeded(timeout) { - var success = _waitFor(function() { return loadStatus == WebEngineView.LoadSucceededStatus }, timeout) loadStatus = null + var success = _waitFor(function() { return loadStatus == WebEngineView.LoadSucceededStatus }, timeout) return success } function waitForLoadFailed(timeout) { - var failure = _waitFor(function() { return loadStatus == WebEngineView.LoadFailedStatus }, timeout) loadStatus = null + var failure = _waitFor(function() { return loadStatus == WebEngineView.LoadFailedStatus }, timeout) return failure } function waitForLoadStopped(timeout) { - var stop = _waitFor(function() { return loadStatus == WebEngineView.LoadStoppedStatus }, timeout) loadStatus = null + var stop = _waitFor(function() { return loadStatus == WebEngineView.LoadStoppedStatus }, timeout) return stop } function waitForWindowCloseRequested() { @@ -55,7 +40,7 @@ WebEngineView { } function _waitFor(predicate, timeout) { if (timeout === undefined) - timeout = 12000; + timeout = 30000; var i = 0 while (i < timeout && !predicate()) { testResult.wait(50) @@ -103,6 +88,21 @@ WebEngineView { return textSelection; } + function getElementValue(element) { + var elementValue; + runJavaScript("document.getElementById('" + element + "').value", function(result) { + elementValue = result; + }); + testCase.tryVerify(function() { return elementValue != undefined; }); + return elementValue; + } + + function compareElementValue(element, expected) { + testCase.tryVerify(function() { return expected == getElementValue(element); }, 5000, + "Value of element \"" + element + "\" is \"" + expected + "\""); + } + + TestResult { id: testResult } onLoadingChanged: function(load) { 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 c86139c13..720e83676 100644 --- a/tests/auto/quick/qmltests/data/titleupdate.js +++ b/tests/auto/quick/qmltests/data/titleupdate.js @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 function updateTitle() { diff --git a/tests/auto/quick/qmltests/data/tst_action.qml b/tests/auto/quick/qmltests/data/tst_action.qml index 91f260e0e..9e49c2dbf 100644 --- a/tests/auto/quick/qmltests/data/tst_action.qml +++ b/tests/auto/quick/qmltests/data/tst_action.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.0 -import QtWebEngine 1.8 +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtTest +import QtWebEngine TestWebEngineView { id: webEngineView @@ -90,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 83a2dc8c9..77968f6b6 100644 --- a/tests/auto/quick/qmltests/data/tst_activeFocusOnPress.qml +++ b/tests/auto/quick/qmltests/data/tst_activeFocusOnPress.qml @@ -1,33 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 -import QtTest 1.0 +import QtQuick +import QtTest Item { id: root diff --git a/tests/auto/quick/qmltests/data/tst_audioMuted.qml b/tests/auto/quick/qmltests/data/tst_audioMuted.qml index d0d9e35c3..85f813f0c 100644 --- a/tests/auto/quick/qmltests/data/tst_audioMuted.qml +++ b/tests/auto/quick/qmltests/data/tst_audioMuted.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.0 -import QtTest 1.0 -import QtWebEngine 1.4 +import QtQuick +import QtTest +import QtWebEngine TestWebEngineView { id: view diff --git a/tests/auto/quick/qmltests/data/tst_basicProfiles.qml b/tests/auto/quick/qmltests/data/tst_basicProfiles.qml new file mode 100644 index 000000000..97a25cdd8 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_basicProfiles.qml @@ -0,0 +1,90 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtTest +import QtWebEngine +import Qt.labs.platform + +Item { + WebEngineProfile { id: otrProfile; /* MEMO implicit offTheRecord: true */ } + WebEngineProfile { id: nonOtrProfile; offTheRecord: false } + + function getPath(path, offset = 1) { return path.substr(path.indexOf(':') + offset, path.length) } + property string appDataLocation: getPath(getPath(StandardPaths.writableLocation(StandardPaths.AppDataLocation).toString(), 3)) + property string cacheLocation: getPath(getPath(StandardPaths.writableLocation(StandardPaths.CacheLocation).toString(), 3)) + property string downloadLocation: getPath(getPath(StandardPaths.writableLocation(StandardPaths.DownloadLocation).toString(), 3)) + + TestCase { + name: "BasicProfiles" + + function test_defaultProfile() { + let p = WebEngine.defaultProfile + verify(p.offTheRecord) + + compare(p.storageName, '') + compare(p.cachePath, '') + compare(getPath(p.persistentStoragePath), appDataLocation + '/QtWebEngine/OffTheRecord') + compare(p.httpCacheType, WebEngineProfile.MemoryHttpCache) + compare(p.httpCacheMaximumSize, 0) + compare(p.persistentCookiesPolicy, WebEngineProfile.NoPersistentCookies) + + compare(getPath(p.downloadPath), downloadLocation) + compare(p.httpAcceptLanguage, '') + verify(p.httpUserAgent !== '') + compare(p.spellCheckEnabled, false) + compare(p.spellCheckLanguages, []) + + compare(p.userScripts.collection, []) + } + + function test_otrProfile() { + let p = otrProfile + verify(p.offTheRecord) + + compare(p.storageName, '') + compare(p.cachePath, '') + compare(getPath(p.persistentStoragePath), appDataLocation + '/QtWebEngine/OffTheRecord') + compare(p.httpCacheType, WebEngineProfile.MemoryHttpCache) + compare(p.httpCacheMaximumSize, 0) + compare(p.persistentCookiesPolicy, WebEngineProfile.NoPersistentCookies) + + compare(getPath(p.downloadPath), downloadLocation) + compare(p.httpAcceptLanguage, '') + verify(p.httpUserAgent !== '') + compare(p.spellCheckEnabled, false) + compare(p.spellCheckLanguages, []) + + compare(p.userScripts.collection, []) + } + + function test_nonOtrProfile() { + let p = nonOtrProfile + verify(!p.offTheRecord) + + compare(p.storageName, '') + compare(p.cachePath, '') + compare(getPath(p.persistentStoragePath), appDataLocation + '/QtWebEngine/UnknownProfile') + compare(p.httpCacheType, WebEngineProfile.MemoryHttpCache) + compare(p.httpCacheMaximumSize, 0) + compare(p.persistentCookiesPolicy, WebEngineProfile.NoPersistentCookies) + + compare(getPath(p.downloadPath), downloadLocation) + compare(p.httpAcceptLanguage, '') + verify(p.httpUserAgent !== '') + compare(p.spellCheckEnabled, false) + compare(p.spellCheckLanguages, []) + + compare(p.userScripts.collection, []) + + p.storageName = 'Test' + compare(p.storageName, 'Test') + compare(getPath(p.cachePath), cacheLocation + '/QtWebEngine/' + p.storageName) + compare(getPath(p.persistentStoragePath), appDataLocation + '/QtWebEngine/' + p.storageName) + + compare(p.httpCacheType, WebEngineProfile.DiskHttpCache) + compare(p.httpCacheMaximumSize, 0) + compare(p.persistentCookiesPolicy, WebEngineProfile.AllowPersistentCookies) + } + } +} diff --git a/tests/auto/quick/qmltests/data/tst_certificateError.qml b/tests/auto/quick/qmltests/data/tst_certificateError.qml index a707f4a74..220ef9ac8 100644 --- a/tests/auto/quick/qmltests/data/tst_certificateError.qml +++ b/tests/auto/quick/qmltests/data/tst_certificateError.qml @@ -1,36 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.0 -import QtWebEngine 1.9 - -import Test.Shared 1.0 as Shared +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtTest +import QtWebEngine + +import Test.Shared as Shared TestWebEngineView { id: view; width: 320; height: 320 @@ -121,5 +96,27 @@ TestWebEngineView { verify(error.overridable) compare(error.type, WebEngineCertificateError.CertificateAuthorityInvalid) } + + function test_fatalError() { + let error = undefined + var handleCertificateError = function(e) { error = e; } + view.certificateError.connect(handleCertificateError); + + view.url = Qt.resolvedUrl('https://revoked.badssl.com'); + if (!view.waitForLoadResult()) { + verify(!error, "There shouldn't be any certificate error if not loaded due to missing internet access!"); + skip("Couldn't load page from network, skipping test."); + } + view.certificateError.disconnect(handleCertificateError); + + // revoked certificate might not be reported as invalid by chromium and the load will silently succeed + const failed = view.loadStatus == WebEngineView.LoadFailedStatus, hasError = Boolean(error) + compare(hasError, failed) + if (failed) { + verify(!error.overridable); + // Fatal certificate errors are implicitly rejected. But second call should not cause crash. + error.rejectCertificate(); + } + } } } diff --git a/tests/auto/quick/qmltests/data/tst_contextMenu.qml b/tests/auto/quick/qmltests/data/tst_contextMenu.qml index d415996bd..58e27b8ba 100644 --- a/tests/auto/quick/qmltests/data/tst_contextMenu.qml +++ b/tests/auto/quick/qmltests/data/tst_contextMenu.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick import QtTest diff --git a/tests/auto/quick/qmltests/data/tst_datalist.qml b/tests/auto/quick/qmltests/data/tst_datalist.qml new file mode 100644 index 000000000..f739639b2 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_datalist.qml @@ -0,0 +1,180 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Controls +import QtTest +import QtWebEngine + +TestWebEngineView { + id: webEngineView + width: 200 + height: 400 + + property string html: "<html><body>" + + "<input id='browserInput' list='browserDatalist'>" + + "<datalist id='browserDatalist'>" + + " <option value='Internet Explorer'>" + + " <option value='Firefox'>" + + " <option value='Chrome'>" + + " <option value='Opera'>" + + " <option value='Safari'>" + + "</datalist>" + + "</body></html>" + + function listView() { + if (webEngineView.parent.visibleChildren.length == 1) { + // No popup case. + return null; + } + + let overlay = null; + for (let i = 0; i < webEngineView.parent.visibleChildren.length; ++i) { + let child = webEngineView.parent.visibleChildren[i]; + if (child instanceof Overlay) { + overlay = child; + break; + } + } + + if (!overlay) + return null; + + let popupItem = null; + for (let i = 0; i < overlay.visibleChildren[0].visibleChildren.length; ++i) { + let child = overlay.visibleChildren[0].visibleChildren[i]; + if (child.objectName == "QQuickPopupItem") { + popupItem = child; + } + } + + if (!popupItem) + return null; + + for (let i = 0; i < popupItem.visibleChildren.length; ++i) { + let child = popupItem.visibleChildren[i]; + if (child instanceof ListView) + return child; + } + + return null; + } + + TestCase { + id: testCase + name: "WebEngineDatalist" + when: windowShown + + function test_showAndHide() { + webEngineView.loadHtml(webEngineView.html); + verify(webEngineView.waitForLoadSucceeded()); + + var values = ""; + webEngineView.runJavaScript( + "(function() {" + + " var browserDatalist = document.getElementById('browserDatalist');" + + " var options = browserDatalist.options;" + + " var result = [];" + + " for (let i = 0; i < options.length; ++i) {" + + " result.push(options[i].value);" + + " }" + + " return result;" + + "})();", function(result) { values = result; }); + tryVerify(function() { return values.length != 0; }); + compare(values, ["Internet Explorer", "Firefox", "Chrome", "Opera", "Safari"]); + compareElementValue("browserInput", ""); + + // Make sure there is no open popup yet. + verify(!listView()); + // Click in the input field. + var browserInputCenter = getElementCenter("browserInput"); + mouseClick(webEngineView, browserInputCenter.x, browserInputCenter.y, Qt.LeftButton); + // Wait for the popup. + tryVerify(function() { return listView() != null; }); + + // No suggestion is selected. + verify(!listView().currentItem); + compare(listView().count, 5); + + // Accepting suggestion does nothing. + keyClick(Qt.Key_Enter); + tryVerify(function() { return listView() != null; }); + verify(!listView().currentItem); + + // Escape should close popup. + keyClick(Qt.Key_Escape); + tryVerify(function() { return listView() == null; }); + + // Key Down should open the popup and select the first suggestion. + keyClick(Qt.Key_Down); + tryVerify(function() { return listView() != null; }); + compare(listView().currentIndex, 0); + verify(listView().currentItem); + } + + function test_keyboardNavigationAndAccept() { + webEngineView.loadHtml(html); + verify(webEngineView.waitForLoadSucceeded()); + setFocusToElement("browserInput"); + + // Make sure there is no open popup yet. + verify(!listView()); + + // Key Down should open the popup and select the first suggestion. + keyClick(Qt.Key_Down); + tryVerify(function() { return listView() != null; }); + compare(listView().currentIndex, 0); + + // Test keyboard navigation in list. + keyClick(Qt.Key_Up); + compare(listView().currentIndex, 4); + keyClick(Qt.Key_Up); + compare(listView().currentIndex, 3); + keyClick(Qt.Key_PageDown); + compare(listView().currentIndex, 4); + keyClick(Qt.Key_PageUp); + compare(listView().currentIndex, 0); + keyClick(Qt.Key_Down); + compare(listView().currentIndex, 1); + keyClick(Qt.Key_Down); + compare(listView().currentIndex, 2); + + // Test accepting suggestion. + compare(listView().currentItem.text, "Chrome"); + keyClick(Qt.Key_Enter); + compareElementValue("browserInput", "Chrome"); + // Accept closes popup. + tryVerify(function() { return listView() == null; }); + + // Clear input field, should not trigger popup. + webEngineView.runJavaScript("document.getElementById('browserInput').value = ''"); + compareElementValue("browserInput", ""); + verify(listView() == null); + } + + function test_filterSuggestion() { + webEngineView.loadHtml(html); + verify(webEngineView.waitForLoadSucceeded()); + setFocusToElement("browserInput"); + + // Make sure there is no open popup yet. + verify(!listView()); + + // Filter suggestions. + keyClick(Qt.Key_F); + tryVerify(function() { return listView() != null; }); + compare(listView().count, 2); + verify(!listView().currentItem); + compare(listView().itemAtIndex(0).text, "Firefox"); + compare(listView().itemAtIndex(1).text, "Safari"); + keyClick(Qt.Key_I); + tryVerify(function() { return listView().count == 1; }); + verify(!listView().currentItem); + compare(listView().itemAtIndex(0).text, "Firefox"); + keyClick(Qt.Key_L); + // Mismatch should close popup. + tryVerify(function() { return listView() == null; }); + compareElementValue("browserInput", "fil"); + } + } +} diff --git a/tests/auto/quick/qmltests/data/tst_desktopBehaviorLoadHtml.qml b/tests/auto/quick/qmltests/data/tst_desktopBehaviorLoadHtml.qml index 780294348..6cb2841ec 100644 --- a/tests/auto/quick/qmltests/data/tst_desktopBehaviorLoadHtml.qml +++ b/tests/auto/quick/qmltests/data/tst_desktopBehaviorLoadHtml.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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.0 -import QtTest 1.0 -import QtWebEngine 1.2 +import QtQuick +import QtTest +import QtWebEngine TestWebEngineView { id: webEngineView @@ -44,7 +19,7 @@ TestWebEngineView { signalName: "linkHovered" } - onLinkHovered: { + onLinkHovered: function(hoveredUrl) { webEngineView.lastUrl = hoveredUrl } diff --git a/tests/auto/quick/qmltests/data/tst_download.qml b/tests/auto/quick/qmltests/data/tst_download.qml index bac4d5cc5..61a363c39 100644 --- a/tests/auto/quick/qmltests/data/tst_download.qml +++ b/tests/auto/quick/qmltests/data/tst_download.qml @@ -1,36 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.0 -import QtTest 1.0 -import QtWebEngine 1.10 -import Qt.labs.platform 1.0 -import Test.util 1.0 +import QtQuick +import QtTest +import QtWebEngine +import Qt.labs.platform +import Test.util TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_dragHandlerUnderView.qml b/tests/auto/quick/qmltests/data/tst_dragHandlerUnderView.qml new file mode 100644 index 000000000..c22bd44c2 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_dragHandlerUnderView.qml @@ -0,0 +1,67 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtTest +import QtWebEngine + +Item { + id: parentItem + width: 400 + height: 300 + + Rectangle { + id: draggableDownUnder + color: "wheat" + width: 350 + height: 250 + + DragHandler { id: dragHandler } + } + + TestWebEngineView { + id: webEngineView + width: 300 + height: 250 + + property var testUrl: Qt.resolvedUrl("test4.html") + + SignalSpy { + id: scrollPositionSpy + target: webEngineView + signalName: "onScrollPositionChanged" + } + + SignalSpy { + id: dragActiveSpy + target: dragHandler + signalName: "activeChanged" + } + + TestCase { + id: testCase + name: "KeepMouseGrabDuringScrolling" + when: windowShown + + function test_scroll() { + webEngineView.url = Qt.resolvedUrl("test4.html"); + verify(webEngineView.waitForLoadSucceeded()); + + mousePress(webEngineView, 295, 20); + mouseMove(webEngineView, 295, 200); + mouseRelease(webEngineView, 295, 200); + + // WebEngineView scrolled if the scrollbar was visible. + // But on macOS, the scrollbar is hidden, so text gets selected. + tryVerify(function() { + return (scrollPositionSpy.count === 1 && webEngineView.scrollPosition.y > 100) + || webEngineView.getTextSelection().length > 0; + }); + + // DragHandler didn't take over and drag + compare(dragActiveSpy.count, 0); + compare(draggableDownUnder.y, 0); + } + } + } +} diff --git a/tests/auto/quick/qmltests/data/tst_favicon.qml b/tests/auto/quick/qmltests/data/tst_favicon.qml index 79c835c70..15f116e5d 100644 --- a/tests/auto/quick/qmltests/data/tst_favicon.qml +++ b/tests/auto/quick/qmltests/data/tst_favicon.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick import QtTest @@ -165,6 +140,38 @@ TestWebEngineView { compare(iconUrl, Qt.resolvedUrl("icons/qt32.ico")) } + function test_faviconLoadPushState_data() { + return [ + { tag: "OTR", profile: defaultProfile }, + { tag: "non-OTR", profile: nonOTRProfile }, + ]; + } + + function test_faviconLoadPushState(row) { + webEngineView.profile = row.profile; + compare(iconChangedSpy.count, 0); + + var iconUrl; + + webEngineView.url = Qt.resolvedUrl("favicon.html"); + verify(webEngineView.waitForLoadSucceeded()); + tryCompare(iconChangedSpy, "count", 1); + iconUrl = removeFaviconProviderPrefix(webEngineView.icon); + compare(iconUrl, Qt.resolvedUrl("icons/favicon.png")); + + iconChangedSpy.clear(); + + // pushState() is a same document navigation and should not reset or + // update favicon. + compare(webEngineView.history.items.rowCount(), 1); + runJavaScript("history.pushState('', '')"); + tryVerify(function() { return webEngineView.history.items.rowCount() === 2; }); + + // Favicon change is not expected. + compare(iconChangedSpy.count, 0); + iconUrl = removeFaviconProviderPrefix(webEngineView.icon); + compare(iconUrl, Qt.resolvedUrl("icons/favicon.png")); + } function test_noFavicon_data() { return [ diff --git a/tests/auto/quick/qmltests/data/tst_faviconDatabase.qml b/tests/auto/quick/qmltests/data/tst_faviconDatabase.qml index 181c652d7..284390619 100644 --- a/tests/auto/quick/qmltests/data/tst_faviconDatabase.qml +++ b/tests/auto/quick/qmltests/data/tst_faviconDatabase.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick import QtTest @@ -110,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); @@ -154,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 5b963c7bf..a7b59b2e9 100644 --- a/tests/auto/quick/qmltests/data/tst_filePicker.qml +++ b/tests/auto/quick/qmltests/data/tst_filePicker.qml @@ -1,42 +1,16 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 -import QtTest 1.0 -import QtWebEngine 1.2 -import "../../qmltests/data" 1.0 -import "../mock-delegates/TestParams" 1.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 +import QtTest +import QtWebEngine +import "../../qmltests/data" +import "../mock-delegates/TestParams" TestWebEngineView { id: webEngineView width: 400 height: 300 - property var titleChanges: [] function driveLetter() { if (Qt.platform.os !== "windows") @@ -55,8 +29,6 @@ TestWebEngineView { signalName: "renderProcessTerminated" } - onTitleChanged: { titleChanges.push(webEngineView.title) } - TestCase { id: testCase name: "WebEngineViewSingleFileUpload" @@ -69,7 +41,6 @@ TestWebEngineView { FilePickerParams.nameFilters = [] titleSpy.clear() terminationSpy.clear() - titleChanges = [] } function cleanup() { @@ -112,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 @@ -123,7 +94,7 @@ TestWebEngineView { function acceptedFileHandler(request) { request.accepted = true; - request.dialogAccept(row.input); + request.dialogAccept([row.input]); finished = true; } @@ -133,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); } @@ -171,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("^([^,]+,)+[^,]+$"); }) @@ -267,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 @@ -278,7 +249,7 @@ TestWebEngineView { function acceptedFileHandler(request) { request.accepted = true; - request.dialogAccept(row.input); + request.dialogAccept([row.input]); finished = true; } @@ -288,10 +259,10 @@ TestWebEngineView { keyClick(Qt.Key_Enter); // Focus is on the button. Open FileDialog. tryVerify(function() { return finished; }); + tryCompare(webEngineView, "title", row.expected); webEngineView.url = Qt.resolvedUrl("about:blank"); verify(webEngineView.waitForLoadSucceeded()); tryCompare(webEngineView, "title", "about:blank"); - compare(titleChanges[titleChanges.length-2], row.expected); webEngineView.fileDialogRequested.disconnect(acceptedFileHandler); } diff --git a/tests/auto/quick/qmltests/data/tst_filesystem.qml b/tests/auto/quick/qmltests/data/tst_filesystem.qml new file mode 100644 index 000000000..fa0da4457 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_filesystem.qml @@ -0,0 +1,124 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtTest +import QtWebEngine +import Test.util +import "../../qmltests/data" +import "../mock-delegates/TestParams" + + +TestWebEngineView { + id: webEngineView + width: 400 + height: 300 + property var logs: [] + property bool accessRequested: false + property url file: tempDir.pathUrl('file.txt') + + onJavaScriptConsoleMessage: function(level, message, lineNumber, source) { + var pair = message.split(':'); + if (pair.length == 2 && pair[0] == "TEST") + logs.push(pair[1]); + } + + TempDir { id: tempDir } + + TestCase { + id: testCase + name: "FileSystemAPI" + when: windowShown + + function init() { + clearLog() + FilePickerParams.filePickerOpened = false + FilePickerParams.selectFiles = false + FilePickerParams.selectedFilesUrl = [] + FilePickerParams.nameFilters = [] + accessRequested = false; + } + + function cleanup() { + clearLog() + } + + function clearLog() { + logs = [] + } + + function logContainsDoneMarker() { + if (logs.indexOf("DONE") > -1) + return true + else + return false + } + + function result() { + return logs[0] + } + + function fileAccessRequest(request) { + testCase.verify(!accessRequested) + accessRequested = true + testCase.verify(request.filePath == file) + testCase.verify(request.accessFlags == WebEngineFileSystemAccessRequest.Write | WebEngineFileSystemAccessRequest.Read) + request.accept() + } + + function directoryAccessRequest(request) { + testCase.verify(!accessRequested) + accessRequested = true + testCase.verify(request.filePath == tempDir.pathUrl()) + testCase.verify(request.accessFlags == WebEngineFileSystemAccessRequest.Read) + request.accept() + } + + function test_saveFile() { + webEngineView.fileSystemAccessRequested.connect(fileAccessRequest); + webEngineView.url = Qt.resolvedUrl("filesystemapi.html?dialog=savePicker"); + verify(webEngineView.waitForLoadSucceeded()); + FilePickerParams.selectFiles = true; + FilePickerParams.selectedFilesUrl.push(file); + keyClick(Qt.Key_Enter); // Open SaveDialog. + tryCompare(FilePickerParams, "filePickerOpened", true); + tryVerify(logContainsDoneMarker,2000) + // write access for save dialogs is automatically granted + verify(!accessRequested) + webEngineView.fileSystemAccessRequested.disconnect(fileAccessRequest); + } + + function test_openFile() { + // first save the file before open + test_saveFile() + init() + webEngineView.fileSystemAccessRequested.connect(fileAccessRequest); + webEngineView.url = Qt.resolvedUrl("filesystemapi.html?dialog=filePicker"); + verify(webEngineView.waitForLoadSucceeded()); + FilePickerParams.selectFiles = true; + FilePickerParams.selectedFilesUrl.push(file); + keyClick(Qt.Key_Enter); // Open FileDialog. + tryCompare(FilePickerParams, "filePickerOpened", true); + tryVerify(logContainsDoneMarker,2000) + verify(logs.indexOf("TEST_CONTENT") > -1) + verify(accessRequested) + webEngineView.fileSystemAccessRequested.disconnect(fileAccessRequest); + } + + function test_selectDirectory() { + tempDir.createDirectory("TEST_DIR") + webEngineView.fileSystemAccessRequested.connect(directoryAccessRequest); + webEngineView.url = Qt.resolvedUrl("filesystemapi.html?dialog=directoryPicker"); + verify(webEngineView.waitForLoadSucceeded()) + FilePickerParams.selectFiles = true; + FilePickerParams.selectedFilesUrl.push(tempDir.pathUrl()); + keyClick(Qt.Key_Enter); // Open showDirectoryDialog. + tryCompare(FilePickerParams, "directoryPickerOpened", true); + tryVerify(logContainsDoneMarker,2000) + verify(logs.indexOf("TEST_DIR") > -1) + verify(accessRequested) + webEngineView.fileSystemAccessRequested.disconnect(directoryAccessRequest); + } + + } +} diff --git a/tests/auto/quick/qmltests/data/tst_findText.qml b/tests/auto/quick/qmltests/data/tst_findText.qml index 0b4a8d459..597cff73e 100644 --- a/tests/auto/quick/qmltests/data/tst_findText.qml +++ b/tests/auto/quick/qmltests/data/tst_findText.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 -import QtTest 1.0 -import QtWebEngine 1.2 +// 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 +import QtTest +import QtWebEngine TestWebEngineView { id: webEngineView @@ -231,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 b2b7374f6..f070e4bc5 100644 --- a/tests/auto/quick/qmltests/data/tst_focusOnNavigation.qml +++ b/tests/auto/quick/qmltests/data/tst_focusOnNavigation.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.5 -import QtTest 1.0 -import QtWebEngine 1.4 +// 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 +import QtTest +import QtWebEngine Item { id: container diff --git a/tests/auto/quick/qmltests/data/tst_fullScreenRequest.qml b/tests/auto/quick/qmltests/data/tst_fullScreenRequest.qml index 2d9247b26..c7996a11e 100644 --- a/tests/auto/quick/qmltests/data/tst_fullScreenRequest.qml +++ b/tests/auto/quick/qmltests/data/tst_fullScreenRequest.qml @@ -1,6 +1,6 @@ -import QtQuick 2.2 -import QtTest 1.0 -import QtWebEngine 1.9 +import QtQuick +import QtTest +import QtWebEngine TestWebEngineView { id: view diff --git a/tests/auto/quick/qmltests/data/tst_geopermission.qml b/tests/auto/quick/qmltests/data/tst_geopermission.qml index 9f613abf3..b99e50acc 100644 --- a/tests/auto/quick/qmltests/data/tst_geopermission.qml +++ b/tests/auto/quick/qmltests/data/tst_geopermission.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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.2 -import QtTest 1.0 -import QtWebEngine 1.1 +import QtQuick +import QtTest +import QtWebEngine TestWebEngineView { id: webEngineView @@ -44,7 +19,7 @@ TestWebEngineView { signalName: "featurePermissionRequested" } - onFeaturePermissionRequested: { + onFeaturePermissionRequested: function(securityOrigin, feature) { if (feature === WebEngineView.Geolocation) { geoPermissionRequested = true if (deniedGeolocation) { diff --git a/tests/auto/quick/qmltests/data/tst_getUserMedia.qml b/tests/auto/quick/qmltests/data/tst_getUserMedia.qml index 4cfbbf090..3b33b7abe 100644 --- a/tests/auto/quick/qmltests/data/tst_getUserMedia.qml +++ b/tests/auto/quick/qmltests/data/tst_getUserMedia.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.0 -import QtWebEngine 1.6 +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtTest +import QtWebEngine TestWebEngineView { id: webEngineView @@ -143,7 +118,7 @@ TestWebEngineView { property variant requestedFeature property variant requestedSecurityOrigin - onFeaturePermissionRequested: { + onFeaturePermissionRequested: function(securityOrigin, feature) { requestedFeature = feature requestedSecurityOrigin = securityOrigin } diff --git a/tests/auto/quick/qmltests/data/tst_inputMethod.qml b/tests/auto/quick/qmltests/data/tst_inputMethod.qml index 16b7a06d5..cf79e8a4d 100644 --- a/tests/auto/quick/qmltests/data/tst_inputMethod.qml +++ b/tests/auto/quick/qmltests/data/tst_inputMethod.qml @@ -1,36 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick import QtTest import QtWebEngine import Test.util -import "../../qmltests/data" 1.0 +import "../../qmltests/data" TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_inputTextDirection.qml b/tests/auto/quick/qmltests/data/tst_inputTextDirection.qml new file mode 100644 index 000000000..2141db4c8 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_inputTextDirection.qml @@ -0,0 +1,43 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtTest +import QtWebEngine + +TestWebEngineView { + id: webEngineView + width: 400 + height: 400 + + TestCase { + id: testCase + name: "WebEngineInputTextDirection" + when: windowShown + + function getInputTextDirection(element) { + var dir; + runJavaScript("document.getElementById('" + element + "').dir", function(result) { + dir = result; + }); + tryVerify(function() { return dir != undefined; }); + return dir; + } + + function test_changeInputTextDirection() { + webEngineView.loadHtml("<html><body><input type='text' id='textfield' value='some text'></body></html>"); + verify(webEngineView.waitForLoadSucceeded()); + setFocusToElement("textfield"); + + var rtlAction = webEngineView.action(WebEngineView.ChangeTextDirectionRTL); + verify(rtlAction); + rtlAction.trigger(); + compare(getInputTextDirection("textfield"), "rtl"); + + var ltrAction = webEngineView.action(WebEngineView.ChangeTextDirectionLTR); + verify(ltrAction); + ltrAction.trigger(); + compare(getInputTextDirection("textfield"), "ltr"); + } + } +} diff --git a/tests/auto/quick/qmltests/data/tst_javaScriptDialogs.qml b/tests/auto/quick/qmltests/data/tst_javaScriptDialogs.qml index 91b57c101..6e91b2e77 100644 --- a/tests/auto/quick/qmltests/data/tst_javaScriptDialogs.qml +++ b/tests/auto/quick/qmltests/data/tst_javaScriptDialogs.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2015 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 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 136863c4c..0f69a7e81 100644 --- a/tests/auto/quick/qmltests/data/tst_keyboardEvents.qml +++ b/tests/auto/quick/qmltests/data/tst_keyboardEvents.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 -import QtTest 1.0 -import QtWebEngine 1.4 +// 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 +import QtTest +import QtWebEngine TestWebEngineView { id: webEngineView @@ -54,20 +29,6 @@ TestWebEngineView { "Element \"" + element + "\" is " + (expected ? "" : "not") + " checked"); } - function getElementValue(element) { - var elementValue; - runJavaScript("document.getElementById('" + element + "').value", function(result) { - elementValue = result; - }); - tryVerify(function() { return elementValue != undefined; }); - return elementValue; - } - - function compareElementValue(element, expected) { - tryVerify(function() { return expected == getElementValue(element); }, 5000, - "Value of element \"" + element + "\" is \"" + expected + "\""); - } - function test_keyboardEvents() { webEngineView.url = Qt.resolvedUrl("keyboardEvents.html"); verify(webEngineView.waitForLoadSucceeded()); diff --git a/tests/auto/quick/qmltests/data/tst_keyboardModifierMapping.qml b/tests/auto/quick/qmltests/data/tst_keyboardModifierMapping.qml index e0a8c0a41..d0bc75619 100644 --- a/tests/auto/quick/qmltests/data/tst_keyboardModifierMapping.qml +++ b/tests/auto/quick/qmltests/data/tst_keyboardModifierMapping.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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.0 -import QtTest 1.0 -import QtWebEngine 1.2 +import QtQuick +import QtTest +import QtWebEngine TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_linkHovered.qml b/tests/auto/quick/qmltests/data/tst_linkHovered.qml index d21d74434..a11bd2450 100644 --- a/tests/auto/quick/qmltests/data/tst_linkHovered.qml +++ b/tests/auto/quick/qmltests/data/tst_linkHovered.qml @@ -1,35 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 import QtTest import QtWebEngine -import "../../qmltests/data" 1.0 +import "../../qmltests/data" TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_loadFail.qml b/tests/auto/quick/qmltests/data/tst_loadFail.qml index 58d6b9e3c..8e9224bbf 100644 --- a/tests/auto/quick/qmltests/data/tst_loadFail.qml +++ b/tests/auto/quick/qmltests/data/tst_loadFail.qml @@ -1,35 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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.0 -import QtTest 1.0 -import QtWebEngine 1.2 -import "../../qmltests/data" 1.0 +import QtQuick +import QtTest +import QtWebEngine +import "../../qmltests/data" TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_loadHtml.qml b/tests/auto/quick/qmltests/data/tst_loadHtml.qml index 6ed9a4317..8f94cd4a2 100644 --- a/tests/auto/quick/qmltests/data/tst_loadHtml.qml +++ b/tests/auto/quick/qmltests/data/tst_loadHtml.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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.0 -import QtTest 1.0 -import QtWebEngine 1.2 +import QtQuick +import QtTest +import QtWebEngine TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_loadProgress.qml b/tests/auto/quick/qmltests/data/tst_loadProgress.qml index 7bfe1d9e9..2c06a0207 100644 --- a/tests/auto/quick/qmltests/data/tst_loadProgress.qml +++ b/tests/auto/quick/qmltests/data/tst_loadProgress.qml @@ -1,36 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.0 -import QtTest 1.0 -import QtWebEngine 1.2 +import QtQuick +import QtTest +import QtWebEngine -import Test.Shared 1.0 as Shared +import Test.Shared as Shared TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_loadRecursionCrash.qml b/tests/auto/quick/qmltests/data/tst_loadRecursionCrash.qml index 81a0f0904..c0eb5932b 100644 --- a/tests/auto/quick/qmltests/data/tst_loadRecursionCrash.qml +++ b/tests/auto/quick/qmltests/data/tst_loadRecursionCrash.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 QtTest 1.0 -import QtWebEngine 1.2 +import QtQuick +import QtTest +import QtWebEngine Item { width: 300 diff --git a/tests/auto/quick/qmltests/data/tst_loadUrl.qml b/tests/auto/quick/qmltests/data/tst_loadUrl.qml index 3a2341fc2..25a62c878 100644 --- a/tests/auto/quick/qmltests/data/tst_loadUrl.qml +++ b/tests/auto/quick/qmltests/data/tst_loadUrl.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 -import QtTest 1.0 -import QtWebEngine 1.2 +// 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 +import QtTest +import QtWebEngine TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_mouseClick.qml b/tests/auto/quick/qmltests/data/tst_mouseClick.qml index 647c84573..c0c6a6967 100644 --- a/tests/auto/quick/qmltests/data/tst_mouseClick.qml +++ b/tests/auto/quick/qmltests/data/tst_mouseClick.qml @@ -1,36 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick import QtTest import QtWebEngine import Test.util -import "../../qmltests/data" 1.0 +import "../../qmltests/data" TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_mouseMove.qml b/tests/auto/quick/qmltests/data/tst_mouseMove.qml index adfa3941c..5ded24c57 100644 --- a/tests/auto/quick/qmltests/data/tst_mouseMove.qml +++ b/tests/auto/quick/qmltests/data/tst_mouseMove.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -import QtQuick 2.0 -import QtTest 1.0 -import QtWebEngine 1.4 +import QtQuick +import QtTest +import QtWebEngine Rectangle { id: root diff --git a/tests/auto/quick/qmltests/data/tst_navigationHistory.qml b/tests/auto/quick/qmltests/data/tst_navigationHistory.qml index d5073bef1..2ea76c387 100644 --- a/tests/auto/quick/qmltests/data/tst_navigationHistory.qml +++ b/tests/auto/quick/qmltests/data/tst_navigationHistory.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.15 -import QtTest 1.0 -import QtWebEngine 1.2 +// 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 +import QtTest +import QtWebEngine TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_navigationRequested.qml b/tests/auto/quick/qmltests/data/tst_navigationRequested.qml index 462dc8297..31c0cf44e 100644 --- a/tests/auto/quick/qmltests/data/tst_navigationRequested.qml +++ b/tests/auto/quick/qmltests/data/tst_navigationRequested.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 -import QtTest 1.0 -import QtWebEngine 1.2 +// 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 +import QtTest +import QtWebEngine TestWebEngineView { id: webEngineView @@ -94,19 +69,17 @@ TestWebEngineView { // Test if we get notified about main frame and iframe loads compare(navigationSpy.count, 0) webEngineView.url = Qt.resolvedUrl("test-iframe.html") - navigationSpy.wait() + verify(webEngineView.waitForLoadSucceeded()) compare(attributes.mainUrl, Qt.resolvedUrl("test-iframe.html")) - navigationSpy.wait() compare(attributes.iframeUrl, Qt.resolvedUrl("test1.html")) compare(navigationSpy.count, 2) - verify(webEngineView.waitForLoadSucceeded()) // Test if we get notified about clicked links mouseClick(webEngineView, 100, 100) - tryCompare(navigationSpy, "count", 3) + verify(webEngineView.waitForLoadSucceeded()) compare(attributes.mainUrl, Qt.resolvedUrl("test1.html")) verify(attributes.linkClickedNavigationRequested) - verify(webEngineView.waitForLoadSucceeded()) + compare(navigationSpy.count, 3) } function test_ignoreLinkClickedRequest() { @@ -117,26 +90,28 @@ TestWebEngineView { shouldIgnoreLinkClicks = true mouseClick(webEngineView, 100, 100) - tryCompare(navigationSpy, "count", 3) - compare(attributes.mainUrl, Qt.resolvedUrl("test1.html")) - verify(attributes.linkClickedNavigationRequested) - verify(attributes.linkClickedNavigationIgnored) // We ignored the main frame request, so we should // get notified that the load has been stopped. verify(webEngineView.waitForLoadStopped()) verify(!webEngineView.loading) + + compare(navigationSpy.count, 3) + compare(attributes.mainUrl, Qt.resolvedUrl("test1.html")) + verify(attributes.linkClickedNavigationRequested) + verify(attributes.linkClickedNavigationIgnored) } function test_ignoreSubFrameRequest() { // Test if we can ignore sub frame requests shouldIgnoreSubFrameRequests = true webEngineView.url = Qt.resolvedUrl("test-iframe.html") - tryCompare(navigationSpy, "count", 2) - compare(attributes.mainUrl, Qt.resolvedUrl("test-iframe.html")) - compare(attributes.iframeUrl, Qt.resolvedUrl("test1.html")) // We ignored the sub frame request, so // the main frame load should still succeed. verify(webEngineView.waitForLoadSucceeded()) + + compare(navigationSpy.count, 2) + compare(attributes.mainUrl, Qt.resolvedUrl("test-iframe.html")) + compare(attributes.iframeUrl, Qt.resolvedUrl("test1.html")) } } } diff --git a/tests/auto/quick/qmltests/data/tst_newViewRequest.qml b/tests/auto/quick/qmltests/data/tst_newViewRequest.qml index ac402674d..68350d107 100644 --- a/tests/auto/quick/qmltests/data/tst_newViewRequest.qml +++ b/tests/auto/quick/qmltests/data/tst_newViewRequest.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 -import QtTest 1.0 -import QtWebEngine 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 + +import QtQuick +import QtTest +import QtWebEngine TestWebEngineView { id: webEngineView @@ -49,10 +24,10 @@ TestWebEngineView { SignalSpy { id: newViewRequestedSpy target: webEngineView - signalName: "newViewRequested" + signalName: "newWindowRequested" } - onNewViewRequested: function(request) { + onNewWindowRequested: function(request) { newViewRequest = { "destination": request.destination, "userInitiated": request.userInitiated, @@ -60,7 +35,7 @@ TestWebEngineView { }; dialog = Qt.createQmlObject( - "import QtQuick.Window 2.0\n" + + "import QtQuick.Window\n" + "Window {\n" + " width: 100; height: 100\n" + " visible: true; flags: Qt.Dialog\n" + @@ -78,7 +53,7 @@ TestWebEngineView { TestCase { id: testCase - name: "NewViewRequest" + name: "NewWindowRequest" when: windowShown function init() { @@ -96,7 +71,7 @@ TestWebEngineView { dialog.destroy(); } - function test_loadNewViewRequest_data() { + function test_loadNewWindowRequest_data() { return [ { tag: "dialog", viewType: "dialog" }, { tag: "invalid", viewType: "null" }, @@ -105,7 +80,7 @@ TestWebEngineView { ]; } - function test_loadNewViewRequest(row) { + function test_loadNewWindowRequest(row) { viewType = row.viewType; var url = 'data:text/html,%3Chtml%3E%3Cbody%3ETest+Page%3C%2Fbody%3E%3C%2Fhtml%3E'; @@ -118,16 +93,15 @@ TestWebEngineView { verify(webEngineView.waitForLoadSucceeded()); tryCompare(newViewRequestedSpy, "count", 1); - compare(newViewRequest.destination, WebEngineNewViewRequest.InNewTab); + compare(newViewRequest.destination, WebEngineNewWindowRequest.InNewTab); verify(!newViewRequest.userInitiated); if (viewType === "dialog") { - verify(dialog.webEngineView.waitForLoadSucceeded()); - compare(dialog.webEngineView.url, ""); + tryVerify(dialog.webEngineView.loadSucceeded) + 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 @@ -139,11 +113,11 @@ TestWebEngineView { verify(webEngineView.waitForLoadSucceeded()); tryCompare(newViewRequestedSpy, "count", 1); - compare(newViewRequest.destination, WebEngineNewViewRequest.InNewDialog); + compare(newViewRequest.destination, WebEngineNewWindowRequest.InNewDialog); compare(newViewRequest.requestedUrl, url); verify(!newViewRequest.userInitiated); if (viewType === "dialog") { - verify(dialog.webEngineView.waitForLoadSucceeded()); + tryVerify(dialog.webEngineView.loadSucceeded) dialog.destroy(); } newViewRequestedSpy.clear(); @@ -163,10 +137,10 @@ TestWebEngineView { tryCompare(newViewRequestedSpy, "count", 1); compare(newViewRequest.requestedUrl, url); - compare(newViewRequest.destination, WebEngineNewViewRequest.InNewDialog); + compare(newViewRequest.destination, WebEngineNewWindowRequest.InNewDialog); verify(newViewRequest.userInitiated); if (viewType === "dialog") { - verify(dialog.webEngineView.waitForLoadSucceeded()); + tryVerify(dialog.webEngineView.loadSucceeded) dialog.destroy(); } newViewRequestedSpy.clear(); @@ -180,7 +154,7 @@ TestWebEngineView { mouseClick(webEngineView, center.x, center.y, Qt.LeftButton, Qt.ControlModifier); tryCompare(newViewRequestedSpy, "count", 1); compare(newViewRequest.requestedUrl, Qt.resolvedUrl("test1.html")); - compare(newViewRequest.destination, WebEngineNewViewRequest.InNewBackgroundTab); + compare(newViewRequest.destination, WebEngineNewWindowRequest.InNewBackgroundTab); verify(newViewRequest.userInitiated); if (viewType === "" || viewType === "null") { compare(loadRequestArray[0].status, WebEngineView.LoadStartedStatus); diff --git a/tests/auto/quick/qmltests/data/tst_notification.qml b/tests/auto/quick/qmltests/data/tst_notification.qml index 6f66256f2..5d55e1201 100644 --- a/tests/auto/quick/qmltests/data/tst_notification.qml +++ b/tests/auto/quick/qmltests/data/tst_notification.qml @@ -1,35 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.2 -import QtTest 1.0 -import QtWebEngine 1.9 -import Test.Shared 1.0 as Shared +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtTest +import QtWebEngine +import Test.Shared as Shared TestWebEngineView { id: view @@ -48,7 +23,7 @@ TestWebEngineView { signalName: 'featurePermissionRequested' } - onFeaturePermissionRequested: { + onFeaturePermissionRequested: function(securityOrigin, feature) { if (feature === WebEngineView.Notifications) { view.permissionRequested = true view.securityOrigin = securityOrigin diff --git a/tests/auto/quick/qmltests/data/tst_properties.qml b/tests/auto/quick/qmltests/data/tst_properties.qml index 89f8af9b8..13d40ed11 100644 --- a/tests/auto/quick/qmltests/data/tst_properties.qml +++ b/tests/auto/quick/qmltests/data/tst_properties.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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.0 -import QtTest 1.0 -import QtWebEngine 1.2 +import QtQuick +import QtTest +import QtWebEngine TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_runJavaScript.qml b/tests/auto/quick/qmltests/data/tst_runJavaScript.qml index beeebc049..f16cd9c41 100644 --- a/tests/auto/quick/qmltests/data/tst_runJavaScript.qml +++ b/tests/auto/quick/qmltests/data/tst_runJavaScript.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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.0 -import QtTest 1.0 -import QtWebEngine 1.2 +import QtQuick +import QtTest +import QtWebEngine TestWebEngineView { id: webEngineView @@ -59,8 +34,7 @@ TestWebEngineView { compare(result, testTitle2); callbackCalled = true; }); - wait(100); - verify(callbackCalled); + tryVerify(function() { return callbackCalled; }); } } } diff --git a/tests/auto/quick/qmltests/data/tst_save.qml b/tests/auto/quick/qmltests/data/tst_save.qml new file mode 100644 index 000000000..3289dbd8b --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_save.qml @@ -0,0 +1,185 @@ +import QtQuick +import QtTest +import QtWebEngine +import Test.util + +TestWebEngineView { + id: webEngineView + width: 200 + height: 200 + profile: testSaveProfile + + property url downloadUrl: "" + property int totalBytes: 0 + property int receivedBytes: 0 + property string downloadDir: "" + property string downloadFileName: "" + property bool isSavePageDownload: false + property var downloadState: [] + property int savePageFormat: WebEngineDownloadRequest.MimeHtmlSaveFormat; + property bool autoCancel: false + + TempDir { + id: tempDir + } + + SignalSpy { + id: downLoadRequestedSpy + target: testSaveProfile + signalName: "downloadRequested" + } + + SignalSpy { + id: downloadFinishedSpy + target: testSaveProfile + signalName: "downloadFinished" + } + + WebEngineProfile { + id: testSaveProfile + + onDownloadRequested: function(download) { + downloadState.push(download.state) + downloadUrl = download.url + savePageFormat = download.savePageFormat + downloadDir = download.downloadDirectory; + downloadFileName = download.downloadFileName + isSavePageDownload = download.isSavePageDownload + + if (autoCancel) + download.cancel() + } + onDownloadFinished: function(download) { + receivedBytes = download.receivedBytes + totalBytes = download.totalBytes + downloadState.push(download.state) + } + } + + TestCase { + name: "WebEngineViewSave" + + function verifyData() { + var isDataValid = false + webEngineView.runJavaScript("(function() {" + + "var title = document.title.toString();" + + "var body = document.body.innerText;" + + " return title === \"Test page 1\" && body.includes(\"Hello.\")" + + "})();", function(result) { + isDataValid = result; + }); + tryVerify(function() { return isDataValid }); + return isDataValid; + } + + function init() { + downLoadRequestedSpy.clear() + downloadFinishedSpy.clear() + totalBytes = 0 + receivedBytes = 0 + downloadDir = "" + downloadFileName = "" + isSavePageDownload = false + downloadState = [] + downloadUrl = "" + autoCancel = false + } + + function test_savePage_data() { + return [ + { tag: "SingleHtmlSaveFormat", savePageFormat: WebEngineDownloadRequest.SingleHtmlSaveFormat }, + { tag: "CompleteHtmlSaveFormat", savePageFormat: WebEngineDownloadRequest.CompleteHtmlSaveFormat }, + { tag: "MimeHtmlSaveFormat", savePageFormat: WebEngineDownloadRequest.MimeHtmlSaveFormat }, + ]; + } + + function test_savePage(row) { + var saveFormat = row.savePageFormat + + var fileDir = tempDir.path() + var fileName = "saved_page.html" + var filePath = fileDir + "/"+ fileName + + // load data to view + webEngineView.url = Qt.resolvedUrl("test1.html") + verify(webEngineView.waitForLoadSucceeded()) + verify(verifyData()) + + webEngineView.save(filePath, saveFormat) + downLoadRequestedSpy.wait() + compare(downLoadRequestedSpy.count, 1) + compare(downloadUrl, webEngineView.url) + compare(savePageFormat, saveFormat) + compare(downloadDir, fileDir) + compare(downloadFileName, fileName) + compare(isSavePageDownload, true) + compare(downloadState[0], WebEngineDownloadRequest.DownloadInProgress) + downloadFinishedSpy.wait() + compare(downloadFinishedSpy.count, 1) + compare(totalBytes, receivedBytes) + compare(downloadState[1], WebEngineDownloadRequest.DownloadCompleted) + + // load some other data + webEngineView.url = Qt.resolvedUrl("about:blank") + verify(webEngineView.waitForLoadSucceeded()) + + // load save file to view + webEngineView.url = Qt.resolvedUrl(filePath) + verify(webEngineView.waitForLoadSucceeded()) + verify(verifyData()) + } + + function test_saveImage_data() { + return [ + { tag: "Auto accept", autoCancel: false }, + { tag: "Cancel", autoCancel: true }, + ]; + } + + function test_saveImage(row) { + autoCancel = row.autoCancel + + var fileDir = tempDir.path() + var fileName = "favicon.png" + var filePath = fileDir + "/"+ fileName + + // Load an image + webEngineView.url = Qt.resolvedUrl("icons/favicon.png") + verify(webEngineView.waitForLoadSucceeded()) + + webEngineView.save(filePath) + downLoadRequestedSpy.wait() + compare(downLoadRequestedSpy.count, 1) + compare(downloadUrl, webEngineView.url) + compare(downloadDir, fileDir) + compare(downloadFileName, fileName) + compare(isSavePageDownload, true) + compare(downloadState[0], WebEngineDownloadRequest.DownloadInProgress) + downloadFinishedSpy.wait() + compare(downloadFinishedSpy.count, 1) + if (autoCancel) { + compare(receivedBytes, 0) + compare(downloadState[1], WebEngineDownloadRequest.DownloadCancelled) + } else { + compare(totalBytes, receivedBytes) + compare(downloadState[1], WebEngineDownloadRequest.DownloadCompleted) + } + } + + function test_saveWebAction() { + // Load an image + webEngineView.url = Qt.resolvedUrl("icons/favicon.png") + verify(webEngineView.waitForLoadSucceeded()) + + // Saving without specifying path shouldn't be auto accepted + webEngineView.triggerWebAction(WebEngineView.SavePage) + downLoadRequestedSpy.wait() + compare(downLoadRequestedSpy.count, 1) + compare(downloadUrl, webEngineView.url) + compare(isSavePageDownload, true) + // The initial download request starts from DownloadRequested state, + // which means it wasn't automatically accepted. + compare(downloadState[0], WebEngineDownloadRequest.DownloadRequested) + } + } +} diff --git a/tests/auto/quick/qmltests/data/tst_scrollPosition.qml b/tests/auto/quick/qmltests/data/tst_scrollPosition.qml index 24b352dde..cc7d15e4c 100644 --- a/tests/auto/quick/qmltests/data/tst_scrollPosition.qml +++ b/tests/auto/quick/qmltests/data/tst_scrollPosition.qml @@ -1,35 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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.2 -import QtQuick.Window 2.0 -import QtTest 1.0 -import QtWebEngine 1.3 +import QtQuick +import QtTest +import QtWebEngine TestWebEngineView { id: webEngineView @@ -61,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() { @@ -74,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 b286a1dae..f47674aa7 100644 --- a/tests/auto/quick/qmltests/data/tst_settings.qml +++ b/tests/auto/quick/qmltests/data/tst_settings.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 -import QtTest 1.0 -import QtWebEngine 1.2 +// 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 +import QtTest +import QtWebEngine TestWebEngineView { id: webEngineView @@ -103,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 7dda5ce33..66a7c115f 100644 --- a/tests/auto/quick/qmltests/data/tst_titleChanged.qml +++ b/tests/auto/quick/qmltests/data/tst_titleChanged.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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.0 -import QtTest 1.0 -import QtWebEngine 1.2 +import QtQuick +import QtTest +import QtWebEngine TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_unhandledKeyEventPropagation.qml b/tests/auto/quick/qmltests/data/tst_unhandledKeyEventPropagation.qml index 5e163fc64..76363fa71 100644 --- a/tests/auto/quick/qmltests/data/tst_unhandledKeyEventPropagation.qml +++ b/tests/auto/quick/qmltests/data/tst_unhandledKeyEventPropagation.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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.0 -import QtTest 1.0 -import QtWebEngine 1.2 +import QtQuick +import QtTest +import QtWebEngine Item { id: parentItem @@ -37,8 +12,12 @@ Item { property var pressEvents: [] property var releaseEvents: [] - Keys.onPressed: pressEvents.push(event.key) - Keys.onReleased: releaseEvents.push(event.key) + Keys.onPressed: function(event) { + pressEvents.push(event.key) + } + Keys.onReleased: function(event) { + releaseEvents.push(event.key) + } TestWebEngineView { id: webEngineView diff --git a/tests/auto/quick/qmltests/data/tst_userScriptCollection.qml b/tests/auto/quick/qmltests/data/tst_userScriptCollection.qml new file mode 100644 index 000000000..94c993771 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_userScriptCollection.qml @@ -0,0 +1,127 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtTest +import QtWebEngine + +Item { + + WebEngineProfile { id: testProfile } + + TestWebEngineView { + id: webEngineView + width: 400 + height: 300 + } + + TestCase { + name: "UserScriptCollection" + + function cleanup() { + webEngineView.url = "" + webEngineView.userScripts.collection = [] + compare(webEngineView.userScripts.collection.length, 0) + } + + function test_collection() { + let scriptFoo = { name: "Foo", + sourceUrl: Qt.resolvedUrl("foo.js"), + injectionPoint: WebEngineScript.DocumentReady + } + let scriptBar = WebEngine.script() + + scriptBar.name = "Bar" + scriptBar.sourceUrl = Qt.resolvedUrl("bar.js") + scriptBar.injectionPoint = WebEngineScript.DocumentReady + + compare(webEngineView.userScripts.collection.length, 0) + webEngineView.userScripts.collection = [ scriptFoo, scriptBar ] + compare(webEngineView.userScripts.collection.length, 2) + compare(webEngineView.userScripts.collection[0].name, scriptFoo.name) + compare(webEngineView.userScripts.collection[0].sourceUrl, scriptFoo.sourceUrl) + compare(webEngineView.userScripts.collection[1].name, scriptBar.name) + compare(webEngineView.userScripts.collection[1].sourceUrl, scriptBar.sourceUrl) + webEngineView.userScripts.collection = [] + compare(webEngineView.userScripts.collection.length, 0) + } + + function test_insert() { + let scriptFoo = WebEngine.script() + scriptFoo.name = "Foo" + scriptFoo.sourceUrl = Qt.resolvedUrl("foo.js") + scriptFoo.injectionPoint = WebEngineScript.DocumentReady + let scriptBar = WebEngine.script() + scriptBar.name = "Bar" + scriptBar.sourceUrl = Qt.resolvedUrl("bar.js") + scriptBar.injectionPoint = WebEngineScript.DocumentReady + + compare(webEngineView.userScripts.collection.length, 0) + webEngineView.userScripts.insert(scriptFoo) + webEngineView.userScripts.insert(scriptBar) + compare(webEngineView.userScripts.collection.length, 2) + compare(webEngineView.userScripts.collection[0].name, scriptFoo.name) + compare(webEngineView.userScripts.collection[1].name, scriptBar.name) + webEngineView.userScripts.collection = [] + compare(webEngineView.userScripts.collection.length, 0) + + var list = [ scriptFoo , scriptBar] + webEngineView.userScripts.insert(list) + compare(webEngineView.userScripts.collection.length, 2) + compare(webEngineView.userScripts.collection[0].name, scriptFoo.name) + compare(webEngineView.userScripts.collection[1].name, scriptBar.name) + } + + function test_find() { + let scriptA = WebEngine.script() + scriptA.name = "A" + scriptA.sourceUrl = Qt.resolvedUrl("A.js") + let scriptB = WebEngine.script() + scriptB.name = "A" + scriptB.sourceUrl = Qt.resolvedUrl("B.js") + let scriptC = WebEngine.script() + scriptC.name = "C" + scriptC.sourceUrl = Qt.resolvedUrl("C.js") + + compare(webEngineView.userScripts.collection.length, 0) + webEngineView.userScripts.collection = [ scriptA, scriptB, scriptC ]; + compare(webEngineView.userScripts.collection.length, 3) + let scriptsA = webEngineView.userScripts.find("A") + let scriptsB = webEngineView.userScripts.find("B") + let scriptsC = webEngineView.userScripts.find("C") + compare(scriptsA.length, 2) + compare(scriptsB.length, 0) + compare(scriptsC.length, 1) + compare(scriptsA[0].name, scriptA.name) + compare(scriptsA[0].sourceUrl, scriptA.sourceUrl) + compare(scriptsA[1].name, scriptB.name) + compare(scriptsA[1].sourceUrl, scriptB.sourceUrl) + compare(scriptsC[0].name, scriptC.name) + compare(scriptsC[0].sourceUrl, scriptC.sourceUrl) + } + + function test_contains() { + let scriptFoo = WebEngine.script() + scriptFoo.name = "Foo" + let scriptBar = WebEngine.script() + scriptBar.name = "Bar" + compare(webEngineView.userScripts.collection.length, 0) + webEngineView.userScripts.collection = [ scriptFoo ] + compare(webEngineView.userScripts.collection.length, 1) + verify(webEngineView.userScripts.contains(scriptFoo)) + verify(!webEngineView.userScripts.contains(scriptBar)) + } + + function test_clear() { + let scriptFoo = WebEngine.script() + scriptFoo.name = "Foo" + let scriptBar = WebEngine.script() + scriptBar.name = "Bar" + compare(webEngineView.userScripts.collection.length, 0) + webEngineView.userScripts.collection = [ scriptFoo ]; + compare(webEngineView.userScripts.collection.length, 1) + webEngineView.userScripts.clear() + compare(webEngineView.userScripts.collection.length, 0) + } + } +} diff --git a/tests/auto/quick/qmltests/data/tst_userScripts.qml b/tests/auto/quick/qmltests/data/tst_userScripts.qml index 4d0bd28bf..30704f47b 100644 --- a/tests/auto/quick/qmltests/data/tst_userScripts.qml +++ b/tests/auto/quick/qmltests/data/tst_userScripts.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 -import QtTest 1.0 -import QtWebEngine 1.2 +// 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 +import QtTest +import QtWebEngine Item { @@ -60,6 +35,8 @@ Item { return script } + WebEngineProfile { id: testProfile } + TestWebEngineView { id: webEngineView width: 400 @@ -77,7 +54,7 @@ Item { width: 400 height: 300 - onNavigationRequested: { + onNavigationRequested: function(request) { var urlString = request.url.toString(); if (urlString.indexOf("test1.html") !== -1) userScripts.collection = [ changeDocumentTitleScript() ]; @@ -89,13 +66,28 @@ Item { } TestCase { - name: "WebEngineViewUserScripts" + name: "UserScripts" - - function init() { + function cleanup() { webEngineView.url = ""; webEngineView.userScripts.collection = []; + compare(webEngineView.userScripts.collection.length, 0) webEngineView.profile.userScripts.collection = []; + compare(webEngineView.profile.userScripts.collection.length, 0) + } + + function test_profileScripts() { + // assusme it is the same type as in View + let t1 = String(testProfile.userScripts), t2 = String(webEngineView.userScripts) + compare(t1.substr(0, t1.indexOf('(')), t2.substr(0, t2.indexOf('('))) + + // ... and just test basic things like access + compare(testProfile.userScripts.collection, []) + let script = changeDocumentTitleScript() + testProfile.userScripts.collection = [ script ] + + compare(testProfile.userScripts.collection.length, 1) + compare(testProfile.userScripts.collection[0].name, script.name) } function test_oneScript() { @@ -103,8 +95,10 @@ Item { webEngineView.waitForLoadSucceeded(); tryCompare(webEngineView, "title", "Test page 1"); - webEngineView.userScripts.collection = [ changeDocumentTitleScript() ] - + let script = changeDocumentTitleScript() + webEngineView.userScripts.collection = [ script ] + compare(webEngineView.userScripts.collection.length, 1) + compare(webEngineView.userScripts.collection[0].name, script.name) compare(webEngineView.title, "Test page 1"); webEngineView.reload(); @@ -116,6 +110,7 @@ Item { tryCompare(webEngineView, "title", "New title"); webEngineView.userScripts.collection = []; + compare(webEngineView.userScripts.collection.length, 0) compare(webEngineView.title, "New title"); webEngineView.reload(); @@ -131,6 +126,7 @@ Item { var script2 = appendDocumentTitleScript(); script2.injectionPoint = WebEngineScript.Deferred; webEngineView.userScripts.collection = [ script1, script2 ]; + compare(webEngineView.userScripts.collection.length, 2) // Make sure the scripts are loaded in order. webEngineView.reload(); @@ -140,12 +136,14 @@ Item { script2.injectionPoint = WebEngineScript.DocumentReady script1.injectionPoint = WebEngineScript.Deferred webEngineView.userScripts.collection = [ script1, script2 ]; + compare(webEngineView.userScripts.collection.length, 2) webEngineView.reload(); webEngineView.waitForLoadSucceeded(); tryCompare(webEngineView, "title", "New title"); // Make sure we can remove scripts from the preload list. webEngineView.userScripts.collection = [ script2 ]; + compare(webEngineView.userScripts.collection.length, 1) webEngineView.reload(); webEngineView.waitForLoadSucceeded(); tryCompare(webEngineView, "title", "Test page 1 with appendix"); @@ -169,6 +167,7 @@ Item { function test_bigScript() { webEngineView.userScripts.collection = [ bigUserScript() ]; + compare(webEngineView.userScripts.collection.length, 1) webEngineView.url = Qt.resolvedUrl("test1.html"); webEngineView.waitForLoadSucceeded(); tryCompare(webEngineView , "title", "Big user script changed title"); @@ -180,6 +179,8 @@ Item { compare(script.injectionPoint, WebEngineScript.DocumentReady); webEngineView.userScripts.collection = [ script ]; + compare(webEngineView.userScripts.collection.length, 1) + compare(webEngineView.userScripts.collection[0].name, script.name) // @include *data/test*.html webEngineView.url = Qt.resolvedUrl("test1.html"); @@ -208,6 +209,7 @@ Item { compare(script.injectionPoint, WebEngineScript.DocumentReady); webEngineView.userScripts.collection = [ script ]; + compare(webEngineView.userScripts.collection.length, 1) // @match some:junk webEngineView.url = Qt.resolvedUrl("test2.html"); @@ -216,7 +218,10 @@ Item { } function test_profileWideScript() { - webEngineView.profile.userScripts.collection = [ changeDocumentTitleScript() ]; + let script = changeDocumentTitleScript() + webEngineView.profile.userScripts.collection = [ script ]; + compare(webEngineView.profile.userScripts.collection.length, 1) + compare(webEngineView.profile.userScripts.collection[0].name, script.name) webEngineView.url = Qt.resolvedUrl("test1.html"); webEngineView.waitForLoadSucceeded(); diff --git a/tests/auto/quick/qmltests/data/tst_viewSource.qml b/tests/auto/quick/qmltests/data/tst_viewSource.qml index 8097758fd..d4449f7de 100644 --- a/tests/auto/quick/qmltests/data/tst_viewSource.qml +++ b/tests/auto/quick/qmltests/data/tst_viewSource.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 import QtTest @@ -46,7 +21,7 @@ TestWebEngineView { SignalSpy { id: newViewRequestedSpy target: webEngineView - signalName: "newViewRequested" + signalName: "newWindowRequested" } SignalSpy { @@ -55,13 +30,13 @@ TestWebEngineView { signalName: "titleChanged" } - onNewViewRequested: { + onNewWindowRequested: function(request) { viewRequest = { "destination": request.destination, "userInitiated": request.userInitiated }; - webEngineView.acceptAsNewView(request); + webEngineView.acceptAsNewWindow(request); } TestCase { @@ -93,7 +68,7 @@ TestWebEngineView { // The first titleChanged signal is emitted by adoptWebContents() tryVerify(function() { return titleChangedSpy.count >= 2; }); - compare(viewRequest.destination, WebEngineNewViewRequest.InNewTab); + compare(viewRequest.destination, WebEngineNewWindowRequest.InNewTab); verify(viewRequest.userInitiated); verify(!webEngineView.action(WebEngineView.ViewSource).enabled); @@ -122,7 +97,7 @@ TestWebEngineView { // The first titleChanged signal is emitted by adoptWebContents() tryVerify(function() { return titleChangedSpy.count >= 2; }); - compare(viewRequest.destination, WebEngineNewViewRequest.InNewTab); + compare(viewRequest.destination, WebEngineNewWindowRequest.InNewTab); verify(viewRequest.userInitiated); tryCompare(webEngineView, "url", "view-source:" + url.replace("user:passwd@", "")); @@ -147,7 +122,7 @@ TestWebEngineView { WebEngine.settings.errorPageEnabled = true webEngineView.url = row.userInputUrl; - tryCompare(loadSpy, 'count', 2); + tryCompare(loadSpy, 'count', 2, 12000); let load = loadSpy.signalArguments[1][0] let expectedStatus = row.loadSucceed ? WebEngineView.LoadSucceededStatus : WebEngineView.LoadFailedStatus compare(load.status, expectedStatus); diff --git a/tests/auto/quick/qmltests/data/tst_webchannel.qml b/tests/auto/quick/qmltests/data/tst_webchannel.qml index 3ca3ccce1..780b55934 100644 --- a/tests/auto/quick/qmltests/data/tst_webchannel.qml +++ b/tests/auto/quick/qmltests/data/tst_webchannel.qml @@ -1,35 +1,11 @@ -/********************************************************************* -** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 -import QtTest 1.0 -import QtWebEngine 1.2 - -import QtWebChannel 1.0 +// 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 + +import QtQuick +import QtTest +import QtWebEngine + +import QtWebChannel Item { id: test @@ -81,6 +57,12 @@ Item { } function test_basic() { + webView.userScripts.collection = [ { + name: "qtwebchanneljs", + sourceUrl: Qt.resolvedUrl("qrc:/qtwebchannel/qwebchannel.js"), + injectionPoint: WebEngineScript.DocumentCreation, + worldId: WebEngineScript.MainWorld + }] webView.url = testUrl; verify(webView.waitForLoadSucceeded()); diff --git a/tests/auto/quick/qmltests/data/webchannel-test.html b/tests/auto/quick/qmltests/data/webchannel-test.html index 92966b24a..d8c3b1305 100644 --- a/tests/auto/quick/qmltests/data/webchannel-test.html +++ b/tests/auto/quick/qmltests/data/webchannel-test.html @@ -2,7 +2,6 @@ <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - <script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script> <script type="text/javascript"> //BEGIN SETUP var channel = new QWebChannel(qt.webChannelTransport, function(channel) { 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 252678754..7d7efda0c 100644 --- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/AlertDialog.qml +++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/AlertDialog.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 // 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 c4d3dd183..6125d0b98 100644 --- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/ConfirmDialog.qml +++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/ConfirmDialog.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 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..71da28843 --- /dev/null +++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/DirectoryPicker.qml @@ -0,0 +1,18 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import "../../TestParams" + +QtObject { + signal folderSelected(var folder) + signal rejected() + + function open() { + FilePickerParams.directoryPickerOpened = true; + if (FilePickerParams.selectFiles) + folderSelected(FilePickerParams.selectedFilesUrl); + else + rejected(); + } +} diff --git a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/FilePicker.qml b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/FilePicker.qml index 0ba92f5ce..247088bcb 100644 --- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/FilePicker.qml +++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/FilePicker.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 import QtQuick import "../../TestParams" 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 aa03a9d30..cd7ed4821 100644 --- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/Menu.qml +++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/Menu.qml @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 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 dcc1ca4b6..67dab1bba 100644 --- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/MenuItem.qml +++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/MenuItem.qml @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 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 800481797..81a63d918 100644 --- a/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/PromptDialog.qml +++ b/tests/auto/quick/qmltests/mock-delegates/QtWebEngine/ControlsDelegates/PromptDialog.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 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 02b0da1d4..67d67dc40 100644 --- a/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml +++ b/tests/auto/quick/qmltests/mock-delegates/TestParams/FilePickerParams.qml @@ -1,37 +1,13 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 pragma Singleton -import QtQuick 2.0 +import QtQuick 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 70696803c..1033b509e 100644 --- a/tests/auto/quick/qmltests/mock-delegates/TestParams/JSDialogParams.qml +++ b/tests/auto/quick/qmltests/mock-delegates/TestParams/JSDialogParams.qml @@ -1,32 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 pragma Singleton -import QtQml 2.0 +import QtQml QtObject { property string dialogMessage: ""; diff --git a/tests/auto/quick/qmltests/mock-delegates/TestParams/MenuParams.qml b/tests/auto/quick/qmltests/mock-delegates/TestParams/MenuParams.qml index 952eb5ebe..d8a01764c 100644 --- a/tests/auto/quick/qmltests/mock-delegates/TestParams/MenuParams.qml +++ b/tests/auto/quick/qmltests/mock-delegates/TestParams/MenuParams.qml @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 pragma Singleton import QtQml diff --git a/tests/auto/quick/qmltests/qmltests.pro b/tests/auto/quick/qmltests/qmltests.pro deleted file mode 100644 index e5d4b26a1..000000000 --- a/tests/auto/quick/qmltests/qmltests.pro +++ /dev/null @@ -1,134 +0,0 @@ -include($$QTWEBENGINE_OUT_ROOT/src/webenginequick/qtwebenginequick-config.pri) # workaround for QTBUG-68093 -QT_FOR_CONFIG += webenginequick-private - -include(../tests.pri) - -QT += qmltest -DEFINES += QUICK_TEST_SOURCE_DIR=\\\"$$re_escape($$OUT_PWD$${QMAKE_DIR_SEP}webengine.qmltests)\\\" -IMPORTPATH += $$PWD/data - -QML_TESTS = \ - $$PWD/data/tst_action.qml \ - $$PWD/data/tst_activeFocusOnPress.qml \ - $$PWD/data/tst_audioMuted.qml \ - $$PWD/data/tst_contextMenu.qml \ - $$PWD/data/tst_desktopBehaviorLoadHtml.qml \ - $$PWD/data/tst_download.qml \ - $$PWD/data/tst_favicon.qml \ - $$PWD/data/tst_faviconDatabase.qml \ - $$PWD/data/tst_findText.qml \ - $$PWD/data/tst_focusOnNavigation.qml \ - $$PWD/data/tst_fullScreenRequest.qml \ - $$PWD/data/tst_geopermission.qml \ - $$PWD/data/tst_getUserMedia.qml \ - $$PWD/data/tst_inputMethod.qml \ - $$PWD/data/tst_javaScriptDialogs.qml - $$PWD/data/tst_keyboardEvents.qml \ - $$PWD/data/tst_keyboardModifierMapping.qml \ - $$PWD/data/tst_linkHovered.qml \ - $$PWD/data/tst_loadFail.qml \ - $$PWD/data/tst_loadHtml.qml \ - $$PWD/data/tst_loadProgress.qml \ - $$PWD/data/tst_loadRecursionCrash.qml \ - $$PWD/data/tst_loadUrl.qml \ - $$PWD/data/tst_mouseClick.qml \ - $$PWD/data/tst_mouseMove.qml \ - $$PWD/data/tst_navigationHistory.qml \ - $$PWD/data/tst_navigationRequested.qml \ - $$PWD/data/tst_newViewRequest.qml \ - $$PWD/data/tst_notification.qml \ - $$PWD/data/tst_profile.qml \ - $$PWD/data/tst_properties.qml \ - $$PWD/data/tst_runJavaScript.qml \ - $$PWD/data/tst_scrollPosition.qml \ - $$PWD/data/tst_settings.qml \ - $$PWD/data/tst_titleChanged.qml \ - $$PWD/data/tst_unhandledKeyEventPropagation.qml \ - $$PWD/data/tst_userScripts.qml \ - $$PWD/data/tst_viewSource.qml - -qtConfig(webengine-webchannel) { - QML_TESTS += $$PWD/data/tst_webchannel.qml -} - -qtConfig(ssl) { - include(../../shared/https.pri) - QML_TESTS += $$PWD/data/tst_certificateError.qml -} else { - include(../../shared/http.pri) -} - -qtHaveModule(quickcontrols) { - QML_TESTS += \ - $$PWD/data/tst_filePicker.qml -} - -OTHER_FILES += \ - $$PWD/data/TestWebEngineView.qml \ - $$PWD/data/accepttypes.html \ - $$PWD/data/alert.html \ - $$PWD/data/confirm.html \ - $$PWD/data/confirmclose.html \ - $$PWD/data/append-document-title.js \ - $$PWD/data/big-user-script.js \ - $$PWD/data/change-document-title.js \ - $$PWD/data/download.zip \ - $$PWD/data/directoryupload.html \ - $$PWD/data/favicon.html \ - $$PWD/data/favicon2.html \ - $$PWD/data/favicon-candidates-gray.html \ - $$PWD/data/favicon-misc.html \ - $$PWD/data/favicon-multi.html \ - $$PWD/data/favicon-multi-gray.html \ - $$PWD/data/favicon-single.html \ - $$PWD/data/favicon-shortcut.html \ - $$PWD/data/favicon-touch.html \ - $$PWD/data/favicon-unavailable.html \ - $$PWD/data/forms.html \ - $$PWD/data/geolocation.html \ - $$PWD/data/javascript.html \ - $$PWD/data/link.html \ - $$PWD/data/localStorage.html \ - $$PWD/data/multifileupload.html \ - $$PWD/data/redirect.html \ - $$PWD/data/script-with-metadata.js \ - $$PWD/data/singlefileupload.html \ - $$PWD/data/test1.html \ - $$PWD/data/test2.html \ - $$PWD/data/test3.html \ - $$PWD/data/test4.html \ - $$PWD/data/test-iframe.html \ - $$PWD/data/keyboardModifierMapping.html \ - $$PWD/data/keyboardEvents.html \ - $$PWD/data/titleupdate.js \ - $$PWD/data/icons/favicon.png \ - $$PWD/data/icons/gray128.png \ - $$PWD/data/icons/gray16.png \ - $$PWD/data/icons/gray255.png \ - $$PWD/data/icons/gray32.png \ - $$PWD/data/icons/gray64.png \ - $$PWD/data/icons/grayicons.ico \ - $$PWD/data/icons/qt144.png \ - $$PWD/data/icons/qt32.ico \ - $$PWD/data/icons/qtmulti.ico \ - $$PWD/data/icons/small-favicon.png \ - $$PWD/mock-delegates/TestParams/FilePickerParams.qml \ - $$PWD/mock-delegates/TestParams/JSDialogParams.qml \ - $$PWD/mock-delegates/TestParams/qmldir \ - $$PWD/mock-delegates/QtWebEngine/ControlsDelegates/AlertDialog.qml \ - $$PWD/mock-delegates/QtWebEngine/ControlsDelegates/ConfirmDialog.qml \ - $$PWD/mock-delegates/QtWebEngine/ControlsDelegates/FilePicker.qml \ - $$PWD/mock-delegates/QtWebEngine/ControlsDelegates/Menu.qml \ - $$PWD/mock-delegates/QtWebEngine/ControlsDelegates/MenuItem.qml \ - $$PWD/mock-delegates/QtWebEngine/ControlsDelegates/PromptDialog.qml - -OTHER_FILES += $$QML_TESTS - -!build_pass:!isEmpty(QML_TESTS) { - for (file, QML_TESTS): QML_TESTS_CONTENT += "$${file}" - TEST_FILE = $$OUT_PWD/webengine.qmltests - write_file($$TEST_FILE, QML_TESTS_CONTENT) -} - -load(qt_build_paths) - diff --git a/tests/auto/quick/qmltests/tst_qmltests.cpp b/tests/auto/quick/qmltests/tst_qmltests.cpp index 78c167106..9e928157e 100644 --- a/tests/auto/quick/qmltests/tst_qmltests.cpp +++ b/tests/auto/quick/qmltests/tst_qmltests.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include <httpserver.h> @@ -130,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 @@ -143,6 +125,8 @@ public: QDir().rmdir(dirname); } + Q_INVOKABLE void createDirectory(const QString dirname) { QDir(tempDir.path()).mkdir(dirname); } + private: QTemporaryDir tempDir; }; @@ -236,7 +220,7 @@ private: QSpontaneKeyEvent::setSpontaneous(&me); if (!qApp->notify(window, &me)) - QTest::qWarn("Mouse click event not accepted by receiving window"); + qWarning("Mouse click event not accepted by receiving window"); } }; @@ -251,22 +235,17 @@ int main(int argc, char **argv) sigaction(SIGSEGV, &sigAction, 0); #endif - QScopedPointer<Application> app; + QtWebEngineQuick::initialize(); // Force to use English language for testing due to error message checks QLocale::setDefault(QLocale("en")); - static QByteArrayList params = {QByteArrayLiteral("--use-fake-device-for-media-stream")}; - QList<const char *> w_argv(argc); \ - for (int i = 0; i < argc; ++i) \ - w_argv[i] = argv[i]; \ - for (int i = 0; i < params.size(); ++i) \ - w_argv.append(params[i].data()); \ - int w_argc = w_argv.size(); \ + static QByteArrayList params = {QByteArrayLiteral("--webEngineArgs"),QByteArrayLiteral("--use-fake-device-for-media-stream")}; + QList<const char *> w_argv(argc); + for (int i = 0; i < argc; ++i) w_argv[i] = argv[i]; + for (int i = 0; i < params.size(); ++i) w_argv.append(params[i].data()); + int w_argc = w_argv.size(); + Application app(w_argc, const_cast<char **>(w_argv.data())); - if (!QCoreApplication::instance()) { - app.reset(new Application(w_argc, const_cast<char **>(w_argv.data()))); - } - QtWebEngineQuick::initialize(); QQuickWebEngineProfile::defaultProfile()->setOffTheRecord(true); qmlRegisterType<TempDir>("Test.util", 1, 0, "TempDir"); qmlRegisterType<TestInputContext>("Test.util", 1, 0, "TestInputContext"); @@ -283,8 +262,9 @@ int main(int argc, char **argv) #if QT_CONFIG(ssl) qmlRegisterSingletonType<HttpsServer>( - "Test.Shared", 1, 0, "HttpsServer", - [&](QQmlEngine *, QJSEngine *) { return new HttpsServer(":/resources/server.pem",":/resources/server.key"); }); + "Test.Shared", 1, 0, "HttpsServer", [&](QQmlEngine *, QJSEngine *) { + return new HttpsServer(":/resources/server.pem", ":/resources/server.key", ""); + }); #endif Setup setup; int i = quick_test_main_with_setup( diff --git a/tests/auto/quick/qquickwebenginedefaultsurfaceformat/CMakeLists.txt b/tests/auto/quick/qquickwebenginedefaultsurfaceformat/CMakeLists.txt index 07b184b89..9856ed513 100644 --- a/tests/auto/quick/qquickwebenginedefaultsurfaceformat/CMakeLists.txt +++ b/tests/auto/quick/qquickwebenginedefaultsurfaceformat/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../util/util.cmake) qt_internal_add_test(tst_qquickwebenginedefaultsurfaceformat diff --git a/tests/auto/quick/qquickwebenginedefaultsurfaceformat/qquickwebenginedefaultsurfaceformat.pro b/tests/auto/quick/qquickwebenginedefaultsurfaceformat/qquickwebenginedefaultsurfaceformat.pro deleted file mode 100644 index 092973032..000000000 --- a/tests/auto/quick/qquickwebenginedefaultsurfaceformat/qquickwebenginedefaultsurfaceformat.pro +++ /dev/null @@ -1,6 +0,0 @@ -include(../tests.pri) - -exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc -QT_PRIVATE += core-private webenginequick-private webenginecore-private - -HEADERS += ../shared/util.h diff --git a/tests/auto/quick/qquickwebenginedefaultsurfaceformat/tst_qquickwebenginedefaultsurfaceformat.cpp b/tests/auto/quick/qquickwebenginedefaultsurfaceformat/tst_qquickwebenginedefaultsurfaceformat.cpp index d75157026..b4c95d671 100644 --- a/tests/auto/quick/qquickwebenginedefaultsurfaceformat/tst_qquickwebenginedefaultsurfaceformat.cpp +++ b/tests/auto/quick/qquickwebenginedefaultsurfaceformat/tst_qquickwebenginedefaultsurfaceformat.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 "testwindow.h" #include "quickutil.h" @@ -63,8 +38,8 @@ void tst_QQuickWebEngineDefaultSurfaceFormat::initEngineAndViewComponent() { m_engine = new QQmlEngine(this); QQuickWebEngineProfile::defaultProfile()->setOffTheRecord(true); m_component.reset(new QQmlComponent(m_engine, this)); - m_component->setData(QByteArrayLiteral("import QtQuick 2.0\n" - "import QtWebEngine 1.2\n" + m_component->setData(QByteArrayLiteral("import QtQuick\n" + "import QtWebEngine\n" "WebEngineView {}") , QUrl()); } diff --git a/tests/auto/quick/qquickwebengineview/CMakeLists.txt b/tests/auto/quick/qquickwebengineview/CMakeLists.txt index 90cbb7a3d..307ea36c9 100644 --- a/tests/auto/quick/qquickwebengineview/CMakeLists.txt +++ b/tests/auto/quick/qquickwebengineview/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../util/util.cmake) qt_internal_add_test(tst_qquickwebengineview @@ -5,7 +8,6 @@ qt_internal_add_test(tst_qquickwebengineview tst_qquickwebengineview.cpp LIBRARIES Qt::WebEngineCorePrivate - Qt::WebEngineWidgets Qt::WebEngineQuick Qt::GuiPrivate Qt::WebEngineQuickPrivate diff --git a/tests/auto/quick/qquickwebengineview/qquickwebengineview.pro b/tests/auto/quick/qquickwebengineview/qquickwebengineview.pro deleted file mode 100644 index ba3737108..000000000 --- a/tests/auto/quick/qquickwebengineview/qquickwebengineview.pro +++ /dev/null @@ -1,6 +0,0 @@ -include(../tests.pri) - -exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc -QT_PRIVATE += core_private gui-private webenginequick-private webenginecore-private - -HEADERS += ../shared/util.h diff --git a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp index ac3397417..dbfa1cb33 100644 --- a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp +++ b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp @@ -1,33 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses #include "testwindow.h" #include "quickutil.h" +#include "util.h" #include <QScopedPointer> #include <QtCore/qelapsedtimer.h> @@ -38,10 +16,11 @@ #include <QtQml/QQmlEngine> #include <QtTest/QtTest> #include <QtWebEngineQuick/QQuickWebEngineProfile> -#include <QtWebEngineQuick/QQuickWebEngineScriptCollection> #include <QtGui/private/qinputmethod_p.h> -#include <QtWebEngineQuick/private/qquickwebengineview_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> #include <QtTest/private/qemulationdetector_p.h> @@ -94,8 +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(); @@ -105,6 +89,11 @@ private: QString m_testSourceDirPath; QScopedPointer<TestWindow> m_window; QScopedPointer<QQmlComponent> m_component; + + QPointingDevice *touchDevice() { + static auto d = QScopedPointer<QPointingDevice>(QTest::createTouchDevice()); + return d.get(); + } }; tst_QQuickWebEngineView::tst_QQuickWebEngineView() @@ -118,8 +107,8 @@ tst_QQuickWebEngineView::tst_QQuickWebEngineView() static QQmlEngine *engine = new QQmlEngine(this); m_component.reset(new QQmlComponent(engine, this)); - m_component->setData(QByteArrayLiteral("import QtQuick 2.0\n" - "import QtWebEngine 1.2\n" + m_component->setData(QByteArrayLiteral("import QtQuick\n" + "import QtWebEngine\n" "WebEngineView {}") , QUrl()); } @@ -442,10 +431,10 @@ void tst_QQuickWebEngineView::transparentWebEngineViews() for (int i = 0; i < image.width(); i++) for (int j = 0; j < image.height(); j++) colors.insert(image.pixel(i, j)); - return colors.count() > 1; + return colors.size() > 1; }); - QVERIFY(colors.count() > 1); + QVERIFY(colors.size() > 1); QVERIFY(colors.contains(qRgb(0, 0, 0))); // black QVERIFY(colors.contains(qRgb(255, 0, 0))); // red for (auto color : colors) { @@ -457,7 +446,6 @@ void tst_QQuickWebEngineView::transparentWebEngineViews() void tst_QQuickWebEngineView::inputMethod() { m_window->show(); - QTRY_VERIFY(qApp->focusObject()); QQuickItem *input; QQuickWebEngineView *view = webEngineView(); @@ -465,18 +453,21 @@ void tst_QQuickWebEngineView::inputMethod() view->setUrl(urlFromTestPath("html/inputmethod.html")); QVERIFY(waitForLoadSucceeded(view)); + QTRY_VERIFY(qobject_cast<QQuickItem *>(qApp->focusObject())); input = qobject_cast<QQuickItem *>(qApp->focusObject()); QVERIFY(!input->flags().testFlag(QQuickItem::ItemAcceptsInputMethod)); QVERIFY(!view->flags().testFlag(QQuickItem::ItemAcceptsInputMethod)); runJavaScript("document.getElementById('inputField').focus();"); QTRY_COMPARE(activeElementId(view), QStringLiteral("inputField")); + QTRY_VERIFY(qobject_cast<QQuickItem *>(qApp->focusObject())); input = qobject_cast<QQuickItem *>(qApp->focusObject()); QTRY_VERIFY(input->flags().testFlag(QQuickItem::ItemAcceptsInputMethod)); QVERIFY(view->flags().testFlag(QQuickItem::ItemAcceptsInputMethod)); runJavaScript("document.getElementById('inputField').blur();"); QTRY_VERIFY(activeElementId(view).isEmpty()); + QTRY_VERIFY(qobject_cast<QQuickItem *>(qApp->focusObject())); input = qobject_cast<QQuickItem *>(qApp->focusObject()); QTRY_VERIFY(!input->flags().testFlag(QQuickItem::ItemAcceptsInputMethod)); QVERIFY(!view->flags().testFlag(QQuickItem::ItemAcceptsInputMethod)); @@ -587,9 +578,8 @@ void tst_QQuickWebEngineView::interruptImeTextComposition() QTest::mouseClick(view->window(), Qt::LeftButton, {}, textInputCenter); } else if (eventType == "Touch") { QPoint textInputCenter = elementCenter(view, QStringLiteral("input2")); - QPointingDevice *touchDevice = QTest::createTouchDevice(); - QTest::touchEvent(view->window(), touchDevice).press(0, textInputCenter, view->window()); - QTest::touchEvent(view->window(), touchDevice).release(0, textInputCenter, view->window()); + QTest::touchEvent(view->window(), touchDevice()).press(0, textInputCenter, view->window()); + QTest::touchEvent(view->window(), touchDevice()).release(0, textInputCenter, view->window()); } QTRY_COMPARE(evaluateJavaScriptSync(view, "document.activeElement.id").toString(), QStringLiteral("input2")); #ifndef Q_OS_WIN @@ -617,12 +607,12 @@ void tst_QQuickWebEngineView::inputContextQueryInput() " <input type='text' id='input1' />" "</body></html>"); QVERIFY(waitForLoadSucceeded(view)); - QCOMPARE(testContext.infos.count(), 0); + QCOMPARE(testContext.infos.size(), 0); // Set focus on an input field. QPoint textInputCenter = elementCenter(view, "input1"); QTest::mouseClick(view->window(), Qt::LeftButton, {}, textInputCenter); - QTRY_COMPARE(testContext.infos.count(), 2); + QTRY_COMPARE(testContext.infos.size(), 2); QCOMPARE(evaluateJavaScriptSync(view, "document.activeElement.id").toString(), QStringLiteral("input1")); foreach (const InputMethodInfo &info, testContext.infos) { QCOMPARE(info.cursorPosition, 0); @@ -634,7 +624,7 @@ void tst_QQuickWebEngineView::inputContextQueryInput() // Change content of an input field from JavaScript. evaluateJavaScriptSync(view, "document.getElementById('input1').value='QtWebEngine';"); - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 11); QCOMPARE(testContext.infos[0].anchorPosition, 11); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine")); @@ -643,7 +633,7 @@ void tst_QQuickWebEngineView::inputContextQueryInput() // Change content of an input field by key press. QTest::keyClick(view->window(), Qt::Key_Exclam); - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 12); QCOMPARE(testContext.infos[0].anchorPosition, 12); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!")); @@ -652,7 +642,7 @@ void tst_QQuickWebEngineView::inputContextQueryInput() // Change cursor position. QTest::keyClick(view->window(), Qt::Key_Left); - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 11); QCOMPARE(testContext.infos[0].anchorPosition, 11); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!")); @@ -667,7 +657,7 @@ void tst_QQuickWebEngineView::inputContextQueryInput() QInputMethodEvent event("", attributes); QGuiApplication::sendEvent(qApp->focusObject(), &event); } - QTRY_COMPARE(testContext.infos.count(), 2); + QTRY_COMPARE(testContext.infos.size(), 2); // As a first step, Chromium moves the cursor to the start of the selection. // We don't filter this in QtWebEngine because we don't know yet if this is part of a selection. @@ -691,7 +681,7 @@ void tst_QQuickWebEngineView::inputContextQueryInput() QInputMethodEvent event("", attributes); QGuiApplication::sendEvent(qApp->focusObject(), &event); } - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 0); QCOMPARE(testContext.infos[0].anchorPosition, 0); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!")); @@ -704,9 +694,9 @@ void tst_QQuickWebEngineView::inputContextQueryInput() QInputMethodEvent event("123", attributes); QGuiApplication::sendEvent(qApp->focusObject(), &event); } - QTRY_COMPARE(testContext.infos.count(), 1); - QCOMPARE(testContext.infos[0].cursorPosition, 3); - QCOMPARE(testContext.infos[0].anchorPosition, 3); + QTRY_COMPARE(testContext.infos.size(), 1); + QCOMPARE(testContext.infos[0].cursorPosition, 0); + QCOMPARE(testContext.infos[0].anchorPosition, 0); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!")); QCOMPARE(testContext.infos[0].selectedText, QStringLiteral("")); QCOMPARE(evaluateJavaScriptSync(view, "document.getElementById('input1').value").toString(), QStringLiteral("123QtWebEngine!")); @@ -718,7 +708,7 @@ void tst_QQuickWebEngineView::inputContextQueryInput() QInputMethodEvent event("", attributes); QGuiApplication::sendEvent(qApp->focusObject(), &event); } - QTRY_COMPARE(testContext.infos.count(), 2); + QTRY_COMPARE(testContext.infos.size(), 2); foreach (const InputMethodInfo &info, testContext.infos) { QCOMPARE(info.cursorPosition, 0); QCOMPARE(info.anchorPosition, 0); @@ -735,7 +725,7 @@ void tst_QQuickWebEngineView::inputContextQueryInput() event.setCommitString(QStringLiteral("123"), 0, 0); QGuiApplication::sendEvent(qApp->focusObject(), &event); } - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 3); QCOMPARE(testContext.infos[0].anchorPosition, 3); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("123QtWebEngine!")); @@ -745,7 +735,7 @@ void tst_QQuickWebEngineView::inputContextQueryInput() // Focus out. QTest::keyPress(view->window(), Qt::Key_Tab); - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QTRY_COMPARE(evaluateJavaScriptSync(view, "document.activeElement.id").toString(), QStringLiteral("")); testContext.infos.clear(); } @@ -802,20 +792,44 @@ void tst_QQuickWebEngineView::inputMethodHints() void tst_QQuickWebEngineView::setZoomFactor() { QQuickWebEngineView *view = webEngineView(); + m_window->show(); + view->setSize(QSizeF(320, 240)); - QVERIFY(qFuzzyCompare(view->zoomFactor(), 1.0)); + QCOMPARE(view->zoomFactor(), 1.0); view->setZoomFactor(2.5); - QVERIFY(qFuzzyCompare(view->zoomFactor(), 2.5)); + QCOMPARE(view->zoomFactor(), 2.5); - view->setUrl(urlFromTestPath("html/basic_page.html")); + const QUrl url1 = urlFromTestPath("html/basic_page.html"), url2 = urlFromTestPath("html/basic_page2.html"); + + view->setUrl(url1); QVERIFY(waitForLoadSucceeded(view)); - QVERIFY(qFuzzyCompare(view->zoomFactor(), 2.5)); + QCOMPARE(view->zoomFactor(), 2.5); view->setZoomFactor(0.1); - QVERIFY(qFuzzyCompare(view->zoomFactor(), 2.5)); + QCOMPARE(view->zoomFactor(), 2.5); view->setZoomFactor(5.5); - QVERIFY(qFuzzyCompare(view->zoomFactor(), 2.5)); + QCOMPARE(view->zoomFactor(), 2.5); + + QScopedPointer<QQuickWebEngineView> view2(newWebEngineView()); + view2->setSize(QSizeF(320, 240)); + view2->setParentItem(m_window->contentItem()); + + // try loading different url and check new values after load + for (auto &&p : { + qMakePair(view, 2.5), // navigating away to different url should keep zoom + qMakePair(view2.get(), 1.0), // same url navigation in diffent page shouldn't be affected + }) { + auto &&view = p.first; auto zoomFactor = p.second; + view->setUrl(url2); + QVERIFY(waitForLoadSucceeded(view)); + QCOMPARE(view->zoomFactor(), zoomFactor); + } + + // should have no influence on first page + view2->setZoomFactor(3.5); + for (auto &&p : { qMakePair(view, 2.5), qMakePair(view2.get(), 3.5), }) + QCOMPARE(p.first->zoomFactor(), p.second); } void tst_QQuickWebEngineView::printToPdf() @@ -832,7 +846,7 @@ void tst_QQuickWebEngineView::printToPdf() QSignalSpy savePdfSpy(view, SIGNAL(pdfPrintingFinished(const QString&, bool))); QString path = tempDir.path() + "/print_success.pdf"; view->printToPdf(path, QQuickWebEngineView::A4, QQuickWebEngineView::Portrait); - QTRY_VERIFY2(savePdfSpy.count() == 1, "Printing to PDF file failed without signal"); + QTRY_VERIFY2(savePdfSpy.size() == 1, "Printing to PDF file failed without signal"); QList<QVariant> successArguments = savePdfSpy.takeFirst(); QVERIFY2(successArguments.at(0).toString() == path, "File path for first saved PDF does not match arguments"); QVERIFY2(successArguments.at(1).toBool() == true, "Printing to PDF file failed though it should succeed"); @@ -843,7 +857,7 @@ void tst_QQuickWebEngineView::printToPdf() path = tempDir.path() + "/print_|fail.pdf"; #endif // #if !defined(Q_OS_WIN) view->printToPdf(path, QQuickWebEngineView::A4, QQuickWebEngineView::Portrait); - QTRY_VERIFY2(savePdfSpy.count() == 1, "Printing to PDF file failed without signal"); + QTRY_VERIFY2(savePdfSpy.size() == 1, "Printing to PDF file failed without signal"); QList<QVariant> failedArguments = savePdfSpy.takeFirst(); QVERIFY2(failedArguments.at(0).toString() == path, "File path for second saved PDF does not match arguments"); QVERIFY2(failedArguments.at(1).toBool() == false, "Printing to PDF file succeeded though it should fail"); @@ -987,11 +1001,9 @@ void tst_QQuickWebEngineView::inputEventForwardingDisabledWhenActiveFocusOnPress QTest::mousePress(view->window(), Qt::LeftButton); QTest::mouseRelease(view->window(), Qt::LeftButton); - QPointingDevice *device = QTest::createTouchDevice(); - - QTest::touchEvent(view->window(), device).press(0, QPoint(0,0), view->window()); - QTest::touchEvent(view->window(), device).move(0, QPoint(1, 1), view->window()); - QTest::touchEvent(view->window(), device).release(0, QPoint(1, 1), view->window()); + QTest::touchEvent(view->window(), touchDevice()).press(0, QPoint(0,0), view->window()); + QTest::touchEvent(view->window(), touchDevice()).move(0, QPoint(1, 1), view->window()); + QTest::touchEvent(view->window(), touchDevice()).release(0, QPoint(1, 1), view->window()); // We expect to catch 7 events - click = 2, press + release = 2, touches = 3. QCOMPARE(item.eventCount(), 7); @@ -1113,7 +1125,7 @@ void tst_QQuickWebEngineView::javascriptClipboard() // - return value of queryCommandEnabled and // - return value of execCommand // - comparing the clipboard / input field - QGuiApplication::clipboard()->clear(); + QGuiApplication::clipboard()->setText(QString()); QCOMPARE(evaluateJavaScriptSync(view, "document.queryCommandEnabled('copy')").toBool(), copyResult); QCOMPARE(evaluateJavaScriptSync(view, "document.execCommand('copy')").toBool(), copyResult); @@ -1140,9 +1152,9 @@ void tst_QQuickWebEngineView::javascriptClipboard() "if (result.state == 'prompt') accessPrompt = true;" "})")); - QTRY_COMPARE(evaluateJavaScriptSync(view, "accessGranted").toBool(), copyResult); - QTRY_COMPARE(evaluateJavaScriptSync(view, "accessDenied").toBool(), !javascriptCanAccessClipboard); - QTRY_COMPARE(evaluateJavaScriptSync(view, "accessPrompt").toBool(), false); + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessGranted").toBool(), javascriptCanAccessClipboard && javascriptCanPaste); + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessDenied").toBool(), false); + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessPrompt").toBool(), !javascriptCanAccessClipboard || !javascriptCanPaste); evaluateJavaScriptSync(view, QStringLiteral( @@ -1156,9 +1168,9 @@ void tst_QQuickWebEngineView::javascriptClipboard() "if (result.state == 'prompt') accessPrompt = true;" "})")); - QTRY_COMPARE(evaluateJavaScriptSync(view, "accessGranted").toBool(), pasteResult); - QTRY_COMPARE(evaluateJavaScriptSync(view, "accessDenied").toBool(), !javascriptCanAccessClipboard || !javascriptCanPaste); - QTRY_COMPARE(evaluateJavaScriptSync(view, "accessPrompt").toBool(), false); + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessGranted").toBool(), javascriptCanAccessClipboard && javascriptCanPaste); + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessDenied").toBool(), false); + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessPrompt").toBool(), !javascriptCanAccessClipboard || !javascriptCanPaste); } void tst_QQuickWebEngineView::setProfile() { @@ -1170,10 +1182,13 @@ void tst_QQuickWebEngineView::setProfile() { QVERIFY(waitForLoadSucceeded(webEngineView())); QCOMPARE(loadSpy.size(), 4); QQuickWebEngineProfile *profile = new QQuickWebEngineProfile(); + auto oldProfile = webEngineView()->profile(); + auto sc = qScopeGuard([&] () { webEngineView()->setProfile(oldProfile); delete profile; }); webEngineView()->setProfile(profile); QTRY_COMPARE(webEngineView()->url() ,urlFromTestPath("html/basic_page2.html")); } +#if QT_CONFIG(accessibility) void tst_QQuickWebEngineView::focusChild_data() { QTest::addColumn<QString>("interfaceName"); @@ -1236,9 +1251,121 @@ void tst_QQuickWebEngineView::focusChild() // <html> -> <body> -> <input> QCOMPARE(traverseToWebDocumentAccessibleInterface(iface)->child(0)->child(0), iface->focusChild()); } +#endif // QT_CONFIG(accessibility) +void tst_QQuickWebEngineView::htmlSelectPopup() +{ + m_window->show(); + QQuickWebEngineView &view = *webEngineView(); + view.settings()->setFocusOnNavigationEnabled(true); + view.setSize(QSizeF(640, 480)); + view.loadHtml("<html><body>" + "<select id='select' onchange='console.log(\"option changed to: \" + this.value)'>" + "<option value='O1'>O1</option><option value='O2'>O2</option><option value='O3'>O3</option></select>" + "</body></html>"); + QVERIFY(waitForLoadSucceeded(&view)); + + auto makeTouch = [this] (QWindow *w, const QPoint &p) { + QTest::touchEvent(w, touchDevice()).press(1, p); + QTest::touchEvent(w, touchDevice()).release(1, p); + }; + + makeTouch(view.window(), elementCenter(&view, "select")); + QPointer<QQuickWindow> popup; + QTRY_VERIFY((popup = m_window->findChild<QQuickWindow *>())); + QCOMPARE(activeElementId(&view), QStringLiteral("select")); + + makeTouch(popup, QPoint(popup->width() / 2, popup->height() / 2)); + QTRY_VERIFY(!popup); + QCOMPARE(evaluateJavaScriptSync(&view, "document.getElementById('select').value").toString(), QStringLiteral("O2")); +} + +void tst_QQuickWebEngineView::savePage_data() +{ + QTest::addColumn<QWebEngineDownloadRequest::SavePageFormat>("savePageFormat"); + + QTest::newRow("SingleHtmlSaveFormat") << QWebEngineDownloadRequest::SingleHtmlSaveFormat; + QTest::newRow("CompleteHtmlSaveFormat") << QWebEngineDownloadRequest::CompleteHtmlSaveFormat; + QTest::newRow("MimeHtmlSaveFormat") << QWebEngineDownloadRequest::MimeHtmlSaveFormat; +} + +void tst_QQuickWebEngineView::savePage() +{ + QFETCH(QWebEngineDownloadRequest::SavePageFormat, savePageFormat); + + QTemporaryDir tempDir(QDir::tempPath() + "/tst_QQuickWebEngineView-XXXXXX"); + QVERIFY(tempDir.isValid()); + const QString filePath = tempDir.path() + "/saved_page.html"; + + QQuickWebEngineView *view = webEngineView(); + int acceptedCount = 0; + int finishedCount = 0; + ScopedConnection sc1 = connect( + view->profile(), &QQuickWebEngineProfile::downloadRequested, + [&](QQuickWebEngineDownloadRequest *downloadRequest) { + QCOMPARE(downloadRequest->state(), + QQuickWebEngineDownloadRequest::DownloadInProgress); + QCOMPARE(downloadRequest->isSavePageDownload(), true); + QCOMPARE(downloadRequest->savePageFormat(), savePageFormat); + QCOMPARE(QDir(downloadRequest->downloadDirectory()) + .filePath(downloadRequest->downloadFileName()), + filePath); + QCOMPARE(downloadRequest->url(), view->url()); + + connect(downloadRequest, &QQuickWebEngineDownloadRequest::isFinishedChanged, + [&, downloadRequest]() { + QCOMPARE(downloadRequest->state(), + QQuickWebEngineDownloadRequest::DownloadCompleted); + QCOMPARE(downloadRequest->isSavePageDownload(), true); + QCOMPARE(downloadRequest->isFinished(), true); + QCOMPARE(downloadRequest->savePageFormat(), savePageFormat); + QCOMPARE(downloadRequest->totalBytes(), + downloadRequest->receivedBytes()); + QVERIFY(downloadRequest->receivedBytes() > 0); + QCOMPARE(QDir(downloadRequest->downloadDirectory()) + .filePath(downloadRequest->downloadFileName()), + filePath); + QCOMPARE(downloadRequest->url(), view->url()); + finishedCount++; + }); + acceptedCount++; + }); + + const QString originalData = QStringLiteral("Basic page"); + view->setUrl(urlFromTestPath("html/basic_page.html")); + QVERIFY(waitForLoadSucceeded(view)); + QCOMPARE(evaluateJavaScriptSync(view, "document.getElementsByTagName('h1')[0].innerText") + .toString(), + originalData); + + // Save the loaded page as HTML. + view->save(filePath, savePageFormat); + QTRY_COMPARE(acceptedCount, 1); + QTRY_COMPARE(finishedCount, 1); + QFile file(filePath); + QVERIFY(file.exists()); + + // Load something else. + view->setUrl(urlFromTestPath("html/basic_page2.html")); + QVERIFY(waitForLoadSucceeded(view)); + QVERIFY(evaluateJavaScriptSync(view, "document.getElementsByTagName('h1')[0].innerText") + .toString() + != originalData); + + // Load the saved page and compare the contents. + view->setUrl(QUrl::fromLocalFile(filePath)); + QVERIFY(waitForLoadSucceeded(view)); + QCOMPARE(evaluateJavaScriptSync(view, "document.getElementsByTagName('h1')[0].innerText") + .toString(), + originalData); +} + +#if QT_CONFIG(accessibility) static QByteArrayList params = QByteArrayList() << "--force-renderer-accessibility"; +#else +static QByteArrayList params; +#endif W_QTEST_MAIN(tst_QQuickWebEngineView, params) #include "tst_qquickwebengineview.moc" diff --git a/tests/auto/quick/qquickwebengineviewgraphics/CMakeLists.txt b/tests/auto/quick/qquickwebengineviewgraphics/CMakeLists.txt index c7e6bc925..f22408d15 100644 --- a/tests/auto/quick/qquickwebengineviewgraphics/CMakeLists.txt +++ b/tests/auto/quick/qquickwebengineviewgraphics/CMakeLists.txt @@ -1,10 +1,12 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../util/util.cmake) qt_internal_add_test(tst_qquickwebengineviewgraphics SOURCES tst_qquickwebengineviewgraphics.cpp LIBRARIES Qt::CorePrivate - Qt::WebEngineWidgets Qt::WebEngineQuickPrivate Qt::Test Test::Util diff --git a/tests/auto/quick/qquickwebengineviewgraphics/qquickwebengineviewgraphics.pro b/tests/auto/quick/qquickwebengineviewgraphics/qquickwebengineviewgraphics.pro deleted file mode 100644 index be6296f5e..000000000 --- a/tests/auto/quick/qquickwebengineviewgraphics/qquickwebengineviewgraphics.pro +++ /dev/null @@ -1,4 +0,0 @@ -include(../tests.pri) -CONFIG -= testcase # remove, once this passes in the CI -exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc -QT_PRIVATE += webenginequick-private gui-private webenginecore-private diff --git a/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp b/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp index 855b01653..3644ac481 100644 --- a/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp +++ b/tests/auto/quick/qquickwebengineviewgraphics/tst_qquickwebengineviewgraphics.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 <quickutil.h> #include <QtTest/QtTest> @@ -97,6 +72,8 @@ static void verifyGreenSquare(QQuickWindow *window) bool ok = QTest::qWaitFor([&](){ actual = window->grabWindow(); expected = getGreenSquare(actual.format()); + if (actual.height() > 150) + actual = actual.scaledToHeight(150); return actual == expected; }, 10000); if (!ok) { @@ -109,7 +86,9 @@ static void verifyGreenSquare(QQuickWindow *window) void tst_QQuickWebEngineViewGraphics::simpleGraphics() { setHtml(greenSquare); + m_view->show(); verifyGreenSquare(m_view.data()); + m_view->hide(); } void tst_QQuickWebEngineViewGraphics::showHideShow() @@ -125,12 +104,15 @@ void tst_QQuickWebEngineViewGraphics::showHideShow() m_view->show(); QVERIFY(exposeSpy.wait()); verifyGreenSquare(m_view.data()); + m_view->hide(); } void tst_QQuickWebEngineViewGraphics::simpleAcceleratedLayer() { + m_view->show(); setHtml(acLayerGreenSquare); verifyGreenSquare(m_view.data()); + m_view->hide(); } void tst_QQuickWebEngineViewGraphics::reparentToOtherWindow() @@ -141,13 +123,15 @@ void tst_QQuickWebEngineViewGraphics::reparentToOtherWindow() window.create(); m_view->rootObject()->setParentItem(window.contentItem()); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); verifyGreenSquare(&window); } void tst_QQuickWebEngineViewGraphics::setHtml(const QString &html) { QString htmlData = QUrl::toPercentEncoding(html); - QString qmlData = QUrl::toPercentEncoding(QStringLiteral("import QtQuick 2.0; import QtWebEngine 1.2; WebEngineView { width: 150; height: 150 }")); + QString qmlData = QUrl::toPercentEncoding(QStringLiteral("import QtQuick; import QtWebEngine; WebEngineView { width: 150; height: 150 }")); m_view->setSource(QUrl(QStringLiteral("data:text/plain,%1").arg(qmlData))); m_view->create(); diff --git a/tests/auto/quick/qtbug-70248/CMakeLists.txt b/tests/auto/quick/qtbug-70248/CMakeLists.txt index b1df50211..b177c5309 100644 --- a/tests/auto/quick/qtbug-70248/CMakeLists.txt +++ b/tests/auto/quick/qtbug-70248/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + qt_internal_add_test(tst_qtbug-70248 SOURCES tst_qtbug-70248.cpp diff --git a/tests/auto/quick/qtbug-70248/qtbug-70248.pro b/tests/auto/quick/qtbug-70248/qtbug-70248.pro deleted file mode 100644 index 7223dcfa0..000000000 --- a/tests/auto/quick/qtbug-70248/qtbug-70248.pro +++ /dev/null @@ -1,5 +0,0 @@ -include(../tests.pri) -QT += webenginequick webenginequick-private - -RESOURCES += \ - test.qrc diff --git a/tests/auto/quick/qtbug-70248/test.qml b/tests/auto/quick/qtbug-70248/test.qml index 35962aff5..5870f593e 100644 --- a/tests/auto/quick/qtbug-70248/test.qml +++ b/tests/auto/quick/qtbug-70248/test.qml @@ -1,6 +1,6 @@ -import QtQuick 2.9 -import QtQuick.Window 2.2 -import QtWebEngine 1.3 +import QtQuick +import QtQuick.Window +import QtWebEngine Window { visible: true diff --git a/tests/auto/quick/qtbug-70248/tst_qtbug-70248.cpp b/tests/auto/quick/qtbug-70248/tst_qtbug-70248.cpp index 6f48c957f..cf5c187c3 100644 --- a/tests/auto/quick/qtbug-70248/tst_qtbug-70248.cpp +++ b/tests/auto/quick/qtbug-70248/tst_qtbug-70248.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 "qtwebenginequickglobal.h" #include <QQuickWebEngineProfile> diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro deleted file mode 100644 index 13e4d78cc..000000000 --- a/tests/auto/quick/quick.pro +++ /dev/null @@ -1,16 +0,0 @@ -include($$QTWEBENGINE_OUT_ROOT/src/webenginequick/qtwebenginequick-config.pri) # workaround for QTBUG-68093 -QT_FOR_CONFIG += webenginequick-private - -TEMPLATE = subdirs - -SUBDIRS += \ - dialogs \ - inspectorserver \ - qmltests \ - publicapi \ - qquickwebenginedefaultsurfaceformat \ - qquickwebengineviewgraphics \ - qquickwebengineview \ - qtbug-70248 - -boot2qt: SUBDIRS -= inspectorserver qquickwebengineview qmltests diff --git a/tests/auto/quick/tests.pri b/tests/auto/quick/tests.pri deleted file mode 100644 index 8cf4c0af5..000000000 --- a/tests/auto/quick/tests.pri +++ /dev/null @@ -1,20 +0,0 @@ -include($$QTWEBENGINE_OUT_ROOT/src/webenginequick/qtwebenginequick-config.pri) # workaround for QTBUG-68093 -QT_FOR_CONFIG += webenginequick-private - -TEMPLATE = app - -CONFIG += testcase - -VPATH += $$_PRO_FILE_PWD_ -TARGET = tst_$$TARGET - -SOURCES += $${TARGET}.cpp -INCLUDEPATH += \ - $$PWD \ - ../shared - -QT += testlib network quick webenginequick - -# This define is used by some tests to look up resources in the source tree -DEFINES += TESTS_SOURCE_DIR=\\\"$$PWD/\\\" -include(../embed_info_plist.pri) diff --git a/tests/auto/quick/uidelegates/CMakeLists.txt b/tests/auto/quick/uidelegates/CMakeLists.txt index d8699ccfc..bdf041e04 100644 --- a/tests/auto/quick/uidelegates/CMakeLists.txt +++ b/tests/auto/quick/uidelegates/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../httpserver/httpserver.cmake) include(../../util/util.cmake) diff --git a/tests/auto/quick/uidelegates/tst_uidelegates.cpp b/tests/auto/quick/uidelegates/tst_uidelegates.cpp index 95f083f12..fb8734f83 100644 --- a/tests/auto/quick/uidelegates/tst_uidelegates.cpp +++ b/tests/auto/quick/uidelegates/tst_uidelegates.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "testwindow.h" #include "quickutil.h" @@ -143,7 +118,6 @@ void tst_UIDelegates::javaScriptDialog() void tst_UIDelegates::fileDialog() { - QSKIP("There is no FilePicker Control in Quick Controls 2."); m_window->show(); QTRY_VERIFY(qApp->focusObject()); QQuickWebEngineView *view = webEngineView(); @@ -152,11 +126,10 @@ void tst_UIDelegates::fileDialog() "<input type='file' id='filePicker'/>" "</body></html>"); QVERIFY(waitForLoadSucceeded(view)); - QString filePickerStr = QStringLiteral("filePicker"); - QPoint filePickerCenter = elementCenter(view, filePickerStr); + QPoint filePickerCenter = elementCenter(view, QStringLiteral("filePicker")); QTest::mouseClick(view->window(), Qt::LeftButton, {}, filePickerCenter); - QTRY_VERIFY(view->findChild<QObject *>(filePickerStr)); + QTRY_VERIFY(view->findChild<QObject *>(QStringLiteral("fileDialog"))); } void tst_UIDelegates::contextMenu() @@ -198,7 +171,6 @@ void tst_UIDelegates::tooltip() void tst_UIDelegates::colorDialog() { - QSKIP("There is no ColorPicker Control in Quick Controls 2."); m_window->show(); QTRY_VERIFY(qApp->focusObject()); QQuickWebEngineView *view = webEngineView(); diff --git a/tests/auto/shared/http.pri b/tests/auto/shared/http.pri deleted file mode 100644 index 7182bcbb0..000000000 --- a/tests/auto/shared/http.pri +++ /dev/null @@ -1,4 +0,0 @@ -HEADERS += $$PWD/httpserver.h $$PWD/httpreqrep.h -SOURCES += $$PWD/httpserver.cpp $$PWD/httpreqrep.cpp -INCLUDEPATH += $$PWD -DEFINES += TESTS_SHARED_DATA_DIR=\\\"$$re_escape($$PWD$${QMAKE_DIR_SEP}data)\\\" diff --git a/tests/auto/shared/https.pri b/tests/auto/shared/https.pri deleted file mode 100644 index ce4c147f7..000000000 --- a/tests/auto/shared/https.pri +++ /dev/null @@ -1,4 +0,0 @@ -include($$PWD/http.pri) - -HEADERS += $$PWD/httpsserver.h -RESOURCES += $$PWD/httpsserver.qrc diff --git a/tests/auto/shared/httpsserver.qrc b/tests/auto/shared/httpsserver.qrc deleted file mode 100644 index ec57a1983..000000000 --- a/tests/auto/shared/httpsserver.qrc +++ /dev/null @@ -1,6 +0,0 @@ -<!DOCTYPE RCC><RCC version="1.0"> -<qresource> - <file>resources/cert.pem</file> - <file>resources/key.pem</file> -</qresource> -</RCC> diff --git a/tests/auto/util/CMakeLists.txt b/tests/auto/util/CMakeLists.txt index fa2f84cec..0af0e5032 100644 --- a/tests/auto/util/CMakeLists.txt +++ b/tests/auto/util/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + cmake_minimum_required(VERSION 3.18) project(minimal LANGUAGES CXX) diff --git a/tests/auto/util/qt_webengine_quicktest.h b/tests/auto/util/qt_webengine_quicktest.h index 0428783bf..bd98693de 100644 --- a/tests/auto/util/qt_webengine_quicktest.h +++ b/tests/auto/util/qt_webengine_quicktest.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 QT_WEBENGINE_QUICKTEST_H #define QT_WEBENGINE_QUICKTEST_H diff --git a/tests/auto/util/quickutil.h b/tests/auto/util/quickutil.h index 323fe3df4..687cb94dc 100644 --- a/tests/auto/util/quickutil.h +++ b/tests/auto/util/quickutil.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 UTIL_H #define UTIL_H @@ -138,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(); } @@ -172,10 +147,11 @@ inline QString activeElementId(QQuickWebEngineView *webEngineView) int main(int argc, char *argv[]) \ { \ QtWebEngineQuick::initialize(); \ - \ QList<const char *> w_argv(argc); \ + QLatin1String arg("--webEngineArgs"); \ for (int i = 0; i < argc; ++i) \ w_argv[i] = argv[i]; \ + w_argv.append(arg.data()); \ for (int i = 0; i < params.size(); ++i) \ w_argv.append(params[i].data()); \ int w_argc = w_argv.size(); \ diff --git a/tests/auto/util/testwindow.h b/tests/auto/util/testwindow.h index 958381ff2..f9ffd381a 100644 --- a/tests/auto/util/testwindow.h +++ b/tests/auto/util/testwindow.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 TESTWINDOW_H #define TESTWINDOW_H diff --git a/tests/auto/util/util.cmake b/tests/auto/util/util.cmake index 84d7f593f..e5142d0b2 100644 --- a/tests/auto/util/util.cmake +++ b/tests/auto/util/util.cmake @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + if (NOT TARGET Test::Util) add_library(qtestutil INTERFACE) target_include_directories(qtestutil INTERFACE ${CMAKE_CURRENT_LIST_DIR}) diff --git a/tests/auto/util/util.h b/tests/auto/util/util.h index a624a978f..5533eed80 100644 --- a/tests/auto/util/util.h +++ b/tests/auto/util/util.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 // Functions and macros that really need to be in QTestLib @@ -33,6 +8,8 @@ #endif #include <QEventLoop> +#include <QPoint> +#include <QRect> #include <QSignalSpy> #include <QTimer> #include <qwebenginefindtextresult.h> @@ -66,7 +43,7 @@ public: bool ensureSignalEmitted() { - bool result = count() > 0; + bool result = size() > 0; if (!result) result = wait(); clear(); @@ -175,41 +152,27 @@ static inline bool loadSync(QWebEnginePage *page, const QUrl &url, bool ok = tru return (!spy.empty() || spy.wait(20000)) && (spy.front().value(0).toBool() == ok); } -static inline QPoint elementCenter(QWebEnginePage *page, const QString &id) -{ - const QString jsCode( - "(function(){" - " var elem = document.getElementById('" + id + "');" - " var rect = elem.getBoundingClientRect();" - " return [(rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2];" - "})()"); - QVariantList rectList = evaluateJavaScriptSync(page, jsCode).toList(); - - if (rectList.count() != 2) { - qWarning("elementCenter failed."); - return QPoint(); - } - - return QPoint(rectList.at(0).toInt(), rectList.at(1).toInt()); -} - static inline QRect elementGeometry(QWebEnginePage *page, const QString &id) { const QString jsCode( "(function() {" " var elem = document.getElementById('" + id + "');" " var rect = elem.getBoundingClientRect();" - " return [rect.left, rect.top, rect.right, rect.bottom];" + " return [rect.left, rect.top, rect.width, rect.height];" "})()"); QVariantList coords = evaluateJavaScriptSync(page, jsCode).toList(); - if (coords.count() != 4) { - qWarning("elementGeometry faield."); + if (coords.size() != 4) { + qWarning("elementGeometry failed."); return QRect(); } return QRect(coords[0].toInt(), coords[1].toInt(), coords[2].toInt(), coords[3].toInt()); } +static inline QPoint elementCenter(QWebEnginePage *page, const QString &id) +{ + return elementGeometry(page, id).center(); +} #define W_QSKIP(a, b) QSKIP(a) diff --git a/tests/auto/util/widgetutil.h b/tests/auto/util/widgetutil.h index b72b56030..67d09ee4f 100644 --- a/tests/auto/util/widgetutil.h +++ b/tests/auto/util/widgetutil.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 // Functions and macros that really need to be in QTestLib @@ -36,8 +11,10 @@ int main(int argc, char *argv[]) \ { \ QList<const char *> w_argv(argc); \ + QLatin1String arg("--webEngineArgs"); \ for (int i = 0; i < argc; ++i) \ w_argv[i] = argv[i]; \ + w_argv.append(arg.data()); \ for (int i = 0; i < params.size(); ++i) \ w_argv.append(params[i].data()); \ int w_argc = w_argv.size(); \ diff --git a/tests/auto/widgets/CMakeLists.txt b/tests/auto/widgets/CMakeLists.txt index bedb00f53..9246be68a 100644 --- a/tests/auto/widgets/CMakeLists.txt +++ b/tests/auto/widgets/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + add_subdirectory(defaultsurfaceformat) add_subdirectory(qwebenginepage) add_subdirectory(qwebengineprofile) @@ -13,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 9653ef1c0..f6a08c9d3 100644 --- a/tests/auto/widgets/accessibility/CMakeLists.txt +++ b/tests/auto/widgets/accessibility/CMakeLists.txt @@ -1,9 +1,13 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../util/util.cmake) -qt_internal_add_test(tst_accessibility +qt_internal_add_test(tst_webengine_accessibility SOURCES tst_accessibility.cpp LIBRARIES Qt::WebEngineWidgets + Qt::WebEngineCorePrivate Test::Util ) diff --git a/tests/auto/widgets/accessibility/accessibility.pro b/tests/auto/widgets/accessibility/accessibility.pro deleted file mode 100644 index e99c7f493..000000000 --- a/tests/auto/widgets/accessibility/accessibility.pro +++ /dev/null @@ -1 +0,0 @@ -include(../tests.pri) diff --git a/tests/auto/widgets/accessibility/tst_accessibility.cpp b/tests/auto/widgets/accessibility/tst_accessibility.cpp index 0aabf0f6d..1579b61e2 100644 --- a/tests/auto/widgets/accessibility/tst_accessibility.cpp +++ b/tests/auto/widgets/accessibility/tst_accessibility.cpp @@ -17,6 +17,7 @@ Boston, MA 02110-1301, USA. */ +#include <QtWebEngineCore/private/qtwebenginecore-config_p.h> #include <qtest.h> #include <widgetutil.h> @@ -48,6 +49,9 @@ private Q_SLOTS: void value(); void roles_data(); void roles(); + void objectName(); + void crossTreeParent(); + void tableCellInterface(); }; // This will be called before the first test function is executed. @@ -338,7 +342,7 @@ void tst_Accessibility::roles_data() QTest::newRow("ax::mojom::Role::kAbbr") << QString("<abbr>a</abbr>") << 1 << QAccessible::StaticText; QTest::newRow("ax::mojom::Role::kAlert") << QString("<div role='alert'>alert</div>") << 0 << QAccessible::AlertMessage; QTest::newRow("ax::mojom::Role::kAlertDialog") << QString("<div role='alertdialog'>alert</div>") << 0 << QAccessible::AlertMessage; - QTest::newRow("ax::mojom::Role::kAnchor") << QString("<a id='a'>Chapter a</a>") << 1 << QAccessible::Link; + QTest::newRow("ax::mojom::Role::kAnchor") << QString("<a id='a'>Chapter a</a>") << 1 << QAccessible::Section; QTest::newRow("ax::mojom::Role::kApplication") << QString("<div role='application'>landmark</div>") << 0 << QAccessible::Document; QTest::newRow("ax::mojom::Role::kArticle") << QString("<article>a</article>") << 0 << QAccessible::Section; QTest::newRow("ax::mojom::Role::kAudio") << QString("<audio controls><source src='test.mp3' type='audio/mpeg'></audio>") << 1 << QAccessible::Sound; @@ -424,7 +428,7 @@ void tst_Accessibility::roles_data() QTest::newRow("ax::mojom::Role::kFigure") << QString("<figure>a</figure>") << 0 << QAccessible::Section; QTest::newRow("ax::mojom::Role::kFooter") << QString("<footer>a</footer>") << 0 << QAccessible::Section; QTest::newRow("ax::mojom::Role::kFooterAsNonLandmark") << QString("<article><footer>a</footer><article>") << 1 << QAccessible::Section; - QTest::newRow("ax::mojom::Role::kForm") << QString("<form></form>") << 0 << QAccessible::Form; + QTest::newRow("ax::mojom::Role::kForm") << QString("<form aria-label=Name></form>") << 0 << QAccessible::Form; QTest::newRow("ax::mojom::Role::kGraphicsDocument") << QString("<div role='graphics-document'></div>") << 0 << QAccessible::Document; QTest::newRow("ax::mojom::Role::kGraphicsObject") << QString("<div role='graphics-object'></div>") << 0 << QAccessible::Pane; QTest::newRow("ax::mojom::Role::kGraphicsSymbol") << QString("<div role='graphics-symbol'></div>") << 0 << QAccessible::Graphic; @@ -473,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; @@ -484,7 +488,7 @@ void tst_Accessibility::roles_data() QTest::newRow("ax::mojom::Role::kRowGroup") << QString("<table role=table><tbody role=rowgroup><tr><td>a</td></tr></tbody></table>") << 1 << QAccessible::Section; QTest::newRow("ax::mojom::Role::kRowHeader") << QString("<table role=table><tr><th>a</td><td>b</td></tr></table>") << 2 << QAccessible::RowHeader; QTest::newRow("ax::mojom::Role::kRuby") << QString("<ruby>a</ruby>") << 1 << QAccessible::Grouping; - QTest::newRow("ax::mojom::Role::kRubyAnnotation") << QString("<ruby><rt tabindex=0>a</rt></ruby>") << 2 << QAccessible::StaticText; + //QTest::newRow("ax::mojom::Role::kRubyAnnotation") // No mapping to ARIA role (presents as property on enclosing ruby element) QTest::newRow("ax::mojom::Role::kScrollBar") << QString("<div role='scrollbar'>a</a>") << 0 << QAccessible::ScrollBar; //QTest::newRow("ax::mojom::Role::kScrollView"); // No mapping to ARIA role QTest::newRow("ax::mojom::Role::kSearch") << QString("<div role='search'>landmark</div>") << 0 << QAccessible::Section; @@ -498,7 +502,7 @@ void tst_Accessibility::roles_data() QTest::newRow("ax::mojom::Role::kStatus") << QString("<output>a</output>") << 1 << QAccessible::Indicator; QTest::newRow("ax::mojom::Role::kStrong") << QString("<strong>a</strong>") << 1 << QAccessible::StaticText; QTest::newRow("ax::mojom::Role::kSuggestion") << QString("<div role='suggestion'></div>") << 0 << QAccessible::Section; - QTest::newRow("ax::mojom::Role::kSvgRoot") << QString("<svg width='10' height='10'></svg>") << 1 << QAccessible::Graphic; + QTest::newRow("ax::mojom::Role::kSvgRoot") << QString("<svg width='10' height='10'><text font-size='10'>SVG</text></svg>") << 1 << QAccessible::WebDocument; QTest::newRow("ax::mojom::Role::kSwitch") << QString("<button aria-checked='false'>a</button>") << 1 << QAccessible::Button; QTest::newRow("ax::mojom::Role::kTable") << QString("<table role=table><td>a</td></table>") << 0 << QAccessible::Table; //QTest::newRow("ax::mojom::Role::kTableHeaderContainer"); // No mapping to ARIA role @@ -527,10 +531,10 @@ void tst_Accessibility::roles() QFETCH(QAccessible::Role, role); QWebEngineView webView; + QSignalSpy spyFinished(&webView, &QWebEngineView::loadFinished); webView.setHtml("<html><body>" + html + "</body></html>"); webView.show(); - QSignalSpy spyFinished(&webView, &QWebEngineView::loadFinished); - QVERIFY(spyFinished.wait()); + QTRY_COMPARE_WITH_TIMEOUT(spyFinished.size(), 1, 20000); QAccessibleInterface *view = QAccessible::queryAccessibleInterface(&webView); @@ -540,7 +544,7 @@ void tst_Accessibility::roles() return; } - QTRY_COMPARE(view->child(0)->childCount(), 1); + QTRY_COMPARE_WITH_TIMEOUT(view->child(0)->childCount(), 1, 20000); QAccessibleInterface *document = view->child(0); QAccessibleInterface *element = document->child(0); @@ -552,9 +556,116 @@ void tst_Accessibility::roles() QCOMPARE(element->role(), role); } +void tst_Accessibility::objectName() +{ + QWebEngineView webView; + QSignalSpy spyFinished(&webView, &QWebEngineView::loadFinished); + webView.setHtml("<html><body><p id='my_id'></p></body></html>"); + webView.show(); + QVERIFY(spyFinished.wait()); + QAccessibleInterface *view = QAccessible::queryAccessibleInterface(&webView); + QAccessibleInterface *document = view->child(0); + QTRY_COMPARE(document->childCount(), 1); + QAccessibleInterface *p = document->child(0); + QVERIFY(p); + QVERIFY(p->object()); + QCOMPARE(p->role(), QAccessible::Paragraph); + QCOMPARE(p->object()->objectName(), QStringLiteral("my_id")); +} + +void tst_Accessibility::crossTreeParent() +{ + QWebEngineView webView; + QSignalSpy spyFinished(&webView, &QWebEngineView::loadFinished); + webView.setHtml("<html><body><iframe src='data:text/html,<html><body><p id=my_id></p></body></html>'>Fallback text</iframe></body></html>"); + 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); + QVERIFY(p); + QCOMPARE(p->parent(), document); + p = p->child(0); + QVERIFY(p); + QCOMPARE(p->role(), QAccessible::WebDocument); + QCOMPARE(p->parent()->parent(), document); + 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); + 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()->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/CMakeLists.txt b/tests/auto/widgets/defaultsurfaceformat/CMakeLists.txt index 6dd05f58f..d95c1355b 100644 --- a/tests/auto/widgets/defaultsurfaceformat/CMakeLists.txt +++ b/tests/auto/widgets/defaultsurfaceformat/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../util/util.cmake) qt_internal_add_test(tst_defaultsurfaceformat diff --git a/tests/auto/widgets/defaultsurfaceformat/defaultsurfaceformat.pro b/tests/auto/widgets/defaultsurfaceformat/defaultsurfaceformat.pro deleted file mode 100644 index e99c7f493..000000000 --- a/tests/auto/widgets/defaultsurfaceformat/defaultsurfaceformat.pro +++ /dev/null @@ -1 +0,0 @@ -include(../tests.pri) diff --git a/tests/auto/widgets/defaultsurfaceformat/tst_defaultsurfaceformat.cpp b/tests/auto/widgets/defaultsurfaceformat/tst_defaultsurfaceformat.cpp index 7a52a372a..c53f6f5b3 100644 --- a/tests/auto/widgets/defaultsurfaceformat/tst_defaultsurfaceformat.cpp +++ b/tests/auto/widgets/defaultsurfaceformat/tst_defaultsurfaceformat.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 <qtest.h> #include <util.h> diff --git a/tests/auto/widgets/defaultsurfaceformat/tst_defaultsurfaceformat.qrc b/tests/auto/widgets/defaultsurfaceformat/tst_defaultsurfaceformat.qrc deleted file mode 100644 index 3d5f1b3b2..000000000 --- a/tests/auto/widgets/defaultsurfaceformat/tst_defaultsurfaceformat.qrc +++ /dev/null @@ -1,5 +0,0 @@ -<!DOCTYPE RCC><RCC version="1.0"> -<qresource> - <file>resources/index.html</file> -</qresource> -</RCC> diff --git a/tests/auto/widgets/favicon/CMakeLists.txt b/tests/auto/widgets/favicon/CMakeLists.txt index 3a2f6f255..0deae6a37 100644 --- a/tests/auto/widgets/favicon/CMakeLists.txt +++ b/tests/auto/widgets/favicon/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../util/util.cmake) qt_internal_add_test(tst_favicon diff --git a/tests/auto/widgets/favicon/favicon.pro b/tests/auto/widgets/favicon/favicon.pro deleted file mode 100644 index e99c7f493..000000000 --- a/tests/auto/widgets/favicon/favicon.pro +++ /dev/null @@ -1 +0,0 @@ -include(../tests.pri) diff --git a/tests/auto/widgets/favicon/tst_favicon.cpp b/tests/auto/widgets/favicon/tst_favicon.cpp index d8b4803c0..c70aa1182 100644 --- a/tests/auto/widgets/favicon/tst_favicon.cpp +++ b/tests/auto/widgets/favicon/tst_favicon.cpp @@ -1,34 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include <QtTest/QtTest> #include <util.h> +#include <QWebEngineHistory> #include <QWebEnginePage> #include <QWebEngineProfile> #include <QWebEngineSettings> @@ -49,6 +25,7 @@ private Q_SLOTS: void faviconLoadFromResources(); void faviconLoadEncodedUrl(); void faviconLoadAfterHistoryNavigation(); + void faviconLoadPushState(); void noFavicon(); void aboutBlank(); void unavailableFavicon(); @@ -111,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()); @@ -124,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))); } @@ -138,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()); @@ -149,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))); } @@ -173,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); @@ -186,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))); } @@ -198,30 +175,65 @@ 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")); } +void tst_Favicon::faviconLoadPushState() +{ + QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool))); + QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl))); + QSignalSpy iconChangedSpy(m_page, SIGNAL(iconChanged(QIcon))); + + QUrl url("qrc:/resources/favicon-single.html"); + m_page->load(url); + + 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()); + QCOMPARE(iconUrl, QUrl("qrc:/resources/icons/qt32.ico")); + + const QIcon &icon = m_page->icon(); + QVERIFY(!icon.isNull()); + + iconUrlChangedSpy.clear(); + iconChangedSpy.clear(); + + // pushState() is a same document navigation and should not reset or + // update favicon. + QCOMPARE(m_page->history()->count(), 1); + evaluateJavaScriptSync(m_page, "history.pushState('', '')"); + QTRY_COMPARE(m_page->history()->count(), 2); + + // Favicon change is not expected. + QCOMPARE(iconUrlChangedSpy.size(), 0); + QCOMPARE(iconChangedSpy.size(), 0); + QCOMPARE(m_page->iconUrl(), QUrl("qrc:/resources/icons/qt32.ico")); +} + void tst_Favicon::noFavicon() { if (!QDir(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath()).exists()) @@ -239,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()); @@ -256,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()); @@ -281,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()); @@ -300,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()); @@ -319,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()); @@ -344,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()); @@ -375,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); @@ -387,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(); @@ -404,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); @@ -416,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))); } @@ -442,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()); @@ -479,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); @@ -490,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); } @@ -512,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)); @@ -523,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)); @@ -543,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(); @@ -558,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() @@ -580,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; @@ -635,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()); @@ -705,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()); @@ -758,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()); } @@ -801,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/favicon/tst_favicon.qrc b/tests/auto/widgets/favicon/tst_favicon.qrc deleted file mode 100644 index a352f8a83..000000000 --- a/tests/auto/widgets/favicon/tst_favicon.qrc +++ /dev/null @@ -1,14 +0,0 @@ -<!DOCTYPE RCC><RCC version="1.0"> -<qresource> - <file>resources/favicon-misc.html</file> - <file>resources/favicon-multi.html</file> - <file>resources/favicon-shortcut.html</file> - <file>resources/favicon-single.html</file> - <file>resources/favicon-touch.html</file> - <file>resources/favicon-unavailable.html</file> - <file>resources/icons/qt144.png</file> - <file>resources/icons/qt32.ico</file> - <file>resources/icons/qtmulti.ico</file> - <file>resources/test1.html</file> -</qresource> -</RCC> diff --git a/tests/auto/widgets/loadsignals/CMakeLists.txt b/tests/auto/widgets/loadsignals/CMakeLists.txt index 5de957148..bbd0387d9 100644 --- a/tests/auto/widgets/loadsignals/CMakeLists.txt +++ b/tests/auto/widgets/loadsignals/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../httpserver/httpserver.cmake) include(../../util/util.cmake) diff --git a/tests/auto/widgets/loadsignals/loadsignals.pro b/tests/auto/widgets/loadsignals/loadsignals.pro deleted file mode 100644 index 9c239f1a7..000000000 --- a/tests/auto/widgets/loadsignals/loadsignals.pro +++ /dev/null @@ -1,2 +0,0 @@ -include(../tests.pri) -include(../../shared/http.pri) diff --git a/tests/auto/widgets/loadsignals/tst_loadsignals.cpp b/tests/auto/widgets/loadsignals/tst_loadsignals.cpp index 0daf0ce05..6140b3766 100644 --- a/tests/auto/widgets/loadsignals/tst_loadsignals.cpp +++ b/tests/auto/widgets/loadsignals/tst_loadsignals.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 <QtTest/QtTest> @@ -136,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(); @@ -233,26 +208,28 @@ void tst_LoadSignals::rejectNavigationRequest_data() QTest::addColumn<QUrl>("rejectedUrl"); QTest::addColumn<int>("expectedNavigations"); QTest::addColumn<QList<int>>("expectedSignals"); + QTest::addColumn<int>("errorCode"); + QTest::addColumn<QWebEngineLoadingInfo::ErrorDomain>("errorDomain"); QTest::newRow("Simple") << QUrl("qrc:///resources/page1.html") << QUrl("qrc:///resources/page1.html") - << 1 << SignalsOrderOnceFailure; + << 1 << SignalsOrderOnceFailure << -3 << QWebEngineLoadingInfo::InternalErrorDomain; QTest::newRow("SamePageImmediate") << QUrl("qrc:///resources/page5.html") << QUrl("qrc:///resources/page5.html#anchor") - << 1 << SignalsOrderOnce; + << 1 << SignalsOrderOnce << 200 << QWebEngineLoadingInfo::HttpStatusCodeDomain; QTest::newRow("SamePageDeferred") << QUrl("qrc:///resources/page3.html") << QUrl("qrc:///resources/page3.html#anchor") - << 1 << SignalsOrderOnce; + << 1 << SignalsOrderOnce << 200 << QWebEngineLoadingInfo::HttpStatusCodeDomain; QTest::newRow("OtherPageImmediate") << QUrl("qrc:///resources/page6.html") << QUrl("qrc:///resources/page2.html#anchor") - << 2 << SignalsOrderOnceFailure; + << 2 << SignalsOrderOnceFailure << -3 << QWebEngineLoadingInfo::InternalErrorDomain; QTest::newRow("OtherPageDeferred") << QUrl("qrc:///resources/page7.html") << QUrl("qrc:///resources/page2.html#anchor") - << 2 << SignalsOrderTwiceWithFailure; + << 2 << SignalsOrderTwiceWithFailure << -3 << QWebEngineLoadingInfo::InternalErrorDomain; } /** @@ -267,6 +244,8 @@ void tst_LoadSignals::rejectNavigationRequest() QFETCH(QUrl, rejectedUrl); QFETCH(int, expectedNavigations); QFETCH(QList<int>, expectedSignals); + QFETCH(int, errorCode); + QFETCH(QWebEngineLoadingInfo::ErrorDomain, errorDomain); page.blacklist.insert(rejectedUrl); page.load(initialUrl); @@ -281,6 +260,8 @@ void tst_LoadSignals::rejectNavigationRequest() // No further loadStarted should have occurred within this time QCOMPARE(loadStartedSpy.size(), expectedLoadCount); QCOMPARE(loadFinishedSpy.size(), expectedLoadCount); + QCOMPARE(page.loadingInfos.last().errorCode(), errorCode); + QCOMPARE(page.loadingInfos.last().errorDomain(), errorDomain); } /** @@ -296,7 +277,7 @@ void tst_LoadSignals::monotonicity() QVERIFY(server.start()); view.load(server.url("/loadprogress/main.html")); - QTRY_COMPARE(loadFinishedSpy.size(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 10000); QVERIFY(loadFinishedSpy[0][0].toBool()); QVERIFY(page.loadProgress.size() >= 3); @@ -368,6 +349,8 @@ void tst_LoadSignals::fileDownload() QTRY_LOOP_IMPL(loadStartedSpy.size() != 2 || loadFinishedSpy.size() != 2, 1000, 100); QCOMPARE(page.signalsOrder, SignalsOrderTwiceWithFailure); + QCOMPARE(page.loadingInfos[3].errorCode(), -3); + QCOMPARE(page.loadingInfos[3].errorDomain(), QWebEngineLoadingInfo::InternalErrorDomain); } void tst_LoadSignals::numberOfStartedAndFinishedSignalsIsSame_data() @@ -406,21 +389,27 @@ void tst_LoadSignals::numberOfStartedAndFinishedSignalsIsSame() resetSpies(); QTRY_LOOP_IMPL(loadStartedSpy.size() || loadFinishedSpy.size(), 1000, 100); QCOMPARE(page.signalsOrder, SignalsOrderOnce); + QCOMPARE(page.loadingInfos[1].errorCode(), 200); + QCOMPARE(page.loadingInfos[1].errorDomain(), QWebEngineLoadingInfo::HttpStatusCodeDomain); } void tst_LoadSignals::loadFinishedAfterNotFoundError_data() { QTest::addColumn<bool>("rfcInvalid"); QTest::addColumn<bool>("withServer"); - QTest::addRow("rfc_invalid") << true << false; - QTest::addRow("non_existent") << false << false; - QTest::addRow("server_404") << false << true; + QTest::addColumn<int>("errorCode"); + QTest::addColumn<int>("errorDomain"); + QTest::addRow("rfc_invalid") << true << false << -105 << int(QWebEngineLoadingInfo::ConnectionErrorDomain); + QTest::addRow("non_existent") << false << false << -105 << int(QWebEngineLoadingInfo::ConnectionErrorDomain); + QTest::addRow("server_404") << false << true << 404 << int(QWebEngineLoadingInfo::HttpStatusCodeDomain); } void tst_LoadSignals::loadFinishedAfterNotFoundError() { - QFETCH(bool, withServer); QFETCH(bool, rfcInvalid); + QFETCH(bool, withServer); + QFETCH(int, errorCode); + QFETCH(int, errorDomain); QScopedPointer<HttpServer> server; if (withServer) { @@ -432,27 +421,53 @@ 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(); + { auto &&loadStart = page.loadingInfos[0], &&loadFinish = page.loadingInfos[1]; + QCOMPARE(loadStart.status(), QWebEngineLoadingInfo::LoadStartedStatus); + QCOMPARE(loadStart.isErrorPage(), false); + QCOMPARE(loadStart.errorCode(), 0); + QCOMPARE(loadStart.errorDomain(), QWebEngineLoadingInfo::NoErrorDomain); + QCOMPARE(loadStart.errorString(), QString()); + QCOMPARE(loadFinish.status(), QWebEngineLoadingInfo::LoadFailedStatus); + QCOMPARE(loadFinish.isErrorPage(), false); + QCOMPARE(loadFinish.errorCode(), errorCode); + QCOMPARE(loadFinish.errorDomain(), errorDomain); + QVERIFY(!loadFinish.errorString().isEmpty()); + } + view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, true); url = server ? 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]; + QCOMPARE(loadStart.status(), QWebEngineLoadingInfo::LoadStartedStatus); + QCOMPARE(loadStart.isErrorPage(), false); + QCOMPARE(loadStart.errorCode(), 0); + QCOMPARE(loadStart.errorDomain(), QWebEngineLoadingInfo::NoErrorDomain); + QCOMPARE(loadStart.errorString(), QString()); + QCOMPARE(loadFinish.status(), QWebEngineLoadingInfo::LoadFailedStatus); + QCOMPARE(loadFinish.isErrorPage(), true); + QCOMPARE(loadFinish.errorCode(), errorCode); + QCOMPARE(loadFinish.errorDomain(), errorDomain); + QVERIFY(!loadFinish.errorString().isEmpty()); + } } void tst_LoadSignals::errorPageTriggered_data() @@ -460,10 +475,12 @@ void tst_LoadSignals::errorPageTriggered_data() QTest::addColumn<QString>("urlPath"); QTest::addColumn<bool>("loadSucceed"); QTest::addColumn<bool>("triggersErrorPage"); - QTest::newRow("/content/200") << QStringLiteral("/content/200") << true << false; - QTest::newRow("/empty/200") << QStringLiteral("/content/200") << true << false; - QTest::newRow("/content/404") << QStringLiteral("/content/404") << false << false; - QTest::newRow("/empty/404") << QStringLiteral("/empty/404") << false << true; + QTest::addColumn<int>("errorCode"); + QTest::addColumn<QWebEngineLoadingInfo::ErrorDomain>("errorDomain"); + QTest::newRow("/content/200") << QStringLiteral("/content/200") << true << false << 200 << QWebEngineLoadingInfo::HttpStatusCodeDomain; + QTest::newRow("/empty/200") << QStringLiteral("/content/200") << true << false << 200 << QWebEngineLoadingInfo::HttpStatusCodeDomain; + QTest::newRow("/content/404") << QStringLiteral("/content/404") << false << false << 404 << QWebEngineLoadingInfo::HttpStatusCodeDomain; + QTest::newRow("/empty/404") << QStringLiteral("/empty/404") << false << true << 404 << QWebEngineLoadingInfo::HttpStatusCodeDomain; } void tst_LoadSignals::errorPageTriggered() @@ -471,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; @@ -490,6 +507,8 @@ void tst_LoadSignals::errorPageTriggered() QFETCH(QString, urlPath); QFETCH(bool, loadSucceed); QFETCH(bool, triggersErrorPage); + QFETCH(int, errorCode); + QFETCH(QWebEngineLoadingInfo::ErrorDomain, errorDomain); view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, true); view.load(server.url(urlPath)); @@ -499,6 +518,8 @@ void tst_LoadSignals::errorPageTriggered() QVERIFY(toPlainTextSync(view.page()).contains("HTTP ERROR 404")); else QVERIFY(toPlainTextSync(view.page()).isEmpty()); + QCOMPARE(page.loadingInfos[1].errorCode(), errorCode); + QCOMPARE(page.loadingInfos[1].errorDomain(), errorDomain); loadFinishedSpy.clear(); view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); @@ -506,6 +527,8 @@ void tst_LoadSignals::errorPageTriggered() QTRY_COMPARE(loadFinishedSpy.size(), 1); QCOMPARE(loadFinishedSpy[0][0].toBool(), loadSucceed); QVERIFY(toPlainTextSync(view.page()).isEmpty()); + QCOMPARE(page.loadingInfos[3].errorCode(), errorCode); + QCOMPARE(page.loadingInfos[3].errorDomain(), errorDomain); loadFinishedSpy.clear(); } diff --git a/tests/auto/widgets/loadsignals/tst_loadsignals.qrc b/tests/auto/widgets/loadsignals/tst_loadsignals.qrc deleted file mode 100644 index b4ee36676..000000000 --- a/tests/auto/widgets/loadsignals/tst_loadsignals.qrc +++ /dev/null @@ -1,13 +0,0 @@ -<RCC> - <qresource prefix="/resources"> - <file alias="page1.html">../../shared/data/loadprogress/page1.html</file> - <file alias="page2.html">../../shared/data/loadprogress/page2.html</file> - <file alias="page3.html">../../shared/data/loadprogress/page3.html</file> - <file alias="page4.html">../../shared/data/loadprogress/page4.html</file> - <file alias="page5.html">../../shared/data/loadprogress/page5.html</file> - <file alias="page6.html">../../shared/data/loadprogress/page6.html</file> - <file alias="page7.html">../../shared/data/loadprogress/page7.html</file> - <file alias="page8.html">../../shared/data/loadprogress/page8.html</file> - <file alias="downloadable.tar.gz">../../shared/data/loadprogress/downloadable.tar.gz</file> - </qresource> -</RCC> diff --git a/tests/auto/widgets/offscreen/CMakeLists.txt b/tests/auto/widgets/offscreen/CMakeLists.txt index d51459a3e..756e53c43 100644 --- a/tests/auto/widgets/offscreen/CMakeLists.txt +++ b/tests/auto/widgets/offscreen/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + qt_internal_add_test(tst_offscreen SOURCES tst_offscreen.cpp diff --git a/tests/auto/widgets/offscreen/offscreen.pro b/tests/auto/widgets/offscreen/offscreen.pro deleted file mode 100644 index 2469f9a53..000000000 --- a/tests/auto/widgets/offscreen/offscreen.pro +++ /dev/null @@ -1,6 +0,0 @@ -include(../tests.pri) -QT += webenginewidgets -qpa.name = QT_QPA_PLATFORM -qpa.value = offscreen -QT_TOOL_ENV += qpa - diff --git a/tests/auto/widgets/offscreen/tst_offscreen.cpp b/tests/auto/widgets/offscreen/tst_offscreen.cpp index 81cbe95f3..553dc653b 100644 --- a/tests/auto/widgets/offscreen/tst_offscreen.cpp +++ b/tests/auto/widgets/offscreen/tst_offscreen.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include <QTest> #include <QSignalSpy> @@ -51,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/offscreen/tst_offscreen.qrc b/tests/auto/widgets/offscreen/tst_offscreen.qrc deleted file mode 100644 index 8a998fe85..000000000 --- a/tests/auto/widgets/offscreen/tst_offscreen.qrc +++ /dev/null @@ -1,6 +0,0 @@ -<!DOCTYPE RCC><RCC version="1.0"> -<qresource> - <file>test.html</file> -</qresource> -</RCC> - diff --git a/tests/auto/widgets/printing/CMakeLists.txt b/tests/auto/widgets/printing/CMakeLists.txt index e61b8cc89..baa3cf747 100644 --- a/tests/auto/widgets/printing/CMakeLists.txt +++ b/tests/auto/widgets/printing/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../util/util.cmake) find_package(PkgConfig) @@ -8,18 +11,15 @@ endif() qt_internal_add_test(tst_printing SOURCES tst_printing.cpp - PUBLIC_LIBRARIES - Qt::WebEngineWidgets LIBRARIES Qt::CorePrivate + Qt::WebEngineWidgets Qt::WebEngineCorePrivate Test::Util ) qt_internal_extend_target(tst_printing - CONDITION POPPLER_CPP_FOUND - DEFINES - POPPLER_CPP + CONDITION POPPLER_CPP_FOUND AND QT_FEATURE_webengine_system_poppler LIBRARIES PkgConfig::POPPLER_CPP ) diff --git a/tests/auto/widgets/printing/printing.pro b/tests/auto/widgets/printing/printing.pro deleted file mode 100644 index 92f5d611c..000000000 --- a/tests/auto/widgets/printing/printing.pro +++ /dev/null @@ -1,10 +0,0 @@ -include($$QTWEBENGINE_OUT_ROOT/src/core/qtwebenginecore-config.pri) # workaround for QTBUG-68093 -QT_FOR_CONFIG += webenginecore-private - -include(../tests.pri) -QT *= core-private webenginecore-private - -qtConfig(webengine-poppler-cpp) { - CONFIG += link_pkgconfig - PKGCONFIG += poppler-cpp -} diff --git a/tests/auto/widgets/printing/tst_printing.cpp b/tests/auto/widgets/printing/tst_printing.cpp index 3f1344ed4..605fb57b5 100644 --- a/tests/auto/widgets/printing/tst_printing.cpp +++ b/tests/auto/widgets/printing/tst_printing.cpp @@ -1,39 +1,16 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 <QtWebEngineCore/private/qtwebenginecoreglobal_p.h> +#include <QtWebEngineCore/qtwebenginecore-config.h> +#include <QWebEngineSettings> #include <QWebEngineView> #include <QTemporaryDir> #include <QTest> #include <QSignalSpy> #include <util.h> -#if defined(POPPLER_CPP) +#if QT_CONFIG(webengine_system_poppler) #include <poppler-document.h> #include <poppler-page.h> #endif @@ -44,9 +21,11 @@ class tst_Printing : public QObject private slots: void printToPdfBasic(); void printRequest(); -#if defined(POPPLER_CPP) && defined(Q_OS_LINUX) && defined(__GLIBCXX__) +#if QT_CONFIG(webengine_system_poppler) void printToPdfPoppler(); + void printFromPdfViewer(); #endif + void interruptPrinting(); }; void tst_Printing::printToPdfBasic() @@ -56,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, &QWebEngineView::pdfPrintingFinished); + 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.printToPdf(path, layout); - QTRY_VERIFY2(savePdfSpy.count() == 1, "Printing to PDF file failed without signal"); + view.page()->printToPdf(path, layout); + 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"); @@ -73,20 +52,20 @@ void tst_Printing::printToPdfBasic() #else path = tempDir.path() + "/print_|2_failed.pdf"; #endif - view.printToPdf(path, QPageLayout()); - QTRY_VERIFY2(savePdfSpy.count() == 1, "Printing to PDF file failed without signal"); + view.page()->printToPdf(path, QPageLayout()); + 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"); CallbackSpy<QByteArray> successfulSpy; - view.printToPdf(successfulSpy.ref(), layout); - QVERIFY(successfulSpy.waitForResult().length() > 0); + view.page()->printToPdf(successfulSpy.ref(), layout); + QVERIFY(successfulSpy.waitForResult().size() > 0); CallbackSpy<QByteArray> failedInvalidLayoutSpy; - view.printToPdf(failedInvalidLayoutSpy.ref(), QPageLayout()); - QCOMPARE(failedInvalidLayoutSpy.waitForResult().length(), 0); + view.page()->printToPdf(failedInvalidLayoutSpy.ref(), QPageLayout()); + QCOMPARE(failedInvalidLayoutSpy.waitForResult().size(), 0); } void tst_Printing::printRequest() @@ -95,20 +74,22 @@ void tst_Printing::printRequest() QPageLayout layout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF(0.0, 0.0, 0.0, 0.0)); QSignalSpy loadFinishedSpy(&view, &QWebEngineView::loadFinished); QSignalSpy printRequestedSpy(&view, &QWebEngineView::printRequested); + QSignalSpy printRequestedSpy2(view.page(), &QWebEnginePage::printRequested); QSignalSpy savePdfSpy(&view, &QWebEngineView::pdfPrintingFinished); 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); + 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 defined(POPPLER_CPP) && defined(Q_OS_LINUX) && defined(__GLIBCXX__) +#if QT_CONFIG(webengine_system_poppler) void tst_Printing::printToPdfPoppler() { // check if generated pdf is correct by searching for a know string on the page @@ -137,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/printing/tst_printing.qrc b/tests/auto/widgets/printing/tst_printing.qrc deleted file mode 100644 index b1795ef8a..000000000 --- a/tests/auto/widgets/printing/tst_printing.qrc +++ /dev/null @@ -1,5 +0,0 @@ -<!DOCTYPE RCC><RCC version="1.0"> -<qresource> - <file>resources/basic_printing_page.html</file> -</qresource> -</RCC> diff --git a/tests/auto/widgets/proxy/CMakeLists.txt b/tests/auto/widgets/proxy/CMakeLists.txt index 0929c050c..95dc903ed 100644 --- a/tests/auto/widgets/proxy/CMakeLists.txt +++ b/tests/auto/widgets/proxy/CMakeLists.txt @@ -1,6 +1,9 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../httpserver/httpserver.cmake) -qt_internal_add_test(tst_proxy +qt_internal_add_test(tst_webengine_proxy SOURCES tst_proxy.cpp LIBRARIES diff --git a/tests/auto/widgets/proxy/proxy.pro b/tests/auto/widgets/proxy/proxy.pro deleted file mode 100644 index ce4ccbfcb..000000000 --- a/tests/auto/widgets/proxy/proxy.pro +++ /dev/null @@ -1,9 +0,0 @@ -include(../tests.pri) -QT += core-private webenginewidgets webenginewidgets-private - -HEADERS += \ - proxy_server.h - -SOURCES += \ - proxy_server.cpp - diff --git a/tests/auto/widgets/proxy/tst_proxy.cpp b/tests/auto/widgets/proxy/tst_proxy.cpp index c3e3c88a4..3dc72618c 100644 --- a/tests/auto/widgets/proxy/tst_proxy.cpp +++ b/tests/auto/widgets/proxy/tst_proxy.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "proxy_server.h" #include <QTest> @@ -33,7 +8,7 @@ #include <QWebEnginePage> #include <QWebEngineView> #include <QWebEngineUrlRequestInterceptor> - +#include <QWebEngineLoadingInfo> struct Interceptor : public QWebEngineUrlRequestInterceptor { @@ -53,6 +28,7 @@ public: private slots: void proxyAuthentication(); void forwardCookie(); + void invalidHostName(); }; @@ -74,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() @@ -94,7 +70,20 @@ void tst_Proxy::forwardCookie() page.setUrlRequestInterceptor(&interceptor); QSignalSpy cookieSpy(&server, &ProxyServer::cookieMatch); page.load(QUrl("http://www.qt.io")); - QTRY_VERIFY2(cookieSpy.count() > 0, "Could not get cookie"); + QTRY_VERIFY2(cookieSpy.size() > 0, "Could not get cookie"); +} + +// Crash test ( https://bugreports.qt.io/browse/QTBUG-113992 ) +void tst_Proxy::invalidHostName() +{ + QNetworkProxy proxy; + proxy.setType(QNetworkProxy::HttpProxy); + proxy.setHostName("999.0.0.0"); + QNetworkProxy::setApplicationProxy(proxy); + QWebEnginePage page; + QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); + page.load(QUrl("http://www.qt.io")); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); } #include "tst_proxy.moc" diff --git a/tests/auto/widgets/proxypac/CMakeLists.txt b/tests/auto/widgets/proxypac/CMakeLists.txt index 1dd2c8bec..f27160cb6 100644 --- a/tests/auto/widgets/proxypac/CMakeLists.txt +++ b/tests/auto/widgets/proxypac/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../httpserver/httpserver.cmake) qt_internal_add_test(tst_proxypac_file @@ -21,6 +24,22 @@ set_tests_properties(tst_proxypac_file PROPERTIES ENVIRONMENT QTWEBENGINE_CHROMIUM_FLAGS=${fileEnvArg} ) +if(NOT (LINUX AND CMAKE_CROSSCOMPILING)) + set(fileEnvArg "--single-process ${fileEnvArg}") + + qt_internal_add_test(tst_proxypac_single_process + SOURCES + tst_proxypac.cpp + LIBRARIES + Qt::WebEngineCore + Test::HttpServer + ) + + set_tests_properties(tst_proxypac_single_process PROPERTIES + ENVIRONMENT QTWEBENGINE_CHROMIUM_FLAGS=${fileEnvArg} + ) +endif() + qt_internal_add_test(tst_proxypac_qrc SOURCES tst_proxypac.cpp @@ -40,8 +59,6 @@ set_tests_properties(tst_proxypac_qrc PROPERTIES ) qt_internal_add_resource(tst_proxypac_qrc "proxypac" - PREFIX - "/" - FILES - "proxy.pac" + PREFIX "/" + FILES "proxy.pac" ) diff --git a/tests/auto/widgets/proxypac/proxy.pac b/tests/auto/widgets/proxypac/proxy.pac index 1d29847b9..966c37ba5 100644 --- a/tests/auto/widgets/proxypac/proxy.pac +++ b/tests/auto/widgets/proxypac/proxy.pac @@ -2,6 +2,6 @@ function FindProxyForURL(url, host) { if (shExpMatch(host, "*.proxy1.com")) return "PROXY localhost:5551"; if (shExpMatch(host, "*.proxy2.com")) return "PROXY localhost:5552"; - return "PROXY proxy.url:8080"; + return "DIRECT"; } diff --git a/tests/auto/widgets/proxypac/proxypac.pri b/tests/auto/widgets/proxypac/proxypac.pri deleted file mode 100644 index f9f23da6f..000000000 --- a/tests/auto/widgets/proxypac/proxypac.pri +++ /dev/null @@ -1,5 +0,0 @@ -TEMPLATE = app -CONFIG += testcase -QT += testlib network webenginewidgets webenginecore -HEADERS += $$PWD/proxyserver.h -SOURCES += $$PWD/proxyserver.cpp $$PWD/tst_proxypac.cpp diff --git a/tests/auto/widgets/proxypac/proxypac.pro b/tests/auto/widgets/proxypac/proxypac.pro deleted file mode 100644 index f2a43d41f..000000000 --- a/tests/auto/widgets/proxypac/proxypac.pro +++ /dev/null @@ -1,4 +0,0 @@ -TEMPLATE = subdirs -SUBDIRS = proxypac_file proxypac_qrc -CONFIG += ordered - diff --git a/tests/auto/widgets/proxypac/proxypac.qrc b/tests/auto/widgets/proxypac/proxypac.qrc deleted file mode 100644 index 9047585a0..000000000 --- a/tests/auto/widgets/proxypac/proxypac.qrc +++ /dev/null @@ -1,7 +0,0 @@ -<!DOCTYPE RCC> -<RCC version="1.0"> -<qresource profix="/"> - <file>proxy.pac</file> -</qresource> -</RCC> - diff --git a/tests/auto/widgets/proxypac/proxypac_file/proxypac_file.pro b/tests/auto/widgets/proxypac/proxypac_file/proxypac_file.pro deleted file mode 100644 index 037123054..000000000 --- a/tests/auto/widgets/proxypac/proxypac_file/proxypac_file.pro +++ /dev/null @@ -1,9 +0,0 @@ -include(../proxypac.pri) - -proxy_pac.name = QTWEBENGINE_CHROMIUM_FLAGS -win32:proxy_pac.value = --proxy-pac-url="file:///$$PWD/../proxy.pac" -else:proxy_pac.value = --proxy-pac-url="file://$$PWD/../proxy.pac" -boot2qt:proxy_pac.value = "--single-process --no-sandbox --proxy-pac-url=file://$$PWD/../proxy.pac" - -QT_TOOL_ENV += proxy_pac - diff --git a/tests/auto/widgets/proxypac/proxypac_qrc/proxypac_qrc.pro b/tests/auto/widgets/proxypac/proxypac_qrc/proxypac_qrc.pro deleted file mode 100644 index a5ab64605..000000000 --- a/tests/auto/widgets/proxypac/proxypac_qrc/proxypac_qrc.pro +++ /dev/null @@ -1,7 +0,0 @@ -include(../proxypac.pri) - -proxy_pac.name = QTWEBENGINE_CHROMIUM_FLAGS -proxy_pac.value = --proxy-pac-url="qrc:///proxy.pac" -boot2qt:proxy_pac.value = "--single-process --no-sandbox --proxy-pac-url=qrc:///proxy.pac" -QT_TOOL_ENV += proxy_pac -RESOURCES+= $$PWD/../proxypac.qrc diff --git a/tests/auto/widgets/proxypac/proxyserver.cpp b/tests/auto/widgets/proxypac/proxyserver.cpp index 4d38c87c9..f7a859747 100644 --- a/tests/auto/widgets/proxypac/proxyserver.cpp +++ b/tests/auto/widgets/proxypac/proxyserver.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 "proxyserver.h" #include <QDataStream> diff --git a/tests/auto/widgets/proxypac/proxyserver.h b/tests/auto/widgets/proxypac/proxyserver.h index ea68286a2..c95856da9 100644 --- a/tests/auto/widgets/proxypac/proxyserver.h +++ b/tests/auto/widgets/proxypac/proxyserver.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 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 223c995e0..43ccbf028 100644 --- a/tests/auto/widgets/proxypac/tst_proxypac.cpp +++ b/tests/auto/widgets/proxypac/tst_proxypac.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 "proxy_server.h" #include <QTest> @@ -33,7 +8,6 @@ #include <QWebEnginePage> #include <QNetworkProxy> - class tst_ProxyPac : public QObject { Q_OBJECT public: @@ -64,11 +38,20 @@ void tst_ProxyPac::proxypac() QWebEngineProfile profile; QWebEnginePage page(&profile); + + const bool v8_proxy_resolver_enabled = !fromEnv.contains("--single-process"); page.load(QUrl("http://test.proxy1.com")); - QTRY_COMPARE(proxySpy1.count() >= 1, true); - 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 , true); + 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(), 200000); + } #include "tst_proxypac.moc" diff --git a/tests/auto/widgets/qtbug_110287/CMakeLists.txt b/tests/auto/widgets/qtbug_110287/CMakeLists.txt new file mode 100644 index 000000000..6d27aa3ef --- /dev/null +++ b/tests/auto/widgets/qtbug_110287/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +qt_internal_add_test(tst_qtbug_110287 + SOURCES + tst_qtbug_110287.cpp + LIBRARIES + Qt::Network + Qt::WebEngineWidgets +) +target_link_options(tst_qtbug_110287 PRIVATE "-Wl,--as-needed") diff --git a/tests/auto/widgets/qtbug_110287/tst_qtbug_110287.cpp b/tests/auto/widgets/qtbug_110287/tst_qtbug_110287.cpp new file mode 100644 index 000000000..9453ae9b8 --- /dev/null +++ b/tests/auto/widgets/qtbug_110287/tst_qtbug_110287.cpp @@ -0,0 +1,41 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <QNetworkAccessManager> +#include <QNetworkRequest> +#include <QSignalSpy> +#include <QTest> +#include <QWebEngineView> + +class tst_qtbug_110287 : public QObject +{ + Q_OBJECT +public: + tst_qtbug_110287() { } + +private slots: + void getAddrInfo(); +}; + +void tst_qtbug_110287::getAddrInfo() +{ + QNetworkAccessManager nam; + QSignalSpy namSpy(&nam, &QNetworkAccessManager::finished); + + QString address("http://www.example.com"); + QScopedPointer<QNetworkReply> reply(nam.get(QNetworkRequest(address))); + + if (!namSpy.wait(25000) || reply->error() != QNetworkReply::NoError) + QSKIP("Couldn't load page from network, skipping test."); + + QWebEngineView view; + QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); + + // load() will trigger system DNS resolution that uses getaddrinfo() + view.load(QUrl(address)); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size() > 0, true, 30000); + QTRY_COMPARE(loadFinishedSpy[0][0].toBool(), true); +} + +#include "tst_qtbug_110287.moc" +QTEST_MAIN(tst_qtbug_110287) diff --git a/tests/auto/widgets/qwebenginedownloadrequest/CMakeLists.txt b/tests/auto/widgets/qwebenginedownloadrequest/CMakeLists.txt index 3a8244c0f..5b76909b1 100644 --- a/tests/auto/widgets/qwebenginedownloadrequest/CMakeLists.txt +++ b/tests/auto/widgets/qwebenginedownloadrequest/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../httpserver/httpserver.cmake) include(../../util/util.cmake) diff --git a/tests/auto/widgets/qwebenginedownloadrequest/qwebenginedownloadrequest.pro b/tests/auto/widgets/qwebenginedownloadrequest/qwebenginedownloadrequest.pro deleted file mode 100644 index 18a66c466..000000000 --- a/tests/auto/widgets/qwebenginedownloadrequest/qwebenginedownloadrequest.pro +++ /dev/null @@ -1,3 +0,0 @@ -include(../tests.pri) -include(../../shared/http.pri) -QT *= core-private diff --git a/tests/auto/widgets/qwebenginedownloadrequest/tst_qwebenginedownloadrequest.cpp b/tests/auto/widgets/qwebenginedownloadrequest/tst_qwebenginedownloadrequest.cpp index 1f24928ab..c81a27b3a 100644 --- a/tests/auto/widgets/qwebenginedownloadrequest/tst_qwebenginedownloadrequest.cpp +++ b/tests/auto/widgets/qwebenginedownloadrequest/tst_qwebenginedownloadrequest.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include <util.h> @@ -133,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(""); @@ -153,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); } @@ -199,8 +177,8 @@ void tst_QWebEngineDownloadRequest::downloadLink_data() /* anchorHasDownloadAttribute */ << false /* fileName */ << QByteArrayLiteral("foo.txt") /* fileContents */ << QByteArrayLiteral("") - /* fileMimeTypeDeclared */ << QByteArrayLiteral("") - /* fileMimeTypeDetected */ << QByteArrayLiteral("") + /* fileMimeTypeDeclared */ << QByteArrayLiteral("text/plain") + /* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain") /* fileDisposition */ << QByteArrayLiteral("") /* fileHasReferer */ << true /* fileAction */ << FileIsDownloaded; @@ -437,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); @@ -475,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); @@ -483,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; @@ -548,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); @@ -573,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 @@ -665,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); @@ -710,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); @@ -721,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); @@ -1154,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(); @@ -1296,7 +1274,7 @@ void tst_QWebEngineDownloadRequest::downloadDataUrls() QSignalSpy loadSpy(m_page, &QWebEnginePage::loadFinished); m_view->load(m_server->url()); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0).toBool(), true); // Trigger download diff --git a/tests/auto/widgets/qwebenginehistory/CMakeLists.txt b/tests/auto/widgets/qwebenginehistory/CMakeLists.txt index b91281a45..e277a7326 100644 --- a/tests/auto/widgets/qwebenginehistory/CMakeLists.txt +++ b/tests/auto/widgets/qwebenginehistory/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../util/util.cmake) qt_internal_add_test(tst_qwebenginehistory diff --git a/tests/auto/widgets/qwebenginehistory/qwebenginehistory.pro b/tests/auto/widgets/qwebenginehistory/qwebenginehistory.pro deleted file mode 100644 index e99c7f493..000000000 --- a/tests/auto/widgets/qwebenginehistory/qwebenginehistory.pro +++ /dev/null @@ -1 +0,0 @@ -include(../tests.pri) 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/qwebenginehistory/tst_qwebenginehistory.qrc b/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.qrc deleted file mode 100644 index cdfe575a0..000000000 --- a/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.qrc +++ /dev/null @@ -1,10 +0,0 @@ -<!DOCTYPE RCC><RCC version="1.0"> -<qresource> - <file>resources/page1.html</file> - <file>resources/page2.html</file> - <file>resources/page3.html</file> - <file>resources/page4.html</file> - <file>resources/page5.html</file> - <file>resources/page6.html</file> -</qresource> -</RCC> diff --git a/tests/auto/widgets/qwebenginepage/BLACKLIST b/tests/auto/widgets/qwebenginepage/BLACKLIST index 0c84b8de1..52def48d1 100644 --- a/tests/auto/widgets/qwebenginepage/BLACKLIST +++ b/tests/auto/widgets/qwebenginepage/BLACKLIST @@ -5,5 +5,11 @@ osx windows macos # Can't move cursor (QTBUG-76312) -[acceptNavigationRequestNavigationType] -b2qt arm +[comboBoxPopupPositionAfterMove] +macos + +[comboBoxPopupPositionAfterChildMove] +macos + +[backgroundColor] +macos diff --git a/tests/auto/widgets/qwebenginepage/CMakeLists.txt b/tests/auto/widgets/qwebenginepage/CMakeLists.txt index bb31d9a97..a15bb6e06 100644 --- a/tests/auto/widgets/qwebenginepage/CMakeLists.txt +++ b/tests/auto/widgets/qwebenginepage/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../httpserver/httpserver.cmake) include(../../util/util.cmake) @@ -5,6 +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 diff --git a/tests/auto/widgets/qwebenginepage/qwebenginepage.pro b/tests/auto/widgets/qwebenginepage/qwebenginepage.pro deleted file mode 100644 index 18a66c466..000000000 --- a/tests/auto/widgets/qwebenginepage/qwebenginepage.pro +++ /dev/null @@ -1,3 +0,0 @@ -include(../tests.pri) -include(../../shared/http.pri) -QT *= core-private 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 c118bd718..d55240abe 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,7 +50,10 @@ #include <qnetworkcookiejar.h> #include <qnetworkreply.h> #include <qnetworkrequest.h> +#include <qwebengineclienthints.h> #include <qwebenginedownloadrequest.h> +#include <qwebenginedesktopmediarequest.h> +#include <qwebenginefilesystemaccessrequest.h> #include <qwebenginefindtextresult.h> #include <qwebenginefullscreenrequest.h> #include <qwebenginehistory.h> @@ -61,17 +67,21 @@ #include <qwebenginescript.h> #include <qwebenginescriptcollection.h> #include <qwebenginesettings.h> +#include <qwebengineurlrequestinterceptor.h> #include <qwebengineurlrequestjob.h> #include <qwebengineurlscheme.h> #include <qwebengineurlschemehandler.h> #include <qwebengineview.h> #include <qimagewriter.h> +#include <QColorSpace> +#include <QQuickRenderControl> +#include <QQuickWindow> static void removeRecursive(const QString& dirname) { QDir dir(dirname); QFileInfoList entries(dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)); - for (int i = 0; i < entries.count(); ++i) + for (int i = 0; i < entries.size(); ++i) if (entries[i].isDir()) removeRecursive(entries[i].filePath()); else @@ -79,6 +89,13 @@ static void removeRecursive(const QString& dirname) QDir().rmdir(dirname); } +struct TestBasePage : QWebEnginePage +{ + explicit TestBasePage(QWebEngineProfile *profile, QObject *parent = nullptr) : QWebEnginePage(profile, parent) { } + explicit TestBasePage(QObject *parent = nullptr) : QWebEnginePage(parent) { } + QSignalSpy loadSpy { this, &QWebEnginePage::loadFinished }; +}; + class tst_QWebEnginePage : public QObject { Q_OBJECT @@ -95,13 +112,17 @@ public Q_SLOTS: private Q_SLOTS: void initTestCase(); void cleanupTestCase(); + void comboBoxPopupPositionAfterMove_data(); void comboBoxPopupPositionAfterMove(); + void comboBoxPopupPositionAfterChildMove_data(); void comboBoxPopupPositionAfterChildMove(); void acceptNavigationRequest(); void acceptNavigationRequestNavigationType(); void acceptNavigationRequestRelativeToNothing(); +#ifndef Q_OS_MACOS void geolocationRequestJS_data(); void geolocationRequestJS(); +#endif void loadFinished(); void actionStates(); void pasteImage(); @@ -142,7 +163,7 @@ private Q_SLOTS: #endif void openWindowDefaultSize(); -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS void macCopyUnicodeToClipboard(); #endif @@ -150,7 +171,8 @@ private Q_SLOTS: void runJavaScriptDisabled(); void runJavaScriptFromSlot(); void fullScreenRequested(); - void quotaRequested(); + void requestQuota_data(); + void requestQuota(); // Tests from tst_QWebEngineFrame @@ -212,7 +234,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(); @@ -233,6 +261,8 @@ private Q_SLOTS: void editActionsWithoutSelection(); void customUserAgentInNewTab(); + void openNewTabInDifferentProfile_data(); + void openNewTabInDifferentProfile(); void renderProcessCrashed(); void renderProcessPid(); void backgroundColor(); @@ -245,20 +275,50 @@ private Q_SLOTS: void testChooseFilesParameters(); 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-" + QDateTime::currentDateTime().toString(QLatin1String("yyyyMMddhhmmss")); return tmpd; } + + 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 { + 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() @@ -304,6 +364,14 @@ void tst_QWebEnginePage::initTestCase() QWebEngineUrlScheme echo("echo"); echo.setSyntax(QWebEngineUrlScheme::Syntax::Path); QWebEngineUrlScheme::registerScheme(echo); + + QWebEngineUrlScheme local("local"); + local.setFlags(QWebEngineUrlScheme::LocalScheme); + QWebEngineUrlScheme::registerScheme(local); + + QWebEngineUrlScheme remote("remote"); + remote.setFlags(QWebEngineUrlScheme::CorsEnabled); + QWebEngineUrlScheme::registerScheme(remote); } void tst_QWebEnginePage::cleanupTestCase() @@ -356,15 +424,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?")); @@ -400,6 +468,7 @@ private: bool m_allowGeolocation; }; +#ifndef Q_OS_MACOS void tst_QWebEnginePage::geolocationRequestJS_data() { QTest::addColumn<bool>("allowed"); @@ -422,7 +491,7 @@ void tst_QWebEnginePage::geolocationRequestJS() QSignalSpy spyLoadFinished(newPage, SIGNAL(loadFinished(bool))); newPage->setHtml(QString("<html><body>test</body></html>"), QUrl("qrc://secure/origin")); - QTRY_COMPARE_WITH_TIMEOUT(spyLoadFinished.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(spyLoadFinished.size(), 1, 20000); // Geolocation is only enabled for visible WebContents. view.show(); @@ -439,6 +508,7 @@ void tst_QWebEnginePage::geolocationRequestJS() QEXPECT_FAIL("", "No location service available.", Continue); QCOMPARE(result, errorCode); } +#endif void tst_QWebEnginePage::loadFinished() { @@ -449,19 +519,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() @@ -518,6 +588,7 @@ void tst_QWebEnginePage::pasteImage() QByteArray data = evaluateJavaScriptSync(page, "window.myImageDataURL").toByteArray(); data.remove(0, data.indexOf(";base64,") + 8); QImage image = QImage::fromData(QByteArray::fromBase64(data), "PNG"); + image.setColorSpace(origImage.colorSpace()); if (image.format() == QImage::Format_RGB32) image.reinterpretAsFormat(QImage::Format_ARGB32); QCOMPARE(image, origImage); @@ -547,7 +618,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); } @@ -606,40 +677,40 @@ void tst_QWebEnginePage::acceptNavigationRequestNavigationType() 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 @@ -660,18 +731,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. @@ -684,18 +755,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); @@ -714,20 +785,19 @@ 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); QTRY_VERIFY(!page.createdWindows[0]->url().isEmpty()); - QString url = page.createdWindows[0]->url().toString(); // Check if the form submission was OK. - QVERIFY(url.contains("?foo=bar")); + QTRY_VERIFY(page.createdWindows[0]->url().toString().contains("?foo=bar")); } class TestNetworkManager : public QNetworkAccessManager @@ -768,16 +838,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")); @@ -826,7 +896,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); @@ -853,7 +923,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()); @@ -874,7 +944,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) { @@ -886,7 +956,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(); } @@ -913,8 +983,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()); @@ -927,13 +997,14 @@ void tst_QWebEnginePage::localStorageVisibility() // ...first check second page (for storage to appear) as applying settings is batched and done asynchronously QTRY_VERIFY(evaluateJavaScriptSync(&webPage2, QString("(window.localStorage != undefined)")).toBool()); // Switching the feature off does not actively remove the object from webPage1. - QVERIFY(evaluateJavaScriptSync(&webPage1, QString("(window.localStorage != undefined)")).toBool()); +// FIXME: 94-based: now it does +// QVERIFY(evaluateJavaScriptSync(&webPage1, QString("(window.localStorage != undefined)")).toBool()); // 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()); } @@ -1034,7 +1105,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, @@ -1068,7 +1139,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()); @@ -1082,7 +1153,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")); } @@ -1093,7 +1164,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()); @@ -1110,7 +1181,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()); } @@ -1121,7 +1192,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")); } @@ -1131,7 +1202,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); @@ -1143,7 +1214,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() }); @@ -1155,7 +1226,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})); @@ -1184,7 +1255,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()); @@ -1206,7 +1277,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; @@ -1243,12 +1314,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); @@ -1256,28 +1327,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); @@ -1287,45 +1358,60 @@ static QWindow *findNewTopLevelWindow(const QWindowList &oldTopLevelWindows) { const auto tlws = QGuiApplication::topLevelWindows(); for (auto w : tlws) { - if (!oldTopLevelWindows.contains(w)) { + // note 'offscreen' window is a top-level window + if (!oldTopLevelWindows.contains(w) + && !QQuickRenderControl::renderWindowFor(qobject_cast<QQuickWindow *>(w))) { return w; } } return nullptr; } +void tst_QWebEnginePage::comboBoxPopupPositionAfterMove_data() +{ + QTest::addColumn<bool>("withTouch"); + QTest::addRow("mouse") << false; + QTest::addRow("touch") << true; +} + void tst_QWebEnginePage::comboBoxPopupPositionAfterMove() { +#if defined(Q_OS_MACOS) && (defined(__arm64__) || defined(__aarch64__)) + QSKIP("This test crashes for Apple M1"); +#endif QWebEngineView view; + QTRY_VERIFY(QGuiApplication::primaryScreen()); view.move(QGuiApplication::primaryScreen()->availableGeometry().topLeft()); view.resize(640, 480); view.show(); - - QSignalSpy loadSpy(&view, SIGNAL(loadFinished(bool))); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + QSignalSpy spyLoadFinished(&view, SIGNAL(loadFinished(bool))); view.setHtml(QLatin1String("<html><head></head><body><select id='foo'>" "<option>fran</option><option>troz</option>" "</select></body></html>")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(spyLoadFinished.size(), 1); const auto oldTlws = QGuiApplication::topLevelWindows(); - QWindow *window = view.windowHandle(); - QTest::mouseClick(window, Qt::LeftButton, Qt::KeyboardModifiers(), - elementCenter(view.page(), "foo")); - + QFETCH(bool, withTouch); + QPointer<QWindow> window = view.windowHandle(); + auto pos = elementCenter(view.page(), "foo"); + makeClick(window, withTouch, pos); QWindow *popup = nullptr; - QTRY_VERIFY(popup = findNewTopLevelWindow(oldTlws)); + QTRY_VERIFY((popup = findNewTopLevelWindow(oldTlws))); + QVERIFY(QTest::qWaitForWindowExposed(popup)); + QTRY_VERIFY(popup->width() > 0 && popup->height() > 0); QTRY_VERIFY(QGuiApplication::topLevelWindows().contains(popup)); QTRY_VERIFY(!popup->position().isNull()); QPoint popupPos = popup->position(); - + QPointer<QWindow> pw(popup); // Close the popup by clicking somewhere into the page. - QTest::mouseClick(window, Qt::LeftButton, Qt::KeyboardModifiers(), QPoint(1, 1)); + makeClick(window, withTouch, QPoint(1, 1)); QTRY_VERIFY(!QGuiApplication::topLevelWindows().contains(popup)); - + QTRY_VERIFY(!pw); auto jsViewPosition = [&view]() { QLatin1String script("(function() { return [window.screenX, window.screenY]; })()"); QVariantList posList = evaluateJavaScriptSync(view.page(), script).toList(); - if (posList.count() != 2) { + if (posList.size() != 2) { qWarning("jsViewPosition failed."); return QPoint(); } @@ -1337,18 +1423,28 @@ void tst_QWebEnginePage::comboBoxPopupPositionAfterMove() const QPoint offset(12, 13); view.move(view.pos() + offset); QTRY_COMPARE(jsViewPosition(), view.pos()); - QTest::mouseClick(window, Qt::LeftButton, Qt::KeyboardModifiers(), - elementCenter(view.page(), "foo")); - QTRY_VERIFY(popup = findNewTopLevelWindow(oldTlws)); + makeClick(window, withTouch, elementCenter(view.page(), "foo")); + QTRY_VERIFY((popup = findNewTopLevelWindow(oldTlws))); + QTRY_VERIFY(popup->width() > 0 && popup->height() > 0); QTRY_VERIFY(QGuiApplication::topLevelWindows().contains(popup)); QTRY_VERIFY(!popup->position().isNull()); QCOMPARE(popupPos + offset, popup->position()); - QTest::mouseClick(window, Qt::LeftButton, Qt::KeyboardModifiers(), QPoint(1, 1)); + makeClick(window, withTouch, QPoint(1, 1)); QTRY_VERIFY(!QGuiApplication::topLevelWindows().contains(popup)); } +void tst_QWebEnginePage::comboBoxPopupPositionAfterChildMove_data() +{ + QTest::addColumn<bool>("withTouch"); + QTest::addRow("mouse") << false; + QTest::addRow("touch") << true; +} + 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); @@ -1359,29 +1455,33 @@ void tst_QWebEnginePage::comboBoxPopupPositionAfterChildMove() mainWidget.layout()->addWidget(&view); QScreen *screen = QGuiApplication::primaryScreen(); + Q_ASSERT(screen); mainWidget.move(screen->availableGeometry().topLeft()); mainWidget.resize(640, 480); mainWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&mainWidget)); QSignalSpy loadSpy(&view, SIGNAL(loadFinished(bool))); view.setHtml(QLatin1String("<html><head></head><body><select autofocus id='foo'>" "<option value=\"narf\">narf</option><option>zort</option>" "</select></body></html>")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); const auto oldTlws = QGuiApplication::topLevelWindows(); - QWindow *window = view.window()->windowHandle(); - QTest::mouseClick(window, Qt::LeftButton, Qt::KeyboardModifiers(), - view.mapTo(view.window(), elementCenter(view.page(), "foo"))); + + QFETCH(bool, withTouch); + QPointer<QWindow> window = view.window()->windowHandle(); + makeClick(window, withTouch, view.mapTo(view.window(), elementCenter(view.page(), "foo"))); QWindow *popup = nullptr; - QTRY_VERIFY(popup = findNewTopLevelWindow(oldTlws)); + QTRY_VERIFY((popup = findNewTopLevelWindow(oldTlws))); + QVERIFY(QTest::qWaitForWindowExposed(popup)); + QTRY_VERIFY(popup->width() > 0 && popup->height() > 0); QTRY_VERIFY(QGuiApplication::topLevelWindows().contains(popup)); QTRY_VERIFY(!popup->position().isNull()); QPoint popupPos = popup->position(); // Close the popup by clicking somewhere into the page. - QTest::mouseClick(window, Qt::LeftButton, Qt::KeyboardModifiers(), - view.mapTo(view.window(), QPoint(1, 1))); + makeClick(window, withTouch, view.mapTo(view.window(), QPoint(1, 1))); QTRY_VERIFY(!QGuiApplication::topLevelWindows().contains(popup)); int originalViewWidth = view.size().width(); @@ -1391,19 +1491,25 @@ void tst_QWebEnginePage::comboBoxPopupPositionAfterChildMove() return viewWidth; }; + QCOMPARE(jsViewWidth(), originalViewWidth); + // Resize the "spacer" widget, and implicitly change the global position of the QWebEngineView. const int offset = 50; spacer.setMinimumWidth(spacer.size().width() + offset); + QTRY_COMPARE(jsViewWidth(), originalViewWidth - offset); - QTest::mouseClick(window, Qt::LeftButton, Qt::KeyboardModifiers(), - view.mapTo(view.window(), elementCenter(view.page(), "foo"))); - QTRY_VERIFY(popup = findNewTopLevelWindow(oldTlws)); + makeClick(window, withTouch, view.mapTo(view.window(), elementCenter(view.page(), "foo"))); + QTRY_VERIFY((popup = findNewTopLevelWindow(oldTlws))); + QVERIFY(QTest::qWaitForWindowExposed(popup)); + QTRY_VERIFY(popup->width() > 0 && popup->height() > 0); QTRY_VERIFY(!popup->position().isNull()); - QCOMPARE(popupPos + QPoint(50, 0), popup->position()); + QCOMPARE(popupPos + QPoint(offset, 0), popup->position()); + makeClick(window, withTouch, QPoint(1, 1)); + QTRY_VERIFY(!QGuiApplication::topLevelWindows().contains(popup)); } -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS void tst_QWebEnginePage::macCopyUnicodeToClipboard() { QString unicodeText = QString::fromUtf8("αβγδεζηθικλμπ"); @@ -1797,14 +1903,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); @@ -1875,7 +1981,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), @@ -1892,7 +1998,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, [&]() { @@ -1916,7 +2022,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")); @@ -1933,7 +2039,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))); @@ -1947,8 +2053,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); @@ -1956,35 +2072,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() @@ -2006,7 +2110,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")); @@ -2020,8 +2124,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. @@ -2099,7 +2203,7 @@ public: setAttribute(QNetworkRequest::RedirectionTargetAttribute, QUrl("qrc:/test2.html")); QTimer::singleShot(0, this, SLOT(continueRedirect())); } -#ifndef QT_NO_OPENSSL +#if QT_CONFIG(openssl) else if (request.url() == QUrl("qrc:/fake-ssl-error.html")) { setError(QNetworkReply::SslHandshakeFailedError, tr("Fake error!")); QTimer::singleShot(0, this, SLOT(continueError())); @@ -2156,7 +2260,7 @@ protected: { QString url = request.url().toString(); if (op == QNetworkAccessManager::GetOperation) { -#ifndef QT_NO_OPENSSL +#if QT_CONFIG(openssl) if (url == "qrc:/fake-ssl-error.html") { FakeReply* reply = new FakeReply(request, this); QList<QSslError> errors; @@ -2180,7 +2284,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()); @@ -2189,7 +2293,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()); @@ -2235,7 +2339,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); @@ -2244,7 +2348,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); @@ -2307,7 +2411,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); @@ -2370,7 +2474,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)); } @@ -2406,7 +2510,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); @@ -2415,7 +2519,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); @@ -2425,12 +2530,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(); @@ -2451,7 +2556,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()); } @@ -2486,7 +2591,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); @@ -2580,7 +2685,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); @@ -2589,7 +2694,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); @@ -2598,7 +2703,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); @@ -2607,7 +2712,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); @@ -2662,9 +2767,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()); @@ -2675,9 +2780,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()); @@ -2701,9 +2806,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()); @@ -2715,9 +2820,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()); @@ -2748,7 +2853,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. @@ -2757,7 +2862,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); @@ -2768,14 +2873,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. @@ -2787,7 +2892,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. @@ -2800,7 +2905,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() @@ -2815,6 +2920,7 @@ void tst_QWebEnginePage::setUrlUsingStateObject() { QUrl url; QSignalSpy urlChangedSpy(m_page, SIGNAL(urlChanged(QUrl))); + QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool))); int expectedUrlChangeCount = 0; QCOMPARE(m_page->history()->count(), 0); @@ -2822,20 +2928,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.size(), 1); QCOMPARE(m_page->url(), url); - QTRY_COMPARE(m_page->history()->count(), 1); + 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()); @@ -2843,7 +2951,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()); @@ -2872,9 +2980,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); @@ -2888,11 +2996,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); @@ -2902,11 +3010,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); @@ -2994,7 +3102,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); } @@ -3005,31 +3113,31 @@ void tst_QWebEnginePage::loadFromQrc() // Standard case. page.load(QStringLiteral("qrc:///resources/foo.txt")); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().value(0).toBool(), true); QCOMPARE(toPlainTextSync(&page), QStringLiteral("foo\n")); // Query and fragment parts are ignored. page.load(QStringLiteral("qrc:///resources/bar.txt?foo=1#bar")); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().value(0).toBool(), true); QCOMPARE(toPlainTextSync(&page), QStringLiteral("bar\n")); // Literal spaces are OK. page.load(QStringLiteral("qrc:///resources/path with spaces.txt")); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().value(0).toBool(), true); QCOMPARE(toPlainTextSync(&page), QStringLiteral("contents with spaces\n")); // Escaped spaces are OK too. page.load(QStringLiteral("qrc:///resources/path%20with%20spaces.txt")); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().value(0).toBool(), true); QCOMPARE(toPlainTextSync(&page), QStringLiteral("contents with spaces\n")); // Resource not found, loading fails. page.load(QStringLiteral("qrc:///nope")); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 10000); QCOMPARE(spy.takeFirst().value(0).toBool(), false); } @@ -3046,7 +3154,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)); @@ -3056,7 +3164,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)); @@ -3079,42 +3187,59 @@ 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() { - QWebEnginePage page; + TestBasePage page, page2; - QVERIFY(qFuzzyCompare(page.zoomFactor(), 1.0)); + QCOMPARE(page.zoomFactor(), 1.0); page.setZoomFactor(2.5); - QVERIFY(qFuzzyCompare(page.zoomFactor(), 2.5)); - - const QUrl urlToLoad("qrc:/resources/test1.html"); - - QSignalSpy finishedSpy(&page, SIGNAL(loadFinished(bool))); - page.load(urlToLoad); - QTRY_COMPARE(finishedSpy.count(), 1); - QVERIFY(finishedSpy.at(0).first().toBool()); - QVERIFY(qFuzzyCompare(page.zoomFactor(), 2.5)); - - page.setZoomFactor(5.5); - QVERIFY(qFuzzyCompare(page.zoomFactor(), 2.5)); + QCOMPARE(page.zoomFactor(), 2.5); + + const QUrl url1("qrc:/resources/test1.html"), url2(QUrl("qrc:/resources/test2.html")); + + page.load(url1); + QTRY_COMPARE(page.loadSpy.size(), 1); + QVERIFY(page.loadSpy.at(0).first().toBool()); + QCOMPARE(page.zoomFactor(), 2.5); + + page.setZoomFactor(5.5); // max accepted zoom: kMaximumPageZoomFactor = 5.0 + QCOMPARE(page.zoomFactor(), 2.5); + + page.setZoomFactor(0.1); // min accepted zoom: kMinimumPageZoomFactor = 0.25 + QCOMPARE(page.zoomFactor(), 2.5); + + // try loading different url and check new values after load + page.loadSpy.clear(); + for (auto &&p : { + qMakePair(&page, 2.5), // navigating away to different url should keep zoom + qMakePair(&page2, 1.0), // same url navigation in diffent page shouldn't be affected + }) { + auto &&page = *p.first; auto zoomFactor = p.second; + page.load(url2); + QTRY_COMPARE(page.loadSpy.size(), 1); + QVERIFY(page.loadSpy.last().first().toBool()); + QCOMPARE(page.zoomFactor(), zoomFactor); + } - page.setZoomFactor(0.1); - QVERIFY(qFuzzyCompare(page.zoomFactor(), 2.5)); + // should have no influence on first page + page2.setZoomFactor(3.5); + for (auto &&p : { qMakePair(&page, 2.5), qMakePair(&page2, 3.5), }) + QCOMPARE(p.first->zoomFactor(), p.second); } void tst_QWebEnginePage::mouseButtonTranslation() @@ -3134,17 +3259,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); @@ -3173,34 +3301,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; @@ -3209,12 +3320,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())); @@ -3269,7 +3380,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); @@ -3304,7 +3415,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))); @@ -3326,7 +3437,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() @@ -3359,7 +3470,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\")"); @@ -3369,7 +3480,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); @@ -3380,7 +3491,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()); @@ -3396,7 +3507,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()); @@ -3420,7 +3531,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); @@ -3432,7 +3543,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); @@ -3443,6 +3554,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() @@ -3464,11 +3577,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()); } @@ -3578,7 +3691,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) { @@ -3593,7 +3706,7 @@ void tst_QWebEnginePage::openLinkInNewPage() break; } - Qt::MouseButton button; + Qt::MouseButton button = Qt::NoButton; switch (cause) { case Cause::TargetBlank: button = Qt::LeftButton; @@ -3608,13 +3721,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); @@ -3623,9 +3736,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; @@ -3647,7 +3760,7 @@ void tst_QWebEnginePage::dynamicFrame() page.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); QSignalSpy spy(&page, &QWebEnginePage::loadFinished); page.load(QStringLiteral("qrc:/resources/dynamicFrame.html")); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(toPlainTextSync(&page).trimmed(), QStringLiteral("foo")); } @@ -3723,7 +3836,7 @@ void tst_QWebEnginePage::notificationPermission() QSignalSpy spy(&page, &QWebEnginePage::loadFinished); page.setHtml(QString("<html><body>Test</body></html>"), baseUrl); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), setOnInit ? permission : QLatin1String("default")); @@ -3781,6 +3894,149 @@ void tst_QWebEnginePage::sendNotification() QTRY_VERIFY2(page.messages.contains("onclose"), page.messages.join("\n").toLatin1().constData()); } +static QString clipboardPermissionQuery(QString variableName, QString permissionName) +{ + return QString("var %1; navigator.permissions.query({ name:'%2' }).then((p) => { %1 = p.state; " + "});") + .arg(variableName) + .arg(permissionName); +} + + +void tst_QWebEnginePage::clipboardReadWritePermissionInitialState_data() +{ + QTest::addColumn<bool>("canAccessClipboard"); + QTest::addColumn<bool>("canPaste"); + QTest::addColumn<QString>("permission"); + QTest::newRow("access and paste should grant") << true << true << "granted"; + QTest::newRow("no access should prompt") << false << true << "prompt"; + QTest::newRow("no paste should prompt") << true << false << "prompt"; + QTest::newRow("no access or paste should prompt") << false << false << "prompt"; +} + +void tst_QWebEnginePage::clipboardReadWritePermissionInitialState() +{ + QFETCH(bool, canAccessClipboard); + QFETCH(bool, canPaste); + QFETCH(QString, permission); + + QWebEngineProfile otr; + QWebEngineView view(&otr); + QWebEnginePage &page = *view.page(); + view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true); + page.settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, + canAccessClipboard); + page.settings()->setAttribute(QWebEngineSettings::JavascriptCanPaste, canPaste); + + QSignalSpy spy(&page, &QWebEnginePage::loadFinished); + QUrl baseUrl("https://www.example.com/somepage.html"); + page.setHtml(QString("<html><body>Test</body></html>"), baseUrl); + QTRY_COMPARE(spy.size(), 1); + + evaluateJavaScriptSync(&page, clipboardPermissionQuery("readPermission", "clipboard-read")); + QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("readPermission")), permission); + evaluateJavaScriptSync(&page, clipboardPermissionQuery("writePermission", "clipboard-write")); + QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("writePermission")), permission); +} + +void tst_QWebEnginePage::clipboardReadWritePermission_data() +{ + QTest::addColumn<bool>("canAccessClipboard"); + QTest::addColumn<QWebEnginePage::PermissionPolicy>("initialPolicy"); + QTest::addColumn<QString>("initialPermission"); + QTest::addColumn<QWebEnginePage::PermissionPolicy>("requestPolicy"); + QTest::addColumn<QString>("finalPermission"); + + QTest::newRow("noAccessGrantGrant") + << false << QWebEnginePage::PermissionGrantedByUser << "granted" + << QWebEnginePage::PermissionGrantedByUser << "granted"; + QTest::newRow("noAccessGrantDeny") + << false << QWebEnginePage::PermissionGrantedByUser << "granted" + << QWebEnginePage::PermissionDeniedByUser << "denied"; + QTest::newRow("noAccessDenyGrant") + << false << QWebEnginePage::PermissionDeniedByUser << "denied" + << QWebEnginePage::PermissionGrantedByUser << "granted"; + QTest::newRow("noAccessDenyDeny") << false << QWebEnginePage::PermissionDeniedByUser << "denied" + << QWebEnginePage::PermissionDeniedByUser << "denied"; + QTest::newRow("noAccessAskGrant") << false << QWebEnginePage::PermissionUnknown << "prompt" + << QWebEnginePage::PermissionGrantedByUser << "granted"; + + // All policies are ignored and overridden by setting JsCanAccessClipboard and JsCanPaste to + // true + QTest::newRow("accessGrantGrant") + << true << QWebEnginePage::PermissionGrantedByUser << "granted" + << QWebEnginePage::PermissionGrantedByUser << "granted"; + QTest::newRow("accessDenyDeny") << true << QWebEnginePage::PermissionDeniedByUser << "granted" + << QWebEnginePage::PermissionDeniedByUser << "granted"; + QTest::newRow("accessAskAsk") << true << QWebEnginePage::PermissionUnknown << "granted" + << QWebEnginePage::PermissionUnknown << "granted"; +} + +void tst_QWebEnginePage::clipboardReadWritePermission() +{ + QFETCH(bool, canAccessClipboard); + QFETCH(QWebEnginePage::PermissionPolicy, initialPolicy); + QFETCH(QString, initialPermission); + QFETCH(QWebEnginePage::PermissionPolicy, requestPolicy); + QFETCH(QString, finalPermission); + + QWebEngineProfile otr; + QWebEngineView view(&otr); + QWebEnginePage &page = *view.page(); + view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true); + page.settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, + canAccessClipboard); + page.settings()->setAttribute(QWebEngineSettings::JavascriptCanPaste, true); + + QUrl baseUrl("https://www.example.com/somepage.html"); + + int permissionRequestCount = 0; + bool errorState = false; + + // if JavascriptCanAccessClipboard is true, this never fires + connect(&page, &QWebEnginePage::featurePermissionRequested, &page, + [&](const QUrl &o, QWebEnginePage::Feature f) { + if (f != QWebEnginePage::ClipboardReadWrite) + return; + if (o != baseUrl.url(QUrl::RemoveFilename)) { + qWarning() << "Unexpected case. Can't proceed." << o; + errorState = true; + return; + } + permissionRequestCount++; + page.setFeaturePermission(o, f, requestPolicy); + }); + + page.setFeaturePermission(baseUrl, QWebEnginePage::ClipboardReadWrite, initialPolicy); + + QSignalSpy spy(&page, &QWebEnginePage::loadFinished); + page.setHtml(QString("<html><body>Test</body></html>"), baseUrl); + QTRY_COMPARE(spy.size(), 1); + + evaluateJavaScriptSync(&page, clipboardPermissionQuery("readPermission", "clipboard-read")); + QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("readPermission")), initialPermission); + evaluateJavaScriptSync(&page, clipboardPermissionQuery("writePermission", "clipboard-write")); + QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("writePermission")), initialPermission); + + auto triggerRequest = [&page](QString variableName, QString apiCall) + { + auto js = QString("var %1; navigator.clipboard.%2.then((v) => { %1 = 'granted' }, (v) => { %1 = " + "'denied' });") + .arg(variableName) + .arg(apiCall); + evaluateJavaScriptSync(&page, js); + }; + + // permission is not 'remembered' from api standpoint, hence is not suppressed on explicit call + // from JS + triggerRequest("readState", "readText()"); + QTRY_COMPARE(evaluateJavaScriptSync(&page, "readState"), finalPermission); + triggerRequest("writeState", "writeText('foo')"); + QTRY_COMPARE(evaluateJavaScriptSync(&page, "writeState"), finalPermission); + QCOMPARE(permissionRequestCount, canAccessClipboard ? 0 : 2); + QVERIFY(!errorState); +} + void tst_QWebEnginePage::contentsSize() { m_view->resize(800, 600); @@ -3791,8 +4047,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); @@ -3809,6 +4065,67 @@ void tst_QWebEnginePage::contentsSize() QCOMPARE(m_page->contentsSize().height(), 1216); } +void tst_QWebEnginePage::localFontAccessPermission_data() +{ + QTest::addColumn<QWebEnginePage::PermissionPolicy>("policy"); + QTest::addColumn<bool>("ignore"); + QTest::addColumn<bool>("shouldBeEmpty"); + + QTest::newRow("ignore") << QWebEnginePage::PermissionDeniedByUser << true << true; + QTest::newRow("setDeny") << QWebEnginePage::PermissionDeniedByUser << false << true; + QTest::newRow("setGrant") << QWebEnginePage::PermissionGrantedByUser << false << false; +} + +void tst_QWebEnginePage::localFontAccessPermission() { + QFETCH(QWebEnginePage::PermissionPolicy, policy); + QFETCH(bool, ignore); + QFETCH(bool, shouldBeEmpty); + + QWebEngineView view; + QWebEnginePage page(&view); + view.setPage(&page); + + connect(&page, &QWebEnginePage::featurePermissionRequested, &page, [&] (const QUrl &o, QWebEnginePage::Feature f) { + if (f != QWebEnginePage::LocalFontsAccess) + return; + + if (!ignore) + page.setFeaturePermission(o, f, policy); + }); + + QSignalSpy spy(&page, &QWebEnginePage::loadFinished); + page.setHtml(QString("<html><body>Test</body></html>"), QUrl("qrc://secure/origin")); + 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::mouseMove(view.windowHandle(), QPoint(10, 10)); + QTest::mouseClick(view.windowHandle(), Qt::LeftButton); + + auto js = QStringLiteral("var done = false; var fonts; window.queryLocalFonts().then(f => { fonts = f; done = true; });"); + evaluateJavaScriptSync(&page, js); + + 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); + } + + // Move mouse to make sure subsequent runs' transient activation will fire + QTest::mouseMove(view.windowHandle(), QPoint(1, 10)); + QTest::mouseClick(view.windowHandle(), Qt::LeftButton); + + // Reset permission, since otherwise it will be stored between runs + page.setFeaturePermission(QUrl("qrc://secure/origin"), QWebEnginePage::LocalFontsAccess, QWebEnginePage::PermissionUnknown); +} + void tst_QWebEnginePage::setLifecycleState() { qRegisterMetaType<QWebEnginePage::LifecycleState>("LifecycleState"); @@ -3820,64 +4137,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)); @@ -3886,21 +4203,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)); } @@ -3916,18 +4233,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); @@ -3936,28 +4253,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); @@ -3966,31 +4283,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)); } @@ -4001,7 +4318,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 @@ -4038,7 +4355,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 @@ -4077,7 +4394,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 @@ -4102,16 +4419,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); @@ -4119,9 +4436,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); @@ -4129,7 +4446,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); @@ -4137,7 +4454,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 @@ -4175,35 +4492,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); } @@ -4220,21 +4537,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()); } @@ -4250,14 +4567,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)); @@ -4267,21 +4584,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); } @@ -4377,32 +4694,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)); } @@ -4417,33 +4734,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)); } @@ -4462,20 +4779,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")); } @@ -4495,13 +4812,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")); } @@ -4521,15 +4838,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")); } @@ -4545,8 +4862,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()); @@ -4558,7 +4875,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")); @@ -4572,6 +4889,28 @@ void tst_QWebEnginePage::editActionsWithoutSelection() QVERIFY(page->action(QWebEnginePage::Unselect)->isEnabled()); } +struct PageWithNewWindowHandler : QWebEnginePage +{ + QScopedPointer<PageWithNewWindowHandler> newPage; + bool handleInSignal; + QWebEngineProfile *targetProfile = nullptr; + QSignalSpy loadSpy { this, &QWebEnginePage::loadFinished }; + PageWithNewWindowHandler(QWebEngineProfile *p, bool inSignal = false, QWebEngineProfile *tp = nullptr) + : QWebEnginePage(p), handleInSignal(inSignal), targetProfile(tp) { + if (handleInSignal) + connect(this, &QWebEnginePage::newWindowRequested, this, [this] (QWebEngineNewWindowRequest &r) { + newPage.reset(new PageWithNewWindowHandler(targetProfile ? targetProfile : profile(), handleInSignal)); + newPage->acceptAsNewWindow(r); + }); + } + QWebEnginePage *createWindow(WebWindowType) override { + if (handleInSignal) + return nullptr; + newPage.reset(new PageWithNewWindowHandler(targetProfile ? targetProfile : profile(), handleInSignal)); + return newPage.get(); + } +}; + void tst_QWebEnginePage::customUserAgentInNewTab() { HttpServer server; @@ -4584,55 +4923,84 @@ void tst_QWebEnginePage::customUserAgentInNewTab() }); QVERIFY(server.start()); - class Page : public QWebEnginePage { - public: - QWebEngineProfile *targetProfile = nullptr; - QScopedPointer<QWebEnginePage> newPage; - Page(QWebEngineProfile *profile) : QWebEnginePage(profile) {} - private: - QWebEnginePage *createWindow(WebWindowType) override - { - newPage.reset(new QWebEnginePage(targetProfile ? targetProfile : profile(), nullptr)); - return newPage.data(); - } - }; - QWebEngineProfile profile1, profile2; - profile1.setHttpUserAgent(QStringLiteral("custom 1")); - profile2.setHttpUserAgent(QStringLiteral("custom 2")); - Page page(&profile1); - QWebEngineView view; - view.resize(500, 500); - view.setPage(&page); - view.show(); + QString expectedUserAgent("custom 1"); + QWebEngineProfile profile; + profile.setHttpUserAgent(expectedUserAgent); + + PageWithNewWindowHandler page(&profile); + QWebEngineView view; view.resize(500, 500); view.setPage(&page); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); - QSignalSpy spy(&page, &QWebEnginePage::loadFinished); // First check we can get the user-agent passed through normally page.setHtml(QString("<html><body><a id='link' target='_blank' href='") + server.url("/test1").toEncoded() + QString("'>link</a></body></html>")); - QTRY_COMPARE(spy.count(), 1); - QVERIFY(spy.takeFirst().value(0).toBool()); - QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("navigator.userAgent")).toString(), profile1.httpUserAgent()); + 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.size(), 1); QTRY_VERIFY(!lastUserAgent.isEmpty()); - QCOMPARE(lastUserAgent, profile1.httpUserAgent().toUtf8()); + QCOMPARE(lastUserAgent, expectedUserAgent); + QCOMPARE(evaluateJavaScriptSync(page.newPage.get(), QStringLiteral("navigator.userAgent")).toString(), expectedUserAgent); // Now check we can get the new user-agent of the profile page.newPage.reset(); - page.targetProfile = &profile2; - spy.clear(); + expectedUserAgent = "custom 2"; + profile.setHttpUserAgent(expectedUserAgent); + page.loadSpy.clear(); lastUserAgent = { }; page.setHtml(QString("<html><body><a id='link' target='_blank' href='") + server.url("/test2").toEncoded() + QString("'>link</a></body></html>")); - QTRY_COMPARE(spy.count(), 1); - QVERIFY(spy.takeFirst().value(0).toBool()); + 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.size(), 1); QTRY_VERIFY(!lastUserAgent.isEmpty()); - QCOMPARE(lastUserAgent, profile2.httpUserAgent().toUtf8()); + QCOMPARE(lastUserAgent, expectedUserAgent); + QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("navigator.userAgent")).toString(), expectedUserAgent); + QCOMPARE(evaluateJavaScriptSync(page.newPage.get(), QStringLiteral("navigator.userAgent")).toString(), expectedUserAgent); +} + +void tst_QWebEnginePage::openNewTabInDifferentProfile_data() +{ + QTest::addColumn<bool>("handleInSignal"); + QTest::addRow("handleInSignal") << true; + QTest::addRow("handleInOverride") << false; +} + +void tst_QWebEnginePage::openNewTabInDifferentProfile() +{ + QFETCH(bool, handleInSignal); + + HttpServer server; + QStringList receivedRequests; + connect(&server, &HttpServer::newRequest, [&] (HttpReqRep *r) { + receivedRequests.append(r->requestPath()); + r->setResponseBody("DUMMY"); + r->sendResponse(); + }); + QVERIFY(server.start()); + + QWebEngineProfile profile1, profile2; + PageWithNewWindowHandler page(&profile1, handleInSignal, &profile2); + QWebEngineView view; view.setPage(&page); view.resize(320, 240); view.show(); + 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.size(), 1); + QVERIFY(page.loadSpy.takeFirst().value(0).toBool()); + + QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, elementCenter(&page, "link")); + QTRY_VERIFY(page.newPage); + QVERIFY(page.profile() == &profile1); + QVERIFY(page.newPage->profile() == &profile2); + // not load should occur or requests to server issued since web_contents is not expected to be adopted from other profile + QTRY_LOOP_IMPL(page.newPage->loadSpy.size() != 0, 1000, 100); + QVERIFY2(receivedRequests.isEmpty(), qPrintable(receivedRequests.join(", "))); } void tst_QWebEnginePage::renderProcessCrashed() @@ -4675,17 +5043,17 @@ void tst_QWebEnginePage::renderProcessPid() class FileSelectionTestPage : public QWebEnginePage { public: - FileSelectionTestPage() - { } + FileSelectionTestPage() : m_tempDir(QDir::tempPath() + "/tst_qwebenginepage-XXXXXX") { } QStringList chooseFiles(FileSelectionMode mode, const QStringList &oldFiles, const QStringList &acceptedMimeTypes) override { Q_UNUSED(oldFiles); chosenFileSelectionMode = mode; chosenAcceptedMimeTypes = acceptedMimeTypes; - return QStringList(); + return QStringList() << (m_tempDir.path() + "/file.txt"); } + QTemporaryDir m_tempDir; int chosenFileSelectionMode = -1; QStringList chosenAcceptedMimeTypes; }; @@ -4704,6 +5072,8 @@ void tst_QWebEnginePage::testChooseFilesParameters_data() << QWebEnginePage::FileSelectOpenMultiple << QStringList(); QTest::addRow("Folder upload") << QString("multiple webkitdirectory") << QString() << QWebEnginePage::FileSelectUploadFolder << QStringList(); + QTest::addRow("Save file") << QString("") << QString() + << QWebEnginePage::FileSelectSave << QStringList(); mimeTypes = QStringList() << "audio/*"; QTest::addRow("MIME type: audio") << QString() << QString("accept='%1'").arg(mimeTypes.join(',')) << QWebEnginePage::FileSelectOpen << mimeTypes; @@ -4737,11 +5107,18 @@ void tst_QWebEnginePage::testChooseFilesParameters() view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); - page.setHtml(QString("<html><body>" - "<input id='filePicker' type='file' name='filePicker' %1 %2 />" - "</body></html>").arg(uploadAttribute, mimeTypeAttribute)); + if (expectedFileSelectionMode != QWebEnginePage::FileSelectSave) { + page.setHtml(QString("<html><body>" + "<input id='filePicker' type='file' name='filePicker' %1 %2 />" + "</body></html>").arg(uploadAttribute, mimeTypeAttribute)); + } else { + page.setHtml(QString("<html><body>" + "<button id='filePicker' value='trigger' " + "onclick='window.showSaveFilePicker()'" + "</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")); @@ -4762,20 +5139,40 @@ void tst_QWebEnginePage::fileSystemAccessDialog() view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); - page.setHtml(QString("<html><body>" + connect(&page, &QWebEnginePage::fileSystemAccessRequested, + [](QWebEngineFileSystemAccessRequest request) { + QCOMPARE(request.accessFlags(), + QWebEngineFileSystemAccessRequest::Read + | QWebEngineFileSystemAccessRequest::Write); + request.accept(); + }); + + page.setHtml(QString("<html><head><script>" + "async function getTemporaryDir() {" + " const newHandle = await window.showSaveFilePicker();" + " const writable = await newHandle.createWritable();" + " await writable.write(new Blob(['New value']));" + " await writable.close();" + "" + " const fileData = await newHandle.getFile();" + " document.title = await fileData.text();" + "}" + "</script></head><body>" "<button id='triggerDialog' value='trigger' " - "onclick='window.showDirectoryPicker()'>" + "onclick='getTemporaryDir()'" "</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(), QStringLiteral("triggerDialog")); QTest::keyClick(view.focusProxy(), Qt::Key_Enter); - QTRY_COMPARE(page.chosenFileSelectionMode, QWebEnginePage::FileSelectUploadFolder); + QTRY_COMPARE(page.title(), "New value"); + + QTRY_COMPARE(page.chosenFileSelectionMode, QWebEnginePage::FileSelectSave); QTRY_COMPARE(page.chosenAcceptedMimeTypes, QStringList()); } @@ -4832,11 +5229,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)); } @@ -4846,9 +5243,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]; @@ -4875,8 +5272,8 @@ void tst_QWebEnginePage::isSafeRedirect_data() fileScheme += "/"; #endif - QString tempDir(fileScheme + QDir::tempPath()); - QTest::newRow(qPrintable(tempDir)) << QUrl(tempDir) << QUrl(tempDir + "/"); + QString tempDir(fileScheme + QDir::tempPath() + "/"); + QTest::newRow(qPrintable(tempDir)) << QUrl(tempDir) << QUrl(tempDir); QTest::newRow(qPrintable(tempDir + QString("/foo/bar"))) << QUrl(tempDir + "/foo/bar") << QUrl(tempDir + "/foo/bar"); QTest::newRow("filesystem:http://foo.com/bar") << QUrl("filesystem:http://foo.com/bar") << QUrl("filesystem:http://foo.com/bar/"); } @@ -4889,11 +5286,452 @@ 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(); } +class LocalRemoteUrlSchemeHandler : public QWebEngineUrlSchemeHandler +{ +public: + LocalRemoteUrlSchemeHandler(QObject *parent = nullptr) + : QWebEngineUrlSchemeHandler(parent) + { + } + ~LocalRemoteUrlSchemeHandler() = default; + + void requestStarted(QWebEngineUrlRequestJob *job) override + { + QBuffer *buffer = new QBuffer(job); + buffer->setData("<html><body><a href='remote://test.html' id='link'>Click link</a></body></html>"); + job->reply("text/html", buffer); + loaded = true; + } + bool loaded = false; +}; + +void tst_QWebEnginePage::localToRemoteNavigation() +{ + LocalRemoteUrlSchemeHandler local; + LocalRemoteUrlSchemeHandler remote; + QWebEngineProfile profile; + profile.installUrlSchemeHandler("local", &local); + profile.installUrlSchemeHandler("remote", &remote); + + QWebEnginePage page(&profile); + QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); + QWebEngineView view; + view.resize(640, 480); + view.show(); + view.setPage(&page); + page.setUrl(QUrl("local://test.html")); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + 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.size(), 2, 20000); + QVERIFY(remote.loaded); + local.loaded = false; + remote.loaded = false; + + page.setUrl(QUrl("local://test.html")); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 3, 20000); + QVERIFY(local.loaded && !remote.loaded); + + // Should not navigate: + page.runJavaScript(QStringLiteral("document.getElementById(\"link\").click()")); + QTest::qWait(500); + QVERIFY(!remote.loaded); +} + +void tst_QWebEnginePage::clientHints_data() +{ + QTest::addColumn<bool>("clientHintsEnabled"); + QTest::addColumn<QString>("arch"); + QTest::addColumn<QString>("platform"); + QTest::addColumn<QString>("model"); + QTest::addColumn<bool>("isMobile"); + QTest::addColumn<QString>("fullVersion"); + QTest::addColumn<QString>("platformVersion"); + QTest::addColumn<QString>("bitness"); + QTest::addColumn<bool>("isWOW64"); + QTest::addColumn<QHash<QString, QString>>("fullVersionList"); + + QTest::newRow("Modify values") << true << "Abc" << "AmigaOS" << "Ultra" << true << "1.99" << "3" << "x64" << true << QHash<QString, QString>({{"APITest", "1.0.0"}, {"App", "5.0"}}); + QTest::newRow("Empty values") << true << "" << "" << "" << false << "" << "" << "" << false << QHash<QString, QString>(); + QTest::newRow("Disable headers") << false << "" << "" << "" << false << "" << "" << "" << false << QHash<QString, QString>(); +} + +void tst_QWebEnginePage::clientHints() +{ + QFETCH(bool, clientHintsEnabled); + QFETCH(QString, arch); + QFETCH(QString, platform); + QFETCH(QString, model); + QFETCH(bool, isMobile); + QFETCH(QString, fullVersion); + QFETCH(QString, platformVersion); + QFETCH(QString, bitness); + QFETCH(bool, isWOW64); + typedef QHash<QString, QString> brandVersionPairs; + QFETCH(brandVersionPairs, fullVersionList); + + QWebEnginePage page; + QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); + + QWebEngineClientHints *clientHints = page.profile()->clientHints(); + clientHints->setAllClientHintsEnabled(clientHintsEnabled); + + HttpServer server; + int requestCount = 0; + connect(&server, &HttpServer::newRequest, [&] (HttpReqRep *r) { + // Platform and Mobile hints are always sent and can't be disabled with this API + QVERIFY(r->hasRequestHeader("Sec-CH-UA-Platform")); + QVERIFY(r->hasRequestHeader("Sec-CH-UA-Mobile")); + if (!clientHintsEnabled) { + QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Arch")); + QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Model")); + QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Full-Version")); + QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Platform-Version")); + QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Bitness")); + QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Wow64")); + QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Full-Version-List")); + } + + // The first request header won't contain any hints, only after a response with "Accept-CH" + if (requestCount > 1 && clientHintsEnabled) { + // All hint values are lower case in the headers + QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Arch")).remove("\""), arch.toLower()); + QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Platform")).remove("\""), platform.toLower()); + QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Model")).remove("\""), model.toLower()); + QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Mobile")).remove("\""), isMobile ? "?1" : "?0"); + QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Full-Version")).remove("\""), fullVersion.toLower()); + QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Platform-Version")).remove("\""), platformVersion.toLower()); + QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Bitness")).remove("\""), bitness.toLower()); + QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Wow64")).remove("\""), isWOW64 ? "?1" : "?0"); + for (auto i = fullVersionList.cbegin(), end = fullVersionList.cend(); i != end; ++i) + QVERIFY(QString(r->requestHeader("Sec-CH-UA-Full-Version-List")).contains(i.key().toLower())); + } + + r->setResponseHeader("Accept-CH", "Sec-CH-UA-Arch, Sec-CH-UA-Bitness, Sec-CH-UA-Full-Version, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform-Version, Sec-CH-UA-Platform, Sec-CH-UA-Wow64, Sec-CH-UA"); + r->sendResponse(); + requestCount++; + }); + QVERIFY(server.start()); + + clientHints->setArch(arch); + clientHints->setPlatform(platform); + clientHints->setModel(model); + clientHints->setIsMobile(isMobile); + clientHints->setFullVersion(fullVersion); + clientHints->setPlatformVersion(platformVersion); + clientHints->setBitness(bitness); + clientHints->setIsWow64(isWOW64); + clientHints->setFullVersionList(fullVersionList); + + page.setUrl(server.url()); + QTRY_COMPARE(loadSpy.size(), 1); + QVERIFY(loadSpy.takeFirst().value(0).toBool()); + + QCOMPARE(clientHints->arch(), arch); + QCOMPARE(clientHints->platform(), platform); + QCOMPARE(clientHints->model(), model); + QCOMPARE(clientHints->isMobile(), isMobile); + QCOMPARE(clientHints->fullVersion(), fullVersion); + QCOMPARE(clientHints->platformVersion(), platformVersion); + QCOMPARE(clientHints->bitness(), bitness); + QCOMPARE(clientHints->isWow64(), isWOW64); + for (auto i = fullVersionList.cbegin(), end = fullVersionList.cend(); i != end; ++i) + QCOMPARE(clientHints->fullVersionList()[i.key()], i.value()); + + // A new user agent string should not override/disable client hints + page.profile()->setHttpUserAgent(QStringLiteral("Custom user agent")); + page.triggerAction(QWebEnginePage::Reload); + QTRY_COMPARE(loadSpy.size(), 1); + + // Reset all to default values + clientHints->resetAll(); + QCOMPARE_NE(clientHints->arch(), arch); +#ifdef Q_OS_LINUX + QCOMPARE(clientHints->platform().toLower(), "linux"); +#elif defined (Q_OS_MACOS) + QCOMPARE(clientHints->platform().toLower(), "macos"); +#elif defined (Q_OS_WIN) + QCOMPARE(clientHints->platform().toLower(), "windows"); +#endif + QCOMPARE_NE(clientHints->fullVersion(), fullVersion); + QCOMPARE_NE(clientHints->platformVersion(), platformVersion); + QCOMPARE_NE(clientHints->bitness(), bitness); + for (auto i = fullVersionList.cbegin(), end = fullVersionList.cend(); i != end; ++i) + QVERIFY(!clientHints->fullVersionList().contains(i.key())); + QVERIFY(clientHints->fullVersionList().contains("Chromium")); +} + +void tst_QWebEnginePage::childFrameInput() +{ + HttpServer server; + server.setHostDomain("localhost"); + + // The cross-origin policy blocks scripting this frame with QWebEnginePage::runJavaScript. + // Use console messages to validate events. + QString innerHtml( + "<html><head><style>body{height:1200px;width:1200px;}</style></head><body>test<script>" + " let lastX, lastY = 0;" + " document.onscroll = (e) => {" + " if (window.scrollY > lastY) console.log(\"Down\");" + " if (window.scrollY < lastY) console.log(\"Up\");" + " if (window.scrollX > lastX) console.log(\"Right\");" + " if (window.scrollX < lastX) console.log(\"Left\");" + " lastX = window.scrollX;" + " lastY = window.scrollY;" + " };" + " window.onload = () => {console.log('loaded');};" + "</script></body></html>"); + + QVERIFY(server.start()); + connect(&server, &HttpServer::newRequest, [&](HttpReqRep *rr) { + if (rr->requestPath() == "/main.html") { + // the Origin-Agent-Cluster header enables dedicated processes for origins + rr->setResponseHeader("Origin-Agent-Cluster", "?1"); + // the same-site-cross-origin page forces to create the frame in a different process + server.setHostDomain("sub.localhost"); + rr->setResponseBody(("<html><body>" + "<iframe id=\"iframe\" width=90% height=90% src=\"" + + server.url().toString().toUtf8() + + "inner.html\"></iframe>" + "</body></html>")); + } + if (rr->requestPath() == "/inner.html") + rr->setResponseBody(innerHtml.toUtf8()); + rr->sendResponse(); + }); + + QWebEngineView view; + ConsolePage page; + view.setPage(&page); + view.resize(640, 480); + QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); + page.load(server.url("/main.html")); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); + + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + QTRY_VERIFY(evaluateJavaScriptSync(&page, "window.originAgentCluster").toBool()); + + // make sure the frame is loaded + QTRY_COMPARE(page.messages.size(), 1); + QTRY_COMPARE(page.messages[0], QString("loaded")); + + // focus + evaluateJavaScriptSync(&page, "document.getElementById('iframe').contentWindow.focus()"); + QTRY_COMPARE(evaluateJavaScriptSync(&page, "document.activeElement.id").toString(), + QStringLiteral("iframe")); + + QPoint globalPos = view.windowHandle()->position(); + QPoint p = elementCenter(&page, QString("iframe")); + + // Even if the document is loaded, it is not necessarily drawn. + // Hit-testing (in Viz) for pointer events will be flacky in this scenario. + // Send keyClick events first so the target frame will be cached for wheel events. + QTest::keyClick(view.focusProxy(), Qt::Key_Down); + QTRY_COMPARE(page.messages.size(), 2); + QTRY_COMPARE(page.messages[1], QString("Down")); + + QTest::keyClick(view.focusProxy(), Qt::Key_Up); + QTRY_COMPARE(page.messages.size(), 3); + QTRY_COMPARE(page.messages[2], QString("Up")); + + QTest::keyClick(view.focusProxy(), Qt::Key_Right); + QTRY_COMPARE(page.messages.size(), 4); + QTRY_COMPARE(page.messages[3], QString("Right")); + + QTest::keyClick(view.focusProxy(), Qt::Key_Left); + QTRY_COMPARE(page.messages.size(), 5); + QTRY_COMPARE(page.messages[4], QString("Left")); + + makeScroll(view.focusProxy(), p, globalPos, QPoint(0, -120)); + QTRY_COMPARE(page.messages.size(), 6); + QTRY_COMPARE(page.messages[5], QString("Down")); + + makeScroll(view.focusProxy(), p, globalPos, QPoint(0, 120)); + QTRY_COMPARE(page.messages.size(), 7); + QTRY_COMPARE(page.messages[6], QString("Up")); + + makeScroll(view.focusProxy(), p, globalPos, QPoint(-120, 0)); + QTRY_COMPARE(page.messages.size(), 8); + QTRY_COMPARE(page.messages[7], QString("Right")); + + makeScroll(view.focusProxy(), p, globalPos, QPoint(120, 0)); + QTRY_COMPARE(page.messages.size(), 9); + QTRY_COMPARE(page.messages[8], QString("Left")); +} + +void tst_QWebEnginePage::openLinkInNewPageWithWebWindowType_data() +{ + QTest::addColumn<QWebEnginePage::WebWindowType>("webWindowType"); + QTest::addColumn<QString>("elementId"); + QTest::addColumn<Qt::MouseButton>("button"); + QTest::addColumn<Qt::KeyboardModifier>("keyboardModififer"); + QTest::newRow("webBrowserWindow") + << QWebEnginePage::WebBrowserWindow << "link" << Qt::LeftButton << Qt::ShiftModifier; + QTest::newRow("webBrowserTab") + << QWebEnginePage::WebBrowserTab << "link" << Qt::LeftButton << Qt::NoModifier; + QTest::newRow("webDialog") << QWebEnginePage::WebDialog << "openWindow" << Qt::LeftButton + << Qt::NoModifier; + QTest::newRow("webBrowserBackgroundTab") << QWebEnginePage::WebBrowserBackgroundTab << "link" + << Qt::MiddleButton << Qt::NoModifier; +} + +class WebWindowTypeTestPage : public QWebEnginePage +{ + Q_OBJECT + +public: + WebWindowType windowType; + +signals: + void windowCreated(); + +private: + QWebEnginePage *createWindow(WebWindowType type) override + { + windowType = type; + emit windowCreated(); + return nullptr; + } +}; + +void tst_QWebEnginePage::openLinkInNewPageWithWebWindowType() +{ + QFETCH(QWebEnginePage::WebWindowType, webWindowType); + QFETCH(QString, elementId); + QFETCH(Qt::MouseButton, button); + QFETCH(Qt::KeyboardModifier, keyboardModififer); + + WebWindowTypeTestPage page; + QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); + QSignalSpy windowCreatedSpy(&page, &WebWindowTypeTestPage::windowCreated); + QWebEngineView view(&page); + view.resize(640, 480); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + page.settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, true); + page.settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true); + QString html = "<html><body>" + "<a id='link' href='hello' target='_blank'>link</a>" + "<br><br>" + "<button id='openWindow' onclick='myFunction()'>Try it</button>" + "<script>" + "function myFunction() {" + " const myWindow = window.open('', '', 'width=300,height=300');" + "}" + "</script>" + "</body></html>"; + + page.setHtml(html); + QVERIFY(loadFinishedSpy.wait()); + + QTest::mouseClick(view.focusProxy(), button, keyboardModififer, + elementCenter(&page, elementId)); + QVERIFY(windowCreatedSpy.wait()); + QCOMPARE(page.windowType, webWindowType); +} + +class DoNothingInterceptor : public QWebEngineUrlRequestInterceptor +{ +public: + DoNothingInterceptor() { } + + void interceptRequest(QWebEngineUrlRequestInfo &) override + { + ran = true; + } + bool ran = false; +}; + +void tst_QWebEnginePage::keepInterceptorAfterNewWindowRequested() +{ + DoNothingInterceptor interceptor; + QWebEnginePage page; + page.setUrlRequestInterceptor(&interceptor); + connect(&page, &QWebEnginePage::newWindowRequested, [&](QWebEngineNewWindowRequest &request) { + request.openIn(&page); + }); + QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); + + QWebEngineView view; + view.resize(500, 500); + view.setPage(&page); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + page.setHtml("<html><body>" + "<a id='link' href='hello' target='_blank'>link</a>" + "</body></html>"); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + QVERIFY(loadFinishedSpy.takeFirst().value(0).toBool()); + QVERIFY(interceptor.ran); + interceptor.ran = false; + + QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, elementCenter(&page, "link")); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + QVERIFY(loadFinishedSpy.takeFirst().value(0).toBool()); + QVERIFY(!interceptor.ran); + + page.setHtml("<html><body></body></html>"); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + QVERIFY(loadFinishedSpy.takeFirst().value(0).toBool()); + QVERIFY(interceptor.ran); +} + +void tst_QWebEnginePage::chooseDesktopMedia() +{ +#if QT_CONFIG(webengine_extensions) && QT_CONFIG(webengine_webrtc) + HttpServer server; + server.setHostDomain("localhost"); + connect(&server, &HttpServer::newRequest, &server, [&] (HttpReqRep *r) { + if (r->requestMethod() == "GET") + r->setResponseBody("<html></html>"); + }); + QVERIFY(server.start()); + + QWebEnginePage page; + QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); + page.settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true); + + bool desktopMediaRequested = false; + bool permissionRequested = false; + + connect(&page, &QWebEnginePage::desktopMediaRequested, + [&](const QWebEngineDesktopMediaRequest &) { + desktopMediaRequested = true; + }); + + connect(&page, &QWebEnginePage::featurePermissionRequested, + [&](const QUrl &securityOrigin, QWebEnginePage::Feature feature) { + permissionRequested = true; + // Handle permission to 'complete' the media request + page.setFeaturePermission(securityOrigin, feature, + QWebEnginePage::PermissionGrantedByUser); + }); + + page.load(QUrl(server.url())); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 20000); + + const QString extensionId("nkeimhogjdpnpccoofpliimaahmaaome"); + page.runJavaScript(QString("(() => {" + " let port = chrome.runtime.connect(\"%1\", {name: \"chooseDesktopMedia\"});" + " port.postMessage({method: \"chooseDesktopMedia\"});" + "})()").arg(extensionId)); + + QTRY_VERIFY(desktopMediaRequested); + QTRY_VERIFY(permissionRequested); +#endif // QT_CONFIG(webengine_extensions) && QT_CONFIG(webengine_webrtc) +} + static QByteArrayList params = {QByteArrayLiteral("--use-fake-device-for-media-stream")}; W_QTEST_MAIN(tst_QWebEnginePage, params) diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc deleted file mode 100644 index 3480341e8..000000000 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc +++ /dev/null @@ -1,32 +0,0 @@ -<!DOCTYPE RCC><RCC version="1.0"> -<qresource> - <file>resources/content.html</file> - <file>resources/dynamicFrame.html</file> - <file>resources/index.html</file> - <file>resources/frame_a.html</file> - <file>resources/frame_c.html</file> - <file>resources/iframe.html</file> - <file>resources/iframe2.html</file> - <file>resources/iframe3.html</file> - <file>resources/framedindex.html</file> - <file>resources/fullscreen.html</file> - <file>resources/script.html</file> - <file>resources/user.css</file> - <file>resources/image.png</file> - <file>resources/pasteimage.html</file> - <file>resources/redirect.html</file> - <file>resources/reload.html</file> - <file>resources/style.css</file> - <file>resources/test1.html</file> - <file>resources/test2.html</file> - <file>resources/testiframe.html</file> - <file>resources/testiframe2.html</file> - <file>resources/foo.txt</file> - <file>resources/bar.txt</file> - <file>resources/path with spaces.txt</file> - <file>resources/lifecycle.html</file> -</qresource> -<qresource prefix='/shared'> - <file alias='notification.html'>../../shared/data/notification.html</file> -</qresource> -</RCC> diff --git a/tests/auto/widgets/qwebengineprofile/CMakeLists.txt b/tests/auto/widgets/qwebengineprofile/CMakeLists.txt index 744f44405..d7393eaef 100644 --- a/tests/auto/widgets/qwebengineprofile/CMakeLists.txt +++ b/tests/auto/widgets/qwebengineprofile/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../httpserver/httpserver.cmake) include(../../util/util.cmake) diff --git a/tests/auto/widgets/qwebengineprofile/qwebengineprofile.pro b/tests/auto/widgets/qwebengineprofile/qwebengineprofile.pro deleted file mode 100644 index ca16cee39..000000000 --- a/tests/auto/widgets/qwebengineprofile/qwebengineprofile.pro +++ /dev/null @@ -1,4 +0,0 @@ -include(../tests.pri) -include(../../shared/http.pri) -exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc -QT *= core-private gui-private diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp index 38f3b04e7..095d4c8f2 100644 --- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp +++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 <util.h> #include <QtCore/qbuffer.h> @@ -57,15 +32,16 @@ class tst_QWebEngineProfile : public QObject private Q_SLOTS: void initTestCase(); - void userDefaultProfile(); + void defaultProfile_data(); void defaultProfile(); - void testProfile(); + void userDefinedProfile(); void clearDataFromCache(); void disableCache(); void urlSchemeHandlers(); void urlSchemeHandlerFailRequest(); void urlSchemeHandlerFailOnRead(); void urlSchemeHandlerStreaming(); + void urlSchemeHandlerStreaming2(); void urlSchemeHandlerRequestHeaders(); void urlSchemeHandlerInstallation(); void urlSchemeHandlerXhrStatus(); @@ -103,32 +79,28 @@ void tst_QWebEngineProfile::initTestCase() QWebEngineUrlScheme::registerScheme(myscheme); } -void tst_QWebEngineProfile::userDefaultProfile() +static QString StandardCacheLocation() { static auto p = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); return p; } +static QString StandardAppDataLocation() { static auto p = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); return p; } + +void tst_QWebEngineProfile::defaultProfile_data() { - QWebEngineProfile profile("Default"); - QVERIFY(!profile.isOffTheRecord()); - QCOMPARE(profile.storageName(), QStringLiteral("Default")); - QCOMPARE(profile.httpCacheType(), QWebEngineProfile::DiskHttpCache); - QCOMPARE(profile.persistentCookiesPolicy(), QWebEngineProfile::AllowPersistentCookies); - QCOMPARE(profile.cachePath(), - QStandardPaths::writableLocation(QStandardPaths::CacheLocation) - + QStringLiteral("/QtWebEngine/Default")); - QCOMPARE(profile.persistentStoragePath(), - QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) - + QStringLiteral("/QtWebEngine/Default")); + QTest::addColumn<bool>("userCreated"); + QTest::addRow("global") << false; + QTest::addRow("user") << true; } void tst_QWebEngineProfile::defaultProfile() { - QWebEngineProfile *profile = QWebEngineProfile::defaultProfile(); + QFETCH(bool, userCreated); + QScopedPointer<QWebEngineProfile> p(userCreated ? new QWebEngineProfile : nullptr); + QWebEngineProfile *profile = userCreated ? p.get() : QWebEngineProfile::defaultProfile(); QVERIFY(profile); QVERIFY(profile->isOffTheRecord()); + QCOMPARE(profile->storageName(), QString()); QCOMPARE(profile->httpCacheType(), QWebEngineProfile::MemoryHttpCache); QCOMPARE(profile->persistentCookiesPolicy(), QWebEngineProfile::NoPersistentCookies); QCOMPARE(profile->cachePath(), QString()); - QCOMPARE(profile->persistentStoragePath(), - QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) - + QStringLiteral("/QtWebEngine/OffTheRecord")); + QCOMPARE(profile->persistentStoragePath(), StandardAppDataLocation() + QStringLiteral("/QtWebEngine/OffTheRecord")); // TBD: setters do not really work profile->setCachePath(QStringLiteral("/home/foo/bar")); QCOMPARE(profile->cachePath(), QString()); @@ -140,18 +112,15 @@ void tst_QWebEngineProfile::defaultProfile() QCOMPARE(profile->persistentCookiesPolicy(), QWebEngineProfile::NoPersistentCookies); } - -void tst_QWebEngineProfile::testProfile() +void tst_QWebEngineProfile::userDefinedProfile() { QWebEngineProfile profile(QStringLiteral("Test")); QVERIFY(!profile.isOffTheRecord()); QCOMPARE(profile.storageName(), QStringLiteral("Test")); QCOMPARE(profile.httpCacheType(), QWebEngineProfile::DiskHttpCache); QCOMPARE(profile.persistentCookiesPolicy(), QWebEngineProfile::AllowPersistentCookies); - QCOMPARE(profile.cachePath(), QStandardPaths::writableLocation(QStandardPaths::CacheLocation) - + QStringLiteral("/QtWebEngine/Test")); - QCOMPARE(profile.persistentStoragePath(), QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) - + QStringLiteral("/QtWebEngine/Test")); + QCOMPARE(profile.cachePath(), StandardCacheLocation() + QStringLiteral("/QtWebEngine/Test")); + QCOMPARE(profile.persistentStoragePath(), StandardAppDataLocation() + QStringLiteral("/QtWebEngine/Test")); } class AutoDir : public QDir @@ -221,6 +190,7 @@ void tst_QWebEngineProfile::clearDataFromCache() AutoDir cacheDir("./tst_QWebEngineProfile_clearDataFromCache"); QWebEngineProfile profile(QStringLiteral("clearDataFromCache")); + QSignalSpy cacheSpy(&profile, &QWebEngineProfile::clearHttpCacheCompleted); profile.setCachePath(cacheDir.path()); profile.setHttpCacheType(QWebEngineProfile::DiskHttpCache); @@ -232,8 +202,7 @@ void tst_QWebEngineProfile::clearDataFromCache() QVERIFY(cacheDir.exists("Cache")); qint64 sizeBeforeClear = totalSize(cacheDir); profile.clearHttpCache(); - // Wait for cache to be cleared. - QTest::qWait(1000); + QTRY_COMPARE(cacheSpy.size(), 1); QVERIFY(sizeBeforeClear > totalSize(cacheDir)); (void)server.stop(); @@ -296,10 +265,12 @@ public: QList<QPointer<QBuffer>> m_buffers; }; -class StreamingIODevice : public QIODevice { +// an evil version constantly claiming to be at end, similar to QNetworkReply +class StreamingIODeviceBasic : public QIODevice +{ Q_OBJECT public: - StreamingIODevice(QObject *parent) : QIODevice(parent), m_bytesRead(0), m_bytesAvailable(0) + StreamingIODeviceBasic(QObject *parent) : QIODevice(parent), m_bytesRead(0), m_bytesAvailable(0) { setOpenMode(QIODevice::ReadOnly); m_timer.start(100, this); @@ -310,12 +281,11 @@ public: const std::lock_guard<QRecursiveMutex> lock(m_mutex); return m_bytesAvailable; } - bool atEnd() const override +protected: + bool internalAtEnd() const { - const std::lock_guard<QRecursiveMutex> lock(m_mutex); return (m_data.size() >= 1000 && m_bytesRead >= 1000); } -protected: void timerEvent(QTimerEvent *) override { const std::lock_guard<QRecursiveMutex> lock(m_mutex); @@ -336,7 +306,7 @@ protected: memcpy(data, m_data.constData() + m_bytesRead, len); m_bytesAvailable -= len; m_bytesRead += len; - } else if (atEnd()) + } else if (internalAtEnd()) return -1; return len; @@ -346,14 +316,26 @@ protected: return 0; } -private: mutable QRecursiveMutex m_mutex; +private: QByteArray m_data; QBasicTimer m_timer; int m_bytesRead; int m_bytesAvailable; }; +// A nicer version implementing atEnd +class StreamingIODevice : public StreamingIODeviceBasic +{ +public: + StreamingIODevice(QObject *parent) : StreamingIODeviceBasic(parent) {} + bool atEnd() const override + { + const std::lock_guard<QRecursiveMutex> lock(m_mutex); + return internalAtEnd(); + } +}; + class StreamingUrlSchemeHandler : public QWebEngineUrlSchemeHandler { public: @@ -368,6 +350,20 @@ public: } }; +class StreamingUrlSchemeHandler2 : public QWebEngineUrlSchemeHandler +{ +public: + StreamingUrlSchemeHandler2(QObject *parent = nullptr) + : QWebEngineUrlSchemeHandler(parent) + { + } + + void requestStarted(QWebEngineUrlRequestJob *job) override + { + job->reply("text/plain;charset=utf-8", new StreamingIODeviceBasic(job)); + } +}; + void tst_QWebEngineProfile::urlSchemeHandlers() { RedirectingUrlSchemeHandler lettertoHandler; @@ -411,8 +407,8 @@ void tst_QWebEngineProfile::urlSchemeHandlers() QCOMPARE(toPlainTextSync(view.page()), url.toString()); // Check that all buffers got deleted - QCOMPARE(gopherHandler.m_buffers.count(), 2); - for (int i = 0; i < gopherHandler.m_buffers.count(); ++i) + QCOMPARE(gopherHandler.m_buffers.size(), 2); + for (int i = 0; i < gopherHandler.m_buffers.size(); ++i) QVERIFY(gopherHandler.m_buffers.at(i).isNull()); } @@ -473,7 +469,7 @@ void tst_QWebEngineProfile::urlSchemeHandlerFailRequest() view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); view.load(QUrl(QStringLiteral("foo://bar"))); view.show(); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); QCOMPARE(toPlainTextSync(view.page()), QString()); } @@ -488,7 +484,7 @@ void tst_QWebEngineProfile::urlSchemeHandlerFailOnRead() view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); view.load(QUrl(QStringLiteral("foo://bar"))); view.show(); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); QCOMPARE(toPlainTextSync(view.page()), QString()); } @@ -503,7 +499,24 @@ void tst_QWebEngineProfile::urlSchemeHandlerStreaming() view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); view.load(QUrl(QStringLiteral("stream://whatever"))); view.show(); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); + QByteArray result; + result.append(1000, 'c'); + QCOMPARE(toPlainTextSync(view.page()), QString::fromLatin1(result)); +} + +void tst_QWebEngineProfile::urlSchemeHandlerStreaming2() +{ + StreamingUrlSchemeHandler2 handler; + QWebEngineProfile profile; + profile.installUrlSchemeHandler("stream", &handler); + QWebEngineView view; + QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); + view.setPage(new QWebEnginePage(&profile, &view)); + view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + view.load(QUrl(QStringLiteral("stream://whatever"))); + view.show(); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); QByteArray result; result.append(1000, 'c'); QCOMPARE(toPlainTextSync(view.page()), QString::fromLatin1(result)); @@ -564,7 +577,7 @@ void tst_QWebEngineProfile::urlSchemeHandlerRequestHeaders() QWebEnginePage page(&profile); QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); page.load(QUrl(QStringLiteral("myscheme://whatever"))); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); } void tst_QWebEngineProfile::urlSchemeHandlerInstallation() @@ -730,12 +743,12 @@ void tst_QWebEngineProfile::urlSchemeHandlerScriptModule() QWebEnginePage page(&profile); QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); page.setHtml(QStringLiteral("<html><head><script src=\"aviancarrier:///\"></script></head><body>Test1</body></html>")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("test")).toString(), QStringLiteral("SUCCESS")); loadFinishedSpy.clear(); page.setHtml(QStringLiteral("<html><head><script type=\"module\" src=\"aviancarrier:///\"></script></head><body>Test2</body></html>")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("test")).toString(), QStringLiteral("SUCCESS")); } @@ -770,7 +783,7 @@ void tst_QWebEngineProfile::customUserAgent() QWebEnginePage page; QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); page.setHtml(QStringLiteral("<html><body>Hello world!</body></html>")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); // First test the user-agent is default QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("navigator.userAgent")).toString(), defaultUserAgent); @@ -783,7 +796,7 @@ void tst_QWebEngineProfile::customUserAgent() QWebEnginePage page2(&testProfile); QSignalSpy loadFinishedSpy2(&page2, SIGNAL(loadFinished(bool))); page2.setHtml(QStringLiteral("<html><body>Hello again!</body></html>")); - QTRY_COMPARE(loadFinishedSpy2.count(), 1); + QTRY_COMPARE(loadFinishedSpy2.size(), 1); QCOMPARE(evaluateJavaScriptSync(&page2, QStringLiteral("navigator.userAgent")).toString(), testUserAgent); QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("navigator.userAgent")).toString(), defaultUserAgent); @@ -797,7 +810,7 @@ void tst_QWebEngineProfile::httpAcceptLanguage() QWebEnginePage page; QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); page.setHtml(QStringLiteral("<html><body>Hello world!</body></html>")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QStringList defaultLanguages = evaluateJavaScriptSync(&page, QStringLiteral("navigator.languages")).toStringList(); @@ -809,7 +822,7 @@ void tst_QWebEngineProfile::httpAcceptLanguage() QWebEnginePage page2(&testProfile); QSignalSpy loadFinishedSpy2(&page2, SIGNAL(loadFinished(bool))); page2.setHtml(QStringLiteral("<html><body>Hello again!</body></html>")); - QTRY_COMPARE(loadFinishedSpy2.count(), 1); + QTRY_COMPARE(loadFinishedSpy2.size(), 1); QCOMPARE(evaluateJavaScriptSync(&page2, QStringLiteral("navigator.languages")).toStringList(), QStringList(testLang)); // Test the old one wasn't affected QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("navigator.languages")).toStringList(), defaultLanguages); @@ -826,7 +839,7 @@ void tst_QWebEngineProfile::downloadItem() QWebEnginePage page(&testProfile); QSignalSpy downloadSpy(&testProfile, SIGNAL(downloadRequested(QWebEngineDownloadRequest *))); page.load(QUrl::fromLocalFile(QCoreApplication::applicationFilePath())); - QTRY_COMPARE(downloadSpy.count(), 1); + QTRY_COMPARE(downloadSpy.size(), 1); } void tst_QWebEngineProfile::changePersistentPath() @@ -952,29 +965,29 @@ void tst_QWebEngineProfile::initiator() QWebEnginePage page(&profile, nullptr); QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); page.load(QUrl("about:blank")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); loadFinishedSpy.clear(); // about:blank has a unique origin, so initiator should be QUrl("null") evaluateJavaScriptSync(&page, "window.location = 'foo:bar'"); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); loadFinishedSpy.clear(); QCOMPARE(handler.initiator, QUrl("null")); page.setHtml("", QUrl("http://test:123/foo%20bar")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); loadFinishedSpy.clear(); // baseUrl determines the origin, so QUrl("http://test:123") evaluateJavaScriptSync(&page, "window.location = 'foo:bar'"); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); loadFinishedSpy.clear(); QCOMPARE(handler.initiator, QUrl("http://test:123")); // Directly calling load/setUrl should have initiator QUrl(), meaning // browser-initiated, trusted. page.load(QUrl("foo:bar")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 10000); QCOMPARE(handler.initiator, QUrl()); } @@ -990,7 +1003,7 @@ void tst_QWebEngineProfile::badDeleteOrder() QSignalSpy spyLoadFinished(page, SIGNAL(loadFinished(bool))); page->setHtml(QStringLiteral("<html><body><h1>Badly handled page!</h1></body></html>")); - QTRY_COMPARE(spyLoadFinished.count(), 1); + QTRY_COMPARE(spyLoadFinished.size(), 1); delete profile; delete view; @@ -1006,7 +1019,7 @@ void tst_QWebEngineProfile::qtbug_71895() view.page()->profile()->setHttpCacheType(QWebEngineProfile::NoCache); view.page()->profile()->cookieStore()->deleteAllCookies(); view.page()->profile()->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies); - bool gotSignal = loadSpy.count() || loadSpy.wait(20000); + bool gotSignal = loadSpy.size() || loadSpy.wait(20000); if (!gotSignal) QSKIP("Couldn't load page from network, skipping test."); } diff --git a/tests/auto/widgets/qwebenginescript/CMakeLists.txt b/tests/auto/widgets/qwebenginescript/CMakeLists.txt index 6e768cf90..d0d499b84 100644 --- a/tests/auto/widgets/qwebenginescript/CMakeLists.txt +++ b/tests/auto/widgets/qwebenginescript/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../util/util.cmake) qt_internal_add_test(tst_qwebenginescript diff --git a/tests/auto/widgets/qwebenginescript/qwebenginescript.pro b/tests/auto/widgets/qwebenginescript/qwebenginescript.pro deleted file mode 100644 index e99c7f493..000000000 --- a/tests/auto/widgets/qwebenginescript/qwebenginescript.pro +++ /dev/null @@ -1 +0,0 @@ -include(../tests.pri) 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 ca2f0cab9..9ba13589f 100644 --- a/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp +++ b/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp @@ -70,11 +70,13 @@ private Q_SLOTS: void webChannelWithExistingQtObject(); void navigation(); void webChannelWithBadString(); + void webChannelWithJavaScriptDisabled(); #endif void noTransportWithoutWebChannel(); void scriptsInNestedIframes(); void matchQrcUrl(); void injectionOrder(); + void reloadWithSubframes(); }; void tst_QWebEngineScript::domEditing() @@ -183,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())); @@ -191,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())); @@ -209,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())); @@ -217,9 +219,9 @@ void tst_QWebEngineScript::loadEvents() // Using window.open from JS QVERIFY(profile.pages.size() == 1); page.load(QUrl("qrc:/resources/test_window_open.html")); - QTRY_COMPARE(profile.pages.size(), 2); - QTRY_COMPARE(profile.pages.front().spy.count(), 1); - QTRY_COMPARE(profile.pages.back().spy.count(), 1); + QTRY_COMPARE(profile.pages.size(), 2u); + 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())); @@ -270,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()); @@ -279,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()); @@ -297,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)); } @@ -456,7 +458,7 @@ void tst_QWebEngineScript::scriptsInNestedIframes() QSignalSpy spyFinished(&page, &QWebEnginePage::loadFinished); page.load(QUrl("qrc:/resources/test_iframe_main.html")); view.show(); - QVERIFY(spyFinished.wait()); + QTRY_VERIFY_WITH_TIMEOUT(spyFinished.size() > 0, 20000); // Check that main frame has modified content. QCOMPARE( @@ -556,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); } @@ -592,6 +594,36 @@ void tst_QWebEngineScript::webChannelWithBadString() QChar data(0xd800); QCOMPARE(host.text(), data); } + +void tst_QWebEngineScript::webChannelWithJavaScriptDisabled() +{ + QWebEnginePage page; + QSignalSpy spyFinished(&page, &QWebEnginePage::loadFinished); + // JavaScript disabled in main world + page.settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, false); + + TestObject testObject; + QScopedPointer<QWebChannel> channel(new QWebChannel(this)); + channel->registerObject(QStringLiteral("object"), &testObject); + page.setWebChannel(channel.data(), QWebEngineScript::ApplicationWorld); + + QWebEngineScript script = webChannelScript(); + script.setWorldId(QWebEngineScript::ApplicationWorld); + page.scripts().insert(script); + + page.setHtml(QStringLiteral("<html><body></body></html>")); + QVERIFY(spyFinished.wait()); + + QSignalSpy spyTextChanged(&testObject, &TestObject::textChanged); + page.runJavaScript(QLatin1String( + "new QWebChannel(qt.webChannelTransport," + " function(channel) {" + " channel.objects.object.text = 'test';" + " }" + ");"), QWebEngineScript::ApplicationWorld); + QVERIFY(spyTextChanged.wait()); + QCOMPARE(testObject.text(), QStringLiteral("test")); +} #endif void tst_QWebEngineScript::matchQrcUrl() @@ -663,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/qwebenginescript/tst_qwebenginescript.qrc b/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.qrc deleted file mode 100644 index 3290cb588..000000000 --- a/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.qrc +++ /dev/null @@ -1,11 +0,0 @@ -<!DOCTYPE RCC><RCC version="1.0"> -<qresource> - <file>resources/test_iframe_main.html</file> - <file>resources/test_iframe_outer.html</file> - <file>resources/test_iframe_inner.html</file> - <file>resources/test_window_open.html</file> - <file>resources/title_a.html</file> - <file>resources/title_b.html</file> - <file>resources/webChannelWithBadString.html</file> -</qresource> -</RCC> diff --git a/tests/auto/widgets/qwebengineview/BLACKLIST b/tests/auto/widgets/qwebengineview/BLACKLIST index eccf02971..26f2da4bb 100644 --- a/tests/auto/widgets/qwebengineview/BLACKLIST +++ b/tests/auto/widgets/qwebengineview/BLACKLIST @@ -1,14 +1,12 @@ -[microFocusCoordinates] -osx +[mixLangLocale:eu_ES] +* -[visibilityState3] +[navigateOnDrop:file] windows -[horizontalScrollbarTest] -osx - -[mixLangLocale:eu_ES] -* +[navigateOnDrop:file_no_navigate] +windows -[reusePage] -b2qt arm +[horizontalScrollbarTest] +macos +rhel # flaky diff --git a/tests/auto/widgets/qwebengineview/CMakeLists.txt b/tests/auto/widgets/qwebengineview/CMakeLists.txt index b06ee7201..9583184d0 100644 --- a/tests/auto/widgets/qwebengineview/CMakeLists.txt +++ b/tests/auto/widgets/qwebengineview/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../util/util.cmake) qt_internal_add_test(tst_qwebengineview diff --git a/tests/auto/widgets/qwebengineview/qwebengineview.pro b/tests/auto/widgets/qwebengineview/qwebengineview.pro deleted file mode 100644 index d91c0074b..000000000 --- a/tests/auto/widgets/qwebengineview/qwebengineview.pro +++ /dev/null @@ -1,2 +0,0 @@ -include(../tests.pri) -QT *= gui-private diff --git a/tests/auto/widgets/qwebengineview/resources/dummy.html b/tests/auto/widgets/qwebengineview/resources/dummy.html index 9075f27c3..f5ba1963a 100644 --- a/tests/auto/widgets/qwebengineview/resources/dummy.html +++ b/tests/auto/widgets/qwebengineview/resources/dummy.html @@ -1,6 +1,6 @@ <html><head> <title>Dummy simple page without real content</title> -<link rel='icon' href='resources/image2.png'/> +<link rel='icon' href='qrc:///resources/image2.png'/> </head><body> <a>This is test content</a> </body></html> diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index 73a41e6fb..f4ff364a2 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> @@ -34,14 +37,18 @@ #include <qtemporarydir.h> #include <QClipboard> #include <QCompleter> +#include <QDropEvent> #include <QLabel> #include <QLineEdit> +#include <QListView> #include <QHBoxLayout> #include <QMenu> +#include <QMimeData> #include <QQuickItem> #include <QQuickWidget> #include <QtWebEngineCore/qwebenginehttprequest.h> #include <QScopeGuard> +#include <QStringListModel> #include <QTcpServer> #include <QTcpSocket> #include <QStyle> @@ -68,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); @@ -104,6 +112,7 @@ private Q_SLOTS: void changePage(); void reusePage_data(); void reusePage(); + void setLoadedPage(); void microFocusCoordinates(); void focusInputTypes(); void unhandledKeyEventPropagation(); @@ -149,7 +158,7 @@ private Q_SLOTS: void mouseLeave(); -#ifndef QT_NO_CLIPBOARD +#if QT_CONFIG(clipboard) void globalMouseSelection(); #endif void noContextMenu(); @@ -173,6 +182,12 @@ private Q_SLOTS: void setViewPreservesExplicitPage(); void closeDiscardsPage(); void loadAfterRendererCrashed(); + void inspectElement(); + void navigateOnDrop_data(); + void navigateOnDrop(); + void datalist(); + void longKeyEventText(); + void pageWithPaintListeners(); }; // This will be called before the first test function is executed. @@ -195,6 +210,96 @@ void tst_QWebEngineView::init() // This will be called after every test function. 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() @@ -293,22 +398,24 @@ 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()); } view->setPage(pageFrom.get()); + 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()); @@ -320,25 +427,28 @@ 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()); } view->setPage(pageTo.get()); + QCOMPARE(view->page(), pageTo.get()); + 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()); @@ -349,10 +459,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() @@ -389,7 +499,7 @@ void tst_QWebEngineView::reusePage() view1->show(); QVERIFY(QTest::qWaitForWindowExposed(view1)); delete view1; - QVERIFY(page != 0); // deleting view must not have deleted the page, since it's not a child of view + QVERIFY(page != nullptr); // deleting view must not have deleted the page, since it's not a child of view QWebEngineView *view2 = new QWebEngineView; view2->setPage(page.data()); @@ -402,6 +512,23 @@ void tst_QWebEngineView::reusePage() QDir::setCurrent(QApplication::applicationDirPath()); } +void tst_QWebEngineView::setLoadedPage() +{ + // MEMO load page first to make sure that just simple attach to view would draw its content + QWebEnginePage page; + QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); + page.setHtml(QString("<html><body bgcolor=\"%1\"></body></html>").arg(QColor(Qt::yellow).name())); + QTRY_VERIFY(loadSpy.size() == 1 && loadSpy.first().first().toBool()); + + QWebEngineView view; + view.resize(480, 320); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + view.setPage(&page); + QTRY_COMPARE(view.grab().toImage().pixelColor(QPoint(view.width() / 2, view.height() / 2)), Qt::yellow); +} + // Class used in crashTests class WebViewCrashTest : public QObject { Q_OBJECT @@ -476,7 +603,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); @@ -486,8 +613,8 @@ void tst_QWebEngineView::microFocusCoordinates() void tst_QWebEngineView::focusInputTypes() { - const QPlatformInputContext *context = QGuiApplicationPrivate::platformIntegration()->inputContext(); - bool imeHasHiddenTextCapability = context && context->hasCapability(QPlatformInputContext::HiddenTextCapability); + const QPlatformInputContext *platformInputContext = QGuiApplicationPrivate::platformIntegration()->inputContext(); + bool imeHasHiddenTextCapability = platformInputContext && platformInputContext->hasCapability(QPlatformInputContext::HiddenTextCapability); QWebEngineView webView; webView.resize(640, 480); @@ -518,7 +645,8 @@ void tst_QWebEngineView::focusInputTypes() QTRY_COMPARE(evaluateJavaScriptSync(webView.page(), "document.activeElement.id").toString(), QStringLiteral("passwordInput")); VERIFY_INPUTMETHOD_HINTS(webView.focusProxy()->inputMethodHints(), (Qt::ImhSensitiveData | Qt::ImhNoPredictiveText | Qt::ImhNoAutoUppercase | Qt::ImhHiddenText)); QVERIFY(!webView.focusProxy()->testAttribute(Qt::WA_InputMethodEnabled)); - QTRY_COMPARE(inputMethodQuery(Qt::ImEnabled).toBool(), imeHasHiddenTextCapability); + QTRY_VERIFY(inputMethodQuery(Qt::ImEnabled).toBool()); + QTRY_COMPARE(platformInputContext->inputMethodAccepted(), imeHasHiddenTextCapability); // 'tel' field QPoint telInputCenter = elementCenter(webView.page(), "telInput"); @@ -557,7 +685,8 @@ void tst_QWebEngineView::focusInputTypes() QTRY_COMPARE(evaluateJavaScriptSync(webView.page(), "document.activeElement.id").toString(), QStringLiteral("passwordInput")); VERIFY_INPUTMETHOD_HINTS(webView.focusProxy()->inputMethodHints(), (Qt::ImhSensitiveData | Qt::ImhNoPredictiveText | Qt::ImhNoAutoUppercase | Qt::ImhHiddenText)); QVERIFY(!webView.focusProxy()->testAttribute(Qt::WA_InputMethodEnabled)); - QTRY_COMPARE(inputMethodQuery(Qt::ImEnabled).toBool(), imeHasHiddenTextCapability); + QTRY_VERIFY(inputMethodQuery(Qt::ImEnabled).toBool()); + QTRY_COMPARE(platformInputContext->inputMethodAccepted(), imeHasHiddenTextCapability); // 'text' type QTest::mouseClick(webView.focusProxy(), Qt::LeftButton, {}, textInputCenter); @@ -571,7 +700,8 @@ void tst_QWebEngineView::focusInputTypes() QTRY_COMPARE(evaluateJavaScriptSync(webView.page(), "document.activeElement.id").toString(), QStringLiteral("passwordInput")); VERIFY_INPUTMETHOD_HINTS(webView.focusProxy()->inputMethodHints(), (Qt::ImhSensitiveData | Qt::ImhNoPredictiveText | Qt::ImhNoAutoUppercase | Qt::ImhHiddenText)); QVERIFY(!webView.focusProxy()->testAttribute(Qt::WA_InputMethodEnabled)); - QTRY_COMPARE(inputMethodQuery(Qt::ImEnabled).toBool(), imeHasHiddenTextCapability); + QTRY_VERIFY(inputMethodQuery(Qt::ImEnabled).toBool()); + QTRY_COMPARE(platformInputContext->inputMethodAccepted(), imeHasHiddenTextCapability); // 'text area' field QPoint textAreaCenter = elementCenter(webView.page(), "textArea"); @@ -584,6 +714,7 @@ void tst_QWebEngineView::focusInputTypes() class KeyEventRecordingWidget : public QWidget { public: + ~KeyEventRecordingWidget() { qDeleteAll(pressEvents); qDeleteAll(releaseEvents); } QList<QKeyEvent *> pressEvents; QList<QKeyEvent *> releaseEvents; void keyPressEvent(QKeyEvent *e) override { pressEvents << e->clone(); } @@ -600,7 +731,7 @@ void tst_QWebEngineView::unhandledKeyEventPropagation() QSignalSpy loadFinishedSpy(&webView, SIGNAL(loadFinished(bool))); webView.load(QUrl("qrc:///resources/keyboardEvents.html")); - QVERIFY(loadFinishedSpy.wait()); + QTRY_VERIFY_WITH_TIMEOUT(loadFinishedSpy.size() > 0, 20000); evaluateJavaScriptSync(webView.page(), "document.getElementById('first_div').focus()"); QTRY_COMPARE(evaluateJavaScriptSync(webView.page(), "document.activeElement.id").toString(), QStringLiteral("first_div")); @@ -646,19 +777,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))); @@ -895,7 +1036,7 @@ public: case QEvent::ContextMenu: case QEvent::KeyPress: case QEvent::KeyRelease: -#ifndef QT_NO_WHEELEVENT +#if QT_CONFIG(wheelevent) case QEvent::Wheel: #endif ++m_eventCounter; @@ -950,7 +1091,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. @@ -997,7 +1138,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()"); @@ -1110,21 +1251,23 @@ void tst_QWebEngineView::focusInternalRenderWidgetHostViewQuickItem() QWebEngineView *webView = new QWebEngineView; QWebEngineSettings *settings = webView->page()->settings(); settings->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false); - webView->resize(300, 300); + webView->resize(300, 100); - QHBoxLayout *layout = new QHBoxLayout; + QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(label); layout->addWidget(webView); + containerWidget->resize(300, 200); containerWidget->setLayout(layout); containerWidget->show(); QVERIFY(QTest::qWaitForWindowExposed(containerWidget.data())); // Load the content, and check that focus is not set. QSignalSpy loadSpy(webView, SIGNAL(loadFinished(bool))); - webView->setHtml("<html><head><title>Title</title></head><body>Hello" - "<input id=\"input\" type=\"text\"></body></html>"); - QTRY_COMPARE(loadSpy.count(), 1); + webView->setHtml("<html><body>" + " <input id='input1' type='text'/>" + "</body></html>"); + QTRY_COMPARE(loadSpy.size(), 1); QTRY_COMPARE(webView->hasFocus(), false); // Manually trigger focus. @@ -1133,15 +1276,43 @@ void tst_QWebEngineView::focusInternalRenderWidgetHostViewQuickItem() // Check that focus is set in QWebEngineView and all internal classes. QTRY_COMPARE(webView->hasFocus(), true); - QQuickWidget *renderWidgetHostViewQtDelegateWidget = - qobject_cast<QQuickWidget *>(webView->focusProxy()); - QVERIFY(renderWidgetHostViewQtDelegateWidget); - QTRY_COMPARE(renderWidgetHostViewQtDelegateWidget->hasFocus(), true); + QQuickWidget *webEngineQuickWidget = qobject_cast<QQuickWidget *>(webView->focusProxy()); + QVERIFY(webEngineQuickWidget); + QTRY_COMPARE(webEngineQuickWidget->hasFocus(), true); + + QQuickItem *root = webEngineQuickWidget->rootObject(); + // The root item should not has focus, otherwise it would handle input events + // instead of the RenderWidgetHostViewQtDelegateItem. + QVERIFY(!root->hasFocus()); + + QCOMPARE(root->childItems().size(), 1); + QQuickItem *renderWidgetHostViewQtDelegateItem = root->childItems().at(0); + QVERIFY(renderWidgetHostViewQtDelegateItem); + QTRY_COMPARE(renderWidgetHostViewQtDelegateItem->hasFocus(), true); + // Test if QWebEngineView handles key events. + QTRY_COMPARE(renderWidgetHostViewQtDelegateItem->hasActiveFocus(), true); + + // Key events should not be forwarded to the unfocused input field. + QTRY_COMPARE(evaluateJavaScriptSync(webView->page(), + "document.getElementById('input1').value").toString(), + QStringLiteral("")); + QTest::keyClick(webView->focusProxy(), Qt::Key_X); + QTest::qWait(100); + QTRY_COMPARE(evaluateJavaScriptSync(webView->page(), + "document.getElementById('input1').value").toString(), + QStringLiteral("")); - QQuickItem *renderWidgetHostViewQuickItem = - renderWidgetHostViewQtDelegateWidget->rootObject(); - QVERIFY(renderWidgetHostViewQuickItem); - QTRY_COMPARE(renderWidgetHostViewQuickItem->hasFocus(), true); + // Focus the input field. Focus rectangle is expected to appear around the input field. + evaluateJavaScriptSync(webView->page(), "document.getElementById('input1').focus()"); + QTRY_COMPARE(evaluateJavaScriptSync(webView->page(), + "document.activeElement.id").toString(), + QStringLiteral("input1")); + + // Test the focused input field with a key event. + QTest::keyClick(webView->focusProxy(), Qt::Key_X); + QTRY_COMPARE(evaluateJavaScriptSync(webView->page(), + "document.getElementById('input1').value").toString(), + QStringLiteral("x")); } void tst_QWebEngineView::doNotBreakLayout() @@ -1181,7 +1352,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); @@ -1191,7 +1362,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); @@ -1204,7 +1375,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); @@ -1236,10 +1407,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()); @@ -1286,18 +1457,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); @@ -1321,8 +1493,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() @@ -1331,7 +1503,7 @@ void tst_QWebEngineView::keyboardEvents() view.show(); QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); view.load(QUrl("qrc:///resources/keyboardEvents.html")); - QVERIFY(loadFinishedSpy.wait()); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); QStringList elements; elements << "first_div" << "second_div"; @@ -1450,17 +1622,21 @@ void tst_QWebEngineView::keyboardFocusAfterPopup() QTRY_COMPARE(QApplication::focusWidget(), window.lineEdit); // Trigger QCompleter's popup and select the first suggestion. - QTest::keyClick(QApplication::focusWindow(), Qt::Key_T); + QTest::keyPress(QApplication::focusWindow(), Qt::Key_T); + QTest::keyRelease(QApplication::focusWindow(), Qt::Key_T); QTRY_VERIFY(QApplication::activePopupWidget()); - QTest::keyClick(QApplication::focusWindow(), Qt::Key_Down); - QTest::keyClick(QApplication::focusWindow(), Qt::Key_Enter); + QTest::keyPress(QApplication::focusWindow(), Qt::Key_Down); + QTest::keyRelease(QApplication::focusWindow(), Qt::Key_Down); + QTest::keyPress(QApplication::focusWindow(), Qt::Key_Enter); + QTest::keyRelease(QApplication::focusWindow(), Qt::Key_Enter); // Due to FocusOnNavigationEnabled, focus should now move to the webView. QTRY_COMPARE(QApplication::focusWidget(), window.webView->focusProxy()); // Keyboard events sent to the window should go to the <input> element. - QVERIFY(loadFinishedSpy.count() || loadFinishedSpy.wait()); - QTest::keyClick(QApplication::focusWindow(), Qt::Key_X); + 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(), QStringLiteral("x")); } @@ -1489,7 +1665,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 @@ -1504,13 +1680,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 @@ -1525,13 +1701,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()); } @@ -1559,12 +1735,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] == "/"; @@ -1572,16 +1748,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") @@ -1590,12 +1766,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 @@ -1668,11 +1844,10 @@ void tst_QWebEngineView::postData() void tst_QWebEngineView::inputFieldOverridesShortcuts() { + QWebEngineView view; bool actionTriggered = false; - QAction *action = new QAction; + QAction *action = new QAction(&view); connect(action, &QAction::triggered, [&actionTriggered] () { actionTriggered = true; }); - - QWebEngineView view; view.addAction(action); QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); @@ -1890,14 +2065,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); @@ -1909,7 +2084,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")); @@ -1918,7 +2093,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!")); @@ -1927,7 +2102,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!")); @@ -1942,8 +2117,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. @@ -1968,8 +2143,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!")); @@ -1983,9 +2158,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!")); @@ -1997,7 +2172,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); @@ -2014,7 +2189,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!")); @@ -2024,7 +2199,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(); } @@ -2068,7 +2243,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); } { @@ -2077,7 +2252,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 @@ -2153,24 +2328,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); @@ -2182,7 +2357,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); @@ -2192,7 +2367,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); @@ -2205,7 +2380,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); @@ -2227,34 +2402,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")); @@ -2262,8 +2434,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()); @@ -2276,7 +2447,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()); @@ -2286,31 +2457,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()); } @@ -2357,7 +2524,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; @@ -2406,7 +2573,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. { @@ -2416,7 +2583,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); @@ -2437,7 +2604,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'. { @@ -2449,7 +2616,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. { @@ -2462,7 +2629,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. @@ -2476,7 +2643,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. { @@ -2489,7 +2656,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. @@ -2507,7 +2674,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. { @@ -2520,7 +2687,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. @@ -2530,7 +2697,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); @@ -2544,7 +2711,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 @@ -2564,7 +2731,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(); @@ -2589,8 +2756,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")); @@ -2606,7 +2773,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() @@ -2761,7 +2928,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")); @@ -2777,7 +2944,7 @@ void tst_QWebEngineView::imeJSInputEvents() qApp->processEvents(); } - QTRY_COMPARE(logLines().count(), 4); + QTRY_COMPARE(logLines().size(), 4); QCOMPARE(logLines()[0], QStringLiteral("[object CompositionEvent] compositionstart ")); QCOMPARE(logLines()[1], QStringLiteral("[object InputEvent] beforeinput preedit")); QCOMPARE(logLines()[2], QStringLiteral("[object CompositionEvent] compositionupdate preedit")); @@ -2791,7 +2958,7 @@ void tst_QWebEngineView::imeJSInputEvents() qApp->processEvents(); } - QTRY_COMPARE(logLines().count(), 9); + QTRY_COMPARE(logLines().size(), 9); QCOMPARE(logLines()[4], QStringLiteral("[object InputEvent] beforeinput commit")); QCOMPARE(logLines()[5], QStringLiteral("[object CompositionEvent] compositionupdate commit")); QCOMPARE(logLines()[6], QStringLiteral("[object TextEvent] textInput commit")); @@ -2809,7 +2976,7 @@ void tst_QWebEngineView::imeJSInputEvents() qApp->processEvents(); } - QTRY_COMPARE(logLines().count(), 4); + QTRY_COMPARE(logLines().size(), 4); QCOMPARE(logLines()[0], QStringLiteral("[object CompositionEvent] compositionstart ")); QCOMPARE(logLines()[1], QStringLiteral("[object InputEvent] beforeinput preedit")); QCOMPARE(logLines()[2], QStringLiteral("[object CompositionEvent] compositionupdate preedit")); @@ -2822,7 +2989,7 @@ void tst_QWebEngineView::imeJSInputEvents() qApp->processEvents(); } - QTRY_COMPARE(logLines().count(), 9); + QTRY_COMPARE(logLines().size(), 9); QCOMPARE(logLines()[4], QStringLiteral("[object InputEvent] beforeinput ")); QCOMPARE(logLines()[5], QStringLiteral("[object CompositionEvent] compositionupdate ")); QCOMPARE(logLines()[6], QStringLiteral("[object TextEvent] textInput ")); @@ -2889,6 +3056,7 @@ void tst_QWebEngineView::imeCompositionQueryEvent() } QInputMethodQueryEvent srrndTextQuery(Qt::ImSurroundingText); + QInputMethodQueryEvent absolutePosQuery(Qt::ImAbsolutePosition); QInputMethodQueryEvent cursorPosQuery(Qt::ImCursorPosition); QInputMethodQueryEvent anchorPosQuery(Qt::ImAnchorPosition); @@ -2900,16 +3068,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 { @@ -2923,16 +3093,67 @@ 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); } -#ifndef QT_NO_CLIPBOARD +#if QT_CONFIG(clipboard) void tst_QWebEngineView::globalMouseSelection() { if (!QApplication::clipboard()->supportsSelection()) { @@ -2954,20 +3175,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 @@ -2993,7 +3214,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()); } @@ -3033,7 +3254,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)); @@ -3041,9 +3262,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); } @@ -3109,46 +3330,50 @@ void tst_QWebEngineView::webUIURLs_data() QTest::addColumn<bool>("supported"); QTest::newRow("about") << QUrl("chrome://about") << false; QTest::newRow("accessibility") << QUrl("chrome://accessibility") << true; - QTest::newRow("appcache-internals") << QUrl("chrome://appcache-internals") << true; + QTest::newRow("app-service-internals") << QUrl("chrome://app-service-internals") << false; + QTest::newRow("app-settings") << QUrl("chrome://app-settings") << false; QTest::newRow("apps") << QUrl("chrome://apps") << false; + QTest::newRow("attribution-internals") << QUrl("chrome://attribution-internals") << true; QTest::newRow("autofill-internals") << QUrl("chrome://autofill-internals") << false; QTest::newRow("blob-internals") << QUrl("chrome://blob-internals") << true; QTest::newRow("bluetooth-internals") << QUrl("chrome://bluetooth-internals") << false; QTest::newRow("bookmarks") << QUrl("chrome://bookmarks") << false; QTest::newRow("chrome-urls") << QUrl("chrome://chrome-urls") << false; QTest::newRow("components") << QUrl("chrome://components") << false; - QTest::newRow("conversion-internals") << QUrl("chrome://conversion-internals") << true; + QTest::newRow("connectors-internals") << QUrl("chrome://connectors-internals") << false; QTest::newRow("crashes") << QUrl("chrome://crashes") << false; QTest::newRow("credits") << QUrl("chrome://credits") << false; QTest::newRow("device-log") << QUrl("chrome://device-log") << true; - QTest::newRow("devices") << QUrl("chrome://devices") << false; QTest::newRow("dino") << QUrl("chrome://dino") << false; // It works but this is an error page QTest::newRow("discards") << QUrl("chrome://discards") << false; QTest::newRow("download-internals") << QUrl("chrome://download-internals") << false; QTest::newRow("downloads") << QUrl("chrome://downloads") << false; QTest::newRow("extensions") << QUrl("chrome://extensions") << false; + QTest::newRow("extensions-internals") << QUrl("chrome://extensions-internals") << false; QTest::newRow("flags") << QUrl("chrome://flags") << false; QTest::newRow("gcm-internals") << QUrl("chrome://gcm-internals") << false; QTest::newRow("gpu") << QUrl("chrome://gpu") << true; QTest::newRow("help") << QUrl("chrome://help") << false; QTest::newRow("histograms") << QUrl("chrome://histograms") << true; QTest::newRow("history") << QUrl("chrome://history") << false; + QTest::newRow("history-clusters-internals") << QUrl("chrome://history-clusters-internals") << false; QTest::newRow("indexeddb-internals") << QUrl("chrome://indexeddb-internals") << true; QTest::newRow("inspect") << QUrl("chrome://inspect") << false; QTest::newRow("interstitials") << QUrl("chrome://interstitials") << false; - QTest::newRow("interventions-internals") << QUrl("chrome://interventions-internals") << false; QTest::newRow("invalidations") << QUrl("chrome://invalidations") << false; QTest::newRow("linux-proxy-config") << QUrl("chrome://linux-proxy-config") << false; QTest::newRow("local-state") << QUrl("chrome://local-state") << false; QTest::newRow("management") << QUrl("chrome://management") << false; QTest::newRow("media-engagement") << QUrl("chrome://media-engagement") << false; QTest::newRow("media-internals") << QUrl("chrome://media-internals") << true; + QTest::newRow("nacl") << QUrl("chrome://nacl") << false; QTest::newRow("net-export") << QUrl("chrome://net-export") << false; QTest::newRow("net-internals") << QUrl("chrome://net-internals") << true; QTest::newRow("network-error") << QUrl("chrome://network-error") << false; QTest::newRow("network-errors") << QUrl("chrome://network-errors") << true; QTest::newRow("ntp-tiles-internals") << QUrl("chrome://ntp-tiles-internals") << false; QTest::newRow("omnibox") << QUrl("chrome://omnibox") << false; + QTest::newRow("optimization-guide-internals") << QUrl("chrome://optimization-guide-internals") << false; QTest::newRow("password-manager-internals") << QUrl("chrome://password-manager-internals") << false; QTest::newRow("policy") << QUrl("chrome://policy") << false; QTest::newRow("predictors") << QUrl("chrome://predictors") << false; @@ -3157,7 +3382,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; @@ -3166,8 +3391,6 @@ void tst_QWebEngineView::webUIURLs_data() QTest::newRow("settings") << QUrl("chrome://settings") << false; QTest::newRow("signin-internals") << QUrl("chrome://signin-internals") << false; QTest::newRow("site-engagement") << QUrl("chrome://site-engagement") << false; - QTest::newRow("suggestions") << QUrl("chrome://suggestions") << false; - QTest::newRow("supervised-user-internals") << QUrl("chrome://supervised-user-internals") << false; QTest::newRow("sync-internals") << QUrl("chrome://sync-internals") << false; QTest::newRow("system") << QUrl("chrome://system") << false; QTest::newRow("terms") << QUrl("chrome://terms") << false; @@ -3177,12 +3400,14 @@ void tst_QWebEngineView::webUIURLs_data() QTest::newRow("usb-internals") << QUrl("chrome://usb-internals") << false; QTest::newRow("user-actions") << QUrl("chrome://user-actions") << true; QTest::newRow("version") << QUrl("chrome://version") << false; + QTest::newRow("web-app-internals") << QUrl("chrome://web-app-internals") << false; #if QT_CONFIG(webengine_webrtc) QTest::newRow("webrtc-internals") << QUrl("chrome://webrtc-internals") << true; #if QT_CONFIG(webengine_extensions) QTest::newRow("webrtc-logs") << QUrl("chrome://webrtc-logs") << true; #endif // QT_CONFIG(webengine_extensions) #endif // QT_CONFIG(webengine_webrtc) + QTest::newRow("whats-new") << QUrl("chrome://whats-new") << false; } void tst_QWebEngineView::webUIURLs() @@ -3194,7 +3419,7 @@ void tst_QWebEngineView::webUIURLs() view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); view.load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 90000); QCOMPARE(loadFinishedSpy.takeFirst().at(0).toBool(), supported); } @@ -3203,7 +3428,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(); @@ -3218,7 +3443,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")); } @@ -3231,8 +3456,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(); @@ -3296,7 +3521,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() @@ -3310,7 +3535,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); @@ -3343,7 +3568,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); @@ -3364,13 +3589,11 @@ void tst_QWebEngineView::switchPage() QSignalSpy loadFinishedSpy2(&page2, SIGNAL(loadFinished(bool))); // TODO fixme: page without the view has no real widget behind, so // reading graphical content will fail, add view for now. - QWebEngineView webView1; - QWebEngineView webView2; - webView1.setPage(&page1); - webView2.setPage(&page2); + QWebEngineView webView1(&page1, nullptr); + 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(); @@ -3441,17 +3664,15 @@ void tst_QWebEngineView::setViewPreservesExplicitPage() void tst_QWebEngineView::closeDiscardsPage() { QWebEngineProfile profile; - QWebEnginePage page(&profile); - QWebEngineView view; - view.setPage(&page); + QWebEngineView view(&profile, nullptr); view.resize(300, 300); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); - QCOMPARE(page.isVisible(), true); - QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); + QCOMPARE(view.page()->isVisible(), true); + QCOMPARE(view.page()->lifecycleState(), QWebEnginePage::LifecycleState::Active); view.close(); - QCOMPARE(page.isVisible(), false); - QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Discarded); + QCOMPARE(view.page()->isVisible(), false); + QCOMPARE(view.page()->lifecycleState(), QWebEnginePage::LifecycleState::Discarded); } @@ -3469,9 +3690,295 @@ 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()); } +void tst_QWebEngineView::inspectElement() +{ + QWebEngineView view; + view.resize(640, 480); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + auto page = view.page(); + // shouldn't do anything until page is set + page->triggerAction(QWebEnginePage::InspectElement); + QTest::qWait(100); + + QSignalSpy spy(&view, &QWebEngineView::loadFinished); + view.load(QUrl("data:text/plain,foobarbaz")); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 12000); + + // shouldn't do anything since inspector is not attached + page->triggerAction(QWebEnginePage::InspectElement); + QTest::qWait(100); + + QWebEngineView inspectorView; + inspectorView.resize(640, 480); + inspectorView.show(); + QVERIFY(QTest::qWaitForWindowExposed(&inspectorView)); + inspectorView.page()->setInspectedPage(page); + + page->triggerAction(QWebEnginePage::InspectElement); + // TODO verify somehow + QTest::qWait(100); +} + +void tst_QWebEngineView::navigateOnDrop_data() +{ + QTest::addColumn<QUrl>("url"); + QTest::addColumn<bool>("navigateOnDrop"); + QTest::newRow("file") << QUrl::fromLocalFile(QDir(QT_TESTCASE_SOURCEDIR).absoluteFilePath("resources/dummy.html")) << true; + QTest::newRow("qrc") << QUrl("qrc:///resources/dummy.html") << true; + QTest::newRow("file_no_navigate") << QUrl::fromLocalFile(QDir(QT_TESTCASE_SOURCEDIR).absoluteFilePath("resources/dummy.html")) << false; + QTest::newRow("qrc_no_navigate") << QUrl("qrc:///resources/dummy.html") << false; +} + +void tst_QWebEngineView::navigateOnDrop() +{ + QFETCH(QUrl, url); + QFETCH(bool, navigateOnDrop); + struct WebEngineView : QWebEngineView { + QWebEngineView* createWindow(QWebEnginePage::WebWindowType /* type */) override { return this; } + } view; + view.page()->settings()->setAttribute(QWebEngineSettings::NavigateOnDropEnabled, navigateOnDrop); + view.resize(640, 480); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QSignalSpy loadSpy(&view, &QWebEngineView::loadFinished); + QMimeData mimeData; + mimeData.setUrls({ url }); + + auto sendEvents = [&] () { + 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); + }; + + sendEvents(); + if (navigateOnDrop) { + QTRY_COMPARE(loadSpy.size(), 1); + QVERIFY(loadSpy.last().first().toBool()); + QCOMPARE(view.url(), url); + } else { + QTest::qWait(500); + QCOMPARE(loadSpy.size(), 0); + QVERIFY(view.url() != url); + } + + // Check dynamically changing the setting + loadSpy.clear(); + view.page()->settings()->setAttribute(QWebEngineSettings::NavigateOnDropEnabled, !navigateOnDrop); + view.setUrl(QUrl("about:blank")); + QTRY_COMPARE(loadSpy.size(), 1); + + sendEvents(); + if (!navigateOnDrop) { + QTRY_COMPARE(loadSpy.size(), 2); + QVERIFY(loadSpy.last().first().toBool()); + QCOMPARE(view.url(), url); + } else { + QTest::qWait(500); + QCOMPARE(loadSpy.size(), 1); + QVERIFY(view.url() != url); + } +} + +void tst_QWebEngineView::datalist() +{ + QString html("<html><body>" + "<input id='browserInput' list='browserDatalist'>" + "<datalist id='browserDatalist'>" + " <option value='Internet Explorer'>" + " <option value='Firefox'>" + " <option value='Chrome'>" + " <option value='Opera'>" + " <option value='Safari'>" + "</datalist>" + "</body></html>"); + + QWebEngineView view; + view.resize(200, 400); + view.show(); + + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QSignalSpy loadSpy(&view, &QWebEngineView::loadFinished); + view.setHtml(html); + QTRY_COMPARE(loadSpy.size(), 1); + + QString listValuesJS("(function() {" + " var browserDatalist = document.getElementById('browserDatalist');" + " var options = browserDatalist.options;" + " var result = [];" + " for (let i = 0; i < options.length; ++i) {" + " result.push(options[i].value);" + " }" + " return result;" + "})();"); + QStringList values = evaluateJavaScriptSync(view.page(), listValuesJS).toStringList(); + QCOMPARE(values, QStringList({ "Internet Explorer", "Firefox", "Chrome", "Opera", "Safari" })); + QCOMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('browserInput').value;") + .toString(), + QStringLiteral("")); + + auto listView = [&view]() -> QListView * { + if (QApplication::topLevelWidgets().size() == 1) { + // No popup case. + return nullptr; + } + + QWidget *autofillPopupWidget = nullptr; + for (QWidget *w : QApplication::topLevelWidgets()) { + if (w != &view) { + autofillPopupWidget = w; + break; + } + } + + if (!autofillPopupWidget) + return nullptr; + + for (QObject *o : autofillPopupWidget->children()) { + if (QListView *listView = qobject_cast<QListView *>(o)) + return listView; + } + + return nullptr; + }; + + // Make sure there is no open popup yet. + QVERIFY(!listView()); + // Click in the input field. + QPoint browserInputCenter = elementCenter(view.page(), "browserInput"); + QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, browserInputCenter); + // Wait for the popup. + QTRY_VERIFY(listView()); + + // No suggestion is selected. + QCOMPARE(listView()->currentIndex(), QModelIndex()); + QCOMPARE(listView()->model()->rowCount(), 5); + + // Accepting suggestion does nothing. + QTest::keyClick(view.windowHandle(), Qt::Key_Enter); + QVERIFY(listView()); + QCOMPARE(listView()->currentIndex(), QModelIndex()); + + // Escape should close popup. + QTest::keyClick(view.windowHandle(), Qt::Key_Escape); + QTRY_VERIFY(!listView()); + + // Key Down should open the popup and select the first suggestion. + QTest::keyClick(view.windowHandle(), Qt::Key_Down); + QTRY_VERIFY(listView()); + QCOMPARE(listView()->currentIndex().row(), 0); + + // Test keyboard navigation in list. + QTest::keyClick(view.windowHandle(), Qt::Key_Up); + QCOMPARE(listView()->currentIndex().row(), 4); + QTest::keyClick(view.windowHandle(), Qt::Key_Up); + QCOMPARE(listView()->currentIndex().row(), 3); + QTest::keyClick(view.windowHandle(), Qt::Key_PageDown); + QCOMPARE(listView()->currentIndex().row(), 4); + QTest::keyClick(view.windowHandle(), Qt::Key_PageUp); + QCOMPARE(listView()->currentIndex().row(), 0); + QTest::keyClick(view.windowHandle(), Qt::Key_Down); + QCOMPARE(listView()->currentIndex().row(), 1); + QTest::keyClick(view.windowHandle(), Qt::Key_Down); + QCOMPARE(listView()->currentIndex().row(), 2); + + // Test accepting suggestion. + QCOMPARE(static_cast<QStringListModel *>(listView()->model()) + ->data(listView()->currentIndex()) + .toString(), + QStringLiteral("Chrome")); + QTest::keyClick(view.windowHandle(), Qt::Key_Enter); + QTRY_COMPARE( + evaluateJavaScriptSync(view.page(), "document.getElementById('browserInput').value") + .toString(), + QStringLiteral("Chrome")); + // Accept closes popup. + QTRY_VERIFY(!listView()); + + // Clear input field, should not trigger popup. + evaluateJavaScriptSync(view.page(), "document.getElementById('browserInput').value = ''"); + QVERIFY(!listView()); + + // Filter suggestions. + QTest::keyClick(view.windowHandle(), Qt::Key_F); + QTRY_VERIFY(listView()); + QCOMPARE(listView()->model()->rowCount(), 2); + QCOMPARE(listView()->currentIndex(), QModelIndex()); + QCOMPARE(static_cast<QStringListModel *>(listView()->model()) + ->data(listView()->model()->index(0, 0)) + .toString(), + QStringLiteral("Firefox")); + QCOMPARE(static_cast<QStringListModel *>(listView()->model()) + ->data(listView()->model()->index(1, 0)) + .toString(), + QStringLiteral("Safari")); + QTest::keyClick(view.windowHandle(), Qt::Key_I); + QTRY_COMPARE(listView()->model()->rowCount(), 1); + QCOMPARE(listView()->currentIndex(), QModelIndex()); + QCOMPARE(static_cast<QStringListModel *>(listView()->model()) + ->data(listView()->model()->index(0, 0)) + .toString(), + QStringLiteral("Firefox")); + QTest::keyClick(view.windowHandle(), Qt::Key_L); + // Mismatch should close popup. + QTRY_VERIFY(!listView()); + QTRY_COMPARE( + evaluateJavaScriptSync(view.page(), "document.getElementById('browserInput').value") + .toString(), + 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/qwebengineview/tst_qwebengineview.qrc b/tests/auto/widgets/qwebengineview/tst_qwebengineview.qrc deleted file mode 100644 index a0e81e242..000000000 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.qrc +++ /dev/null @@ -1,11 +0,0 @@ -<RCC> - <qresource prefix="/"> - <file>resources/index.html</file> - <file>resources/frame_a.html</file> - <file>resources/input_types.html</file> - <file>resources/scrolltest_page.html</file> - <file>resources/keyboardEvents.html</file> - <file>resources/image2.png</file> - <file>resources/dummy.html</file> - </qresource> -</RCC> diff --git a/tests/auto/widgets/schemes/CMakeLists.txt b/tests/auto/widgets/schemes/CMakeLists.txt index 446ae5751..5299b3148 100644 --- a/tests/auto/widgets/schemes/CMakeLists.txt +++ b/tests/auto/widgets/schemes/CMakeLists.txt @@ -1,7 +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/schemes.pro b/tests/auto/widgets/schemes/schemes.pro deleted file mode 100644 index e56bbe8f7..000000000 --- a/tests/auto/widgets/schemes/schemes.pro +++ /dev/null @@ -1,3 +0,0 @@ -include(../tests.pri) -exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc -QT *= core-private gui-private diff --git a/tests/auto/widgets/schemes/tst_schemes.cpp b/tests/auto/widgets/schemes/tst_schemes.cpp index a4a0e34ff..188c112e4 100644 --- a/tests/auto/widgets/schemes/tst_schemes.cpp +++ b/tests/auto/widgets/schemes/tst_schemes.cpp @@ -1,47 +1,50 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include <QtTest/QtTest> -#include <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: @@ -118,5 +121,161 @@ void tst_Schemes::unknownUrlSchemePolicy() QCOMPARE(page.acceptNavigationRequestCalls, shouldAccept ? 1 : 0); } +class CustomScheme : public QWebEngineUrlSchemeHandler +{ +public: + CustomScheme(const QString &linkUrl) : m_linkUrl(linkUrl) { } + + void requestStarted(QWebEngineUrlRequestJob *requestJob) override + { + QString html = QString("<html><body>" + "<p style='height: 2000px;'>" + "<a href='%1' id='link'>Click link</a>" + "</p><p id='anchor'>Anchor</p>" + "</body></html>") + .arg(m_linkUrl); + QBuffer *buffer = new QBuffer(requestJob); + buffer->setData(html.toUtf8()); + requestJob->reply("text/html", buffer); + } + + QString m_linkUrl; +}; + +void tst_Schemes::customSchemeFragmentNavigation_data() +{ + QTest::addColumn<QUrl>("baseUrl"); + QTest::addColumn<QString>("linkUrl"); + QTest::addColumn<QUrl>("expectedUrl"); + + // Path syntax + // - Preserves each part of the URL after navigation + QTest::newRow("Path syntax, path only, relative url") + << QUrl("path://path") << "#anchor" << QUrl("path://path#anchor"); + QTest::newRow("Path syntax, path only, absolute url") + << QUrl("path://path") << "path://path#anchor" << QUrl("path://path#anchor"); + QTest::newRow("Path syntax, host/path, relative url") + << QUrl("path://host/path") << "#anchor" << QUrl("path://host/path#anchor"); + QTest::newRow("Path syntax, host/path, absolute url") + << QUrl("path://host/path") << "path://host/path#anchor" + << QUrl("path://host/path#anchor"); + QTest::newRow("Path syntax, host:port, relative url") + << QUrl("path://host:3000") << "#anchor" << QUrl("path://host:3000#anchor"); + QTest::newRow("Path syntax, host:port, absolute url") + << QUrl("path://host:3000") << "path://host:3000#anchor" + << QUrl("path://host:3000#anchor"); + QTest::newRow("Path syntax, userinfo@host:port, relative url") + << QUrl("path://user:password@host:3000") << "#anchor" + << QUrl("path://user:password@host:3000#anchor"); + QTest::newRow("Path syntax, userinfo@host:port, absolute url") + << QUrl("path://user:password@host:3000") << "path://user:password@host:3000#anchor" + << QUrl("path://user:password@host:3000#anchor"); + + // Host syntax + // - We lose the port and the user info from the authority after navigation + QTest::newRow("Host syntax, host only, relative url") + << QUrl("host://host") << "#anchor" << QUrl("host://host/#anchor"); + QTest::newRow("Host syntax, host only, absolute url") + << QUrl("host://host") << "host://host#anchor" << QUrl("host://host/#anchor"); + QTest::newRow("Host syntax, host/path, relative url") + << QUrl("host://host/path") << "#anchor" << QUrl("host://host/path#anchor"); + QTest::newRow("Host syntax, host/path, absolute url") + << QUrl("host://host/path") << "host://host/path#anchor" + << QUrl("host://host/path#anchor"); + QTest::newRow("Host syntax, host:port, relative url") + << QUrl("host://host:3000") << "#anchor" << QUrl("host://host/#anchor"); + QTest::newRow("Host syntax, host:port, absolute url") + << QUrl("host://host:3000") << "host://host:3000#anchor" << QUrl("host://host/#anchor"); + QTest::newRow("Host syntax, userinfo@host:port, relative url") + << QUrl("host://user:password@host:3000") << "#anchor" << QUrl("host://host/#anchor"); + QTest::newRow("Host syntax, userinfo@host:port, absolute url") + << QUrl("host://user:password@host:3000") << "host://user:password@host:3000#anchor" + << QUrl("host://host/#anchor"); + + // HostAndPort syntax + // - We lose the port and the user info from the authority after navigation + QTest::newRow("HostAndPort syntax, host only, relative url") + << QUrl("hostandport://host") << "#anchor" << QUrl("hostandport://host/#anchor"); + QTest::newRow("HostAndPort syntax, host only, absolute url") + << QUrl("hostandport://host") << "hostandport://host#anchor" + << QUrl("hostandport://host/#anchor"); + QTest::newRow("HostAndPort syntax, host/path, relative url") + << QUrl("hostandport://host/path") << "#anchor" + << QUrl("hostandport://host/path#anchor"); + QTest::newRow("HostAndPort syntax, host/path, absolute url") + << QUrl("hostandport://host/path") << "hostandport://host/path#anchor" + << QUrl("hostandport://host/path#anchor"); + QTest::newRow("HostAndPort syntax, host:port, relative url") + << QUrl("hostandport://host:3000") << "#anchor" << QUrl("hostandport://host/#anchor"); + QTest::newRow("HostAndPort syntax, host:port, absolute url") + << QUrl("hostandport://host:3000") << "hostandport://host:3000#anchor" + << QUrl("hostandport://host/#anchor"); + QTest::newRow("HostAndPort syntax, userinfo@host:port, relative url") + << QUrl("hostandport://user:password@host:3000") << "#anchor" + << QUrl("hostandport://host/#anchor"); + QTest::newRow("HostAndPort syntax, userinfo@host:port, absolute url") + << QUrl("hostandport://user:password@host:3000") + << "hostandport://user:password@host:3000#anchor" << QUrl("hostandport://host/#anchor"); + + // HostPortAndUserInformation syntax + // - We lose the port and it preserves the user info in the authority after navigation + QTest::newRow("HostPortAndUserInformation syntax, host only, relative url") + << QUrl("hostportuserinfo://host") << "#anchor" + << QUrl("hostportuserinfo://host/#anchor"); + QTest::newRow("HostPortAndUserInformation syntax, host only, absolute url") + << QUrl("hostportuserinfo://host") << "hostportuserinfo://host#anchor" + << QUrl("hostportuserinfo://host/#anchor"); + QTest::newRow("HostPortAndUserInformation syntax, host/path, relative url") + << QUrl("hostportuserinfo://host/path") << "#anchor" + << QUrl("hostportuserinfo://host/path#anchor"); + QTest::newRow("HostPortAndUserInformation syntax, host/path, absolute url") + << QUrl("hostportuserinfo://host/path") << "hostportuserinfo://host/path#anchor" + << QUrl("hostportuserinfo://host/path#anchor"); + QTest::newRow("HostPortAndUserInformation syntax, host:port, relative url") + << QUrl("hostportuserinfo://host:3000") << "#anchor" + << QUrl("hostportuserinfo://host/#anchor"); + QTest::newRow("HostPortAndUserInformation syntax, host:port, absolute url") + << QUrl("hostportuserinfo://host:3000") << "hostportuserinfo://host:3000#anchor" + << QUrl("hostportuserinfo://host/#anchor"); + QTest::newRow("HostPortAndUserInformation syntax, userinfo@host:port, relative url") + << QUrl("hostportuserinfo://user:password@host:3000") << "#anchor" + << QUrl("hostportuserinfo://user:password@host/#anchor"); + QTest::newRow("HostPortAndUserInformation syntax, userinfo@host:port, absolute url") + << QUrl("hostportuserinfo://user:password@host:3000") + << "hostportuserinfo://user:password@host:3000#anchor" + << QUrl("hostportuserinfo://user:password@host/#anchor"); +} + +void tst_Schemes::customSchemeFragmentNavigation() +{ + QFETCH(QUrl, baseUrl); + QFETCH(QUrl, expectedUrl); + QFETCH(QString, linkUrl); + + QWebEngineProfile profile; + QWebEnginePage page(&profile); + QWebEngineView view; + view.setPage(&page); + view.resize(800, 600); + view.show(); + QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); + QSignalSpy urlChangedSpy(&page, SIGNAL(urlChanged(QUrl))); + + CustomScheme *schemeHandler = new CustomScheme(linkUrl); + page.profile()->installUrlSchemeHandler(baseUrl.scheme().toUtf8(), schemeHandler); + + view.load(baseUrl); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "window.scrollY").toInt() == 0); + + QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, elementCenter(&page, "link")); + QVERIFY(urlChangedSpy.wait()); + QCOMPARE(page.url(), expectedUrl); + QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "window.scrollY").toInt() > 0); + + // Same document navigation doesn't emit loadFinished + QTRY_COMPARE(loadFinishedSpy.size(), 1); +} + QTEST_MAIN(tst_Schemes) #include "tst_schemes.moc" diff --git a/tests/auto/widgets/shutdown/CMakeLists.txt b/tests/auto/widgets/shutdown/CMakeLists.txt index 12ca27c3d..e2ce9eeb9 100644 --- a/tests/auto/widgets/shutdown/CMakeLists.txt +++ b/tests/auto/widgets/shutdown/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + qt_internal_add_test(tst_shutdown SOURCES tst_shutdown.cpp diff --git a/tests/auto/widgets/shutdown/shutdown.pro b/tests/auto/widgets/shutdown/shutdown.pro deleted file mode 100644 index e99c7f493..000000000 --- a/tests/auto/widgets/shutdown/shutdown.pro +++ /dev/null @@ -1 +0,0 @@ -include(../tests.pri) diff --git a/tests/auto/widgets/shutdown/tst_shutdown.cpp b/tests/auto/widgets/shutdown/tst_shutdown.cpp index 5c1e426d2..c2b31bb80 100644 --- a/tests/auto/widgets/shutdown/tst_shutdown.cpp +++ b/tests/auto/widgets/shutdown/tst_shutdown.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 #include <QtTest/QtTest> diff --git a/tests/auto/widgets/spellchecking/CMakeLists.txt b/tests/auto/widgets/spellchecking/CMakeLists.txt index afed7e28b..d0c7656c1 100644 --- a/tests/auto/widgets/spellchecking/CMakeLists.txt +++ b/tests/auto/widgets/spellchecking/CMakeLists.txt @@ -1,4 +1,8 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../util/util.cmake) +include(../../../../src/core/api/Qt6WebEngineCoreMacros.cmake) qt_internal_add_test(tst_spellchecking SOURCES @@ -16,34 +20,14 @@ qt_internal_add_resource(tst_spellchecking "tst_spellchecking" ) file(GLOB_RECURSE dicts - RELATIVE ${CMAKE_CURRENT_LIST_DIR}/dict + ABSOLUTE ${CMAKE_CURRENT_LIST_DIR}/dict *.dic ) -if(QT_GENERATOR_IS_MULTI_CONFIG) - set(spellcheckerDir ${CMAKE_CURRENT_BINARY_DIR}/dict/qtwebengine_dictionaries) -else() - set(spellcheckerDir ${CMAKE_CURRENT_BINARY_DIR}/qtwebengine_dictionaries) -endif() - foreach(dictFile ${dicts}) - get_filename_component(dictName ${dictFile} NAME_WE) - add_custom_command(TARGET tst_spellchecking - PRE_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory ${spellcheckerDir} - COMMAND $<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::qwebengine_convert_dict> - ${CMAKE_CURRENT_SOURCE_DIR}/dict/${dictFile} - ${spellcheckerDir}/${dictName}.bdic - COMMENT "Running qwebengine_convert_dict" + qt_add_webengine_dictionary( + TARGET tst_spellchecking + SOURCE "${dictFile}" + OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) endforeach() - -# copy dictionaries to $<CONFIG> build dir -if(QT_GENERATOR_IS_MULTI_CONFIG) - add_custom_command(TARGET tst_spellchecking - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E echo Copying dictionares - COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_BINARY_DIR}/dict - ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG> - ) -endif() diff --git a/tests/auto/widgets/spellchecking/spellchecking.pro b/tests/auto/widgets/spellchecking/spellchecking.pro deleted file mode 100644 index a36c82e20..000000000 --- a/tests/auto/widgets/spellchecking/spellchecking.pro +++ /dev/null @@ -1,24 +0,0 @@ -include(../tests.pri) - -DISTFILES += \ - dict/en-US.dic \ - dict/en-US.aff \ - dict/de-DE.dic \ - dict/de-DE.aff \ - -qtPrepareTool(CONVERT_TOOL, qwebengine_convert_dict) - -debug_and_release { - CONFIG(debug, debug|release): DICTIONARIES_DIR = debug/qtwebengine_dictionaries - else: DICTIONARIES_DIR = release/qtwebengine_dictionaries -} else { - DICTIONARIES_DIR = qtwebengine_dictionaries -} - -dict.files = $$PWD/dict/en-US.dic $$PWD/dict/de-DE.dic -dictoolbuild.input = dict.files -dictoolbuild.output = $${DICTIONARIES_DIR}/${QMAKE_FILE_BASE}.bdic -dictoolbuild.commands = $${CONVERT_TOOL} ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT} -dictoolbuild.name = Build ${QMAKE_FILE_IN_BASE} -dictoolbuild.CONFIG = no_link target_predeps -QMAKE_EXTRA_COMPILERS += dictoolbuild diff --git a/tests/auto/widgets/spellchecking/tst_spellchecking.cpp b/tests/auto/widgets/spellchecking/tst_spellchecking.cpp index 7263904ce..c643a56ba 100644 --- a/tests/auto/widgets/spellchecking/tst_spellchecking.cpp +++ b/tests/auto/widgets/spellchecking/tst_spellchecking.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 <util.h> #include <QtTest/QtTest> @@ -170,19 +145,20 @@ void tst_Spellchecking::spellcheck() QVariantList list = evaluateJavaScriptSync(m_view->page(), "findWordPosition('I lowe Qt ....','lowe');").toList(); QRect rect(list[0].value<int>(),list[1].value<int>(),list[2].value<int>(),list[3].value<int>()); + QTRY_VERIFY(m_view->focusWidget()); //type text, spellchecker needs time QTest::mouseMove(m_view->focusWidget(), QPoint(20,20)); 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); } // make sure text is there QString result = evaluateJavaScriptSync(m_view->page(), "text();").toString(); - QVERIFY(result == text); + QCOMPARE(result, text); bool gotMisspelledWord = false; // clumsy QTRY_VERIFY still execs expr after first success QString detail; diff --git a/tests/auto/widgets/tests.pri b/tests/auto/widgets/tests.pri deleted file mode 100644 index e69820ffe..000000000 --- a/tests/auto/widgets/tests.pri +++ /dev/null @@ -1,21 +0,0 @@ -include($$QTWEBENGINE_OUT_ROOT/src/core/qtwebenginecore-config.pri) # workaround for QTBUG-68093 -QT_FOR_CONFIG += webenginecore-private - -TEMPLATE = app - -CONFIG += testcase - -VPATH += $$_PRO_FILE_PWD_ -TARGET = tst_$$TARGET - -SOURCES += $${TARGET}.cpp -INCLUDEPATH += $$PWD - -exists($$_PRO_FILE_PWD_/$${TARGET}.qrc): RESOURCES += $${TARGET}.qrc - -QT += testlib network webenginewidgets widgets quick quickwidgets - -# This define is used by some tests to look up resources in the source tree -DEFINES += TESTS_SOURCE_DIR=\\\"$$PWD/\\\" - -include(../embed_info_plist.pri) diff --git a/tests/auto/widgets/touchinput/CMakeLists.txt b/tests/auto/widgets/touchinput/CMakeLists.txt index 82e3fca4a..bd76666d6 100644 --- a/tests/auto/widgets/touchinput/CMakeLists.txt +++ b/tests/auto/widgets/touchinput/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include(../../util/util.cmake) qt_internal_add_test(tst_touchinput diff --git a/tests/auto/widgets/touchinput/touchinput.pro b/tests/auto/widgets/touchinput/touchinput.pro deleted file mode 100644 index d91c0074b..000000000 --- a/tests/auto/widgets/touchinput/touchinput.pro +++ /dev/null @@ -1,2 +0,0 @@ -include(../tests.pri) -QT *= gui-private diff --git a/tests/auto/widgets/touchinput/tst_touchinput.cpp b/tests/auto/widgets/touchinput/tst_touchinput.cpp index d60fd1d7b..42178558c 100644 --- a/tests/auto/widgets/touchinput/tst_touchinput.cpp +++ b/tests/auto/widgets/touchinput/tst_touchinput.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include <util.h> @@ -37,6 +12,12 @@ static QPointingDevice* s_touchDevice = nullptr; +struct Page : QWebEnginePage +{ + QStringList alerts; + void javaScriptAlert(const QUrl &/*origin*/, const QString &msg) override { alerts.append(msg); } +}; + class TouchInputTest : public QObject { Q_OBJECT @@ -54,29 +35,38 @@ private Q_SLOTS: void pinchZoom_data(); void pinchZoom(); void complexSequence(); + void buttonClickHandler(); + void htmlSelectPopup(); private: + Page page; QWebEngineView view; QSignalSpy loadSpy { &view, &QWebEngineView::loadFinished }; QPoint notextCenter, textCenter, inputCenter; QString activeElement() { return evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(); } + void makeTouch(QWindow *w, const QPoint &p) { + QTest::touchEvent(w, s_touchDevice).press(1, p); + QTest::touchEvent(w, s_touchDevice).release(1, p); + } + void makeTouch(const QPoint &p) { makeTouch(view.windowHandle(), p); } + void gestureScroll(bool down) { auto target = view.focusProxy(); QPoint p(target->width() / 2, target->height() / 4 * (down ? 3 : 1)); - QTest::touchEvent(target, s_touchDevice).press(42, p, target); + QTest::touchEvent(target, s_touchDevice).press(1, p, target); QSignalSpy spy(view.page(), &QWebEnginePage::scrollPositionChanged); for (int i = 0; i < 3; ++i) { down ? p -= QPoint(5, 15) : p += QPoint(5, 15); QTest::qWait(100); // too fast and events are recognized as fling gesture - QTest::touchEvent(target, s_touchDevice).move(42, p, target); + QTest::touchEvent(target, s_touchDevice).move(1, p, target); spy.wait(); } - QTest::touchEvent(target, s_touchDevice).release(42, p, target); + QTest::touchEvent(target, s_touchDevice).release(1, p, target); } void gesturePinch(bool zoomIn, bool tapOneByOne = false) { @@ -85,10 +75,10 @@ private: auto t1 = p - QPoint(zoomIn ? 50 : 150, 10), t2 = p + QPoint(zoomIn ? 50 : 150, 10); if (tapOneByOne) { - QTest::touchEvent(target, s_touchDevice).press(42, t1, target); - QTest::touchEvent(target, s_touchDevice).stationary(42).press(24, t2, target); + QTest::touchEvent(target, s_touchDevice).press(0, t1, target); + QTest::touchEvent(target, s_touchDevice).stationary(0).press(1, t2, target); } else { - QTest::touchEvent(target, s_touchDevice).press(42, t1, target).press(24, t2, target); + QTest::touchEvent(target, s_touchDevice).press(0, t1, target).press(1, t2, target); } for (int i = 0; i < 3; ++i) { @@ -100,14 +90,14 @@ private: t2 -= QPoint(35, 5); } QTest::qWait(100); // too fast and events are recognized as fling gesture - QTest::touchEvent(target, s_touchDevice).move(24, t1, target).move(42, t2, target); + QTest::touchEvent(target, s_touchDevice).move(1, t1, target).move(0, t2, target); } if (tapOneByOne) { - QTest::touchEvent(target, s_touchDevice).stationary(42).release(24, t2, target); - QTest::touchEvent(target, s_touchDevice).release(42, t1, target); + QTest::touchEvent(target, s_touchDevice).stationary(0).release(1, t2, target); + QTest::touchEvent(target, s_touchDevice).release(0, t1, target); } else { - QTest::touchEvent(target, s_touchDevice).release(42, t1, target).release(24, t2, target); + QTest::touchEvent(target, s_touchDevice).release(0, t1, target).release(1, t2, target); } } @@ -131,6 +121,7 @@ void TouchInputTest::initTestCase() { s_touchDevice = QTest::createTouchDevice(); + view.setPage(&page); view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false); view.show(); view.resize(480, 320); @@ -139,7 +130,10 @@ 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>" "<table style='width: 100%; padding: 15px; text-align: center;'>" "<tr><td>BEFORE</td><td><div class='rect' style='background-color: #00f;'></div></td><td>AFTER</td></tr>" "<tr><td>BEFORE</td><td><div class='rect' style='background-color: #0f0;'></div></td><td>AFTER</td></tr>" @@ -163,6 +157,7 @@ void TouchInputTest::cleanup() evaluateJavaScriptSync(view.page(), "window.scrollTo(0, 0)"); QTRY_COMPARE(getScrollPosition(), 0); QTRY_COMPARE(pageScrollPosition(), 0); + page.alerts.clear(); } void TouchInputTest::touchTap() @@ -308,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); } @@ -345,5 +340,33 @@ void TouchInputTest::complexSequence() } } +void TouchInputTest::buttonClickHandler() +{ + auto buttonCenter = elementGeometry(view.page(), "btn").center(); + makeTouch(buttonCenter); + QTRY_VERIFY(!page.alerts.isEmpty()); + QCOMPARE(page.alerts.first(), "button clicked!"); + QCOMPARE(page.alerts.size(), 1); + QEXPECT_FAIL("", "Shouldn't trigger twice due to synthesized mouse events for touch", Continue); + QTRY_VERIFY_WITH_TIMEOUT(page.alerts.size() == 2, 500); +} + +void TouchInputTest::htmlSelectPopup() +{ + auto selectRect = elementGeometry(view.page(), "select"); + makeTouch(selectRect.center()); + QTRY_VERIFY(QApplication::activePopupWidget()); + QCOMPARE(activeElement(), QStringLiteral("select")); + + auto popup = QApplication::activePopupWidget(); + makeTouch(popup->windowHandle(), QPoint(popup->width() / 2, popup->height() / 2)); + QTRY_VERIFY(!QApplication::activePopupWidget()); + + QTRY_VERIFY(!page.alerts.isEmpty()); + QCOMPARE(page.alerts.first(), "option changed to: O2"); + QEXPECT_FAIL("", "Shouldn't trigger twice due to synthesized mouse events for touch", Continue); + QTRY_VERIFY_WITH_TIMEOUT(page.alerts.size() == 2, 500); +} + QTEST_MAIN(TouchInputTest) #include "tst_touchinput.moc" diff --git a/tests/auto/widgets/widgets.pro b/tests/auto/widgets/widgets.pro deleted file mode 100644 index b793ce69e..000000000 --- a/tests/auto/widgets/widgets.pro +++ /dev/null @@ -1,47 +0,0 @@ -include($$QTWEBENGINE_OUT_ROOT/src/core/qtwebenginecore-config.pri) # workaround for QTBUG-68093 -QT_FOR_CONFIG += webenginecore webenginecore-private - -TEMPLATE = subdirs - -SUBDIRS += \ - defaultsurfaceformat \ - faviconmanager \ - loadsignals \ - offscreen \ - proxy \ - proxypac \ - schemes \ - shutdown \ - qwebenginedownloadrequest \ - qwebenginepage \ - qwebenginehistory \ - qwebengineprofile \ - qwebenginescript \ - qwebengineview - -# Synthetic touch events are not supported on macOS -!macos: SUBDIRS += touchinput - -qtConfig(accessibility) { - SUBDIRS += accessibility -} - -qtConfig(webengine-printing-and-pdf) { - SUBDIRS += printing -} - -qtConfig(webengine-spellchecker):!cross_compile { - !qtConfig(webengine-native-spellchecker) { - SUBDIRS += spellchecking - } else { - message("Spellcheck test will not be built because it depends on usage of Hunspell dictionaries.") - } -} - -# QTBUG-60268 -boot2qt: SUBDIRS -= accessibility defaultsurfaceformat devtools \ - qwebenginepage \ - qwebengineprofile \ - qwebengineview - -darwin|win32: SUBDIRS -= offscreen diff --git a/tests/manual/CMakeLists.txt b/tests/manual/CMakeLists.txt new file mode 100644 index 000000000..4bb14e72d --- /dev/null +++ b/tests/manual/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(examples) +add_subdirectory(quick) +add_subdirectory(widgets) diff --git a/tests/manual/examples/CMakeLists.txt b/tests/manual/examples/CMakeLists.txt new file mode 100644 index 000000000..6c9b56c43 --- /dev/null +++ b/tests/manual/examples/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(widgets) +add_subdirectory(quick) 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..e0f57ed7e --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/CMakeLists.txt @@ -0,0 +1,56 @@ +# 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.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..a893a24eb --- /dev/null +++ b/tests/manual/examples/quick/customtouchhandle/CMakeLists.txt @@ -0,0 +1,36 @@ +# 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.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/quick/touchbrowser/qml.qrc b/tests/manual/examples/quick/customtouchhandle/qml.qrc index 45210fe36..5f6483ac3 100644 --- a/tests/manual/quick/touchbrowser/qml.qrc +++ b/tests/manual/examples/quick/customtouchhandle/qml.qrc @@ -1,6 +1,5 @@ <RCC> <qresource prefix="/"> <file>main.qml</file> - <file>AddressBar.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..a46591ed0 --- /dev/null +++ b/tests/manual/examples/quick/minimal/CMakeLists.txt @@ -0,0 +1,36 @@ +# 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.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/auto/widgets/spellchecking/tst_spellchecking.qrc b/tests/manual/examples/quick/minimal/qml.qrc index 505b932c7..0ff3892d9 100644 --- a/tests/auto/widgets/spellchecking/tst_spellchecking.qrc +++ b/tests/manual/examples/quick/minimal/qml.qrc @@ -1,5 +1,6 @@ <RCC> <qresource prefix="/"> - <file>resources/index.html</file> + <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..5a9b512e1 --- /dev/null +++ b/tests/manual/examples/quick/webengineaction/CMakeLists.txt @@ -0,0 +1,37 @@ +# 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.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/auto/quick/qtbug-70248/test.qrc b/tests/manual/examples/quick/webengineaction/qml.qrc index 83fea5eb0..5f6483ac3 100644 --- a/tests/auto/quick/qtbug-70248/test.qrc +++ b/tests/manual/examples/quick/webengineaction/qml.qrc @@ -1,5 +1,5 @@ <RCC> <qresource prefix="/"> - <file>test.qml</file> + <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..463e12f5b --- /dev/null +++ b/tests/manual/examples/widgets/minimal/CMakeLists.txt @@ -0,0 +1,24 @@ +# 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.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 new file mode 100644 index 000000000..d6c4b88a9 --- /dev/null +++ b/tests/manual/quick/CMakeLists.txt @@ -0,0 +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..e0ff6f3e7 --- /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 BSD-3-Clause + +#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..36317c176 --- /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 WITH Qt-GPL-exception-1.0 + +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 + + onFeaturePermissionRequested: function(securityOrigin, feature) { + if (feature === WebEngineView.Geolocation) { + geoPermissionRequested = true + if (deniedGeolocation) { + webEngineView.grantFeaturePermission(securityOrigin, feature, false) + } + else { + webEngineView.grantFeaturePermission(securityOrigin, feature, true) + } + } + } + +} diff --git a/tests/manual/quick/pdf/bookmarks-list.qml b/tests/manual/quick/pdf/bookmarks-list.qml new file mode 100644 index 000000000..2be0d6848 --- /dev/null +++ b/tests/manual/quick/pdf/bookmarks-list.qml @@ -0,0 +1,136 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs +import QtQuick.Pdf +import QtQuick.Shapes + +ApplicationWindow { + id: root + width: 800 + height: 940 + title: doc.source + visible: true + + property PdfDocument doc: PdfDocument { source: "test.pdf" } + + Component.onCompleted: { + if (Application.arguments.length > 2) + doc.source = Application.arguments[Application.arguments.length - 1] + } + FileDialog { + id: fileDialog + title: "Open a PDF file" + nameFilters: [ "PDF files (*.pdf)" ] + onAccepted: doc.source = selectedFile + } + + SplitView { + anchors.fill: parent + + Pane { + SplitView.minimumWidth: 6 + SplitView.preferredWidth: 200 + clip: true + ListView { + ScrollIndicator.vertical: ScrollIndicator { } + anchors.fill: parent + delegate: ItemDelegate { + width: parent.width + text: model.title + background: Item { } + onClicked: image.currentFrame = page + } + model: PdfBookmarkModel { + document: root.doc + } + } + } + + Flickable { + contentWidth: paper.width + contentHeight: paper.height + z: -1 + Rectangle { + id: paper + width: image.width + height: image.height + PdfPageImage { + id: image + document: doc + + property real zoomFactor: Math.sqrt(2) + + Shortcut { + sequence: StandardKey.MoveToNextPage + enabled: image.currentFrame < image.frameCount - 1 + onActivated: image.currentFrame++ + } + Shortcut { + sequence: StandardKey.MoveToPreviousPage + enabled: image.currentFrame > 0 + onActivated: image.currentFrame-- + } + Shortcut { + sequence: StandardKey.ZoomIn + enabled: image.sourceSize.width < 5000 + onActivated: { + image.sourceSize.width = image.implicitWidth * image.zoomFactor + image.sourceSize.height = image.implicitHeight * image.zoomFactor + } + } + Shortcut { + sequence: StandardKey.ZoomOut + enabled: image.width > 50 + onActivated: { + image.sourceSize.width = image.implicitWidth / image.zoomFactor + image.sourceSize.height = image.implicitHeight / image.zoomFactor + } + } + Shortcut { + sequence: "Ctrl+0" + onActivated: image.sourceSize = undefined + } + Shortcut { + sequence: StandardKey.Open + onActivated: fileDialog.open() + } + Shortcut { + sequence: StandardKey.Quit + onActivated: Qt.quit() + } + } + + Repeater { + model: PdfLinkModel { + id: linkModel + document: doc + page: image.currentFrame + } + delegate: Rectangle { + color: "transparent" + border.color: "lightgrey" + x: rect.x + y: rect.y + width: rect.width + height: rect.height + HoverHandler { cursorShape: Qt.PointingHandCursor } + TapHandler { + onTapped: { + if (page >= 0) + image.currentFrame = page + else + Qt.openUrlExternally(url) + } + } + } + } + } + } + } + Text { + anchors { bottom: parent.bottom; right: parent.right; margins: 6 } + text: "page " + doc.pageLabel(image.currentFrame) + " of " + doc.pageCount + } +} diff --git a/tests/manual/quick/pdf/bookmarks.qml b/tests/manual/quick/pdf/bookmarks.qml new file mode 100644 index 000000000..e12629b31 --- /dev/null +++ b/tests/manual/quick/pdf/bookmarks.qml @@ -0,0 +1,147 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs +import QtQuick.Pdf +import QtQuick.Shapes + +ApplicationWindow { + id: root + width: 800 + height: 940 + color: "darkgrey" + title: doc.source + visible: true + + property PdfDocument doc: PdfDocument { source: "test.pdf" } + + Component.onCompleted: { + if (Application.arguments.length > 2) + doc.source = Application.arguments[Application.arguments.length - 1] + } + FileDialog { + id: fileDialog + title: "Open a PDF file" + nameFilters: [ "PDF files (*.pdf)" ] + onAccepted: doc.source = selectedFile + } + + SplitView { + anchors.fill: parent + + Pane { + SplitView.minimumWidth: 6 + SplitView.preferredWidth: 200 + TreeView { + id: bookmarksTree + anchors.fill: parent + columnWidthProvider: function() { return width } + onWidthChanged: forceLayout() // workaround to avoid column width getting stuck + clip: true + delegate: TreeViewDelegate { + width: parent.width + onClicked: image.currentFrame = page + } + model: PdfBookmarkModel { + document: root.doc + } + ScrollIndicator.vertical: ScrollIndicator { + // get the ScrollIndicator out into the margin area of the Pane... + // no need to overlap the tree when so much space is wasted anyway + parent: bookmarksTree.parent + anchors { + top: bookmarksTree.top + left: bookmarksTree.right + bottom: bookmarksTree.bottom + } + } + } + } + + ScrollView { + contentWidth: paper.width + contentHeight: paper.height + + Rectangle { + id: paper + width: image.width + height: image.height + PdfPageImage { + id: image + document: doc + + property real zoomFactor: Math.sqrt(2) + + Shortcut { + sequence: StandardKey.MoveToNextPage + enabled: image.currentFrame < image.frameCount - 1 + onActivated: image.currentFrame++ + } + Shortcut { + sequence: StandardKey.MoveToPreviousPage + enabled: image.currentFrame > 0 + onActivated: image.currentFrame-- + } + Shortcut { + sequence: StandardKey.ZoomIn + enabled: image.sourceSize.width < 5000 + onActivated: { + image.sourceSize.width = image.implicitWidth * image.zoomFactor + image.sourceSize.height = image.implicitHeight * image.zoomFactor + } + } + Shortcut { + sequence: StandardKey.ZoomOut + enabled: image.width > 50 + onActivated: { + image.sourceSize.width = image.implicitWidth / image.zoomFactor + image.sourceSize.height = image.implicitHeight / image.zoomFactor + } + } + Shortcut { + sequence: "Ctrl+0" + onActivated: image.sourceSize = undefined + } + Shortcut { + sequence: StandardKey.Open + onActivated: fileDialog.open() + } + Shortcut { + sequence: StandardKey.Quit + onActivated: Qt.quit() + } + } + + Repeater { + model: PdfLinkModel { + id: linkModel + document: doc + page: image.currentFrame + } + delegate: Rectangle { + color: "transparent" + border.color: "lightgrey" + x: rect.x + y: rect.y + width: rect.width + height: rect.height + HoverHandler { cursorShape: Qt.PointingHandCursor } + TapHandler { + onTapped: { + if (page >= 0) + image.currentFrame = page + else + Qt.openUrlExternally(url) + } + } + } + } + } + } + } + Label { + anchors { bottom: parent.bottom; right: parent.right; margins: 6 } + text: "page " + doc.pageLabel(image.currentFrame) + " of " + doc.pageCount + } +} diff --git a/tests/manual/quick/pdf/gridview.qml b/tests/manual/quick/pdf/gridview.qml new file mode 100644 index 000000000..773e72388 --- /dev/null +++ b/tests/manual/quick/pdf/gridview.qml @@ -0,0 +1,62 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +import QtQuick +import QtQuick.Pdf + +Window { + width: 320 + height: 440 + color: "lightgrey" + title: doc.source + visible: true + + property real cellSize: 150 + + PdfDocument { + id: doc + source: "test.pdf" + } + + GridView { + id: view + anchors.fill: parent + anchors.margins: 10 + model: doc.pageModel + cellWidth: cellSize + cellHeight: cellSize + delegate: Item { + required property int index + required property string label + required property size pointSize + width: view.cellWidth + height: view.cellHeight + Rectangle { + id: paper + width: image.width + height: image.height + x: (parent.width - width) / 2 + y: (parent.height - height - pageNumber.height) / 2 + PdfPageImage { + id: image + document: doc + currentFrame: index + asynchronous: true + fillMode: Image.PreserveAspectFit + property bool landscape: pointSize.width > pointSize.height + width: landscape ? Math.min(view.cellWidth, pointSize.width) + : height * pointSize.width / pointSize.height + height: landscape ? width * pointSize.height / pointSize.width + : Math.min(view.cellHeight - pageNumber.height, pointSize.height) + sourceSize.width: width + sourceSize.height: height + } + } + Text { + id: pageNumber + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + text: "Page " + label + } + } + } +} diff --git a/tests/manual/quick/pdf/listview.qml b/tests/manual/quick/pdf/listview.qml index 361ae7d89..d01be9e86 100644 --- a/tests/manual/quick/pdf/listview.qml +++ b/tests/manual/quick/pdf/listview.qml @@ -1,55 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -import QtQuick 2.14 -import QtQuick.Window 2.14 -import QtQuick.Pdf 5.15 +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +import QtQuick +import QtQuick.Pdf Window { width: 600 @@ -82,7 +34,7 @@ Window { } } Text { - text: "Page " + (image.currentFrame + 1) + text: "Page " + doc.pageLabel(image.currentFrame) } } } diff --git a/tests/manual/quick/pdf/multipleDocuments.qml b/tests/manual/quick/pdf/multipleDocuments.qml new file mode 100644 index 000000000..055808ab6 --- /dev/null +++ b/tests/manual/quick/pdf/multipleDocuments.qml @@ -0,0 +1,182 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs +import QtQuick.Layouts +import QtQuick.Pdf +import Qt.labs.animation + +ApplicationWindow { + id: root + width: 1280 + height: 1024 + color: "transparent" + flags: Qt.FramelessWindowHint + visibility: Window.FullScreen + property string source // for main.cpp + property real scaleStep: Math.sqrt(2) + + Component { + id: pdfWindow + + PdfPageView { + property alias source: document.source + + Rectangle { + visible: parent.activeFocus + color: "transparent" + border.color: "cyan" + border.width: 3 + anchors.fill: parent + anchors.margins: -border.width + anchors.topMargin: -toolbar.height - border.width + } + + ToolBar { + id: toolbar + width: parent.width + y: -height + RowLayout { + anchors.fill: parent + anchors.rightMargin: 6 + ToolButton { + action: Action { + shortcut: StandardKey.ZoomIn + enabled: pageView.sourceSize.width < 10000 + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-in.svg" + onTriggered: pageView.renderScale *= root.scaleStep + } + } + ToolButton { + action: Action { + shortcut: StandardKey.ZoomOut + enabled: pageView.sourceSize.width > 50 + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-out.svg" + onTriggered: pageView.renderScale /= root.scaleStep + } + } + ToolButton { + action: Action { + shortcut: "Ctrl+0" + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-original.svg" + onTriggered: pageView.resetScale() + } + } + ToolButton { + action: Action { + icon.source: "../../../../examples/pdf/singlepage/resources/go-previous-view-page.svg" + enabled: pageView.backEnabled + onTriggered: pageView.back() + } + ToolTip.visible: enabled && hovered + ToolTip.delay: 2000 + ToolTip.text: "go back" + } + SpinBox { + id: currentPageSB + from: 1 + to: document.pageCount + editable: true + value: pageView.currentPage + 1 + onValueModified: pageView.goToPage(value - 1) + Layout.maximumWidth: 60 + } + ToolButton { + action: Action { + icon.source: "../../../../examples/pdf/singlepage/resources/go-next-view-page.svg" + enabled: pageView.forwardEnabled + onTriggered: pageView.forward() + } + ToolTip.visible: enabled && hovered + ToolTip.delay: 2000 + ToolTip.text: "go forward" + } + Text { + text: document.title + elide: Text.ElideRight + Layout.fillWidth: true + } + DragHandler { + target: pageView + } + TapHandler { + onTapped: pageView.z++ + } + ToolButton { + action: Action { + shortcut: StandardKey.Close + text: "☒" + onTriggered: pageView.destroy() + } + } + } + } + + id: pageView + document: PdfDocument { + id: document + source: Qt.resolvedUrl(root.source) + onStatusChanged: (status) => { if (status === PdfDocument.Error) errorDialog.open() } + } + + DragHandler { + acceptedButtons: Qt.MiddleButton + } + DragHandler { + acceptedDevices: PointerDevice.TouchScreen + } + HoverHandler { + onHoveredChanged: if (hovered) pageView.forceActiveFocus() + } + + Dialog { + id: errorDialog + title: "Error loading " + document.source + standardButtons: Dialog.Ok + modal: true + closePolicy: Popup.CloseOnEscape + anchors.centerIn: parent + width: 300 + + Label { + id: errorField + text: document.error + } + } + } + } + + Shortcut { + sequence: StandardKey.MoveToPreviousPage + onActivated: root.activeFocusItem.goToPage(root.activeFocusItem.currentPage - 1) + } + Shortcut { + sequence: StandardKey.MoveToNextPage + onActivated: root.activeFocusItem.goToPage(root.activeFocusItem.currentPage + 1) + } + Shortcut { + sequence: StandardKey.Open + onActivated: fileDialog.open() + } + Shortcut { + sequence: StandardKey.Quit + onActivated: Qt.quit() + } + + function open(src) { + var win = pdfWindow.createObject(root, { source: src, y: 50 }) + } + + Component.onCompleted: { + if (Application.arguments.length > 2) + root.open(Application.arguments[Application.arguments.length - 1]) + } + + FileDialog { + id: fileDialog + title: "Open a PDF file" + nameFilters: [ "PDF files (*.pdf)" ] + onAccepted: root.open(selectedFile) + } +} diff --git a/tests/manual/quick/pdf/pdfPageView.qml b/tests/manual/quick/pdf/pdfPageView.qml new file mode 100644 index 000000000..22c0d5ac2 --- /dev/null +++ b/tests/manual/quick/pdf/pdfPageView.qml @@ -0,0 +1,316 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs +import QtQuick.Layouts +import QtQuick.Pdf +import Qt.labs.animation + +ApplicationWindow { + id: root + width: 800 + height: 1024 + color: "lightgrey" + title: document.title + visible: true + property string source // for main.cpp + property real scaleStep: Math.sqrt(2) + + header: ToolBar { + RowLayout { + anchors.fill: parent + anchors.rightMargin: 6 + ToolButton { + action: Action { + shortcut: StandardKey.Open + icon.source: "../../../../examples/pdf/singlepage/resources/document-open.svg" + onTriggered: fileDialog.open() + } + } + ToolButton { + action: Action { + shortcut: StandardKey.ZoomIn + enabled: pageView.sourceSize.width < 10000 + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-in.svg" + onTriggered: pageView.renderScale *= root.scaleStep + } + } + ToolButton { + action: Action { + shortcut: StandardKey.ZoomOut + enabled: pageView.sourceSize.width > 50 + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-out.svg" + onTriggered: pageView.renderScale /= root.scaleStep + } + } + ToolButton { + action: Action { + 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/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/singlepage/resources/zoom-original.svg" + onTriggered: pageView.resetScale() + } + } + ToolButton { + action: Action { + shortcut: "Ctrl+L" + icon.source: "../../../../examples/pdf/singlepage/resources/rotate-left.svg" + onTriggered: pageView.rotation -= 90 + } + } + ToolButton { + action: Action { + shortcut: "Ctrl+R" + icon.source: "../../../../examples/pdf/singlepage/resources/rotate-right.svg" + onTriggered: pageView.rotation += 90 + } + } + ToolButton { + action: Action { + icon.source: "../../../../examples/pdf/singlepage/resources/go-previous-view-page.svg" + enabled: pageView.backEnabled + onTriggered: pageView.back() + } + ToolTip.visible: enabled && hovered + ToolTip.delay: 2000 + ToolTip.text: "go back" + } + SpinBox { + id: currentPageSB + from: 0 + to: document.pageCount + editable: true + value: pageView.currentPage + onValueModified: pageView.goToPage(value) + + textFromValue: function(value) { return document.pageLabel(value) } + valueFromText: function(text) { + for (var i = 0; i < document.pageCount; ++i) { + if (document.pageLabel(i).toLowerCase().indexOf(text.toLowerCase()) === 0) + return i + } + return spinBox.value + } + + Shortcut { + sequence: StandardKey.MoveToPreviousPage + onActivated: pageView.goToPage(currentPageSB.value - 1) + } + Shortcut { + sequence: StandardKey.MoveToNextPage + onActivated: pageView.goToPage(currentPageSB.value + 1) + } + } + ToolButton { + action: Action { + icon.source: "../../../../examples/pdf//resources/go-next-view-page.svg" + enabled: pageView.forwardEnabled + onTriggered: pageView.forward() + } + ToolTip.visible: enabled && hovered + ToolTip.delay: 2000 + ToolTip.text: "go forward" + } + ToolButton { + action: Action { + shortcut: StandardKey.Copy + icon.source: "../../../../examples/pdf/singlepage/resources/edit-copy.svg" + enabled: pageView.selectedText !== "" + onTriggered: pageView.copySelectionToClipboard() + } + } + Shortcut { + sequence: StandardKey.Quit + onActivated: Qt.quit() + } + } + } + + FileDialog { + id: fileDialog + title: "Open a PDF file" + nameFilters: [ "PDF files (*.pdf)" ] + onAccepted: document.source = selectedFile + } + + Component.onCompleted: { + if (Application.arguments.length > 2) + document.source = Application.arguments[Application.arguments.length - 1] + } + + Dialog { + id: errorDialog + title: "Error loading " + document.source + standardButtons: Dialog.Ok + modal: true + closePolicy: Popup.CloseOnEscape + anchors.centerIn: parent + width: 300 + + Label { + id: errorField + text: document.error + } + } + + PdfPageView { + id: pageView + x: searchDrawer.position * searchDrawer.width // TODO binding gets broken during centering + document: PdfDocument { + id: document + source: Qt.resolvedUrl(root.source) + onStatusChanged: (status) => { if (status === PdfDocument.Error) errorDialog.open() } + } + searchString: searchField.text + onRenderScaleChanged: pageView.returnToBounds() + + DragHandler { + acceptedButtons: Qt.MiddleButton + onActiveChanged: if (!active) pageView.returnToBounds() + } + DragHandler { + acceptedDevices: PointerDevice.TouchScreen + onActiveChanged: if (!active) pageView.returnToBounds() + } + + BoundaryRule on x { + id: brx + minimumOvershoot: 100 + maximumOvershoot: 100 + minimum: Math.min(0, root.width - pageView.width) + maximum: 0 + } + + BoundaryRule on y { + id: bry + minimumOvershoot: 100 + maximumOvershoot: 100 + minimum: Math.min(0, root.height - pageView.height) + maximum: 0 + } + + function returnToBounds() { + bry.returnToBounds() + brx.returnToBounds() + } + } + + WheelHandler { + target: pageView + property: "x" + orientation: Qt.Horizontal + acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad + acceptedModifiers: Qt.NoModifier + onActiveChanged: if (!active) brx.returnToBounds() + } + WheelHandler { + target: pageView + property: "y" + orientation: Qt.Vertical + acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad + acceptedModifiers: Qt.NoModifier + onActiveChanged: if (!active) bry.returnToBounds() + } + + Drawer { + id: searchDrawer + edge: Qt.LeftEdge + modal: false + width: searchLayout.implicitWidth + y: root.header.height + height: root.contentItem.height + dim: false + Shortcut { + sequence: StandardKey.Find + onActivated: { + searchDrawer.open() + searchField.forceActiveFocus() + } + } + ColumnLayout { + id: searchLayout + anchors.fill: parent + anchors.margins: 2 + RowLayout { + ToolButton { + action: Action { + icon.source: "../../../../examples/pdf/singlepage/resources/go-up-search.svg" + shortcut: StandardKey.FindPrevious + onTriggered: pageView.searchBack() + } + ToolTip.visible: enabled && hovered + ToolTip.delay: 2000 + ToolTip.text: "find previous" + } + TextField { + id: searchField + placeholderText: "search" + Layout.minimumWidth: 200 + Layout.fillWidth: true + Image { + visible: searchField.text !== "" + source: "../../../../examples/pdf/singlepage/resources/edit-clear.svg" + anchors { + right: parent.right + top: parent.top + bottom: parent.bottom + margins: 3 + rightMargin: 5 + } + TapHandler { + onTapped: searchField.clear() + } + } + } + ToolButton { + action: Action { + icon.source: "../../../../examples/pdf/singlepage/resources/go-down-search.svg" + shortcut: StandardKey.FindNext + onTriggered: pageView.searchForward() + } + ToolTip.visible: enabled && hovered + ToolTip.delay: 2000 + ToolTip.text: "find next" + } + } + ListView { + id: searchResultsList + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + model: pageView.searchModel + ScrollBar.vertical: ScrollBar { } + delegate: ItemDelegate { + width: parent ? parent.width : 0 + text: "page " + document.pageLabel(page) + ": " + contextBefore + pageView.searchString + contextAfter + highlighted: ListView.isCurrentItem + onClicked: { + searchResultsList.currentIndex = index + pageView.goToLocation(page, location, 0) + pageView.searchModel.currentResult = indexOnPage + } + } + } + } + } + + footer: Label { + property size implicitPointSize: document.pagePointSize(pageView.currentPage) + text: "page " + (pageView.currentPage + 1) + " of " + document.pageCount + + " scale " + pageView.renderScale.toFixed(2) + + " original " + implicitPointSize.width.toFixed(1) + "x" + implicitPointSize.height.toFixed(1) + "pts" + visible: document.status === PdfDocument.Ready + } +} diff --git a/tests/manual/quick/pdf/pessimizedListView.qml b/tests/manual/quick/pdf/pessimizedListView.qml index 4ae0edabe..1b514668e 100644 --- a/tests/manual/quick/pdf/pessimizedListView.qml +++ b/tests/manual/quick/pdf/pessimizedListView.qml @@ -1,57 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -import QtQuick 2.14 -import QtQuick.Controls 2.14 -import QtQuick.Layouts 1.14 -import QtQuick.Pdf 5.15 -import Qt.labs.platform 1.1 as P +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs +import QtQuick.Layouts +import QtQuick.Pdf ApplicationWindow { width: 900 @@ -127,11 +80,11 @@ ApplicationWindow { source: "test.pdf" } - P.FileDialog { + FileDialog { id: fileDialog title: "Open a PDF file" nameFilters: [ "PDF files (*.pdf)" ] - onAccepted: doc.source = file + onAccepted: doc.source = selectedFile } ListView { @@ -150,15 +103,15 @@ ApplicationWindow { anchors.centerIn: parent running: image.status === Image.Loading } - Image { + PdfPageImage { id: image + document: doc scale: imageScale anchors.centerIn: parent sourceSize.width: doc.pagePointSize(index).width * oversamplingSB.value height: 100 fillMode: Image.PreserveAspectFit objectName: "PDF page " + index - source: doc.source currentFrame: index asynchronous: asyncCB.checked cache: cacheCB.checked diff --git a/tests/manual/quick/pdf/simplest.qml b/tests/manual/quick/pdf/simplest.qml index 0571493af..3f39bb213 100644 --- a/tests/manual/quick/pdf/simplest.qml +++ b/tests/manual/quick/pdf/simplest.qml @@ -1,53 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -import QtQuick 2.14 +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +import QtQuick Image { id: image diff --git a/tests/manual/quick/pdf/test.pdf b/tests/manual/quick/pdf/test.pdf Binary files differindex a9dc1bc29..0832dfbed 100644 --- a/tests/manual/quick/pdf/test.pdf +++ b/tests/manual/quick/pdf/test.pdf diff --git a/tests/manual/quick/pdf/underscoredLinks.qml b/tests/manual/quick/pdf/underscoredLinks.qml new file mode 100644 index 000000000..514008ca2 --- /dev/null +++ b/tests/manual/quick/pdf/underscoredLinks.qml @@ -0,0 +1,146 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs +import QtQuick.Pdf +import QtQuick.Shapes + +ApplicationWindow { + id: root + width: 800 + height: 940 + color: "darkgrey" + title: doc.source + visible: true + + property PdfDocument doc: PdfDocument { source: "test.pdf" } + + Component.onCompleted: { + if (Application.arguments.length > 2) + doc.source = Application.arguments[Application.arguments.length - 1] + } + FileDialog { + id: fileDialog + title: "Open a PDF file" + nameFilters: [ "PDF files (*.pdf)" ] + onAccepted: doc.source = selectedFile + } + ScrollView { + anchors.fill: parent + contentWidth: paper.width + contentHeight: paper.height + + Rectangle { + id: paper + width: image.width + height: image.height + PdfPageImage { + id: image + document: doc + + property real zoomFactor: Math.sqrt(2) + + Shortcut { + sequence: StandardKey.MoveToNextPage + enabled: image.currentFrame < image.frameCount - 1 + onActivated: image.currentFrame++ + } + Shortcut { + sequence: StandardKey.MoveToPreviousPage + enabled: image.currentFrame > 0 + onActivated: image.currentFrame-- + } + Shortcut { + sequence: StandardKey.ZoomIn + enabled: image.sourceSize.width < 5000 + onActivated: { + image.sourceSize.width = image.implicitWidth * image.zoomFactor + image.sourceSize.height = image.implicitHeight * image.zoomFactor + } + } + Shortcut { + sequence: StandardKey.ZoomOut + enabled: image.width > 50 + onActivated: { + image.sourceSize.width = image.implicitWidth / image.zoomFactor + image.sourceSize.height = image.implicitHeight / image.zoomFactor + } + } + Shortcut { + sequence: "Ctrl+0" + onActivated: image.sourceSize = undefined + } + Shortcut { + sequence: StandardKey.Open + onActivated: fileDialog.open() + } + Shortcut { + sequence: StandardKey.Quit + onActivated: Qt.quit() + } + } + + Menu { + id: linkContextMenu + property var currentLink + MenuItem { + text: "Go" + onTriggered: { + if (linkContextMenu.currentLink.page >= 0) + image.currentFrame = linkContextMenu.currentLink.page + else + Qt.openUrlExternally(linkContextMenu.currentLink.url) + } + } + MenuItem { + text: "Copy" + onTriggered: linkContextMenu.currentLink.copyToClipboard() + } + } + + Repeater { + model: PdfLinkModel { + id: linkModel + document: doc + page: image.currentFrame + } + delegate: PdfLinkDelegate { + x: rectangle.x + y: rectangle.y + width: rectangle.width + height: rectangle.height + onTapped: + (link) => { + if (link.page >= 0) + image.currentFrame = link.page + else + Qt.openUrlExternally(url) + } + onContextMenuRequested: + (link) => { + linkContextMenu.currentLink = link + linkContextMenu.x = x + linkContextMenu.y = y + linkContextMenu.open() + } + Shape { + anchors.fill: parent + ShapePath { + strokeWidth: 1 + strokeColor: palette.link + strokeStyle: ShapePath.DashLine + dashPattern: [ 1, 4 ] + startX: 0; startY: height + PathLine { x: width; y: height } + } + } + } + } + } + } + Label { + anchors { bottom: parent.bottom; right: parent.right; margins: 6 } + text: "page " + (image.currentFrame + 1) + " of " + doc.pageCount + } +} diff --git a/tests/manual/quick/pdf/withdoc.qml b/tests/manual/quick/pdf/withdoc.qml index 2d82a6abf..d4bf12e29 100644 --- a/tests/manual/quick/pdf/withdoc.qml +++ b/tests/manual/quick/pdf/withdoc.qml @@ -1,58 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** BSD License Usage -** Alternatively, you may use this file under the terms of the BSD license -** as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -import QtQuick 2.14 -import QtQuick.Controls 2.14 -import Qt.labs.platform 1.1 as Platform -import QtQuick.Pdf 5.15 -import QtQuick.Shapes 1.14 -import QtQuick.Window 2.14 +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +import QtQuick +import QtQuick.Controls +import QtQuick.Dialogs +import QtQuick.Pdf +import QtQuick.Shapes Window { width: 800 @@ -64,21 +16,42 @@ Window { PdfDocument { id: doc source: "test.pdf" + onPasswordRequired: function() { passwordDialog.open() } } - Platform.FileDialog { + FileDialog { id: fileDialog title: "Open a PDF file" nameFilters: [ "PDF files (*.pdf)" ] - onAccepted: doc.source = file + onAccepted: doc.source = selectedFile + } + + Dialog { + id: passwordDialog + title: "Password" + standardButtons: Dialog.Ok | Dialog.Cancel + modal: true + closePolicy: Popup.CloseOnEscape + anchors.centerIn: parent + width: 300 + + contentItem: TextField { + id: passwordField + placeholderText: qsTr("Please provide the password") + echoMode: TextInput.Password + width: parent.width + onAccepted: passwordDialog.accept() + } + onOpened: function() { passwordField.forceActiveFocus() } + onAccepted: doc.password = passwordField.text } PdfSelection { id: selection document: doc page: image.currentFrame - fromPoint: dragHandler.centroid.pressPosition - toPoint: dragHandler.centroid.position + from: dragHandler.centroid.pressPosition + to: dragHandler.centroid.position hold: !dragHandler.active } @@ -109,9 +82,9 @@ Window { id: paper width: image.width height: image.height - Image { + PdfPageImage { id: image - source: doc.status === PdfDocument.Ready ? doc.source : "" + document: doc property real zoomFactor: Math.sqrt(2) @@ -185,7 +158,7 @@ Window { y: rect.y width: rect.width height: rect.height -// HoverHandler { cursorShape: Qt.PointingHandCursor } // 5.15 onward (QTBUG-68073) + HoverHandler { cursorShape: Qt.PointingHandCursor } TapHandler { onTapped: { if (page >= 0) @@ -201,6 +174,6 @@ Window { } Text { anchors.bottom: parent.bottom - text: "page " + (image.currentFrame + 1) + " of " + doc.pageCount + text: "page " + (image.currentFrame + 1) + " of " + doc.pageCount + " label: " + doc.pageLabel(image.currentFrame) } } 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 1daae6dbf..42188c94e 100644 --- a/tests/manual/quick/touchbrowser/AddressBar.qml +++ b/tests/manual/quick/touchbrowser/AddressBar.qml @@ -1,34 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 +// 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 + +import QtQuick +import QtQuick.Controls Rectangle { id: root @@ -59,6 +33,14 @@ Rectangle { TextField { id: addressField anchors.fill: parent + leftPadding: 30 + + background: Rectangle { + color: "transparent" + border.color: "black" + border.width: 1 + radius: root.radius + } Image { anchors.verticalCenter: addressField.verticalCenter @@ -78,17 +60,6 @@ Rectangle { visible: root.progress < 100 } - style: TextFieldStyle { - padding.left: 30 - - background: Rectangle { - color: "transparent" - border.color: "black" - border.width: 1 - radius: root.radius - } - } - onActiveFocusChanged: { if (activeFocus) selectAll(); diff --git a/tests/manual/quick/touchbrowser/CMakeLists.txt b/tests/manual/quick/touchbrowser/CMakeLists.txt new file mode 100644 index 000000000..0d3275e58 --- /dev/null +++ b/tests/manual/quick/touchbrowser/CMakeLists.txt @@ -0,0 +1,31 @@ +# 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(-DQUICK_TOUCHBROWSER) + +qt_internal_add_manual_test(touchbrowser-quick + GUI + SOURCES + main.cpp + resources.qrc + ${TOUCHMOCKING_DIR}/touchmockingapplication.cpp + ${TOUCHMOCKING_DIR}/touchmockingapplication.h + ${TOUCHMOCKING_DIR}/utils.h + LIBRARIES + Qt::Core + Qt::Quick + Qt::WebEngineQuick + ENABLE_AUTOGEN_TOOLS + moc +) + diff --git a/tests/manual/quick/touchbrowser/MockTouchPoint.qml b/tests/manual/quick/touchbrowser/MockTouchPoint.qml new file mode 100644 index 000000000..895e12e70 --- /dev/null +++ b/tests/manual/quick/touchbrowser/MockTouchPoint.qml @@ -0,0 +1,26 @@ +// 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 +import QtQuick + +Item { + id: mockTouchPoint + + property bool pressed: false + property int pointId: 0 + + Image { + source: "qrc:/touchpoint.png" + x: -(width / 2) + y: -(height / 2) + opacity: mockTouchPoint.pressed ? 0.6 : 0.0 + + Behavior on opacity { + NumberAnimation { duration: 200 } + } + + Text { + text: mockTouchPoint.pointId + anchors.centerIn: parent + } + } +} diff --git a/tests/manual/quick/touchbrowser/main.cpp b/tests/manual/quick/touchbrowser/main.cpp index ffbe81179..1f4d7d869 100644 --- a/tests/manual/quick/touchbrowser/main.cpp +++ b/tests/manual/quick/touchbrowser/main.cpp @@ -1,48 +1,21 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 -#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); @@ -54,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 926b3a941..83ede7d75 100644 --- a/tests/manual/quick/touchbrowser/main.qml +++ b/tests/manual/quick/touchbrowser/main.qml @@ -1,34 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 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 -import QtQuick.Layouts 1.3 -import QtWebEngine 1.3 +import QtQuick +import QtQuick.Layouts +import QtWebEngine Item { function load(url) { diff --git a/tests/manual/quick/touchbrowser/resources.qrc b/tests/manual/quick/touchbrowser/resources.qrc new file mode 100644 index 000000000..87d655a27 --- /dev/null +++ b/tests/manual/quick/touchbrowser/resources.qrc @@ -0,0 +1,7 @@ +<RCC> + <qresource prefix="/"> + <file>main.qml</file> + <file>AddressBar.qml</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 41b731a6a..000000000 --- a/tests/manual/quick/touchbrowser/touchmockingapplication.cpp +++ /dev/null @@ -1,287 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "touchmockingapplication.h" - -#include <qpa/qwindowsysteminterface.h> -#include <QtCore/QEvent> -#include <QtGui/QMouseEvent> -#include <QtGui/QTouchDevice> -#include <QtGui/QTouchEvent> -#include <QtQuick/QQuickItem> -#include <QtQuick/QQuickView> - -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))) { - m_touchPoints[id].setState(Qt::TouchPointReleased); - m_heldTouchPoints.remove(id); - } else - m_touchPoints[id].setState(Qt::TouchPointStationary); - - 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); - - QTouchEvent::TouchPoint touchPoint; - touchPoint.setPressure(1); - - QEvent::Type touchType = QEvent::None; - - switch (mouseEvent->type()) { - case QEvent::MouseButtonPress: - touchPoint.setId(mouseEvent->button()); - if (m_touchPoints.contains(touchPoint.id())) { - touchPoint.setState(Qt::TouchPointMoved); - touchType = QEvent::TouchUpdate; - } else { - touchPoint.setState(Qt::TouchPointPressed); - // 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; - touchPoint.setId(mouseEvent->buttons()); - touchPoint.setState(Qt::TouchPointMoved); - 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; - touchPoint.setId(mouseEvent->button()); - touchPoint.setState(Qt::TouchPointReleased); - 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() == Qt::TouchPointReleased) { - // 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, QTouchEvent::TouchPoint>::iterator it = m_touchPoints.begin(), end = m_touchPoints.end(); it != end; ++it) { - if (!(it.value().id() & touchPoint.id())) - it.value().setState(Qt::TouchPointStationary); - } - - 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, QTouchEvent::TouchPoint 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() == Qt::TouchPointReleased) { - 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. - touchPoint.setPos(mouseEvent->localPos()); - touchPoint.setScenePos(mouseEvent->screenPos()); - - if (touchPoint.state() == Qt::TouchPointPressed) - touchPoint.setStartScenePos(mouseEvent->screenPos()); - else { - const QTouchEvent::TouchPoint& oldTouchPoint = m_touchPoints[mouseButton]; - touchPoint.setStartScenePos(oldTouchPoint.startScenePos()); - touchPoint.setLastPos(oldTouchPoint.pos()); - touchPoint.setLastScenePos(oldTouchPoint.scenePos()); - } - - // Update current touch-point. - touchPoint.setId(mouseButton); - m_touchPoints.insert(mouseButton, touchPoint); -} - -bool TouchMockingApplication::sendTouchEvent(QQuickView* window, QEvent::Type type, ulong timestamp) -{ - static QTouchDevice* device = 0; - if (!device) { - device = new QTouchDevice; - device->setType(QTouchDevice::TouchScreen); - QWindowSystemInterface::registerTouchDevice(device); - } - - m_pendingFakeTouchEventCount++; - - const QList<QTouchEvent::TouchPoint>& currentTouchPoints = m_touchPoints.values(); - Qt::TouchPointStates touchPointStates = Qt::TouchPointState(); - foreach (const QTouchEvent::TouchPoint& touchPoint, currentTouchPoints) - touchPointStates |= touchPoint.state(); - - QTouchEvent event(type, device, Qt::NoModifier, touchPointStates, currentTouchPoints); - event.setTimestamp(timestamp); - event.setAccepted(false); - - QGuiApplication::notify(window, &event); - - updateVisualMockTouchPoints(window,m_holdingControl ? currentTouchPoints : QList<QTouchEvent::TouchPoint>()); - - // Get rid of touch-points that are no longer valid - foreach (const QTouchEvent::TouchPoint& touchPoint, currentTouchPoints) { - if (touchPoint.state() == Qt::TouchPointReleased) - m_touchPoints.remove(touchPoint.id()); - } - - return event.isAccepted(); -} - -void TouchMockingApplication::updateVisualMockTouchPoints(QQuickView* window,const QList<QTouchEvent::TouchPoint>& touchPoints) -{ - if (touchPoints.isEmpty()) { - // Hide all touch indicator items. - foreach (QQuickItem* item, m_activeMockComponents.values()) - item->setProperty("pressed", false); - - return; - } - - foreach (const QTouchEvent::TouchPoint& touchPoint, touchPoints) { - QQuickItem* mockTouchPointItem = m_activeMockComponents.value(touchPoint.id()); - - if (!mockTouchPointItem) { - QQmlComponent touchMockPointComponent(window->engine(), QUrl("qrc:///qml/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.pos().x()); - mockTouchPointItem->setY(touchPoint.pos().y()); - mockTouchPointItem->setWidth(touchPoint.ellipseDiameters().width()); - mockTouchPointItem->setHeight(touchPoint.ellipseDiameters().height()); - mockTouchPointItem->setProperty("pressed", QVariant(touchPoint.state() != Qt::TouchPointReleased)); - } -} diff --git a/tests/manual/quick/touchbrowser/touchmockingapplication.h b/tests/manual/quick/touchbrowser/touchmockingapplication.h deleted file mode 100644 index cdabe871f..000000000 --- a/tests/manual/quick/touchbrowser/touchmockingapplication.h +++ /dev/null @@ -1,72 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef TOUCHMOCKINGAPPLICATION_H -#define TOUCHMOCKINGAPPLICATION_H - -#include <QtCore/QHash> -#include <QtCore/QSet> -#include <QtCore/QUrl> -#include <QtGui/QGuiApplication> -#include <QtGui/QTouchEvent> - -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 *, QTouchEvent::TouchPoint, Qt::MouseButton); - bool sendTouchEvent(QQuickView *, QEvent::Type, ulong timestamp); - void updateVisualMockTouchPoints(QQuickView *,const QList<QTouchEvent::TouchPoint> &touchPoints); - -private: - bool m_realTouchEventReceived; - int m_pendingFakeTouchEventCount; - - QPointF m_lastPos; - QPointF m_lastScreenPos; - QPointF m_startScreenPos; - - QHash<int, QTouchEvent::TouchPoint> m_touchPoints; - QSet<int> m_heldTouchPoints; - QHash<int, QQuickItem*> m_activeMockComponents; - - bool m_holdingControl; -}; - -#endif // TOUCHMOCKINGAPPLICATION_H diff --git a/tests/manual/quick/touchbrowser/utils.h b/tests/manual/quick/touchbrowser/utils.h deleted file mode 100644 index b7aeefce0..000000000 --- a/tests/manual/quick/touchbrowser/utils.h +++ /dev/null @@ -1,49 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef 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/touchmocking/touchmockingapplication.cpp b/tests/manual/touchmocking/touchmockingapplication.cpp new file mode 100644 index 000000000..feedae5cd --- /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 LGPL-3.0-only OR GPL-2.0-only 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..4f6e744fc --- /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 LGPL-3.0-only OR GPL-2.0-only 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/touchmocking/touchpoint.png b/tests/manual/touchmocking/touchpoint.png Binary files differnew file mode 100644 index 000000000..7649ee991 --- /dev/null +++ b/tests/manual/touchmocking/touchpoint.png diff --git a/tests/manual/touchmocking/utils.h b/tests/manual/touchmocking/utils.h new file mode 100644 index 000000000..12d493d3f --- /dev/null +++ b/tests/manual/touchmocking/utils.h @@ -0,0 +1,25 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef UTILS_H +#define UTILS_H + +#include <QFileInfo> +#include <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/widgets/CMakeLists.txt b/tests/manual/widgets/CMakeLists.txt new file mode 100644 index 000000000..2baf669d8 --- /dev/null +++ b/tests/manual/widgets/CMakeLists.txt @@ -0,0 +1,7 @@ +add_subdirectory(inputmethods) +add_subdirectory(geolocation) +add_subdirectory(touchbrowser) +add_subdirectory(webgl) +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..f33cf5798 --- /dev/null +++ b/tests/manual/widgets/geolocation/main.cpp @@ -0,0 +1,51 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <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 handleFeaturePermissionRequested(const QUrl &securityOrigin, + QWebEnginePage::Feature feature) + { + 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) + page()->setFeaturePermission(securityOrigin, feature, + QWebEnginePage::PermissionGrantedByUser); + else + page()->setFeaturePermission(securityOrigin, feature, + QWebEnginePage::PermissionDeniedByUser); + } + +}; + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + QMainWindow w; + GeoPermissionWebView webview; + QWebEnginePage page; + QObject::connect(&page, &QWebEnginePage::featurePermissionRequested, &webview, + &GeoPermissionWebView::handleFeaturePermissionRequested); + 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/CMakeLists.txt b/tests/manual/widgets/inputmethods/CMakeLists.txt new file mode 100644 index 000000000..acd5bca50 --- /dev/null +++ b/tests/manual/widgets/inputmethods/CMakeLists.txt @@ -0,0 +1,37 @@ +# 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.16) + project(inputmethods LANGUAGES CXX) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_manual_test(inputmethods + GUI + SOURCES + colorpicker.cpp + controlview.cpp + main.cpp + referenceview.cpp + testview.cpp + webview.cpp + LIBRARIES + Qt::Core + Qt::Gui + Qt::Test + Qt::WebEngineWidgets + ENABLE_AUTOGEN_TOOLS + moc +) + +set(inputmethods_resource_files + "testdata.csv" +) + +qt_add_resources(inputmethods "inputmethods" + PREFIX + "/" + FILES + ${inputmethods_resource_files} +) diff --git a/tests/manual/widgets/inputmethods/colorpicker.cpp b/tests/manual/widgets/inputmethods/colorpicker.cpp index ee2e2e43f..cc0840bcd 100644 --- a/tests/manual/widgets/inputmethods/colorpicker.cpp +++ b/tests/manual/widgets/inputmethods/colorpicker.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 "colorpicker.h" diff --git a/tests/manual/widgets/inputmethods/colorpicker.h b/tests/manual/widgets/inputmethods/colorpicker.h index f5448ad2d..0b6b3257a 100644 --- a/tests/manual/widgets/inputmethods/colorpicker.h +++ b/tests/manual/widgets/inputmethods/colorpicker.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 COLORPICKER_H #define COLORPICKER_H @@ -32,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 4538ced4b..86bf8cca9 100644 --- a/tests/manual/widgets/inputmethods/controlview.cpp +++ b/tests/manual/widgets/inputmethods/controlview.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 "controlview.h" @@ -69,7 +44,7 @@ ControlView::ControlView(QWidget *parent) layout->addRow(m_sendEventButton); setLayout(layout); - connect(m_sendEventButton, &QPushButton::clicked, this, &ControlView::createAndSendInputMethodEvent); + connect(m_sendEventButton, &QPushButton::clicked, this, &ControlView::requestInputMethodEvent); } void ControlView::receiveInputMethodData(int start, @@ -87,7 +62,12 @@ void ControlView::receiveInputMethodData(int start, m_inputLine->setText(input); } -void ControlView::createAndSendInputMethodEvent() +const QString ControlView::getText() const +{ + return m_inputLine->text(); +} + +const QList<QInputMethodEvent::Attribute> ControlView::getAtrributes() const { int start = m_startSpin->value(); int length = m_lengthSpin->value(); @@ -102,7 +82,6 @@ void ControlView::createAndSendInputMethodEvent() QList<QInputMethodEvent::Attribute> attrs; attrs.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, start, length, format)); - QInputMethodEvent im(m_inputLine->text(), attrs); - emit sendInputMethodEvent(im); + return attrs; } diff --git a/tests/manual/widgets/inputmethods/controlview.h b/tests/manual/widgets/inputmethods/controlview.h index 0ef10f6a8..caa08593f 100644 --- a/tests/manual/widgets/inputmethods/controlview.h +++ b/tests/manual/widgets/inputmethods/controlview.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 CONTROLVIEW_H #define CONTROLVIEW_H @@ -33,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 { @@ -46,12 +24,13 @@ class ControlView : public QWidget public: explicit ControlView(QWidget *parent = 0); + const QString getText() const; + const QList<QInputMethodEvent::Attribute> getAtrributes() const; + public slots: void receiveInputMethodData(int, int, QTextCharFormat::UnderlineStyle, const QColor &, const QColor &, const QString &); - void createAndSendInputMethodEvent(); - signals: - void sendInputMethodEvent(QInputMethodEvent); + void requestInputMethodEvent(); private: QComboBox *m_underlineStyleCombo; diff --git a/tests/manual/widgets/inputmethods/main.cpp b/tests/manual/widgets/inputmethods/main.cpp index a96deb83a..2378e95ae 100644 --- a/tests/manual/widgets/inputmethods/main.cpp +++ b/tests/manual/widgets/inputmethods/main.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 <QApplication> #include <QFormLayout> @@ -49,7 +24,12 @@ class MainWindow : public QMainWindow public: explicit MainWindow(QWidget *parent = 0); +private slots: + void sendInputMethodEvents(); + private: + void closeEvent(QCloseEvent *event) override; + ControlView *m_controlView; ReferenceView *m_referenceView; WebView *m_webView; @@ -109,29 +89,44 @@ MainWindow::MainWindow(QWidget *parent) centralLayout->addLayout(leftLayout); centralLayout->addWidget(m_testView); - connect(m_testView, &TestView::sendInputMethodData, m_controlView, &ControlView::receiveInputMethodData); - connect(m_testView, &TestView::requestInputMethodEvent, m_controlView, &ControlView::createAndSendInputMethodEvent); - - connect(m_controlView, &ControlView::sendInputMethodEvent, [=](QInputMethodEvent im) { - bool processed; - QString resultText; - - processed = QApplication::sendEvent(m_referenceView->referenceInput(), &im); - resultText = processed ? QStringLiteral("<font color='green'>TRUE</font>") - : QStringLiteral("<font color='red'>FALSE</font>"); - m_referenceProcessed->setText(resultText); - - processed = QApplication::sendEvent(m_webView->focusProxy(), &im); - resultText = processed ? QStringLiteral("<font color='green'>TRUE</font>") - : QStringLiteral("<font color='red'>FALSE</font>"); - m_webProcessed->setText(resultText); - }); + connect(m_testView, &TestView::sendInputMethodData, m_controlView, + &ControlView::receiveInputMethodData); + connect(m_testView, &TestView::requestInputMethodEvent, this, + &MainWindow::sendInputMethodEvents); + connect(m_controlView, &ControlView::requestInputMethodEvent, this, + &MainWindow::sendInputMethodEvents); centralWidget->setLayout(centralLayout); setCentralWidget(centralWidget); setWindowTitle(tr("Input Methods Format Manual Test")); } +void MainWindow::sendInputMethodEvents() +{ + bool processed; + QString resultText; + + QString text = m_controlView->getText(); + QList<QInputMethodEvent::Attribute> attrs = m_controlView->getAtrributes(); + QInputMethodEvent im(text, attrs); + + processed = QApplication::sendEvent(m_referenceView->referenceInput(), &im); + resultText = processed ? QStringLiteral("<font color='green'>TRUE</font>") + : QStringLiteral("<font color='red'>FALSE</font>"); + m_referenceProcessed->setText(resultText); + + processed = QApplication::sendEvent(m_webView->focusProxy(), &im); + resultText = processed ? QStringLiteral("<font color='green'>TRUE</font>") + : QStringLiteral("<font color='red'>FALSE</font>"); + m_webProcessed->setText(resultText); +} + +void MainWindow::closeEvent(QCloseEvent *event) +{ + m_testView->cancelTest(); + QMainWindow::closeEvent(event); +} + int main(int argc, char *argv[]) { QApplication a(argc, argv); diff --git a/tests/manual/widgets/inputmethods/referenceview.cpp b/tests/manual/widgets/inputmethods/referenceview.cpp index 906a3001e..27e784fbc 100644 --- a/tests/manual/widgets/inputmethods/referenceview.cpp +++ b/tests/manual/widgets/inputmethods/referenceview.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 "referenceview.h" diff --git a/tests/manual/widgets/inputmethods/referenceview.h b/tests/manual/widgets/inputmethods/referenceview.h index 4025d4886..d943a93d0 100644 --- a/tests/manual/widgets/inputmethods/referenceview.h +++ b/tests/manual/widgets/inputmethods/referenceview.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 REFERENCEVIEW_H #define REFERENCEVIEW_H diff --git a/tests/manual/widgets/inputmethods/testview.cpp b/tests/manual/widgets/inputmethods/testview.cpp index 14e355caf..d57b22cc5 100644 --- a/tests/manual/widgets/inputmethods/testview.cpp +++ b/tests/manual/widgets/inputmethods/testview.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 "testview.h" @@ -94,11 +69,19 @@ void TestView::loadTestData(const QString &testDataPath) testDataFile.close(); } +void TestView::cancelTest() +{ + if (!m_testRunning) + return; + + m_testRunning = false; + m_testButton->setText(tr("Start Test")); +} + void TestView::startOrCancelTest() { if (m_testRunning) { - m_testRunning = false; - m_testButton->setText(tr("Start Test")); + cancelTest(); return; } @@ -120,10 +103,7 @@ void TestView::startOrCancelTest() QTest::qWait(1000); } - if (m_testRunning) { - m_testRunning = false; - m_testButton->setText(tr("Start Test")); - } + cancelTest(); } void TestView::collectAndSendData() diff --git a/tests/manual/widgets/inputmethods/testview.h b/tests/manual/widgets/inputmethods/testview.h index 4934a5f87..b99e60d75 100644 --- a/tests/manual/widgets/inputmethods/testview.h +++ b/tests/manual/widgets/inputmethods/testview.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 TESTVIEW_H #define TESTVIEW_H @@ -32,8 +7,10 @@ #include <QTextCharFormat> #include <QWidget> +QT_BEGIN_NAMESPACE class QPushButton; class QTableView; +QT_END_NAMESPACE class TestView : public QWidget { @@ -41,6 +18,8 @@ class TestView : public QWidget public: explicit TestView(QWidget *parent = 0); + void cancelTest(); + public slots: void loadTestData(const QString &); void startOrCancelTest(); diff --git a/tests/manual/widgets/inputmethods/webview.cpp b/tests/manual/widgets/inputmethods/webview.cpp index 62e210ecf..915d73a7f 100644 --- a/tests/manual/widgets/inputmethods/webview.cpp +++ b/tests/manual/widgets/inputmethods/webview.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 "webview.h" #include <QWebEngineSettings> diff --git a/tests/manual/widgets/inputmethods/webview.h b/tests/manual/widgets/inputmethods/webview.h index be57fbf29..a46dcb2f6 100644 --- a/tests/manual/widgets/inputmethods/webview.h +++ b/tests/manual/widgets/inputmethods/webview.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 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..d4fb544cc --- /dev/null +++ b/tests/manual/widgets/touchbrowser/CMakeLists.txt @@ -0,0 +1,30 @@ +# 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 + +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..18baf79e8 --- /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 LGPL-3.0-only OR GPL-2.0-only 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/CMakeLists.txt b/tests/manual/widgets/webgl/CMakeLists.txt new file mode 100644 index 000000000..034a06a79 --- /dev/null +++ b/tests/manual/widgets/webgl/CMakeLists.txt @@ -0,0 +1,20 @@ +# 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.16) + project(webgl LANGUAGES CXX) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_manual_test(webgl + GUI + SOURCES + main.cpp + LIBRARIES + Qt::Core + Qt::Gui + Qt::WebEngineWidgets + ENABLE_AUTOGEN_TOOLS + moc +) diff --git a/tests/manual/widgets/webgl/main.cpp b/tests/manual/widgets/webgl/main.cpp index c18a15bac..7037c34db 100644 --- a/tests/manual/widgets/webgl/main.cpp +++ b/tests/manual/widgets/webgl/main.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// 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 <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..4d58abf8c --- /dev/null +++ b/tests/manual/widgets/webrtc/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (C) 2023 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.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..328e4ae36 --- /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 BSD-3-Clause + +#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::featurePermissionRequested, this, + &Page::handlePermissionRequest); + connect(this, &QWebEnginePage::desktopMediaRequested, this, &Page::handleDesktopMediaRequest); +} + +void Page::handlePermissionRequest(const QUrl &origin, Feature feature) +{ + if (QMessageBox::question(QApplication::activeWindow(), tr("Permission request"), + tr("allow access?")) + == QMessageBox::Yes) + setFeaturePermission(origin, feature, PermissionGrantedByUser); + else + setFeaturePermission(origin, feature, PermissionDeniedByUser); +} + +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/auto/quick/dialogs/dialogs.qrc b/tests/manual/widgets/webrtc/qrc.qrc index a0715dbce..c3322b454 100644 --- a/tests/auto/quick/dialogs/dialogs.qrc +++ b/tests/manual/widgets/webrtc/qrc.qrc @@ -1,6 +1,5 @@ <RCC> <qresource prefix="/"> <file>index.html</file> - <file>WebView.qml</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 a2e83e1e6..000000000 --- a/tests/quicktestbrowser/ApplicationRoot.qml +++ /dev/null @@ -1,68 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -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 9f286125e..000000000 --- a/tests/quicktestbrowser/BrowserDialog.qml +++ /dev/null @@ -1,44 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -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 2c4972366..000000000 --- a/tests/quicktestbrowser/BrowserWindow.qml +++ /dev/null @@ -1,532 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -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() - } - } - - onNewViewRequested: { - 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 b8d4f743c..000000000 --- a/tests/quicktestbrowser/ButtonWithMenu.qml +++ /dev/null @@ -1,58 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -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 eb945eccc..000000000 --- a/tests/quicktestbrowser/DownloadView.qml +++ /dev/null @@ -1,153 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -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 500d13206..000000000 --- a/tests/quicktestbrowser/FeaturePermissionBar.qml +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -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 90f27cca6..000000000 --- a/tests/quicktestbrowser/FullScreenNotification.qml +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -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 122ae8815..000000000 --- a/tests/quicktestbrowser/ZoomController.qml +++ /dev/null @@ -1,90 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -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 cb21518c9..000000000 --- a/tests/quicktestbrowser/main.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "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 63ff051de..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/webengine/quicknanobrowser/icons/3rdparty/go-next.png</file> - <file alias="go-previous.png">../../examples/webengine/quicknanobrowser/icons/3rdparty/go-previous.png</file> - <file alias="process-stop.png">../../examples/webengine/quicknanobrowser/icons/3rdparty/process-stop.png</file> - <file alias="view-refresh.png">../../examples/webengine/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 d4f3dba0e..000000000 --- a/tests/quicktestbrowser/utils.h +++ /dev/null @@ -1,54 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#ifndef 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 diff --git a/tests/tests.pro b/tests/tests.pro deleted file mode 100644 index cb7d78798..000000000 --- a/tests/tests.pro +++ /dev/null @@ -1,7 +0,0 @@ -TEMPLATE = subdirs - -SUBDIRS += auto - -qtHaveModule(webengine-quick) { - SUBDIRS += quicktestbrowser -} |