diff options
Diffstat (limited to 'tests/auto/widgets')
49 files changed, 3001 insertions, 1314 deletions
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 bd04bddd0..f6a08c9d3 100644 --- a/tests/auto/widgets/accessibility/CMakeLists.txt +++ b/tests/auto/widgets/accessibility/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_webengine_accessibility @@ -5,5 +8,6 @@ qt_internal_add_test(tst_webengine_accessibility tst_accessibility.cpp LIBRARIES Qt::WebEngineWidgets + Qt::WebEngineCorePrivate Test::Util ) diff --git a/tests/auto/widgets/accessibility/tst_accessibility.cpp b/tests/auto/widgets/accessibility/tst_accessibility.cpp index 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/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/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/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/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/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/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/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/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/tst_printing.cpp b/tests/auto/widgets/printing/tst_printing.cpp index 0559fb787..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.page(), &QWebEnginePage::pdfPrintingFinished); QPageLayout layout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF(0.0, 0.0, 0.0, 0.0)); QString path = tempDir.path() + "/print_1_success.pdf"; view.page()->printToPdf(path, layout); - QTRY_VERIFY2(savePdfSpy.count() == 1, "Printing to PDF file failed without signal"); + QTRY_VERIFY2(savePdfSpy.size() == 1, "Printing to PDF file failed without signal"); QList<QVariant> successArguments = savePdfSpy.takeFirst(); QVERIFY2(successArguments.at(0).toString() == path, "File path for first saved PDF does not match arguments"); @@ -74,7 +53,7 @@ void tst_Printing::printToPdfBasic() path = tempDir.path() + "/print_|2_failed.pdf"; #endif view.page()->printToPdf(path, QPageLayout()); - QTRY_VERIFY2(savePdfSpy.count() == 1, "Printing to PDF file failed without signal"); + QTRY_VERIFY2(savePdfSpy.size() == 1, "Printing to PDF file failed without signal"); QList<QVariant> failedArguments = savePdfSpy.takeFirst(); QVERIFY2(failedArguments.at(0).toString() == path, "File path for second saved PDF does not match arguments"); @@ -82,11 +61,11 @@ void tst_Printing::printToPdfBasic() CallbackSpy<QByteArray> successfulSpy; view.page()->printToPdf(successfulSpy.ref(), layout); - QVERIFY(successfulSpy.waitForResult().length() > 0); + QVERIFY(successfulSpy.waitForResult().size() > 0); CallbackSpy<QByteArray> failedInvalidLayoutSpy; view.page()->printToPdf(failedInvalidLayoutSpy.ref(), QPageLayout()); - QCOMPARE(failedInvalidLayoutSpy.waitForResult().length(), 0); + QCOMPARE(failedInvalidLayoutSpy.waitForResult().size(), 0); } void tst_Printing::printRequest() @@ -100,17 +79,17 @@ void tst_Printing::printRequest() CallbackSpy<QByteArray> resultSpy; view.load(QUrl("qrc:///resources/basic_printing_page.html")); - QTRY_VERIFY(loadFinishedSpy.count() == 1); + QTRY_VERIFY(loadFinishedSpy.size() == 1); view.page()->runJavaScript("window.print()"); - QTRY_VERIFY(printRequestedSpy.count() == 1); - QVERIFY(printRequestedSpy2.count() == 1); + QTRY_VERIFY(printRequestedSpy.size() == 1); + QVERIFY(printRequestedSpy2.size() == 1); //check if printing still works view.printToPdf(resultSpy.ref(), layout); const QByteArray data = resultSpy.waitForResult(); - QVERIFY(data.length() > 0); + QVERIFY(data.size() > 0); } -#if 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 @@ -139,8 +118,65 @@ void tst_Printing::printToPdfPoppler() QVERIFY2(pdfPage->search(ustring::from_latin1("Hello Paper World"), rect, page::search_from_top, case_sensitive ), "Could not find text"); } + +void tst_Printing::printFromPdfViewer() +{ + using namespace poppler; + + QWebEngineView view; + view.page()->settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true); + view.page()->settings()->setAttribute(QWebEngineSettings::PdfViewerEnabled, true); + + // Load a basic HTML + QSignalSpy spy(&view, &QWebEngineView::loadFinished); + view.load(QUrl("qrc:///resources/basic_printing_page.html")); + QTRY_COMPARE(spy.size(), 1); + + // Create a PDF + QTemporaryDir tempDir(QDir::tempPath() + "/tst_printing-XXXXXX"); + QVERIFY(tempDir.isValid()); + QString path = tempDir.path() + "/basic_page.pdf"; + QSignalSpy savePdfSpy(view.page(), &QWebEnginePage::pdfPrintingFinished); + view.page()->printToPdf(path); + QTRY_COMPARE(savePdfSpy.size(), 1); + + // Open the new file with the PDF viewer plugin + view.load(QUrl("file://" + path)); + QTRY_COMPARE(spy.size(), 2); + + // Print from the plugin + // loadFinished signal is not reliable when loading a PDF file, because it has multiple phases. + // Workaround: Try to print it a couple of times until the result matches the expected. + CallbackSpy<QByteArray> resultSpy; + bool ok = QTest::qWaitFor([&]() -> bool { + view.printToPdf(resultSpy.ref()); + QByteArray data = resultSpy.waitForResult(); + + // Check if the result contains text from the original basic HTML + // This catches all the typical issues: empty result or printing the WebUI without PDF content. + QScopedPointer<document> pdf(document::load_from_raw_data(data.constData(), data.length())); + QScopedPointer<page> pdfPage(pdf->create_page(0)); + rectf rect; + return pdfPage->search(ustring::from_latin1("Hello Paper World"), rect, page::search_from_top, + case_sensitive); + }, 10000); + QVERIFY(ok); +} #endif +void tst_Printing::interruptPrinting() +{ + QWebEngineView view; + QSignalSpy spy(&view, &QWebEngineView::loadFinished); + view.load(QUrl("qrc:///resources/basic_printing_page.html")); + QTRY_VERIFY(spy.size() == 1); + + QTemporaryDir tempDir(QDir::tempPath() + "/tst_qwebengineview-XXXXXX"); + QVERIFY(tempDir.isValid()); + view.page()->printToPdf(tempDir.path() + "/file.pdf"); + // Navigation stop interrupts print job, preferably do this without crash/assert + view.page()->triggerAction(QWebEnginePage::Stop); +} QTEST_MAIN(tst_Printing) #include "tst_printing.moc" diff --git a/tests/auto/widgets/proxy/CMakeLists.txt b/tests/auto/widgets/proxy/CMakeLists.txt index 03af3c35f..95dc903ed 100644 --- a/tests/auto/widgets/proxy/CMakeLists.txt +++ b/tests/auto/widgets/proxy/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_webengine_proxy 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/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..ac7926dc0 --- /dev/null +++ b/tests/auto/widgets/qtbug_110287/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_test(tst_qtbug_110287 + SOURCES + tst_qtbug_110287.cpp + LIBRARIES + Qt::Network + Qt::WebEngineWidgets +) +target_link_options(tst_qtbug_110287 PRIVATE "-Wl,--as-needed") diff --git a/tests/auto/widgets/qtbug_110287/tst_qtbug_110287.cpp b/tests/auto/widgets/qtbug_110287/tst_qtbug_110287.cpp new file mode 100644 index 000000000..9453ae9b8 --- /dev/null +++ b/tests/auto/widgets/qtbug_110287/tst_qtbug_110287.cpp @@ -0,0 +1,41 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <QNetworkAccessManager> +#include <QNetworkRequest> +#include <QSignalSpy> +#include <QTest> +#include <QWebEngineView> + +class tst_qtbug_110287 : public QObject +{ + Q_OBJECT +public: + tst_qtbug_110287() { } + +private slots: + void getAddrInfo(); +}; + +void tst_qtbug_110287::getAddrInfo() +{ + QNetworkAccessManager nam; + QSignalSpy namSpy(&nam, &QNetworkAccessManager::finished); + + QString address("http://www.example.com"); + QScopedPointer<QNetworkReply> reply(nam.get(QNetworkRequest(address))); + + if (!namSpy.wait(25000) || reply->error() != QNetworkReply::NoError) + QSKIP("Couldn't load page from network, skipping test."); + + QWebEngineView view; + QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); + + // load() will trigger system DNS resolution that uses getaddrinfo() + view.load(QUrl(address)); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size() > 0, true, 30000); + QTRY_COMPARE(loadFinishedSpy[0][0].toBool(), true); +} + +#include "tst_qtbug_110287.moc" +QTEST_MAIN(tst_qtbug_110287) diff --git a/tests/auto/widgets/qwebenginedownloadrequest/CMakeLists.txt b/tests/auto/widgets/qwebenginedownloadrequest/CMakeLists.txt index 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/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/tst_qwebenginehistory.cpp b/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp index f67c2e03d..ad66e972c 100644 --- a/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp +++ b/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp @@ -39,7 +39,7 @@ protected : { loadFinishedSpy->clear(); page->load(QUrl("qrc:/resources/page" + QString::number(nr) + ".html")); - QTRY_COMPARE(loadFinishedSpy->count(), 1); + QTRY_COMPARE(loadFinishedSpy->size(), 1); loadFinishedSpy->clear(); } @@ -150,8 +150,8 @@ void tst_QWebEngineHistory::back() for (int i = histsize;i > 1;i--) { QTRY_COMPARE(toPlainTextSync(page), QString("page") + QString::number(i)); hist->back(); - QTRY_COMPARE(loadFinishedSpy->count(), histsize-i+1); - QTRY_COMPARE(titleChangedSpy.count(), histsize-i+1); + QTRY_COMPARE(loadFinishedSpy->size(), histsize-i+1); + QTRY_COMPARE(titleChangedSpy.size(), histsize-i+1); } //try one more time (too many). crash test hist->back(); @@ -168,15 +168,15 @@ void tst_QWebEngineHistory::forward() while (hist->canGoBack()) { hist->back(); histBackCount++; - QTRY_COMPARE(loadFinishedSpy->count(), histBackCount); + QTRY_COMPARE(loadFinishedSpy->size(), histBackCount); } QSignalSpy titleChangedSpy(page, SIGNAL(titleChanged(const QString&))); for (int i = 1;i < histsize;i++) { QTRY_COMPARE(toPlainTextSync(page), QString("page") + QString::number(i)); hist->forward(); - QTRY_COMPARE(loadFinishedSpy->count(), i+histBackCount); - QTRY_COMPARE(titleChangedSpy.count(), i); + QTRY_COMPARE(loadFinishedSpy->size(), i+histBackCount); + QTRY_COMPARE(titleChangedSpy.size(), i); } //try one more time (too many). crash test hist->forward(); @@ -205,15 +205,15 @@ void tst_QWebEngineHistory::goToItem() QWebEngineHistoryItem current = hist->currentItem(); hist->back(); - QTRY_COMPARE(loadFinishedSpy->count(), 1); + QTRY_COMPARE(loadFinishedSpy->size(), 1); hist->back(); - QTRY_COMPARE(loadFinishedSpy->count(), 2); + QTRY_COMPARE(loadFinishedSpy->size(), 2); QVERIFY(hist->currentItem().title() != current.title()); hist->goToItem(current); - QTRY_COMPARE(loadFinishedSpy->count(), 2); + QTRY_COMPARE(loadFinishedSpy->size(), 2); QTRY_COMPARE(hist->currentItem().title(), current.title()); } @@ -225,7 +225,7 @@ void tst_QWebEngineHistory::items() { QList<QWebEngineHistoryItem> items = hist->items(); //check count - QTRY_COMPARE(histsize, items.count()); + QTRY_COMPARE(histsize, items.size()); //check order for (int i = 1;i <= histsize;i++) { @@ -236,10 +236,10 @@ void tst_QWebEngineHistory::items() void tst_QWebEngineHistory::backForwardItems() { hist->back(); - QTRY_COMPARE(loadFinishedSpy->count(), 1); + QTRY_COMPARE(loadFinishedSpy->size(), 1); hist->back(); - QTRY_COMPARE(loadFinishedSpy->count(), 2); + QTRY_COMPARE(loadFinishedSpy->size(), 2); QTRY_COMPARE(hist->items().size(), 5); QTRY_COMPARE(hist->backItems(100).size(), 2); @@ -297,9 +297,9 @@ void tst_QWebEngineHistory::serialize_2() hist->back(); QTRY_VERIFY(evaluateJavaScriptSync(page, "location.hash").toString().isEmpty()); hist->back(); - QTRY_COMPARE(loadFinishedSpy->count(), 1); + QTRY_COMPARE(loadFinishedSpy->size(), 1); hist->back(); - QTRY_COMPARE(loadFinishedSpy->count(), 2); + QTRY_COMPARE(loadFinishedSpy->size(), 2); //check if current index was changed (make sure that it is not last item) QVERIFY(hist->currentItemIndex() != initialCurrentIndex); //save current index @@ -310,18 +310,18 @@ void tst_QWebEngineHistory::serialize_2() load >> *hist; QVERIFY(load.status() == QDataStream::Ok); // Restoring the history will trigger a load. - QTRY_COMPARE(loadFinishedSpy->count(), 3); + QTRY_COMPARE(loadFinishedSpy->size(), 3); //check current index QTRY_COMPARE(hist->currentItemIndex(), oldCurrentIndex); hist->forward(); - QTRY_COMPARE(loadFinishedSpy->count(), 4); + QTRY_COMPARE(loadFinishedSpy->size(), 4); hist->forward(); - QTRY_COMPARE(loadFinishedSpy->count(), 5); + QTRY_COMPARE(loadFinishedSpy->size(), 5); hist->forward(); // In-page navigation, the last url was the page5.html - QTRY_COMPARE(loadFinishedSpy->count(), 5); + QTRY_COMPARE(loadFinishedSpy->size(), 5); QTRY_COMPARE(hist->currentItemIndex(), initialCurrentIndex); } @@ -429,7 +429,7 @@ void tst_QWebEngineHistory::saveAndRestore_crash_4() QSignalSpy loadFinishedSpy2(page2.data(), SIGNAL(loadFinished(bool))); QDataStream load(&buffer, QIODevice::ReadOnly); load >> *page2->history(); - QTRY_COMPARE(loadFinishedSpy2.count(), 1); + QTRY_COMPARE(loadFinishedSpy2.size(), 1); } void tst_QWebEngineHistory::saveAndRestore_InternalPage() @@ -468,7 +468,7 @@ void tst_QWebEngineHistory::popPushState() QWebEnginePage page; QSignalSpy spyLoadFinished(&page, SIGNAL(loadFinished(bool))); page.setHtml("<html><body>long live Qt!</body></html>"); - QTRY_COMPARE(spyLoadFinished.count(), 1); + QTRY_COMPARE(spyLoadFinished.size(), 1); evaluateJavaScriptSync(&page, script); } @@ -487,9 +487,9 @@ void tst_QWebEngineHistory::clear() QWebEnginePage page2(this); QWebEngineHistory* hist2 = page2.history(); - QVERIFY(hist2->count() == 0); + QCOMPARE(hist2->count(), 0); hist2->clear(); - QVERIFY(hist2->count() == 0); // Do not change anything. + QCOMPARE(hist2->count(), 0); // Do not change anything. } void tst_QWebEngineHistory::historyItemFromDeletedPage() diff --git a/tests/auto/widgets/qwebenginepage/BLACKLIST b/tests/auto/widgets/qwebenginepage/BLACKLIST index 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..f63d6211c 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 @@ -18,6 +24,7 @@ set(tst_qwebenginepage_resource_files "resources/content.html" "resources/dynamicFrame.html" "resources/foo.txt" + "resources/fontaccess.html" "resources/frame_a.html" "resources/frame_c.html" "resources/framedindex.html" diff --git a/tests/auto/widgets/qwebenginepage/resources/fontaccess.html b/tests/auto/widgets/qwebenginepage/resources/fontaccess.html new file mode 100644 index 000000000..1a0fe8af9 --- /dev/null +++ b/tests/auto/widgets/qwebenginepage/resources/fontaccess.html @@ -0,0 +1,14 @@ +<html> +<body onkeypress='onKeyPress()'> +<a>This is test content</a> +<script> +var done = false; +var fonts; +var activated = false; +function onKeyPress() { + activated = true; + window.queryLocalFonts().then(f => { fonts = f; done = true; }); +} +</script> +</body> +</html> diff --git a/tests/auto/widgets/qwebenginepage/resources/reload.html b/tests/auto/widgets/qwebenginepage/resources/reload.html index d9c33dfcd..062d06807 100644 --- a/tests/auto/widgets/qwebenginepage/resources/reload.html +++ b/tests/auto/widgets/qwebenginepage/resources/reload.html @@ -1,6 +1,6 @@ <html> <head> -<meta http-equiv="refresh" content="2"> +<meta http-equiv="refresh" content="2;url=qrc:///resources/content.html"> </head> <body> This is test content diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index 8a44af3fb..f1d64776b 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -1,5 +1,5 @@ /* - Copyright (C) 2016 The Qt Company Ltd. + Copyright (C) 2023 The Qt Company Ltd. Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in> Copyright (C) 2010 Holger Hans Peter Freyther @@ -20,7 +20,9 @@ */ #include <widgetutil.h> +#include <QtNetwork/private/qtnetworkglobal_p.h> #include <QtWebEngineCore/qtwebenginecore-config.h> +#include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h> #include <QByteArray> #include <QClipboard> #include <QDir> @@ -34,6 +36,7 @@ #include <QPaintEngine> #include <QPushButton> #include <QScreen> +#include <QWheelEvent> #if defined(QT_STATEMACHINE_LIB) # include <QStateMachine> #endif @@ -47,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,18 @@ public Q_SLOTS: private Q_SLOTS: void initTestCase(); void cleanupTestCase(); + void comboBoxPopupPositionAfterMove_data(); void comboBoxPopupPositionAfterMove(); + void comboBoxPopupPositionAfterChildMove_data(); void comboBoxPopupPositionAfterChildMove(); void acceptNavigationRequest(); + void acceptNavigationRequestWithFormData(); void acceptNavigationRequestNavigationType(); void acceptNavigationRequestRelativeToNothing(); +#ifndef Q_OS_MACOS void geolocationRequestJS_data(); void geolocationRequestJS(); +#endif void loadFinished(); void actionStates(); void pasteImage(); @@ -142,7 +164,7 @@ private Q_SLOTS: #endif void openWindowDefaultSize(); -#ifdef Q_OS_MAC +#ifdef Q_OS_MACOS void macCopyUnicodeToClipboard(); #endif @@ -150,7 +172,8 @@ private Q_SLOTS: void runJavaScriptDisabled(); void runJavaScriptFromSlot(); void fullScreenRequested(); - void quotaRequested(); + void requestQuota_data(); + void requestQuota(); // Tests from tst_QWebEngineFrame @@ -212,7 +235,13 @@ private Q_SLOTS: void notificationPermission_data(); void notificationPermission(); void sendNotification(); + void clipboardReadWritePermissionInitialState_data(); + void clipboardReadWritePermissionInitialState(); + void clipboardReadWritePermission_data(); + void clipboardReadWritePermission(); void contentsSize(); + void localFontAccessPermission_data(); + void localFontAccessPermission(); void setLifecycleState(); void setVisible(); @@ -247,20 +276,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() @@ -306,6 +365,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() @@ -358,15 +425,15 @@ void tst_QWebEnginePage::acceptNavigationRequest() page.setHtml(QString("<html><body><form name='tstform' action='foo' method='get'>" "<input type='text'><input type='submit'></form></body></html>"), QUrl("echo:/")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); evaluateJavaScriptSync(&page, "tstform.submit();"); - QTRY_COMPARE(loadSpy.count(), 2); + QTRY_COMPARE(loadSpy.size(), 2); // Content hasn't changed so the form submit will still work page.m_acceptNavigationRequest = true; evaluateJavaScriptSync(&page, "tstform.submit();"); - QTRY_COMPARE(loadSpy.count(), 3); + QTRY_COMPARE(loadSpy.size(), 3); // Now the content has changed QCOMPARE(toPlainTextSync(&page), QString("/foo?")); @@ -402,6 +469,7 @@ private: bool m_allowGeolocation; }; +#ifndef Q_OS_MACOS void tst_QWebEnginePage::geolocationRequestJS_data() { QTest::addColumn<bool>("allowed"); @@ -424,7 +492,7 @@ void tst_QWebEnginePage::geolocationRequestJS() QSignalSpy spyLoadFinished(newPage, SIGNAL(loadFinished(bool))); newPage->setHtml(QString("<html><body>test</body></html>"), QUrl("qrc://secure/origin")); - QTRY_COMPARE_WITH_TIMEOUT(spyLoadFinished.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(spyLoadFinished.size(), 1, 20000); // Geolocation is only enabled for visible WebContents. view.show(); @@ -441,6 +509,7 @@ void tst_QWebEnginePage::geolocationRequestJS() QEXPECT_FAIL("", "No location service available.", Continue); QCOMPARE(result, errorCode); } +#endif void tst_QWebEnginePage::loadFinished() { @@ -451,19 +520,19 @@ void tst_QWebEnginePage::loadFinished() page.load(QUrl("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html," "<head><meta http-equiv='refresh' content='1'></head>foo \">" "<frame src=\"data:text/html,bar\"></frameset>")); - QTRY_COMPARE_WITH_TIMEOUT(spyLoadFinished.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(spyLoadFinished.size(), 1, 20000); QEXPECT_FAIL("", "Behavior change: Load signals are emitted only for the main frame in QtWebEngine.", Continue); - QTRY_VERIFY_WITH_TIMEOUT(spyLoadStarted.count() > 1, 100); + QTRY_VERIFY_WITH_TIMEOUT(spyLoadStarted.size() > 1, 100); QEXPECT_FAIL("", "Behavior change: Load signals are emitted only for the main frame in QtWebEngine.", Continue); - QTRY_VERIFY_WITH_TIMEOUT(spyLoadFinished.count() > 1, 100); + QTRY_VERIFY_WITH_TIMEOUT(spyLoadFinished.size() > 1, 100); spyLoadFinished.clear(); page.load(QUrl("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html," "foo \"><frame src=\"data:text/html,bar\"></frameset>")); - QTRY_COMPARE(spyLoadFinished.count(), 1); - QCOMPARE(spyLoadFinished.count(), 1); + QTRY_COMPARE(spyLoadFinished.size(), 1); + QCOMPARE(spyLoadFinished.size(), 1); } void tst_QWebEnginePage::actionStates() @@ -520,6 +589,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); @@ -549,7 +619,7 @@ void tst_QWebEnginePage::consoleOutput() ConsolePage page; // We don't care about the result but want this to be synchronous evaluateJavaScriptSync(&page, "this is not valid JavaScript"); - QCOMPARE(page.messages.count(), 1); + QCOMPARE(page.messages.size(), 1); QCOMPARE(page.lineNumbers.at(0), 1); } @@ -567,6 +637,7 @@ public: QWebEngineNavigationRequest::NavigationType type; QUrl url; bool isMainFrame; + bool hasFormData; }; QList<Navigation> navigations; @@ -584,6 +655,7 @@ private Q_SLOTS: n.url = request.url(); n.type = request.navigationType(); n.isMainFrame = request.isMainFrame(); + n.hasFormData = request.hasFormData(); navigations.append(n); request.accept(); } @@ -601,47 +673,71 @@ private Q_SLOTS: } }; -void tst_QWebEnginePage::acceptNavigationRequestNavigationType() +void tst_QWebEnginePage::acceptNavigationRequestWithFormData() { + QWebEngineProfile profile; + profile.installUrlSchemeHandler("echo", new EchoingUrlSchemeHandler(&profile)); + TestPage page(nullptr, &profile); + QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); + + page.setHtml(QString("<html><body><form name='tstform' action='foo' method='post'>" + "<input type='text'><input type='submit'></form></body></html>"), + QUrl("echo:/")); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); + QCOMPARE(page.navigations[0].type, QWebEngineNavigationRequest::TypedNavigation); + QVERIFY(!page.navigations[0].hasFormData); + + evaluateJavaScriptSync(&page, "tstform.submit();"); + QTRY_COMPARE(loadSpy.size(), 2); + QCOMPARE(page.navigations[1].type, QWebEngineNavigationRequest::FormSubmittedNavigation); + QVERIFY(page.navigations[1].hasFormData); + page.triggerAction(QWebEnginePage::Reload); + QTRY_COMPARE(loadSpy.size(), 3); + QCOMPARE(page.navigations[2].type, QWebEngineNavigationRequest::ReloadNavigation); + QVERIFY(page.navigations[2].hasFormData); +} + +void tst_QWebEnginePage::acceptNavigationRequestNavigationType() +{ TestPage page; QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); page.load(QUrl("qrc:///resources/script.html")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); - QTRY_COMPARE(page.navigations.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); + QTRY_COMPARE(page.navigations.size(), 1); page.load(QUrl("qrc:///resources/content.html")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 2, 20000); - QTRY_COMPARE(page.navigations.count(), 2); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 2, 20000); + QTRY_COMPARE(page.navigations.size(), 2); page.triggerAction(QWebEnginePage::Stop); QVERIFY(page.history()->canGoBack()); page.triggerAction(QWebEnginePage::Back); - QTRY_COMPARE(loadSpy.count(), 3); - QTRY_COMPARE(page.navigations.count(), 3); + QTRY_COMPARE(loadSpy.size(), 3); + QTRY_COMPARE(page.navigations.size(), 3); page.triggerAction(QWebEnginePage::Reload); - QTRY_COMPARE(loadSpy.count(), 4); - QTRY_COMPARE(page.navigations.count(), 4); - - page.load(QUrl("qrc:///resources/reload.html")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 6, 20000); - QTRY_COMPARE(page.navigations.count(), 6); + QTRY_COMPARE(loadSpy.size(), 4); + QTRY_COMPARE(page.navigations.size(), 4); QList<QWebEngineNavigationRequest::NavigationType> expectedList; expectedList << QWebEngineNavigationRequest::TypedNavigation << QWebEngineNavigationRequest::TypedNavigation << QWebEngineNavigationRequest::BackForwardNavigation - << QWebEngineNavigationRequest::ReloadNavigation - << QWebEngineNavigationRequest::TypedNavigation - << QWebEngineNavigationRequest::RedirectNavigation; + << QWebEngineNavigationRequest::ReloadNavigation; // client side redirect + page.load(QUrl("qrc:///resources/reload.html")); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 6, 20000); + QTRY_COMPARE(page.navigations.size(), 6); + expectedList += { QWebEngineNavigationRequest::TypedNavigation, QWebEngineNavigationRequest::RedirectNavigation }; + + page.load(QUrl("qrc:///resources/redirect.html")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 7, 20000); - QTRY_COMPARE(page.navigations.count(), 8); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 7, 20000); + QTRY_COMPARE(page.navigations.size(), 8); expectedList += { QWebEngineNavigationRequest::TypedNavigation, QWebEngineNavigationRequest::RedirectNavigation }; // server side redirect @@ -662,18 +758,18 @@ void tst_QWebEnginePage::acceptNavigationRequestNavigationType() }); QVERIFY(server.start()); page.load(QUrl(server.url("/redirect1.html"))); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 8, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 8, 20000); expectedList += { QWebEngineNavigationRequest::TypedNavigation, QWebEngineNavigationRequest::RedirectNavigation, QWebEngineNavigationRequest::RedirectNavigation }; - for (int i = 0; i < expectedList.count(); ++i) { - QTRY_VERIFY(i < page.navigations.count()); + for (int i = 0; i < expectedList.size(); ++i) { + QTRY_VERIFY(i < page.navigations.size()); QCOMPARE(page.navigations[i].type, expectedList[i]); } - QVERIFY(expectedList.count() == page.navigations.count()); + QVERIFY(expectedList.size() == page.navigations.size()); } // Relative url without base url. @@ -686,18 +782,18 @@ void tst_QWebEnginePage::acceptNavigationRequestRelativeToNothing() page.setHtml(QString("<html><body><a id='link' href='S0'>limited time offer</a></body></html>"), /* baseUrl: */ QUrl()); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); page.runJavaScript(QStringLiteral("document.getElementById(\"link\").click()")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 2, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 2, 20000); page.setHtml(QString("<html><body><a id='link' href='S0'>limited time offer</a></body></html>"), /* baseUrl: */ QString("qrc:/")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 3, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 3, 20000); page.runJavaScript(QStringLiteral("document.getElementById(\"link\").click()")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 4, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 4, 20000); // The two setHtml and the second click are counted, while the // first click is ignored due to the empty base url. - QCOMPARE(page.navigations.count(), 3); + QCOMPARE(page.navigations.size(), 3); QCOMPARE(page.navigations[0].type, QWebEngineNavigationRequest::TypedNavigation); QCOMPARE(page.navigations[1].type, QWebEngineNavigationRequest::TypedNavigation); QCOMPARE(page.navigations[2].type, QWebEngineNavigationRequest::LinkClickedNavigation); @@ -716,20 +812,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 @@ -770,16 +865,16 @@ void tst_QWebEnginePage::multipleProfilesAndLocalStorage() page1.setHtml(QString("<html><body> </body></html>"), QUrl("http://wwww.example.com")); page2.setHtml(QString("<html><body> </body></html>"), QUrl("http://wwww.example.com")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy1.count(), 1, 20000); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy2.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy1.size(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy2.size(), 1, 20000); evaluateJavaScriptSync(&page1, "localStorage.setItem('test', 'value1');"); evaluateJavaScriptSync(&page2, "localStorage.setItem('test', 'value2');"); page1.setHtml(QString("<html><body> </body></html>"), QUrl("http://wwww.example.com")); page2.setHtml(QString("<html><body> </body></html>"), QUrl("http://wwww.example.com")); - QTRY_COMPARE(loadSpy1.count(), 2); - QTRY_COMPARE(loadSpy2.count(), 2); + QTRY_COMPARE(loadSpy1.size(), 2); + QTRY_COMPARE(loadSpy2.size(), 2); QVariant s1 = evaluateJavaScriptSync(&page1, "localStorage.getItem('test')"); QCOMPARE(s1.toString(), QString("value1")); @@ -828,7 +923,7 @@ void tst_QWebEnginePage::textSelection() QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); page.setHtml(content); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); // these actions must exist QVERIFY(page.action(QWebEnginePage::SelectAll) != 0); @@ -855,7 +950,7 @@ void tst_QWebEnginePage::textSelection() // navigate away and check that selection is cleared page.load(QUrl("about:blank")); - QTRY_COMPARE(loadSpy.count(), 2); + QTRY_COMPARE(loadSpy.size(), 2); QVERIFY(!page.hasSelection()); QVERIFY(page.selectedText().isEmpty()); @@ -876,7 +971,7 @@ void tst_QWebEnginePage::backActionUpdate() QVERIFY(!action->isEnabled()); page->load(QUrl("qrc:///resources/framedindex.html")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); QVERIFY(!action->isEnabled()); auto firstAnchorCenterInFrame = [](QWebEnginePage *page, const QString &frameName) { @@ -888,7 +983,7 @@ void tst_QWebEnginePage::backActionUpdate() "return [(rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2];" "})()").toList(); - if (rectList.count() != 2) { + if (rectList.size() != 2) { qWarning("firstAnchorCenterInFrame failed."); return QPoint(); } @@ -915,8 +1010,8 @@ void tst_QWebEnginePage::localStorageVisibility() QSignalSpy loadSpy2(&webPage2, &QWebEnginePage::loadFinished); webPage1.setHtml(QString("<html><body>test</body></html>"), QUrl("http://www.example.com/")); webPage2.setHtml(QString("<html><body>test</body></html>"), QUrl("http://www.example.com/")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy1.count(), 1, 20000); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy2.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy1.size(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy2.size(), 1, 20000); // The attribute determines the visibility of the window.localStorage object. QVERIFY(evaluateJavaScriptSync(&webPage1, QString("(window.localStorage != undefined)")).toBool()); @@ -929,13 +1024,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()); } @@ -1036,7 +1132,7 @@ void tst_QWebEnginePage::testJSPrompt() bool res; QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); page.setHtml(QStringLiteral("<html><body></body></html>")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); // OK + QString() res = evaluateJavaScriptSync(&page, @@ -1070,7 +1166,7 @@ void tst_QWebEnginePage::findText() // Showing is required, otherwise all find operations fail. m_view->show(); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); // Select whole page contents. QTRY_VERIFY(m_view->page()->action(QWebEnginePage::SelectAll)->isEnabled()); @@ -1084,7 +1180,7 @@ void tst_QWebEnginePage::findText() QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished); m_view->findText("", {}, callbackSpy.ref()); QVERIFY(callbackSpy.wasCalled()); - QCOMPARE(signalSpy.count(), 1); + QCOMPARE(signalSpy.size(), 1); QTRY_COMPARE(m_view->selectedText(), QString("foo bar")); } @@ -1095,7 +1191,7 @@ void tst_QWebEnginePage::findText() QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished); m_view->findText("Will not be found", {}, callbackSpy.ref()); QCOMPARE(callbackSpy.waitForResult().numberOfMatches(), 0); - QTRY_COMPARE(signalSpy.count(), 1); + QTRY_COMPARE(signalSpy.size(), 1); auto result = signalSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); QCOMPARE(result.numberOfMatches(), 0); QTRY_VERIFY(m_view->selectedText().isEmpty()); @@ -1112,7 +1208,7 @@ void tst_QWebEnginePage::findText() QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished); m_view->findText("foo", {}, callbackSpy.ref()); QVERIFY(callbackSpy.waitForResult().numberOfMatches() > 0); - QTRY_COMPARE(signalSpy.count(), 1); + QTRY_COMPARE(signalSpy.size(), 1); QTRY_VERIFY(m_view->selectedText().isEmpty()); } @@ -1123,7 +1219,7 @@ void tst_QWebEnginePage::findText() QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished); m_view->findText("", {}, callbackSpy.ref()); QTRY_VERIFY(callbackSpy.wasCalled()); - QTRY_COMPARE(signalSpy.count(), 1); + QTRY_COMPARE(signalSpy.size(), 1); QTRY_COMPARE(m_view->selectedText(), QString("foo")); } @@ -1133,7 +1229,7 @@ void tst_QWebEnginePage::findText() QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished); m_view->findText("foo", {}); m_view->findText("foo", {}); - QTRY_COMPARE(signalSpy.count(), 2); + QTRY_COMPARE(signalSpy.size(), 2); QTRY_VERIFY(m_view->selectedText().isEmpty()); QCOMPARE(signalSpy.at(0).value(0).value<QWebEngineFindTextResult>().numberOfMatches(), 0); @@ -1145,7 +1241,7 @@ void tst_QWebEnginePage::findTextResult() { QSignalSpy findTextSpy(m_view->page(), &QWebEnginePage::findTextFinished); auto signalResult = [&findTextSpy]() -> QList<int> { - if (findTextSpy.count() != 1) + if (findTextSpy.size() != 1) return QList<int>({-1, -1}); auto r = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); return QList<int>({ r.numberOfMatches(), r.activeMatch() }); @@ -1157,7 +1253,7 @@ void tst_QWebEnginePage::findTextResult() QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool))); m_view->setHtml(QString("<html><head></head><body><div>foo bar</div></body></html>")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(findTextSync(m_page, ""), false); QCOMPARE(signalResult(), QList<int>({0, 0})); @@ -1186,7 +1282,7 @@ void tst_QWebEnginePage::findTextSuccessiveShouldCallAllCallbacks() CallbackSpy<QWebEngineFindTextResult> spy5; QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool))); m_view->setHtml(QString("<html><head></head><body><div>abcdefg abcdefg abcdefg abcdefg abcdefg</div></body></html>")); - QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); m_page->findText("abcde", {}, spy1.ref()); m_page->findText("abcd", {}, spy2.ref()); m_page->findText("abc", {}, spy3.ref()); @@ -1208,7 +1304,7 @@ void tst_QWebEnginePage::findTextCalledOnMatch() m_view->resize(800, 600); m_view->show(); m_view->setHtml(QString("<html><head></head><body><div>foo bar</div></body></html>")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); // CALLBACK bool callbackCalled = false; @@ -1245,12 +1341,12 @@ void tst_QWebEnginePage::findTextActiveMatchOrdinal() m_view->resize(800, 600); m_view->show(); m_view->setHtml(QString("<html><head></head><body><div>foo bar foo bar foo</div></body></html>")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); // Iterate over all "foo" matches. for (int i = 1; i <= 3; ++i) { m_view->page()->findText("foo", {}); - QTRY_COMPARE(findTextSpy.count(), 1); + QTRY_COMPARE(findTextSpy.size(), 1); result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); QCOMPARE(result.numberOfMatches(), 3); QCOMPARE(result.activeMatch(), i); @@ -1258,28 +1354,28 @@ void tst_QWebEnginePage::findTextActiveMatchOrdinal() // The last match is followed by the fist one. m_view->page()->findText("foo", {}); - QTRY_COMPARE(findTextSpy.count(), 1); + QTRY_COMPARE(findTextSpy.size(), 1); result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); QCOMPARE(result.numberOfMatches(), 3); QCOMPARE(result.activeMatch(), 1); // The first match is preceded by the last one. m_view->page()->findText("foo", QWebEnginePage::FindBackward); - QTRY_COMPARE(findTextSpy.count(), 1); + QTRY_COMPARE(findTextSpy.size(), 1); result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); QCOMPARE(result.numberOfMatches(), 3); QCOMPARE(result.activeMatch(), 3); // Finding another word resets the activeMatch. m_view->page()->findText("bar", {}); - QTRY_COMPARE(findTextSpy.count(), 1); + QTRY_COMPARE(findTextSpy.size(), 1); result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); QCOMPARE(result.numberOfMatches(), 2); QCOMPARE(result.activeMatch(), 1); // If no match activeMatch is 0. m_view->page()->findText("bla", {}); - QTRY_COMPARE(findTextSpy.count(), 1); + QTRY_COMPARE(findTextSpy.size(), 1); result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>(); QCOMPARE(result.numberOfMatches(), 0); QCOMPARE(result.activeMatch(), 0); @@ -1289,45 +1385,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(); } @@ -1339,18 +1450,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); @@ -1361,29 +1482,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(); @@ -1393,19 +1518,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("αβγδεζηθικλμπ"); @@ -1799,14 +1930,14 @@ void tst_QWebEnginePage::openWindowDefaultSize() page.settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true); view.setUrl(QUrl("about:blank")); view.show(); - QTRY_COMPARE(spyFinished.count(), 1); + QTRY_COMPARE(spyFinished.size(), 1); // Open a default window. page.runJavaScript("window.open()"); - QTRY_COMPARE(windowCreatedSpy.count(), 1); + QTRY_COMPARE(windowCreatedSpy.size(), 1); // Open a too small window. evaluateJavaScriptSync(&page, "window.open('','about:blank','width=10,height=10')"); - QTRY_COMPARE(windowCreatedSpy.count(), 2); + QTRY_COMPARE(windowCreatedSpy.size(), 2); // The number of popups created should be two. QCOMPARE(page.createdWindows.size(), 2); @@ -1877,7 +2008,7 @@ void tst_QWebEnginePage::runJavaScriptDisabled() // Settings changes take effect asynchronously. The load and wait ensure // that the settings are applied by the time we start to execute JavaScript. page.load(QStringLiteral("about:blank")); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 20000); QCOMPARE(evaluateJavaScriptSyncInWorld(&page, QStringLiteral("1+1"), QWebEngineScript::MainWorld), QVariant()); QCOMPARE(evaluateJavaScriptSyncInWorld(&page, QStringLiteral("1+1"), QWebEngineScript::ApplicationWorld), @@ -1894,7 +2025,7 @@ void tst_QWebEnginePage::runJavaScriptFromSlot() page.setHtml("<html><body>" " <input type='text' id='input1' value='QtWebEngine' size='50' />" "</body></html>"); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); bool done = false; connect(&page, &QWebEnginePage::selectionChanged, [&]() { @@ -1918,7 +2049,7 @@ void tst_QWebEnginePage::fullScreenRequested() QSignalSpy loadSpy(&view, SIGNAL(loadFinished(bool))); page->load(QUrl("qrc:///resources/fullscreen.html")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QTRY_VERIFY(isTrueJavaScriptResult(page, "document.webkitFullscreenEnabled")); QVERIFY(isFalseJavaScriptResult(page, "document.webkitIsFullScreen")); @@ -1935,7 +2066,7 @@ void tst_QWebEnginePage::fullScreenRequested() QTest::mouseMove(view.windowHandle(), QPoint(10,10)); QTest::mouseClick(view.windowHandle(), Qt::RightButton); - QTRY_COMPARE(view.findChildren<QMenu *>().count(), 1); + QTRY_COMPARE(view.findChildren<QMenu *>().size(), 1); auto menu = view.findChildren<QMenu *>().first(); QVERIFY(menu->actions().contains(page->action(QWebEnginePage::ExitFullScreen))); @@ -1949,8 +2080,18 @@ void tst_QWebEnginePage::fullScreenRequested() QTRY_VERIFY(isFalseJavaScriptResult(page, "document.webkitIsFullScreen")); } -void tst_QWebEnginePage::quotaRequested() +void tst_QWebEnginePage::requestQuota_data() { + QTest::addColumn<QString>("storage"); + QTest::addRow("webkitPersistentStorage") << "navigator.webkitPersistentStorage"; + QTest::addRow("webkitTemporaryStorage") << "navigator.webkitTemporaryStorage"; + +} + +void tst_QWebEnginePage::requestQuota() +{ + QFETCH(QString, storage); + ConsolePage page; QWebEngineView view; view.setPage(&page); @@ -1958,35 +2099,23 @@ void tst_QWebEnginePage::quotaRequested() page.load(QUrl("qrc:///resources/content.html")); QVERIFY(loadFinishedSpy.wait()); - connect(&page, &QWebEnginePage::quotaRequested, - [] (QWebEngineQuotaRequest request) - { - if (request.requestedSize() <= 5000) - request.accept(); - else - request.reject(); - }); - - evaluateJavaScriptSync(&page, - "navigator.webkitPersistentStorage.requestQuota(1024, function(grantedSize) {" \ - "console.log(grantedSize);" \ - "});"); - QTRY_COMPARE(page.messages.count(), 1); + evaluateJavaScriptSync(&page, QString( + "var storage = %1;" + "storage.requestQuota(1024, function(grantedSize) {" + " console.log(grantedSize);" + "});").arg(storage)); + QTRY_COMPARE(page.messages.size(), 1); QTRY_COMPARE(page.messages[0], QString("1024")); - evaluateJavaScriptSync(&page, - "navigator.webkitPersistentStorage.requestQuota(6000, function(grantedSize) {" \ - "console.log(grantedSize);" \ - "});"); - QTRY_COMPARE(page.messages.count(), 2); - QTRY_COMPARE(page.messages[1], QString("1024")); - - evaluateJavaScriptSync(&page, - "navigator.webkitPersistentStorage.queryUsageAndQuota(function(usedBytes, grantedBytes) {" \ - "console.log(usedBytes + ', ' + grantedBytes);" \ - "});"); - QTRY_COMPARE(page.messages.count(), 3); - QTRY_COMPARE(page.messages[2], QString("0, 1024")); + evaluateJavaScriptSync(&page, QString( + "var storage = %1;" + "storage.queryUsageAndQuota(function(usedBytes, grantedBytes) {" + " console.log(usedBytes);" + " console.log(grantedBytes);" + "});").arg(storage)); + QTRY_COMPARE(page.messages.size(), 3); + QTRY_COMPARE(page.messages[1], QString("0")); + QTRY_VERIFY(page.messages[2].toLongLong() >= 1024); } void tst_QWebEnginePage::symmetricUrl() @@ -2008,7 +2137,7 @@ void tst_QWebEnginePage::symmetricUrl() // loading is _not_ immediate, so the text isn't set just yet. QVERIFY(toPlainTextSync(view.page()).isEmpty()); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 20000); QCOMPARE(view.history()->count(), 1); QCOMPARE(toPlainTextSync(view.page()), QString("Test")); @@ -2022,8 +2151,8 @@ void tst_QWebEnginePage::symmetricUrl() QCOMPARE(view.url(), dataUrl3); // setUrl(dataUrl3) might override the pending load for dataUrl2. Or not. - QTRY_VERIFY(loadFinishedSpy.count() >= 2); - QTRY_VERIFY(loadFinishedSpy.count() <= 3); + QTRY_VERIFY(loadFinishedSpy.size() >= 2); + QTRY_VERIFY(loadFinishedSpy.size() <= 3); // setUrl(dataUrl3) might stop Chromium from adding a navigation entry for dataUrl2, // depending on whether the load of dataUrl2 could be completed in time. @@ -2101,7 +2230,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())); @@ -2158,7 +2287,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; @@ -2182,7 +2311,7 @@ void tst_QWebEnginePage::requestedUrlAfterSetAndLoadFailures() const QUrl first("http://abcdef.abcdef/"); page.setUrl(first); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 20000); QCOMPARE(page.url(), first); QCOMPARE(page.requestedUrl(), first); QVERIFY(!spy.at(0).first().toBool()); @@ -2191,7 +2320,7 @@ void tst_QWebEnginePage::requestedUrlAfterSetAndLoadFailures() QVERIFY(first != second); page.load(second); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 2, 20000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 2, 20000); QCOMPARE(page.url(), first); QCOMPARE(page.requestedUrl(), second); QVERIFY(!spy.at(1).first().toBool()); @@ -2237,7 +2366,7 @@ void tst_QWebEnginePage::setHtmlWithImageResource() QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); page.setHtml(html, QUrl("file:///path/to/file")); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 12000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 12000); QCOMPARE(evaluateJavaScriptSync(&page, "document.images.length").toInt(), 1); QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].width").toInt(), 128); @@ -2246,7 +2375,7 @@ void tst_QWebEnginePage::setHtmlWithImageResource() // Now we test the opposite: without a baseUrl as a local file, we can still request qrc resources. page.setHtml(html); - QTRY_COMPARE(spy.count(), 2); + QTRY_COMPARE(spy.size(), 2); QCOMPARE(evaluateJavaScriptSync(&page, "document.images.length").toInt(), 1); QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].width").toInt(), 128); QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].height").toInt(), 128); @@ -2309,7 +2438,7 @@ void tst_QWebEnginePage::setHtmlWithBaseURL() QString("%1/foo.html").arg(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath()))); QSignalSpy spyFinished(&page, &QWebEnginePage::loadFinished); QVERIFY(spyFinished.wait()); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(evaluateJavaScriptSync(&page, "document.images.length").toInt(), 1); QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].width").toInt(), 128); @@ -2372,7 +2501,7 @@ void tst_QWebEnginePage::setHtmlWithModuleImport() QWebEnginePage page; QSignalSpy spy(&page, &QWebEnginePage::loadFinished); page.setHtml(html, server.url()); - QVERIFY(spy.count() || spy.wait()); + QVERIFY(spy.size() || spy.wait()); QCOMPARE(evaluateJavaScriptSync(&page, "fib7"), QVariant(13)); } @@ -2408,7 +2537,7 @@ void tst_QWebEnginePage::baseUrl() QSignalSpy loadSpy(m_page, SIGNAL(loadFinished(bool))); m_page->setHtml(html, loadUrl); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(m_page->url(), url); QEXPECT_FAIL("null", "Slight change: We now translate QUrl() to about:blank for the virtual url, but not for the baseUrl", Continue); QCOMPARE(baseUrlSync(m_page), baseUrl); @@ -2417,7 +2546,8 @@ void tst_QWebEnginePage::baseUrl() void tst_QWebEnginePage::scrollPosition() { // enlarged image in a small viewport, to provoke the scrollbars to appear - QString html("<html><body><img src='qrc:/image.png' height=500 width=500/></body></html>"); + QString html( + "<html><body><img src='qrc:/resources/image.png' height=500 width=500/></body></html>"); QWebEngineView view; view.setFixedSize(200,200); @@ -2427,12 +2557,12 @@ void tst_QWebEnginePage::scrollPosition() QSignalSpy loadSpy(view.page(), SIGNAL(loadFinished(bool))); view.setHtml(html); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); // try to set the scroll offset programmatically view.page()->runJavaScript("window.scrollTo(23, 29);"); - QTRY_COMPARE(view.page()->scrollPosition().x(), 23 * view.windowHandle()->devicePixelRatio()); - QCOMPARE(view.page()->scrollPosition().y(), 29 * view.windowHandle()->devicePixelRatio()); + QTRY_COMPARE(view.page()->scrollPosition().x(), 23); + QCOMPARE(view.page()->scrollPosition().y(), 29); int x = evaluateJavaScriptSync(view.page(), "window.scrollX").toInt(); int y = evaluateJavaScriptSync(view.page(), "window.scrollY").toInt(); @@ -2453,7 +2583,7 @@ void tst_QWebEnginePage::scrollbarsOff() QSignalSpy loadSpy(&view, SIGNAL(loadFinished(bool))); view.setHtml(html); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QVERIFY(evaluateJavaScriptSync(view.page(), "innerWidth == document.documentElement.offsetWidth").toBool()); } @@ -2488,7 +2618,7 @@ void tst_QWebEnginePage::evaluateWillCauseRepaint() QSignalSpy loadSpy(&view, SIGNAL(loadFinished(bool))); view.setHtml(html); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); evaluateJavaScriptSync(view.page(), "document.getElementById('junk').style.display = 'none';"); QSignalSpy repaintSpy(&view, &WebView::repaintRequested); @@ -2582,7 +2712,7 @@ void tst_QWebEnginePage::setUrlToEmpty() expectedLoadFinishedCount++; QVERIFY(spy.wait()); - QCOMPARE(spy.count(), expectedLoadFinishedCount); + QCOMPARE(spy.size(), expectedLoadFinishedCount); QCOMPARE(page.url(), url); QCOMPARE(page.requestedUrl(), url); QCOMPARE(baseUrlSync(&page), url); @@ -2591,7 +2721,7 @@ void tst_QWebEnginePage::setUrlToEmpty() page.setUrl(QUrl()); expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QTRY_COMPARE(spy.size(), expectedLoadFinishedCount); QCOMPARE(page.url(), aboutBlank); QCOMPARE(page.requestedUrl(), QUrl()); QCOMPARE(baseUrlSync(&page), aboutBlank); @@ -2600,7 +2730,7 @@ void tst_QWebEnginePage::setUrlToEmpty() page.setUrl(url); expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QTRY_COMPARE(spy.size(), expectedLoadFinishedCount); QCOMPARE(page.url(), url); QCOMPARE(page.requestedUrl(), url); QCOMPARE(baseUrlSync(&page), url); @@ -2609,7 +2739,7 @@ void tst_QWebEnginePage::setUrlToEmpty() page.load(QUrl()); expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QTRY_COMPARE(spy.size(), expectedLoadFinishedCount); QCOMPARE(page.url(), aboutBlank); QCOMPARE(page.requestedUrl(), QUrl()); QCOMPARE(baseUrlSync(&page), aboutBlank); @@ -2664,9 +2794,9 @@ void tst_QWebEnginePage::setUrlToBadDomain() page.setUrl(url1); - QTRY_COMPARE(urlSpy.count(), 1); - QTRY_COMPARE_WITH_TIMEOUT(titleSpy.count(), 1, 20000); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(urlSpy.size(), 1); + QTRY_COMPARE_WITH_TIMEOUT(titleSpy.size(), 1, 20000); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), url1); QCOMPARE(titleSpy.takeFirst().value(0).toString(), url1.host()); @@ -2677,9 +2807,9 @@ void tst_QWebEnginePage::setUrlToBadDomain() page.setUrl(url2); - QTRY_COMPARE(urlSpy.count(), 1); - QTRY_COMPARE_WITH_TIMEOUT(titleSpy.count(), 1, 20000); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(urlSpy.size(), 1); + QTRY_COMPARE_WITH_TIMEOUT(titleSpy.size(), 1, 20000); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), url2); QCOMPARE(titleSpy.takeFirst().value(0).toString(), url2.host()); @@ -2703,9 +2833,9 @@ void tst_QWebEnginePage::setUrlToBadPort() page.setUrl(url1); - QTRY_COMPARE(urlSpy.count(), 1); - QTRY_COMPARE(titleSpy.count(), 2); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(urlSpy.size(), 1); + QTRY_COMPARE(titleSpy.size(), 2); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), url1); QCOMPARE(titleSpy.takeFirst().value(0).toString(), url1.authority()); @@ -2717,9 +2847,9 @@ void tst_QWebEnginePage::setUrlToBadPort() page.setUrl(url2); - QTRY_COMPARE(urlSpy.count(), 1); - QTRY_COMPARE(titleSpy.count(), 2); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(urlSpy.size(), 1); + QTRY_COMPARE(titleSpy.size(), 2); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), url2); QCOMPARE(titleSpy.takeFirst().value(0).toString(), url2.authority()); @@ -2750,7 +2880,7 @@ void tst_QWebEnginePage::setUrlHistory() m_page->setUrl(QUrl()); expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QTRY_COMPARE(spy.size(), expectedLoadFinishedCount); QCOMPARE(m_page->url(), aboutBlank); QCOMPARE(m_page->requestedUrl(), QUrl()); // Chromium stores navigation entry for every successful loads. The load of the empty page is committed and stored as about:blank. @@ -2759,7 +2889,7 @@ void tst_QWebEnginePage::setUrlHistory() url = QUrl("http://url.invalid/"); m_page->setUrl(url); expectedLoadFinishedCount++; - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), expectedLoadFinishedCount, 20000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), expectedLoadFinishedCount, 20000); // When error page is disabled in case of LoadFail the entry of the unavailable page is not stored. // We expect the url of the previously loaded page here. QCOMPARE(m_page->url(), aboutBlank); @@ -2770,14 +2900,14 @@ void tst_QWebEnginePage::setUrlHistory() url = QUrl("qrc:/resources/test1.html"); m_page->setUrl(url); expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QTRY_COMPARE(spy.size(), expectedLoadFinishedCount); QCOMPARE(m_page->url(), url); QCOMPARE(m_page->requestedUrl(), url); QCOMPARE(collectHistoryUrls(m_page->history()), QStringList() << aboutBlank.toString() << QStringLiteral("qrc:/resources/test1.html")); m_page->setUrl(QUrl()); expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QTRY_COMPARE(spy.size(), expectedLoadFinishedCount); QCOMPARE(m_page->url(), aboutBlank); QCOMPARE(m_page->requestedUrl(), QUrl()); // Chromium stores navigation entry for every successful loads. The load of the empty page is committed and stored as about:blank. @@ -2789,7 +2919,7 @@ void tst_QWebEnginePage::setUrlHistory() url = QUrl("qrc:/resources/test1.html"); m_page->setUrl(url); expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QTRY_COMPARE(spy.size(), expectedLoadFinishedCount); QCOMPARE(m_page->url(), url); QCOMPARE(m_page->requestedUrl(), url); // The history count DOES change since the about:blank is in the list. @@ -2802,7 +2932,7 @@ void tst_QWebEnginePage::setUrlHistory() url = QUrl("qrc:/resources/test2.html"); m_page->setUrl(url); expectedLoadFinishedCount++; - QTRY_COMPARE(spy.count(), expectedLoadFinishedCount); + QTRY_COMPARE(spy.size(), expectedLoadFinishedCount); QCOMPARE(m_page->url(), url); QCOMPARE(m_page->requestedUrl(), url); QCOMPARE(collectHistoryUrls(m_page->history()), QStringList() @@ -2817,6 +2947,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); @@ -2824,20 +2955,22 @@ void tst_QWebEnginePage::setUrlUsingStateObject() url = QUrl("qrc:/resources/test1.html"); m_page->setUrl(url); expectedUrlChangeCount++; - QTRY_COMPARE(urlChangedSpy.count(), expectedUrlChangeCount); + QTRY_COMPARE(urlChangedSpy.size(), expectedUrlChangeCount); QCOMPARE(m_page->url(), url); - QTRY_COMPARE(m_page->history()->count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + QCOMPARE(m_page->url(), url); + QCOMPARE(m_page->history()->count(), 1); evaluateJavaScriptSync(m_page, "window.history.pushState(null, 'push', 'navigate/to/here')"); expectedUrlChangeCount++; - QTRY_COMPARE(urlChangedSpy.count(), expectedUrlChangeCount); + QTRY_COMPARE(urlChangedSpy.size(), expectedUrlChangeCount); QCOMPARE(m_page->url(), QUrl("qrc:/resources/navigate/to/here")); QCOMPARE(m_page->history()->count(), 2); QVERIFY(m_page->history()->canGoBack()); evaluateJavaScriptSync(m_page, "window.history.replaceState(null, 'replace', 'another/location')"); expectedUrlChangeCount++; - QTRY_COMPARE(urlChangedSpy.count(), expectedUrlChangeCount); + QTRY_COMPARE(urlChangedSpy.size(), expectedUrlChangeCount); QCOMPARE(m_page->url(), QUrl("qrc:/resources/navigate/to/another/location")); QCOMPARE(m_page->history()->count(), 2); QVERIFY(!m_page->history()->canGoForward()); @@ -2845,7 +2978,7 @@ void tst_QWebEnginePage::setUrlUsingStateObject() evaluateJavaScriptSync(m_page, "window.history.back()"); expectedUrlChangeCount++; - QTRY_COMPARE(urlChangedSpy.count(), expectedUrlChangeCount); + QTRY_COMPARE(urlChangedSpy.size(), expectedUrlChangeCount); QCOMPARE(m_page->url(), QUrl("qrc:/resources/test1.html")); QVERIFY(m_page->history()->canGoForward()); QVERIFY(!m_page->history()->canGoBack()); @@ -2874,9 +3007,9 @@ void tst_QWebEnginePage::setUrlThenLoads() QSignalSpy finishedSpy(m_page, SIGNAL(loadFinished(bool))); m_page->setUrl(url); - QTRY_COMPARE(startedSpy.count(), 1); - QTRY_COMPARE(urlChangedSpy.count(), 1); - QTRY_COMPARE(finishedSpy.count(), 1); + QTRY_COMPARE(startedSpy.size(), 1); + QTRY_COMPARE(urlChangedSpy.size(), 1); + QTRY_COMPARE(finishedSpy.size(), 1); QVERIFY(finishedSpy.at(0).first().toBool()); QCOMPARE(m_page->url(), url); QCOMPARE(m_page->requestedUrl(), url); @@ -2890,11 +3023,11 @@ void tst_QWebEnginePage::setUrlThenLoads() QTRY_COMPARE(m_page->requestedUrl(), urlToLoad1); // baseUrlSync spins an event loop and this sometimes return the next result. // QCOMPARE(baseUrlSync(m_page), baseUrl); - QTRY_COMPARE(startedSpy.count(), 2); + QTRY_COMPARE(startedSpy.size(), 2); // After first URL changed. - QTRY_COMPARE(urlChangedSpy.count(), 2); - QTRY_COMPARE(finishedSpy.count(), 2); + QTRY_COMPARE(urlChangedSpy.size(), 2); + QTRY_COMPARE(finishedSpy.size(), 2); QVERIFY(finishedSpy.at(1).first().toBool()); QCOMPARE(m_page->url(), urlToLoad1); QCOMPARE(m_page->requestedUrl(), urlToLoad1); @@ -2904,11 +3037,11 @@ void tst_QWebEnginePage::setUrlThenLoads() QCOMPARE(m_page->url(), urlToLoad1); QCOMPARE(m_page->requestedUrl(), urlToLoad2); QCOMPARE(baseUrlSync(m_page), extractBaseUrl(urlToLoad1)); - QTRY_COMPARE(startedSpy.count(), 3); + QTRY_COMPARE(startedSpy.size(), 3); // After second URL changed. - QTRY_COMPARE(urlChangedSpy.count(), 3); - QTRY_COMPARE(finishedSpy.count(), 3); + QTRY_COMPARE(urlChangedSpy.size(), 3); + QTRY_COMPARE(finishedSpy.size(), 3); QVERIFY(finishedSpy.at(2).first().toBool()); QCOMPARE(m_page->url(), urlToLoad2); QCOMPARE(m_page->requestedUrl(), urlToLoad2); @@ -2996,7 +3129,7 @@ void tst_QWebEnginePage::loadInSignalHandlers() URLSetter setter(m_page, signal, type, urlForSetter); QSignalSpy spy(&setter, &URLSetter::finished); m_page->load(url); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 20000); + QTRY_VERIFY_WITH_TIMEOUT(spy.size() >= 1, 20000); QCOMPARE(m_page->url(), urlForSetter); } @@ -3007,31 +3140,31 @@ void tst_QWebEnginePage::loadFromQrc() // Standard case. page.load(QStringLiteral("qrc:///resources/foo.txt")); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().value(0).toBool(), true); QCOMPARE(toPlainTextSync(&page), QStringLiteral("foo\n")); // Query and fragment parts are ignored. page.load(QStringLiteral("qrc:///resources/bar.txt?foo=1#bar")); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().value(0).toBool(), true); QCOMPARE(toPlainTextSync(&page), QStringLiteral("bar\n")); // Literal spaces are OK. page.load(QStringLiteral("qrc:///resources/path with spaces.txt")); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().value(0).toBool(), true); QCOMPARE(toPlainTextSync(&page), QStringLiteral("contents with spaces\n")); // Escaped spaces are OK too. page.load(QStringLiteral("qrc:///resources/path%20with%20spaces.txt")); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().value(0).toBool(), true); QCOMPARE(toPlainTextSync(&page), QStringLiteral("contents with spaces\n")); // Resource not found, loading fails. page.load(QStringLiteral("qrc:///nope")); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 10000); QCOMPARE(spy.takeFirst().value(0).toBool(), false); } @@ -3048,7 +3181,7 @@ void tst_QWebEnginePage::restoreHistory() QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); page.load(QUrl(QStringLiteral("qrc:/resources/test1.html"))); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(page.webChannel(), &channel); QVERIFY(page.scripts().contains(script)); @@ -3058,7 +3191,7 @@ void tst_QWebEnginePage::restoreHistory() out << *page.history(); QDataStream in(&data, QIODevice::ReadOnly); in >> *page.history(); - QTRY_COMPARE(spy.count(), 2); + QTRY_COMPARE(spy.size(), 2); QCOMPARE(page.webChannel(), &channel); QVERIFY(page.scripts().contains(script)); @@ -3081,42 +3214,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)); + QCOMPARE(page.zoomFactor(), 2.5); - const QUrl urlToLoad("qrc:/resources/test1.html"); + const QUrl url1("qrc:/resources/test1.html"), url2(QUrl("qrc:/resources/test2.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.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(5.5); - QVERIFY(qFuzzyCompare(page.zoomFactor(), 2.5)); + page.setZoomFactor(0.1); // min accepted zoom: kMinimumPageZoomFactor = 0.25 + QCOMPARE(page.zoomFactor(), 2.5); - page.setZoomFactor(0.1); - QVERIFY(qFuzzyCompare(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); + } + + // 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() @@ -3136,17 +3286,20 @@ void tst_QWebEnginePage::mouseButtonTranslation() view.resize(640, 480); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); - QTRY_VERIFY(spy.count() == 1); + QTRY_VERIFY(spy.size() == 1); QVERIFY(view.focusProxy() != nullptr); - QMouseEvent evpres(QEvent::MouseButtonPress, view.rect().center(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + const QPoint mousePos = view.rect().center(); + QMouseEvent evpres(QEvent::MouseButtonPress, mousePos, view.mapToGlobal(mousePos), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QGuiApplication::sendEvent(view.focusProxy(), &evpres); QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "lastEvent.button").toInt(), 0); QCOMPARE(evaluateJavaScriptSync(view.page(), "lastEvent.buttons").toInt(), 1); - QMouseEvent evpres2(QEvent::MouseButtonPress, view.rect().center(), Qt::RightButton, Qt::LeftButton | Qt::RightButton, Qt::NoModifier); + QMouseEvent evpres2(QEvent::MouseButtonPress, mousePos, view.mapToGlobal(mousePos), + Qt::RightButton, Qt::LeftButton | Qt::RightButton, Qt::NoModifier); QGuiApplication::sendEvent(view.focusProxy(), &evpres2); QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "lastEvent.button").toInt(), 2); @@ -3175,34 +3328,17 @@ void tst_QWebEnginePage::mouseMovementProperties() loadFinishedSpy.wait(); QTest::mouseMove(&view, QPoint(20, 20)); - QTRY_COMPARE(page.messages.count(), 1); + QTRY_COMPARE(page.messages.size(), 1); QTest::mouseMove(&view, QPoint(30, 30)); - QTRY_COMPARE(page.messages.count(), 2); + QTRY_COMPARE(page.messages.size(), 2); QTRY_COMPARE(page.messages[1], QString("10, 10")); QTest::mouseMove(&view, QPoint(20, 20)); - QTRY_COMPARE(page.messages.count(), 3); + QTRY_COMPARE(page.messages.size(), 3); QTRY_COMPARE(page.messages[2], QString("-10, -10")); } -QPoint tst_QWebEnginePage::elementCenter(QWebEnginePage *page, const QString &id) -{ - QVariantList rectList = evaluateJavaScriptSync(page, - "(function(){" - "var elem = document.getElementById('" + id + "');" - "var rect = elem.getBoundingClientRect();" - "return [(rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2];" - "})()").toList(); - - if (rectList.count() != 2) { - qWarning("elementCenter failed."); - return QPoint(); - } - - return QPoint(rectList.at(0).toInt(), rectList.at(1).toInt()); -} - void tst_QWebEnginePage::viewSource() { TestPage page; @@ -3211,12 +3347,12 @@ void tst_QWebEnginePage::viewSource() const QUrl url("qrc:/resources/test1.html"); page.load(url); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QCOMPARE(page.title(), QStringLiteral("Test page 1")); QVERIFY(page.action(QWebEnginePage::ViewSource)->isEnabled()); page.triggerAction(QWebEnginePage::ViewSource); - QTRY_COMPARE(windowCreatedSpy.count(), 1); + QTRY_COMPARE(windowCreatedSpy.size(), 1); QCOMPARE(page.createdWindows.size(), 1); QTRY_COMPARE(page.createdWindows[0]->url().toString(), QStringLiteral("view-source:%1").arg(url.toString())); @@ -3271,7 +3407,7 @@ void tst_QWebEnginePage::viewSourceURL() QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); page.load(userInputUrl); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 12000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 12000); QList<QVariant> arguments = loadFinishedSpy.takeFirst(); QCOMPARE(arguments.at(0).toBool(), loadSucceed); @@ -3306,7 +3442,7 @@ void tst_QWebEnginePage::viewSourceCredentials() QVERIFY(page.action(QWebEnginePage::ViewSource)->isEnabled()); page.triggerAction(QWebEnginePage::ViewSource); - QTRY_COMPARE(windowCreatedSpy.count(), 1); + QTRY_COMPARE(windowCreatedSpy.size(), 1); QCOMPARE(page.createdWindows.size(), 1); QTRY_COMPARE(page.createdWindows[0]->url().toString(), QString("view-source:" + url.toDisplayString(QUrl::RemoveUserInfo))); @@ -3328,7 +3464,7 @@ void tst_QWebEnginePage::proxyConfigWithUnexpectedHostPortPair() QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool))); m_page->load(QStringLiteral("http://127.0.0.1:245/")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); } void tst_QWebEnginePage::registerProtocolHandler_data() @@ -3361,7 +3497,7 @@ void tst_QWebEnginePage::registerProtocolHandler() QSignalSpy permissionSpy(&page, &QWebEnginePage::registerProtocolHandlerRequested); page.setUrl(server.url("/")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0).toBool(), true); QString callFormat = QStringLiteral("window.navigator.registerProtocolHandler(\"%1\", \"%2\", \"%3\")"); @@ -3371,7 +3507,7 @@ void tst_QWebEnginePage::registerProtocolHandler() QString call = callFormat.arg(scheme).arg(url).arg(title); page.runJavaScript(call); - QTRY_COMPARE(permissionSpy.count(), 1); + QTRY_COMPARE(permissionSpy.size(), 1); auto request = permissionSpy.takeFirst().value(0).value<QWebEngineRegisterProtocolHandlerRequest>(); QCOMPARE(request.origin(), QUrl(url)); QCOMPARE(request.scheme(), scheme); @@ -3382,7 +3518,7 @@ void tst_QWebEnginePage::registerProtocolHandler() page.runJavaScript(QStringLiteral("document.getElementById(\"link\").click()")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0).toBool(), permission); QCOMPARE(mailRequestCount, permission ? 1 : 0); QVERIFY(server.stop()); @@ -3398,7 +3534,7 @@ void tst_QWebEnginePage::dataURLFragment() m_page->setHtml("<html><body>" "<a id='link' href='#anchor'>anchor</a>" "</body></html>", QUrl("http://test.qt.io/mytest.html")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QTest::mouseClick(m_view->focusProxy(), Qt::LeftButton, {}, elementCenter(m_page, "link")); QVERIFY(urlChangedSpy.wait()); @@ -3422,7 +3558,7 @@ void tst_QWebEnginePage::devTools() QCOMPARE(devToolsPage.devToolsPage(), nullptr); QCOMPARE(devToolsPage.inspectedPage(), &inspectedPage1); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 90000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 90000); QVERIFY(spy.takeFirst().value(0).toBool()); devToolsPage.setInspectedPage(&inspectedPage2); @@ -3434,7 +3570,7 @@ void tst_QWebEnginePage::devTools() QCOMPARE(devToolsPage.devToolsPage(), nullptr); QCOMPARE(devToolsPage.inspectedPage(), &inspectedPage2); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 90000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 90000); QVERIFY(spy.takeFirst().value(0).toBool()); devToolsPage.setInspectedPage(nullptr); @@ -3445,6 +3581,8 @@ void tst_QWebEnginePage::devTools() QCOMPARE(inspectedPage2.inspectedPage(), nullptr); QCOMPARE(devToolsPage.devToolsPage(), nullptr); QCOMPARE(devToolsPage.inspectedPage(), nullptr); + + QVERIFY(!inspectedPage1.devToolsId().isEmpty()); } void tst_QWebEnginePage::openLinkInDifferentProfile() @@ -3466,11 +3604,11 @@ void tst_QWebEnginePage::openLinkInDifferentProfile() page1.setHtml("<html><body>" "<a id='link' href='hello'>link</a>" "</body></html>", QUrl("echo:/")); - QTRY_COMPARE(spy1.count(), 1); + QTRY_COMPARE(spy1.size(), 1); QVERIFY(spy1.takeFirst().value(0).toBool()); targetPage = &page2; QTest::mouseClick(view.focusProxy(), Qt::MiddleButton, {}, elementCenter(&page1, "link")); - QTRY_COMPARE(spy2.count(), 1); + QTRY_COMPARE(spy2.size(), 1); QVERIFY(spy2.takeFirst().value(0).toBool()); } @@ -3580,7 +3718,7 @@ void tst_QWebEnginePage::openLinkInNewPage() page1.setHtml("<html><body>" "<a id='link' href='hello' target='_blank'>link</a>" "</body></html>", QUrl("echo:/")); - QTRY_COMPARE(page1.spy.count(), 1); + QTRY_COMPARE(page1.spy.size(), 1); QVERIFY(page1.spy.takeFirst().value(0).toBool()); switch (decision) { @@ -3595,7 +3733,7 @@ void tst_QWebEnginePage::openLinkInNewPage() break; } - Qt::MouseButton button; + Qt::MouseButton button = Qt::NoButton; switch (cause) { case Cause::TargetBlank: button = Qt::LeftButton; @@ -3610,13 +3748,13 @@ void tst_QWebEnginePage::openLinkInNewPage() case Effect::Blocked: // Test nothing new loaded QTest::qWait(500); - QCOMPARE(page1.spy.count(), 0); - QCOMPARE(page2.spy.count(), 0); + QCOMPARE(page1.spy.size(), 0); + QCOMPARE(page2.spy.size(), 0); break; case Effect::LoadInSelf: - QTRY_COMPARE(page1.spy.count(), 1); + QTRY_COMPARE(page1.spy.size(), 1); QVERIFY(page1.spy.takeFirst().value(0).toBool()); - QCOMPARE(page2.spy.count(), 0); + QCOMPARE(page2.spy.size(), 0); if (decision == Decision::ReturnSelf && cause == Cause::TargetBlank) // History was discarded due to AddNewContents QCOMPARE(page1.history()->count(), 1); @@ -3625,9 +3763,9 @@ void tst_QWebEnginePage::openLinkInNewPage() QCOMPARE(page2.history()->count(), 0); break; case Effect::LoadInOther: - QTRY_COMPARE(page2.spy.count(), 1); + QTRY_COMPARE(page2.spy.size(), 1); QVERIFY(page2.spy.takeFirst().value(0).toBool()); - QCOMPARE(page1.spy.count(), 0); + QCOMPARE(page1.spy.size(), 0); QCOMPARE(page1.history()->count(), 1); QCOMPARE(page2.history()->count(), 1); break; @@ -3649,7 +3787,7 @@ void tst_QWebEnginePage::dynamicFrame() page.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); QSignalSpy spy(&page, &QWebEnginePage::loadFinished); page.load(QStringLiteral("qrc:/resources/dynamicFrame.html")); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(toPlainTextSync(&page).trimmed(), QStringLiteral("foo")); } @@ -3725,7 +3863,7 @@ void tst_QWebEnginePage::notificationPermission() QSignalSpy spy(&page, &QWebEnginePage::loadFinished); page.setHtml(QString("<html><body>Test</body></html>"), baseUrl); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), setOnInit ? permission : QLatin1String("default")); @@ -3783,6 +3921,149 @@ void tst_QWebEnginePage::sendNotification() QTRY_VERIFY2(page.messages.contains("onclose"), page.messages.join("\n").toLatin1().constData()); } +static QString clipboardPermissionQuery(QString variableName, QString permissionName) +{ + return QString("var %1; navigator.permissions.query({ name:'%2' }).then((p) => { %1 = p.state; " + "});") + .arg(variableName) + .arg(permissionName); +} + + +void tst_QWebEnginePage::clipboardReadWritePermissionInitialState_data() +{ + QTest::addColumn<bool>("canAccessClipboard"); + QTest::addColumn<bool>("canPaste"); + QTest::addColumn<QString>("permission"); + QTest::newRow("access and paste should grant") << true << true << "granted"; + QTest::newRow("no access should prompt") << false << true << "prompt"; + QTest::newRow("no paste should prompt") << true << false << "prompt"; + QTest::newRow("no access or paste should prompt") << false << false << "prompt"; +} + +void tst_QWebEnginePage::clipboardReadWritePermissionInitialState() +{ + QFETCH(bool, canAccessClipboard); + QFETCH(bool, canPaste); + QFETCH(QString, permission); + + QWebEngineProfile otr; + QWebEngineView view(&otr); + QWebEnginePage &page = *view.page(); + view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true); + page.settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, + canAccessClipboard); + page.settings()->setAttribute(QWebEngineSettings::JavascriptCanPaste, canPaste); + + QSignalSpy spy(&page, &QWebEnginePage::loadFinished); + QUrl baseUrl("https://www.example.com/somepage.html"); + page.setHtml(QString("<html><body>Test</body></html>"), baseUrl); + QTRY_COMPARE(spy.size(), 1); + + evaluateJavaScriptSync(&page, clipboardPermissionQuery("readPermission", "clipboard-read")); + QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("readPermission")), permission); + evaluateJavaScriptSync(&page, clipboardPermissionQuery("writePermission", "clipboard-write")); + QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("writePermission")), permission); +} + +void tst_QWebEnginePage::clipboardReadWritePermission_data() +{ + QTest::addColumn<bool>("canAccessClipboard"); + QTest::addColumn<QWebEnginePage::PermissionPolicy>("initialPolicy"); + QTest::addColumn<QString>("initialPermission"); + QTest::addColumn<QWebEnginePage::PermissionPolicy>("requestPolicy"); + QTest::addColumn<QString>("finalPermission"); + + QTest::newRow("noAccessGrantGrant") + << false << QWebEnginePage::PermissionGrantedByUser << "granted" + << QWebEnginePage::PermissionGrantedByUser << "granted"; + QTest::newRow("noAccessGrantDeny") + << false << QWebEnginePage::PermissionGrantedByUser << "granted" + << QWebEnginePage::PermissionDeniedByUser << "denied"; + QTest::newRow("noAccessDenyGrant") + << false << QWebEnginePage::PermissionDeniedByUser << "denied" + << QWebEnginePage::PermissionGrantedByUser << "granted"; + QTest::newRow("noAccessDenyDeny") << false << QWebEnginePage::PermissionDeniedByUser << "denied" + << QWebEnginePage::PermissionDeniedByUser << "denied"; + QTest::newRow("noAccessAskGrant") << false << QWebEnginePage::PermissionUnknown << "prompt" + << QWebEnginePage::PermissionGrantedByUser << "granted"; + + // All policies are ignored and overridden by setting JsCanAccessClipboard and JsCanPaste to + // true + QTest::newRow("accessGrantGrant") + << true << QWebEnginePage::PermissionGrantedByUser << "granted" + << QWebEnginePage::PermissionGrantedByUser << "granted"; + QTest::newRow("accessDenyDeny") << true << QWebEnginePage::PermissionDeniedByUser << "granted" + << QWebEnginePage::PermissionDeniedByUser << "granted"; + QTest::newRow("accessAskAsk") << true << QWebEnginePage::PermissionUnknown << "granted" + << QWebEnginePage::PermissionUnknown << "granted"; +} + +void tst_QWebEnginePage::clipboardReadWritePermission() +{ + QFETCH(bool, canAccessClipboard); + QFETCH(QWebEnginePage::PermissionPolicy, initialPolicy); + QFETCH(QString, initialPermission); + QFETCH(QWebEnginePage::PermissionPolicy, requestPolicy); + QFETCH(QString, finalPermission); + + QWebEngineProfile otr; + QWebEngineView view(&otr); + QWebEnginePage &page = *view.page(); + view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true); + page.settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, + canAccessClipboard); + page.settings()->setAttribute(QWebEngineSettings::JavascriptCanPaste, true); + + QUrl baseUrl("https://www.example.com/somepage.html"); + + int permissionRequestCount = 0; + bool errorState = false; + + // if JavascriptCanAccessClipboard is true, this never fires + connect(&page, &QWebEnginePage::featurePermissionRequested, &page, + [&](const QUrl &o, QWebEnginePage::Feature f) { + if (f != QWebEnginePage::ClipboardReadWrite) + return; + if (o != baseUrl.url(QUrl::RemoveFilename)) { + qWarning() << "Unexpected case. Can't proceed." << o; + errorState = true; + return; + } + permissionRequestCount++; + page.setFeaturePermission(o, f, requestPolicy); + }); + + page.setFeaturePermission(baseUrl, QWebEnginePage::ClipboardReadWrite, initialPolicy); + + QSignalSpy spy(&page, &QWebEnginePage::loadFinished); + page.setHtml(QString("<html><body>Test</body></html>"), baseUrl); + QTRY_COMPARE(spy.size(), 1); + + evaluateJavaScriptSync(&page, clipboardPermissionQuery("readPermission", "clipboard-read")); + QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("readPermission")), initialPermission); + evaluateJavaScriptSync(&page, clipboardPermissionQuery("writePermission", "clipboard-write")); + QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("writePermission")), initialPermission); + + auto triggerRequest = [&page](QString variableName, QString apiCall) + { + auto js = QString("var %1; navigator.clipboard.%2.then((v) => { %1 = 'granted' }, (v) => { %1 = " + "'denied' });") + .arg(variableName) + .arg(apiCall); + evaluateJavaScriptSync(&page, js); + }; + + // permission is not 'remembered' from api standpoint, hence is not suppressed on explicit call + // from JS + triggerRequest("readState", "readText()"); + QTRY_COMPARE(evaluateJavaScriptSync(&page, "readState"), finalPermission); + triggerRequest("writeState", "writeText('foo')"); + QTRY_COMPARE(evaluateJavaScriptSync(&page, "writeState"), finalPermission); + QCOMPARE(permissionRequestCount, canAccessClipboard ? 0 : 2); + QVERIFY(!errorState); +} + void tst_QWebEnginePage::contentsSize() { m_view->resize(800, 600); @@ -3793,8 +4074,8 @@ void tst_QWebEnginePage::contentsSize() m_view->setHtml(QString("<html><body style=\"width: 1600px; height: 1200px;\"><p>hi</p></body></html>")); - QTRY_COMPARE(loadSpy.count(), 1); - QTRY_COMPARE(contentsSizeChangedSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); + QTRY_COMPARE(contentsSizeChangedSpy.size(), 1); // Verify the page's contents size is not limited by the view's size. QCOMPARE(m_page->contentsSize().width(), 1608); @@ -3811,6 +4092,60 @@ void tst_QWebEnginePage::contentsSize() QCOMPARE(m_page->contentsSize().height(), 1216); } +void tst_QWebEnginePage::localFontAccessPermission_data() +{ + QTest::addColumn<QWebEnginePage::PermissionPolicy>("policy"); + QTest::addColumn<bool>("ignore"); + QTest::addColumn<bool>("shouldBeEmpty"); + + QTest::newRow("ignore") << QWebEnginePage::PermissionDeniedByUser << true << true; + QTest::newRow("setDeny") << QWebEnginePage::PermissionDeniedByUser << false << true; + QTest::newRow("setGrant") << QWebEnginePage::PermissionGrantedByUser << false << false; +} + +void tst_QWebEnginePage::localFontAccessPermission() { + QFETCH(QWebEnginePage::PermissionPolicy, policy); + QFETCH(bool, ignore); + QFETCH(bool, shouldBeEmpty); + + QWebEngineView view; + QWebEnginePage page(&view); + view.setPage(&page); + + connect(&page, &QWebEnginePage::featurePermissionRequested, &page, [&] (const QUrl &o, QWebEnginePage::Feature f) { + if (f != QWebEnginePage::LocalFontsAccess) + return; + + if (!ignore) + page.setFeaturePermission(o, f, policy); + }); + + QSignalSpy spy(&page, &QWebEnginePage::loadFinished); + page.load(QUrl("qrc:///resources/fontaccess.html")); + QTRY_COMPARE(spy.size(), 1); + + // Font access is only enabled for visible WebContents. + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + if (evaluateJavaScriptSync(&page, QStringLiteral("!window.queryLocalFonts")).toBool()) + W_QSKIP("Local fonts access is not supported.", SkipSingle); + + // Access to the API requires recent user interaction + QTest::keyPress(view.focusProxy(), Qt::Key_Space); + QTRY_COMPARE(evaluateJavaScriptSync(&page, QStringLiteral("activated")).toBool(), true); + + if (ignore) { + QTRY_COMPARE_NE_WITH_TIMEOUT(evaluateJavaScriptSync(&page, QStringLiteral("done")).toBool(), true, 1000); + } else { + QTRY_VERIFY_WITH_TIMEOUT(evaluateJavaScriptSync(&page, QStringLiteral("done")).toBool() == true, 1000); + QVERIFY((evaluateJavaScriptSync(&page, QStringLiteral("fonts.length")).toInt() == 0) == shouldBeEmpty); + } + + // Reset permission, since otherwise it will be stored between runs + page.setFeaturePermission(QUrl("qrc:///resources/fontaccess.html"), QWebEnginePage::LocalFontsAccess, QWebEnginePage::PermissionUnknown); +} + void tst_QWebEnginePage::setLifecycleState() { qRegisterMetaType<QWebEnginePage::LifecycleState>("LifecycleState"); @@ -3822,64 +4157,64 @@ void tst_QWebEnginePage::setLifecycleState() QSignalSpy visibleSpy(&page, &QWebEnginePage::visibleChanged); page.load(QStringLiteral("qrc:/resources/lifecycle.html")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); - QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(lifecycleSpy.size(), 0); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(visibleSpy.size(), 0); QCOMPARE(page.isVisible(), false); QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant(false)); QCOMPARE(evaluateJavaScriptSync(&page, "frozenness"), QVariant(0)); // Active -> Frozen page.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Frozen); - QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(visibleSpy.size(), 0); QCOMPARE(page.isVisible(), false); QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant(false)); QCOMPARE(evaluateJavaScriptSync(&page, "frozenness"), QVariant(1)); // Frozen -> Active page.setLifecycleState(QWebEnginePage::LifecycleState::Active); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(visibleSpy.size(), 0); QCOMPARE(page.isVisible(), false); QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant(false)); QCOMPARE(evaluateJavaScriptSync(&page, "frozenness"), QVariant(0)); // Active -> Discarded page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Discarded); - QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(visibleSpy.size(), 0); QCOMPARE(page.isVisible(), false); QTest::ignoreMessage(QtWarningMsg, "runJavaScript: disabled in Discarded state"); QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant()); QTest::ignoreMessage(QtWarningMsg, "runJavaScript: disabled in Discarded state"); QCOMPARE(evaluateJavaScriptSync(&page, "frozenness"), QVariant()); - QCOMPARE(loadSpy.count(), 0); + QCOMPARE(loadSpy.size(), 0); // Discarded -> Frozen (illegal!) QTest::ignoreMessage(QtWarningMsg, "setLifecycleState: failed to transition from Discarded to Frozen state: " "illegal transition"); page.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); - QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(lifecycleSpy.size(), 0); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Discarded); // Discarded -> Active page.setLifecycleState(QWebEnginePage::LifecycleState::Active); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(visibleSpy.size(), 0); QCOMPARE(page.isVisible(), false); QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant(true)); QCOMPARE(evaluateJavaScriptSync(&page, "frozenness"), QVariant(0)); @@ -3888,21 +4223,21 @@ void tst_QWebEnginePage::setLifecycleState() page.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); page.setLifecycleState(QWebEnginePage::LifecycleState::Active); - QCOMPARE(lifecycleSpy.count(), 3); + QCOMPARE(lifecycleSpy.size(), 3); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(visibleSpy.size(), 0); QCOMPARE(page.isVisible(), false); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant(true)); QCOMPARE(evaluateJavaScriptSync(&page, "frozenness"), QVariant(0)); // Reload clears document.wasDiscarded page.triggerAction(QWebEnginePage::Reload); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant(false)); } @@ -3918,18 +4253,18 @@ void tst_QWebEnginePage::setVisible() QSignalSpy visibleSpy(&page, &QWebEnginePage::visibleChanged); page.load(QStringLiteral("about:blank")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); - QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(lifecycleSpy.size(), 0); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(visibleSpy.size(), 0); QCOMPARE(page.isVisible(), false); // hidden -> visible page.setVisible(true); - QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(lifecycleSpy.size(), 0); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(visibleSpy.count(), 1); + QCOMPARE(visibleSpy.size(), 1); QCOMPARE(visibleSpy.takeFirst().value(0), QVariant(true)); QCOMPARE(page.isVisible(), true); @@ -3938,28 +4273,28 @@ void tst_QWebEnginePage::setVisible() QtWarningMsg, "setLifecycleState: failed to transition from Active to Frozen state: page is visible"); page.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); - QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(lifecycleSpy.size(), 0); // visible -> hidden page.setVisible(false); - QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(lifecycleSpy.size(), 0); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(visibleSpy.count(), 1); + QCOMPARE(visibleSpy.size(), 1); QCOMPARE(visibleSpy.takeFirst().value(0), QVariant(false)); QCOMPARE(page.isVisible(), false); // Active -> Frozen page.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Frozen); // hidden -> visible (triggers Frozen -> Active) page.setVisible(true); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(visibleSpy.count(), 1); + QCOMPARE(visibleSpy.size(), 1); QCOMPARE(visibleSpy.takeFirst().value(0), QVariant(true)); QCOMPARE(page.isVisible(), true); @@ -3968,31 +4303,31 @@ void tst_QWebEnginePage::setVisible() "setLifecycleState: failed to transition from Active to Discarded state: " "page is visible"); page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); - QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(lifecycleSpy.size(), 0); // visible -> hidden page.setVisible(false); - QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(lifecycleSpy.size(), 0); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(visibleSpy.count(), 1); + QCOMPARE(visibleSpy.size(), 1); QCOMPARE(visibleSpy.takeFirst().value(0), QVariant(false)); QCOMPARE(page.isVisible(), false); // Active -> Discarded page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Discarded); // hidden -> visible (triggers Discarded -> Active) page.setVisible(true); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(visibleSpy.count(), 1); + QCOMPARE(visibleSpy.size(), 1); QCOMPARE(visibleSpy.takeFirst().value(0), QVariant(true)); QCOMPARE(page.isVisible(), true); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); } @@ -4003,7 +4338,7 @@ void tst_QWebEnginePage::discardPreservesProperties() QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); page.load(QStringLiteral("about:blank")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); // Change as many properties as possible to non-default values @@ -4040,7 +4375,7 @@ void tst_QWebEnginePage::discardPreservesProperties() // Discard + undiscard page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); page.setLifecycleState(QWebEnginePage::LifecycleState::Active); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); // Property changes should be preserved @@ -4079,7 +4414,7 @@ void tst_QWebEnginePage::automaticUndiscard() QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); page.load(QStringLiteral("about:blank")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); // setUrl @@ -4104,16 +4439,16 @@ void tst_QWebEnginePage::setLifecycleStateWithDevTools() // Ensure pages are initialized inspectedPage.load(QStringLiteral("about:blank")); devToolsPage.load(QStringLiteral("about:blank")); - QTRY_COMPARE_WITH_TIMEOUT(inspectedSpy.count(), 1, 90000); + QTRY_COMPARE_WITH_TIMEOUT(inspectedSpy.size(), 1, 90000); QCOMPARE(inspectedSpy.takeFirst().value(0), QVariant(true)); - QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.count(), 1, 90000); + QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.size(), 1, 90000); QCOMPARE(devToolsSpy.takeFirst().value(0), QVariant(true)); // Open DevTools with Frozen inspectedPage inspectedPage.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); inspectedPage.setDevToolsPage(&devToolsPage); QCOMPARE(inspectedPage.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.count(), 1, 90000); + QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.size(), 1, 90000); QCOMPARE(devToolsSpy.takeFirst().value(0), QVariant(true)); inspectedPage.setDevToolsPage(nullptr); @@ -4121,9 +4456,9 @@ void tst_QWebEnginePage::setLifecycleStateWithDevTools() inspectedPage.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); inspectedPage.setDevToolsPage(&devToolsPage); QCOMPARE(inspectedPage.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.count(), 1, 90000); + QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.size(), 1, 90000); QCOMPARE(devToolsSpy.takeFirst().value(0), QVariant(true)); - QTRY_COMPARE(inspectedSpy.count(), 1); + QTRY_COMPARE(inspectedSpy.size(), 1); QCOMPARE(inspectedSpy.takeFirst().value(0), QVariant(true)); inspectedPage.setDevToolsPage(nullptr); @@ -4131,7 +4466,7 @@ void tst_QWebEnginePage::setLifecycleStateWithDevTools() devToolsPage.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); devToolsPage.setInspectedPage(&inspectedPage); QCOMPARE(devToolsPage.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.count(), 1, 90000); + QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.size(), 1, 90000); QCOMPARE(devToolsSpy.takeFirst().value(0), QVariant(true)); devToolsPage.setInspectedPage(nullptr); @@ -4139,7 +4474,7 @@ void tst_QWebEnginePage::setLifecycleStateWithDevTools() devToolsPage.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); devToolsPage.setInspectedPage(&inspectedPage); QCOMPARE(devToolsPage.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.count(), 1, 90000); + QTRY_COMPARE_WITH_TIMEOUT(devToolsSpy.size(), 1, 90000); QCOMPARE(devToolsSpy.takeFirst().value(0), QVariant(true)); // keep DevTools open @@ -4177,35 +4512,35 @@ void tst_QWebEnginePage::discardPreservesCommittedLoad() QString url = QStringLiteral("qrc:/resources/lifecycle.html"); page.setUrl(url); - QTRY_COMPARE(loadStartedSpy.count(), 1); + QTRY_COMPARE(loadStartedSpy.size(), 1); loadStartedSpy.clear(); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QCOMPARE(loadFinishedSpy.takeFirst().value(0), QVariant(true)); - QCOMPARE(urlChangedSpy.count(), 1); + QCOMPARE(urlChangedSpy.size(), 1); QCOMPARE(urlChangedSpy.takeFirst().value(0), QVariant(QUrl(url))); QCOMPARE(page.url(), url); - QCOMPARE(titleChangedSpy.count(), 2); + QCOMPARE(titleChangedSpy.size(), 2); QCOMPARE(titleChangedSpy.takeFirst().value(0), QVariant(url)); QString title = QStringLiteral("Lifecycle"); QCOMPARE(titleChangedSpy.takeFirst().value(0), QVariant(title)); QCOMPARE(page.title(), title); page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); - QCOMPARE(loadStartedSpy.count(), 0); - QCOMPARE(loadFinishedSpy.count(), 0); - QCOMPARE(urlChangedSpy.count(), 0); + QCOMPARE(loadStartedSpy.size(), 0); + QCOMPARE(loadFinishedSpy.size(), 0); + QCOMPARE(urlChangedSpy.size(), 0); QCOMPARE(page.url(), QUrl(url)); - QCOMPARE(titleChangedSpy.count(), 0); + QCOMPARE(titleChangedSpy.size(), 0); QCOMPARE(page.title(), title); page.setLifecycleState(QWebEnginePage::LifecycleState::Active); - QTRY_COMPARE(loadStartedSpy.count(), 1); + QTRY_COMPARE(loadStartedSpy.size(), 1); loadStartedSpy.clear(); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QCOMPARE(loadFinishedSpy.takeFirst().value(0), QVariant(true)); - QCOMPARE(urlChangedSpy.count(), 0); + QCOMPARE(urlChangedSpy.size(), 0); QCOMPARE(page.url(), url); - QCOMPARE(titleChangedSpy.count(), 0); + QCOMPARE(titleChangedSpy.size(), 0); QCOMPARE(page.title(), title); } @@ -4222,21 +4557,21 @@ void tst_QWebEnginePage::discardAbortsPendingLoad() [&]() { page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); }); QUrl url = QStringLiteral("qrc:/resources/lifecycle.html"); page.setUrl(url); - QTRY_COMPARE(loadStartedSpy.count(), 1); + QTRY_COMPARE(loadStartedSpy.size(), 1); loadStartedSpy.clear(); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QCOMPARE(loadFinishedSpy.takeFirst().value(0), QVariant(false)); - QCOMPARE(urlChangedSpy.count(), 2); + QCOMPARE(urlChangedSpy.size(), 2); QCOMPARE(urlChangedSpy.takeFirst().value(0), QVariant(url)); QCOMPARE(urlChangedSpy.takeFirst().value(0), QVariant(QUrl())); - QCOMPARE(titleChangedSpy.count(), 0); + QCOMPARE(titleChangedSpy.size(), 0); QCOMPARE(page.url(), QUrl()); QCOMPARE(page.title(), QString()); page.setLifecycleState(QWebEnginePage::LifecycleState::Active); - QCOMPARE(loadStartedSpy.count(), 0); - QCOMPARE(loadFinishedSpy.count(), 0); - QCOMPARE(urlChangedSpy.count(), 0); + QCOMPARE(loadStartedSpy.size(), 0); + QCOMPARE(loadFinishedSpy.size(), 0); + QCOMPARE(urlChangedSpy.size(), 0); QCOMPARE(page.url(), QUrl()); QCOMPARE(page.title(), QString()); } @@ -4252,14 +4587,14 @@ void tst_QWebEnginePage::discardAbortsPendingLoadAndPreservesCommittedLoad() QString url1 = QStringLiteral("qrc:/resources/lifecycle.html"); page.setUrl(url1); - QTRY_COMPARE(loadStartedSpy.count(), 1); + QTRY_COMPARE(loadStartedSpy.size(), 1); loadStartedSpy.clear(); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QCOMPARE(loadFinishedSpy.takeFirst().value(0), QVariant(true)); - QCOMPARE(urlChangedSpy.count(), 1); + QCOMPARE(urlChangedSpy.size(), 1); QCOMPARE(urlChangedSpy.takeFirst().value(0), QVariant(QUrl(url1))); QCOMPARE(page.url(), url1); - QCOMPARE(titleChangedSpy.count(), 2); + QCOMPARE(titleChangedSpy.size(), 2); QCOMPARE(titleChangedSpy.takeFirst().value(0), QVariant(url1)); QString title = QStringLiteral("Lifecycle"); QCOMPARE(titleChangedSpy.takeFirst().value(0), QVariant(title)); @@ -4269,21 +4604,21 @@ void tst_QWebEnginePage::discardAbortsPendingLoadAndPreservesCommittedLoad() [&]() { page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); }); QString url2 = QStringLiteral("about:blank"); page.setUrl(url2); - QTRY_COMPARE(loadStartedSpy.count(), 1); + QTRY_COMPARE(loadStartedSpy.size(), 1); loadStartedSpy.clear(); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QCOMPARE(loadFinishedSpy.takeFirst().value(0), QVariant(false)); - QCOMPARE(urlChangedSpy.count(), 2); + QCOMPARE(urlChangedSpy.size(), 2); QCOMPARE(urlChangedSpy.takeFirst().value(0), QVariant(QUrl(url2))); QCOMPARE(urlChangedSpy.takeFirst().value(0), QVariant(QUrl(url1))); - QCOMPARE(titleChangedSpy.count(), 0); + QCOMPARE(titleChangedSpy.size(), 0); QCOMPARE(page.url(), url1); QCOMPARE(page.title(), title); page.setLifecycleState(QWebEnginePage::LifecycleState::Active); - QCOMPARE(loadStartedSpy.count(), 0); - QCOMPARE(loadFinishedSpy.count(), 0); - QCOMPARE(urlChangedSpy.count(), 0); + QCOMPARE(loadStartedSpy.size(), 0); + QCOMPARE(loadFinishedSpy.size(), 0); + QCOMPARE(urlChangedSpy.size(), 0); QCOMPARE(page.url(), url1); QCOMPARE(page.title(), title); } @@ -4379,32 +4714,32 @@ void tst_QWebEnginePage::recommendedStateAuto() connect(&page, &QWebEnginePage::recommendedStateChanged, &page, &QWebEnginePage::setLifecycleState); page.load(QStringLiteral("qrc:/resources/lifecycle.html")); - QTRY_COMPARE(lifecycleSpy.count(), 2); + QTRY_COMPARE(lifecycleSpy.size(), 2); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); page.setVisible(true); - QTRY_COMPARE(lifecycleSpy.count(), 1); + QTRY_COMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); page.setVisible(false); - QTRY_COMPARE(lifecycleSpy.count(), 2); + QTRY_COMPARE(lifecycleSpy.size(), 2); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); page.triggerAction(QWebEnginePage::Reload); - QTRY_COMPARE(lifecycleSpy.count(), 3); + QTRY_COMPARE(lifecycleSpy.size(), 3); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); QWebEnginePage devTools; page.setDevToolsPage(&devTools); - QTRY_COMPARE(lifecycleSpy.count(), 1); + QTRY_COMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); page.setDevToolsPage(nullptr); - QTRY_COMPARE(lifecycleSpy.count(), 2); + QTRY_COMPARE(lifecycleSpy.size(), 2); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); } @@ -4419,33 +4754,33 @@ void tst_QWebEnginePage::setLifecycleStateAndReload() QSignalSpy lifecycleSpy(&page, &QWebEnginePage::lifecycleStateChanged); page.load(QStringLiteral("qrc:/resources/lifecycle.html")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); - QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(lifecycleSpy.size(), 0); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); page.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Frozen); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); page.triggerAction(QWebEnginePage::Reload); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Discarded); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); page.triggerAction(QWebEnginePage::Reload); QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); - QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.size(), 1); QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); } @@ -4464,20 +4799,20 @@ void tst_QWebEnginePage::editActionsWithExplicitFocus() QVERIFY(!page->action(QWebEnginePage::SelectAll)->isEnabled()); page->setHtml(QString("<html><body><div>foo bar</div></body></html>")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); // Still no focus because focus on navigation is disabled. Edit actions don't do anything (should not crash). QVERIFY(!page->action(QWebEnginePage::SelectAll)->isEnabled()); view.page()->triggerAction(QWebEnginePage::SelectAll); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); QCOMPARE(page->hasSelection(), false); // Focus content by focusing window from JavaScript. Edit actions should be enabled and functional. evaluateJavaScriptSync(page, "window.focus();"); - QTRY_COMPARE(actionChangedSpy.count(), 1); + QTRY_COMPARE(actionChangedSpy.size(), 1); QVERIFY(page->action(QWebEnginePage::SelectAll)->isEnabled()); view.page()->triggerAction(QWebEnginePage::SelectAll); - QTRY_COMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(selectionChangedSpy.size(), 1); QCOMPARE(page->hasSelection(), true); QCOMPARE(page->selectedText(), QStringLiteral("foo bar")); } @@ -4497,13 +4832,13 @@ void tst_QWebEnginePage::editActionsWithInitialFocus() QVERIFY(!page->action(QWebEnginePage::SelectAll)->isEnabled()); page->setHtml(QString("<html><body><div>foo bar</div></body></html>")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); // Content gets initial focus. - QTRY_COMPARE(actionChangedSpy.count(), 1); + QTRY_COMPARE(actionChangedSpy.size(), 1); QVERIFY(page->action(QWebEnginePage::SelectAll)->isEnabled()); view.page()->triggerAction(QWebEnginePage::SelectAll); - QTRY_COMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(selectionChangedSpy.size(), 1); QCOMPARE(page->hasSelection(), true); QCOMPARE(page->selectedText(), QStringLiteral("foo bar")); } @@ -4523,15 +4858,15 @@ void tst_QWebEnginePage::editActionsWithFocusOnIframe() QVERIFY(!page->action(QWebEnginePage::SelectAll)->isEnabled()); page->load(QUrl("qrc:///resources/iframe2.html")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QVERIFY(!page->action(QWebEnginePage::SelectAll)->isEnabled()); // Focusing an iframe. evaluateJavaScriptSync(page, "document.getElementsByTagName('iframe')[0].contentWindow.focus()"); - QTRY_COMPARE(actionChangedSpy.count(), 1); + QTRY_COMPARE(actionChangedSpy.size(), 1); QVERIFY(page->action(QWebEnginePage::SelectAll)->isEnabled()); view.page()->triggerAction(QWebEnginePage::SelectAll); - QTRY_COMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(selectionChangedSpy.size(), 1); QCOMPARE(page->hasSelection(), true); QCOMPARE(page->selectedText(), QStringLiteral("inner")); } @@ -4547,8 +4882,8 @@ void tst_QWebEnginePage::editActionsWithoutSelection() QSignalSpy actionChangedSpy(page->action(QWebEnginePage::SelectAll), &QAction::changed); page->setHtml(QString("<html><body><div>foo bar</div></body></html>")); - QTRY_COMPARE(loadFinishedSpy.count(), 1); - QTRY_COMPARE(actionChangedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + QTRY_COMPARE(actionChangedSpy.size(), 1); QVERIFY(!page->action(QWebEnginePage::Cut)->isEnabled()); QVERIFY(!page->action(QWebEnginePage::Copy)->isEnabled()); @@ -4560,7 +4895,7 @@ void tst_QWebEnginePage::editActionsWithoutSelection() QVERIFY(!page->action(QWebEnginePage::Unselect)->isEnabled()); page->triggerAction(QWebEnginePage::SelectAll); - QTRY_COMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(selectionChangedSpy.size(), 1); QCOMPARE(page->hasSelection(), true); QCOMPARE(page->selectedText(), QStringLiteral("foo bar")); @@ -4620,12 +4955,12 @@ void tst_QWebEnginePage::customUserAgentInNewTab() page.setHtml(QString("<html><body><a id='link' target='_blank' href='") + server.url("/test1").toEncoded() + QString("'>link</a></body></html>")); - QTRY_COMPARE(page.loadSpy.count(), 1); + QTRY_COMPARE(page.loadSpy.size(), 1); QVERIFY(page.loadSpy.takeFirst().value(0).toBool()); QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("navigator.userAgent")).toString(), expectedUserAgent); QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, elementCenter(&page, "link")); QTRY_VERIFY(page.newPage); - QTRY_COMPARE(page.newPage->loadSpy.count(), 1); + QTRY_COMPARE(page.newPage->loadSpy.size(), 1); QTRY_VERIFY(!lastUserAgent.isEmpty()); QCOMPARE(lastUserAgent, expectedUserAgent); QCOMPARE(evaluateJavaScriptSync(page.newPage.get(), QStringLiteral("navigator.userAgent")).toString(), expectedUserAgent); @@ -4639,11 +4974,11 @@ void tst_QWebEnginePage::customUserAgentInNewTab() page.setHtml(QString("<html><body><a id='link' target='_blank' href='") + server.url("/test2").toEncoded() + QString("'>link</a></body></html>")); - QTRY_COMPARE(page.loadSpy.count(), 1); + QTRY_COMPARE(page.loadSpy.size(), 1); QVERIFY(page.loadSpy.takeFirst().value(0).toBool()); QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, elementCenter(&page, "link")); QTRY_VERIFY(page.newPage); - QTRY_COMPARE(page.newPage->loadSpy.count(), 1); + QTRY_COMPARE(page.newPage->loadSpy.size(), 1); QTRY_VERIFY(!lastUserAgent.isEmpty()); QCOMPARE(lastUserAgent, expectedUserAgent); QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("navigator.userAgent")).toString(), expectedUserAgent); @@ -4676,7 +5011,7 @@ void tst_QWebEnginePage::openNewTabInDifferentProfile() QVERIFY(QTest::qWaitForWindowExposed(&view)); page.setHtml(QString("<html><body><a id='link' target='_blank' href='%1'>link</a></body></html>").arg(server.url("/first.html").toEncoded())); - QTRY_COMPARE(page.loadSpy.count(), 1); + QTRY_COMPARE(page.loadSpy.size(), 1); QVERIFY(page.loadSpy.takeFirst().value(0).toBool()); QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, elementCenter(&page, "link")); @@ -4728,17 +5063,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; }; @@ -4757,6 +5092,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; @@ -4790,11 +5127,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")); @@ -4815,20 +5159,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()); } @@ -4885,11 +5249,11 @@ void tst_QWebEnginePage::audioMuted() page.setAudioMuted(true); loadSync(&page, QUrl("about:blank")); QCOMPARE(page.isAudioMuted(), true); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy[0][0], QVariant(true)); page.setAudioMuted(false); QCOMPARE(page.isAudioMuted(), false); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); QCOMPARE(spy[1][0], QVariant(false)); } @@ -4899,9 +5263,9 @@ void tst_QWebEnginePage::closeContents() QSignalSpy spyFinished(&page, &QWebEnginePage::loadFinished); QSignalSpy windowCreatedSpy(&page, &TestPage::windowCreated); page.setUrl(QUrl("about:blank")); - QTRY_COMPARE(spyFinished.count(), 1); + QTRY_COMPARE(spyFinished.size(), 1); page.runJavaScript("var dialog = window.open('', '', 'width=100, height=100');"); - QTRY_COMPARE(windowCreatedSpy.count(), 1); + QTRY_COMPARE(windowCreatedSpy.size(), 1); QWebEngineView *dialogView = new QWebEngineView; QWebEnginePage *dialogPage = page.createdWindows[0]; @@ -4928,8 +5292,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/"); } @@ -4942,11 +5306,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/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/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/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/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/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 d6a945e65..f4ed06e14 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp @@ -18,6 +18,9 @@ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses + #include <QtWebEngineCore/private/qtwebenginecore-config_p.h> #include <qtest.h> #include <util.h> @@ -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,13 @@ private Q_SLOTS: void setViewPreservesExplicitPage(); void closeDiscardsPage(); void loadAfterRendererCrashed(); + void inspectElement(); + void navigateOnDrop_data(); + void navigateOnDrop(); + void emptyUriListOnDrop(); + void datalist(); + void longKeyEventText(); + void pageWithPaintListeners(); }; // This will be called before the first test function is executed. @@ -195,6 +211,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,10 +399,10 @@ void tst_QWebEngineView::changePage() QSignalSpy pageFromLoadSpy(pageFrom.get(), &QWebEnginePage::loadFinished); QSignalSpy pageFromIconLoadSpy(pageFrom.get(), &QWebEnginePage::iconChanged); pageFrom->load(urlFrom); - QTRY_COMPARE(pageFromLoadSpy.count(), 1); + QTRY_COMPARE(pageFromLoadSpy.size(), 1); QCOMPARE(pageFromLoadSpy.last().value(0).toBool(), true); if (!fromIsNullPage) { - QTRY_COMPARE(pageFromIconLoadSpy.count(), 1); + QTRY_COMPARE(pageFromIconLoadSpy.size(), 1); QVERIFY(!pageFromIconLoadSpy.last().value(0).isNull()); } @@ -304,13 +410,13 @@ void tst_QWebEngineView::changePage() QCOMPARE(view->page(), pageFrom.get()); QCOMPARE(QWebEngineView::forPage(pageFrom.get()), view.get()); - QTRY_COMPARE(spyUrl.count(), 1); + QTRY_COMPARE(spyUrl.size(), 1); QCOMPARE(spyUrl.last().value(0).toUrl(), pageFrom->url()); - QTRY_COMPARE(spyTitle.count(), 1); + QTRY_COMPARE(spyTitle.size(), 1); QCOMPARE(spyTitle.last().value(0).toString(), pageFrom->title()); - QTRY_COMPARE(spyIconUrl.count(), fromIsNullPage ? 0 : 1); - QTRY_COMPARE(spyIcon.count(), fromIsNullPage ? 0 : 1); + QTRY_COMPARE(spyIconUrl.size(), fromIsNullPage ? 0 : 1); + QTRY_COMPARE(spyIcon.size(), fromIsNullPage ? 0 : 1); if (!fromIsNullPage) { QVERIFY(!pageFrom->iconUrl().isEmpty()); QCOMPARE(spyIconUrl.last().value(0).toUrl(), pageFrom->iconUrl()); @@ -322,10 +428,10 @@ void tst_QWebEngineView::changePage() QSignalSpy pageToLoadSpy(pageTo.get(), &QWebEnginePage::loadFinished); QSignalSpy pageToIconLoadSpy(pageTo.get(), &QWebEnginePage::iconChanged); pageTo->load(urlTo); - QTRY_COMPARE(pageToLoadSpy.count(), 1); + QTRY_COMPARE(pageToLoadSpy.size(), 1); QCOMPARE(pageToLoadSpy.last().value(0).toBool(), true); if (!toIsNullPage) { - QTRY_COMPARE(pageToIconLoadSpy.count(), 1); + QTRY_COMPARE(pageToIconLoadSpy.size(), 1); QVERIFY(!pageToIconLoadSpy.last().value(0).isNull()); } @@ -334,16 +440,16 @@ void tst_QWebEngineView::changePage() QCOMPARE(QWebEngineView::forPage(pageTo.get()), view.get()); QCOMPARE(QWebEngineView::forPage(pageFrom.get()), nullptr); - QTRY_COMPARE(spyUrl.count(), 2); + QTRY_COMPARE(spyUrl.size(), 2); QCOMPARE(spyUrl.last().value(0).toUrl(), pageTo->url()); - QTRY_COMPARE(spyTitle.count(), 2); + QTRY_COMPARE(spyTitle.size(), 2); QCOMPARE(spyTitle.last().value(0).toString(), pageTo->title()); bool iconIsSame = fromIsNullPage == toIsNullPage; int iconChangeNotifyCount = fromIsNullPage ? (iconIsSame ? 0 : 1) : (iconIsSame ? 1 : 2); - QTRY_COMPARE(spyIconUrl.count(), iconChangeNotifyCount); - QTRY_COMPARE(spyIcon.count(), iconChangeNotifyCount); + QTRY_COMPARE(spyIconUrl.size(), iconChangeNotifyCount); + QTRY_COMPARE(spyIcon.size(), iconChangeNotifyCount); QCOMPARE(pageFrom->iconUrl() == pageTo->iconUrl(), iconIsSame); if (!iconIsSame) { QCOMPARE(spyIconUrl.last().value(0).toUrl(), pageTo->iconUrl()); @@ -354,10 +460,10 @@ void tst_QWebEngineView::changePage() // verify no emits on destroy with the same number of signals in spy view.reset(); qApp->processEvents(); - QTRY_COMPARE(spyUrl.count(), 2); - QTRY_COMPARE(spyTitle.count(), 2); - QTRY_COMPARE(spyIconUrl.count(), iconChangeNotifyCount); - QTRY_COMPARE(spyIcon.count(), iconChangeNotifyCount); + QTRY_COMPARE(spyUrl.size(), 2); + QTRY_COMPARE(spyTitle.size(), 2); + QTRY_COMPARE(spyIconUrl.size(), iconChangeNotifyCount); + QTRY_COMPARE(spyIcon.size(), iconChangeNotifyCount); } void tst_QWebEngineView::reusePage_data() @@ -394,7 +500,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()); @@ -407,6 +513,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 @@ -481,7 +604,7 @@ void tst_QWebEngineView::microFocusCoordinates() QVariant initialMicroFocus = webView.focusProxy()->inputMethodQuery(Qt::ImCursorRectangle); evaluateJavaScriptSync(webView.page(), "window.scrollBy(0, 50)"); - QTRY_VERIFY(scrollSpy.count() > 0); + QTRY_VERIFY(scrollSpy.size() > 0); QTRY_VERIFY(webView.focusProxy()->inputMethodQuery(Qt::ImCursorRectangle).isValid()); QVariant currentMicroFocus = webView.focusProxy()->inputMethodQuery(Qt::ImCursorRectangle); @@ -592,6 +715,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(); } @@ -608,7 +732,7 @@ void tst_QWebEngineView::unhandledKeyEventPropagation() QSignalSpy loadFinishedSpy(&webView, SIGNAL(loadFinished(bool))); webView.load(QUrl("qrc:///resources/keyboardEvents.html")); - QVERIFY(loadFinishedSpy.wait()); + QTRY_VERIFY_WITH_TIMEOUT(loadFinishedSpy.size() > 0, 20000); evaluateJavaScriptSync(webView.page(), "document.getElementById('first_div').focus()"); QTRY_COMPARE(evaluateJavaScriptSync(webView.page(), "document.activeElement.id").toString(), QStringLiteral("first_div")); @@ -654,19 +778,29 @@ void tst_QWebEngineView::unhandledKeyEventPropagation() void tst_QWebEngineView::horizontalScrollbarTest() { +#if QT_CONFIG(webengine_embedded_build) + // Embedded builds enable the OverlayScrollbar and Viewport features (see 'useEmbeddedSwitches' in web_engine_context.cpp). + // These features make the scrollbar simpler assuming we are on a device with small (usually touch) display. + // These scrollbars behave differently on mouse events. + QSKIP("Embedded builds have different scrollbar, skipping test."); +#endif QString html("<html><body>" "<div style='width: 1000px; height: 1000px; background-color: green' />" "</body></html>"); QWebEngineView view; + PageWithPaintListeners page; + view.setPage(&page); view.setFixedSize(600, 600); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); + QSignalSpy firstPaintSpy(&page, &PageWithPaintListeners::firstContentfulPaint); QSignalSpy loadSpy(view.page(), SIGNAL(loadFinished(bool))); view.setHtml(html); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); + QTRY_COMPARE(firstPaintSpy.size(), 1); QVERIFY(view.page()->scrollPosition() == QPoint(0, 0)); QSignalSpy scrollSpy(view.page(), SIGNAL(scrollPositionChanged(QPointF))); @@ -903,7 +1037,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; @@ -958,7 +1092,7 @@ void tst_QWebEngineView::doNotSendMouseKeyboardEventsWhenDisabled() QSignalSpy loadSpy(&webView, SIGNAL(loadFinished(bool))); webView.setHtml("<html><head><title>Title</title></head><body>Hello" "<input id=\"input\" type=\"text\"></body></html>"); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); // When the webView is enabled, the events are swallowed by it, and the parent widget // does not receive any events, otherwise all events are processed by the parent widget. @@ -1005,7 +1139,7 @@ void tst_QWebEngineView::stopSettingFocusWhenDisabled() QSignalSpy loadSpy(&webView, SIGNAL(loadFinished(bool))); webView.setHtml("<html><head><title>Title</title></head><body>Hello" "<input id=\"input\" type=\"text\"></body></html>"); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QTRY_COMPARE_WITH_TIMEOUT(webView.hasFocus(), focusResult, 1000); evaluateJavaScriptSync(webView.page(), "document.getElementById(\"input\").focus()"); @@ -1118,21 +1252,23 @@ void tst_QWebEngineView::focusInternalRenderWidgetHostViewQuickItem() QWebEngineView *webView = new QWebEngineView; QWebEngineSettings *settings = webView->page()->settings(); settings->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false); - webView->resize(300, 300); + webView->resize(300, 100); - QHBoxLayout *layout = new QHBoxLayout; + QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(label); layout->addWidget(webView); + containerWidget->resize(300, 200); containerWidget->setLayout(layout); containerWidget->show(); QVERIFY(QTest::qWaitForWindowExposed(containerWidget.data())); // Load the content, and check that focus is not set. QSignalSpy loadSpy(webView, SIGNAL(loadFinished(bool))); - webView->setHtml("<html><head><title>Title</title></head><body>Hello" - "<input id=\"input\" type=\"text\"></body></html>"); - QTRY_COMPARE(loadSpy.count(), 1); + webView->setHtml("<html><body>" + " <input id='input1' type='text'/>" + "</body></html>"); + QTRY_COMPARE(loadSpy.size(), 1); QTRY_COMPARE(webView->hasFocus(), false); // Manually trigger focus. @@ -1141,15 +1277,43 @@ void tst_QWebEngineView::focusInternalRenderWidgetHostViewQuickItem() // Check that focus is set in QWebEngineView and all internal classes. QTRY_COMPARE(webView->hasFocus(), true); - QQuickWidget *renderWidgetHostViewQtDelegateWidget = - qobject_cast<QQuickWidget *>(webView->focusProxy()); - QVERIFY(renderWidgetHostViewQtDelegateWidget); - QTRY_COMPARE(renderWidgetHostViewQtDelegateWidget->hasFocus(), true); + QQuickWidget *webEngineQuickWidget = qobject_cast<QQuickWidget *>(webView->focusProxy()); + QVERIFY(webEngineQuickWidget); + QTRY_COMPARE(webEngineQuickWidget->hasFocus(), true); + + QQuickItem *root = webEngineQuickWidget->rootObject(); + // The root item should not has focus, otherwise it would handle input events + // instead of the RenderWidgetHostViewQtDelegateItem. + QVERIFY(!root->hasFocus()); + + QCOMPARE(root->childItems().size(), 1); + QQuickItem *renderWidgetHostViewQtDelegateItem = root->childItems().at(0); + QVERIFY(renderWidgetHostViewQtDelegateItem); + QTRY_COMPARE(renderWidgetHostViewQtDelegateItem->hasFocus(), true); + // Test if QWebEngineView handles key events. + QTRY_COMPARE(renderWidgetHostViewQtDelegateItem->hasActiveFocus(), true); + + // Key events should not be forwarded to the unfocused input field. + QTRY_COMPARE(evaluateJavaScriptSync(webView->page(), + "document.getElementById('input1').value").toString(), + QStringLiteral("")); + QTest::keyClick(webView->focusProxy(), Qt::Key_X); + QTest::qWait(100); + QTRY_COMPARE(evaluateJavaScriptSync(webView->page(), + "document.getElementById('input1').value").toString(), + QStringLiteral("")); + + // 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")); - QQuickItem *renderWidgetHostViewQuickItem = - renderWidgetHostViewQtDelegateWidget->rootObject(); - QVERIFY(renderWidgetHostViewQuickItem); - QTRY_COMPARE(renderWidgetHostViewQuickItem->hasFocus(), true); + // 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() @@ -1189,7 +1353,7 @@ void tst_QWebEngineView::changeLocale() QWebEngineView viewDE; QSignalSpy loadFinishedSpyDE(&viewDE, SIGNAL(loadFinished(bool))); viewDE.load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpyDE.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpyDE.size(), 1, 20000); QTRY_VERIFY(!toPlainTextSync(viewDE.page()).isEmpty()); errorLines = toPlainTextSync(viewDE.page()).split(QRegularExpression("[\r\n]"), Qt::SkipEmptyParts); @@ -1199,7 +1363,7 @@ void tst_QWebEngineView::changeLocale() QWebEngineView viewEN; QSignalSpy loadFinishedSpyEN(&viewEN, SIGNAL(loadFinished(bool))); viewEN.load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpyEN.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpyEN.size(), 1, 20000); QTRY_VERIFY(!toPlainTextSync(viewEN.page()).isEmpty()); errorLines = toPlainTextSync(viewEN.page()).split(QRegularExpression("[\r\n]"), Qt::SkipEmptyParts); @@ -1212,7 +1376,7 @@ void tst_QWebEngineView::changeLocale() // Check whether an existing QWebEngineView keeps the language settings after changing the default locale viewDE.load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpyDE.count(), 1, 20000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpyDE.size(), 1, 20000); QTRY_VERIFY(!toPlainTextSync(viewDE.page()).isEmpty()); errorLines = toPlainTextSync(viewDE.page()).split(QRegularExpression("[\r\n]"), Qt::SkipEmptyParts); @@ -1244,10 +1408,10 @@ void tst_QWebEngineView::mixLangLocale() auto sc = connect(view.page(), &QWebEnginePage::renderProcessTerminated, [&] () { terminated = true; }); view.load(QUrl("qrc:///resources/dummy.html")); - QTRY_VERIFY(terminated || loadSpy.count() == 1); + QTRY_VERIFY(terminated || loadSpy.size() == 1); QVERIFY2(!terminated, - qPrintable(QString("Locale [%1] terminated: %2, loaded: %3").arg(locale).arg(terminated).arg(loadSpy.count()))); + qPrintable(QString("Locale [%1] terminated: %2, loaded: %3").arg(locale).arg(terminated).arg(loadSpy.size()))); QVERIFY(loadSpy.first().first().toBool()); QString content = toPlainTextSync(view.page()); @@ -1294,18 +1458,19 @@ void tst_QWebEngineView::inputMethodsTextFormat_data() void tst_QWebEngineView::inputMethodsTextFormat() { - QWebEngineView view; - view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true); - QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); + QWebEnginePage page; + QWebEngineView view(&page); + page.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true); + QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); - view.setHtml("<html><body>" + page.setHtml("<html><body>" " <input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/>" "</body></html>"); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); - evaluateJavaScriptSync(view.page(), "document.getElementById('input1').focus()"); view.show(); QVERIFY(QTest::qWaitForWindowExposed(&view)); + evaluateJavaScriptSync(&page, "document.getElementById('input1').focus()"); QFETCH(QString, string); QFETCH(int, start); @@ -1329,8 +1494,8 @@ void tst_QWebEngineView::inputMethodsTextFormat() attrs.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, start, length, format)); QInputMethodEvent im(string, attrs); - QVERIFY(QApplication::sendEvent(view.focusProxy(), &im)); - QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), string); + QApplication::sendEvent(view.focusProxy(), &im); + QTRY_COMPARE_WITH_TIMEOUT(evaluateJavaScriptSync(&page, "document.getElementById('input1').value").toString(), string, 20000); } void tst_QWebEngineView::keyboardEvents() @@ -1339,7 +1504,7 @@ void tst_QWebEngineView::keyboardEvents() view.show(); QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); view.load(QUrl("qrc:///resources/keyboardEvents.html")); - QVERIFY(loadFinishedSpy.wait()); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 30000); QStringList elements; elements << "first_div" << "second_div"; @@ -1458,17 +1623,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")); } @@ -1497,7 +1666,7 @@ void tst_QWebEngineView::mouseClick() textInputCenter = elementCenter(view.page(), "input"); QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter); QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); QVERIFY(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString().isEmpty()); // Double click @@ -1512,13 +1681,13 @@ void tst_QWebEngineView::mouseClick() textInputCenter = elementCenter(view.page(), "input"); QTest::mouseMultiClick(view.focusProxy(), textInputCenter, 2); QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 1); + QCOMPARE(selectionChangedSpy.size(), 1); QCOMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input")); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QStringLiteral("Company")); QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter); QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 2); + QCOMPARE(selectionChangedSpy.size(), 2); QVERIFY(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString().isEmpty()); // Triple click @@ -1533,13 +1702,13 @@ void tst_QWebEngineView::mouseClick() textInputCenter = elementCenter(view.page(), "input"); QTest::mouseMultiClick(view.focusProxy(), textInputCenter, 3); QVERIFY(selectionChangedSpy.wait()); - QTRY_COMPARE(selectionChangedSpy.count(), 2); + QTRY_COMPARE(selectionChangedSpy.size(), 2); QCOMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input")); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QStringLiteral("The Qt Company")); QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter); QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 3); + QCOMPARE(selectionChangedSpy.size(), 3); QVERIFY(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString().isEmpty()); } @@ -1567,12 +1736,12 @@ void tst_QWebEngineView::postData() // examine request QStringList request = lines[0].split(" ", Qt::SkipEmptyParts); - bool requestOk = request.length() > 2 + bool requestOk = request.size() > 2 && request[2].toUpper().startsWith("HTTP/") && request[0].toUpper() == "POST" && request[1] == "/"; if (!requestOk) // POST and HTTP/... can be switched(?) - requestOk = request.length() > 2 + requestOk = request.size() > 2 && request[0].toUpper().startsWith("HTTP/") && request[2].toUpper() == "POST" && request[1] == "/"; @@ -1580,16 +1749,16 @@ void tst_QWebEngineView::postData() // examine headers int line = 1; bool headersOk = true; - for (; headersOk && line < lines.length(); line++) { + for (; headersOk && line < lines.size(); line++) { QStringList headerParts = lines[line].split(":"); - if (headerParts.length() < 2) + if (headerParts.size() < 2) break; QString headerKey = headerParts[0].trimmed().toLower(); QString headerValue = headerParts[1].trimmed().toLower(); if (headerKey == "host") headersOk = headersOk && (headerValue == "127.0.0.1") - && (headerParts.length() == 3) + && (headerParts.size() == 3) && (headerParts[2].trimmed() == QString::number(server.serverPort())); if (headerKey == "content-type") @@ -1598,12 +1767,12 @@ void tst_QWebEngineView::postData() // examine body bool bodyOk = true; - if (lines.length() == line+2) { + if (lines.size() == line+2) { QStringList postedFields = lines[line+1].split("&"); QMap<QString, QString> postedData; - for (int i = 0; bodyOk && i < postedFields.length(); i++) { + for (int i = 0; bodyOk && i < postedFields.size(); i++) { QStringList postedField = postedFields[i].split("="); - if (postedField.length() == 2) + if (postedField.size() == 2) postedData[QUrl::fromPercentEncoding(postedField[0].toLocal8Bit())] = QUrl::fromPercentEncoding(postedField[1].toLocal8Bit()); else @@ -1676,11 +1845,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))); @@ -1898,14 +2066,14 @@ void tst_QWebEngineView::inputContextQueryInput() view.setHtml("<html><body>" " <input type='text' id='input1' value='' size='50'/>" "</body></html>"); - QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QVERIFY(QTest::qWaitForWindowExposed(&view)); - QCOMPARE(testContext.infos.count(), 0); + QCOMPARE(testContext.infos.size(), 0); // Set focus on an input field. QPoint textInputCenter = elementCenter(view.page(), "input1"); QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter); - QTRY_COMPARE(testContext.infos.count(), 2); + QTRY_COMPARE(testContext.infos.size(), 2); QCOMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input1")); foreach (const InputMethodInfo &info, testContext.infos) { QCOMPARE(info.cursorPosition, 0); @@ -1917,7 +2085,7 @@ void tst_QWebEngineView::inputContextQueryInput() // Change content of an input field from JavaScript. evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value='QtWebEngine';"); - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 11); QCOMPARE(testContext.infos[0].anchorPosition, 11); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine")); @@ -1926,7 +2094,7 @@ void tst_QWebEngineView::inputContextQueryInput() // Change content of an input field by key press. QTest::keyClick(view.focusProxy(), Qt::Key_Exclam); - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 12); QCOMPARE(testContext.infos[0].anchorPosition, 12); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!")); @@ -1935,7 +2103,7 @@ void tst_QWebEngineView::inputContextQueryInput() // Change cursor position. QTest::keyClick(view.focusProxy(), Qt::Key_Left); - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 11); QCOMPARE(testContext.infos[0].anchorPosition, 11); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!")); @@ -1950,8 +2118,8 @@ void tst_QWebEngineView::inputContextQueryInput() QInputMethodEvent event("", attributes); QApplication::sendEvent(view.focusProxy(), &event); } - QTRY_COMPARE(testContext.infos.count(), 2); - QTRY_COMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 2); + QTRY_COMPARE(selectionChangedSpy.size(), 1); // As a first step, Chromium moves the cursor to the start of the selection. // We don't filter this in QtWebEngine because we don't know yet if this is part of a selection. @@ -1976,8 +2144,8 @@ void tst_QWebEngineView::inputContextQueryInput() QInputMethodEvent event("", attributes); QApplication::sendEvent(view.focusProxy(), &event); } - QTRY_COMPARE(testContext.infos.count(), 1); - QTRY_COMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); + QTRY_COMPARE(selectionChangedSpy.size(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 0); QCOMPARE(testContext.infos[0].anchorPosition, 0); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!")); @@ -1991,9 +2159,9 @@ void tst_QWebEngineView::inputContextQueryInput() QInputMethodEvent event("123", attributes); QApplication::sendEvent(view.focusProxy(), &event); } - QTRY_COMPARE(testContext.infos.count(), 1); - QCOMPARE(testContext.infos[0].cursorPosition, 3); - QCOMPARE(testContext.infos[0].anchorPosition, 3); + QTRY_COMPARE(testContext.infos.size(), 1); + QCOMPARE(testContext.infos[0].cursorPosition, 0); + QCOMPARE(testContext.infos[0].anchorPosition, 0); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!")); QCOMPARE(testContext.infos[0].selectedText, QStringLiteral("")); QCOMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), QStringLiteral("123QtWebEngine!")); @@ -2005,7 +2173,7 @@ void tst_QWebEngineView::inputContextQueryInput() QInputMethodEvent event("", attributes); QApplication::sendEvent(view.focusProxy(), &event); } - QTRY_COMPARE(testContext.infos.count(), 2); + QTRY_COMPARE(testContext.infos.size(), 2); foreach (const InputMethodInfo &info, testContext.infos) { QCOMPARE(info.cursorPosition, 0); QCOMPARE(info.anchorPosition, 0); @@ -2022,7 +2190,7 @@ void tst_QWebEngineView::inputContextQueryInput() event.setCommitString(QStringLiteral("123"), 0, 0); QApplication::sendEvent(view.focusProxy(), &event); } - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 3); QCOMPARE(testContext.infos[0].anchorPosition, 3); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("123QtWebEngine!")); @@ -2032,7 +2200,7 @@ void tst_QWebEngineView::inputContextQueryInput() // Focus out. QTest::keyPress(view.focusProxy(), Qt::Key_Tab); - QTRY_COMPARE(testContext.infos.count(), 1); + QTRY_COMPARE(testContext.infos.size(), 1); QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("")); testContext.infos.clear(); } @@ -2076,7 +2244,7 @@ void tst_QWebEngineView::inputMethods() QInputMethodEvent eventText(text, inputAttributes); QApplication::sendEvent(view.focusProxy(), &eventText); QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), text); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); } { @@ -2085,7 +2253,7 @@ void tst_QWebEngineView::inputMethods() eventText.setCommitString(text, 0, 0); QApplication::sendEvent(view.focusProxy(), &eventText); QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), text); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); } // ImMaximumTextLength @@ -2161,24 +2329,24 @@ void tst_QWebEngineView::textSelectionInInputField() QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 11); QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 11); // There was no selection to be changed by the click - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); QList<QInputMethodEvent::Attribute> attributes; QInputMethodEvent event(QString(), attributes); event.setCommitString("XXX", 0, 0); QApplication::sendEvent(view.focusProxy(), &event); QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("QtWebEngineXXX")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); event.setCommitString(QString(), -2, 2); // Erase two characters. QApplication::sendEvent(view.focusProxy(), &event); QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("QtWebEngineX")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); event.setCommitString(QString(), -1, 1); // Erase one character. QApplication::sendEvent(view.focusProxy(), &event); QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("QtWebEngine")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); // Move to the start of the line QTest::keyClick(view.focusProxy(), Qt::Key_Home); @@ -2190,7 +2358,7 @@ void tst_QWebEngineView::textSelectionInInputField() // Select to the end of the line QTest::keyClick(view.focusProxy(), Qt::Key_End, Qt::ShiftModifier); QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 1); + QCOMPARE(selectionChangedSpy.size(), 1); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 2); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 11); @@ -2200,7 +2368,7 @@ void tst_QWebEngineView::textSelectionInInputField() // Deselect the selection (this moves the current cursor to the end of the text) QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter); QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 2); + QCOMPARE(selectionChangedSpy.size(), 2); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 11); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 11); @@ -2213,7 +2381,7 @@ void tst_QWebEngineView::textSelectionInInputField() // Select to the start of the line QTest::keyClick(view.focusProxy(), Qt::Key_Home, Qt::ShiftModifier); QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 3); + QCOMPARE(selectionChangedSpy.size(), 3); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 9); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 0); @@ -2235,34 +2403,31 @@ void tst_QWebEngineView::textSelectionOutOfInputField() QVERIFY(loadFinishedSpy.wait()); QVERIFY(QTest::qWaitForWindowExposed(&view)); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); QVERIFY(!view.hasSelection()); QVERIFY(view.page()->selectedText().isEmpty()); // Simple click should not update text selection, however it updates selection bounds in Chromium QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, view.geometry().center()); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); QVERIFY(!view.hasSelection()); QVERIFY(view.page()->selectedText().isEmpty()); // Select text by ctrl+a QTest::keyClick(view.windowHandle(), Qt::Key_A, Qt::ControlModifier); - QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(selectionChangedSpy.size(), 1); QVERIFY(view.hasSelection()); QCOMPARE(view.page()->selectedText(), QString("This is a text")); // Deselect text by mouse click QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, view.geometry().center()); - QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 2); + QTRY_COMPARE(selectionChangedSpy.size(), 2); QVERIFY(!view.hasSelection()); QVERIFY(view.page()->selectedText().isEmpty()); // Select text by ctrl+a QTest::keyClick(view.windowHandle(), Qt::Key_A, Qt::ControlModifier); - QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 3); + QTRY_COMPARE(selectionChangedSpy.size(), 3); QVERIFY(view.hasSelection()); QCOMPARE(view.page()->selectedText(), QString("This is a text")); @@ -2270,8 +2435,7 @@ void tst_QWebEngineView::textSelectionOutOfInputField() view.hide(); view.page()->setLifecycleState(QWebEnginePage::LifecycleState::Discarded); view.show(); - QVERIFY(loadFinishedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 4); + QTRY_COMPARE(selectionChangedSpy.size(), 4); QVERIFY(!view.hasSelection()); QVERIFY(view.page()->selectedText().isEmpty()); @@ -2284,7 +2448,7 @@ void tst_QWebEngineView::textSelectionOutOfInputField() QVERIFY(loadFinishedSpy.wait()); QVERIFY(QTest::qWaitForWindowExposed(&view)); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); QVERIFY(!view.hasSelection()); QVERIFY(view.page()->selectedText().isEmpty()); @@ -2294,31 +2458,27 @@ void tst_QWebEngineView::textSelectionOutOfInputField() // Select the whole page by ctrl+a QTest::keyClick(view.windowHandle(), Qt::Key_A, Qt::ControlModifier); - QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(selectionChangedSpy.size(), 1); QVERIFY(view.hasSelection()); QVERIFY(view.page()->selectedText().startsWith(QString("This is a text"))); // Remove selection by clicking into an input field QPoint textInputCenter = elementCenter(view.page(), "input1"); QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter); - QVERIFY(selectionChangedSpy.wait()); + QTRY_COMPARE(selectionChangedSpy.size(), 2); QCOMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input1")); - QCOMPARE(selectionChangedSpy.count(), 2); QVERIFY(!view.hasSelection()); QVERIFY(view.page()->selectedText().isEmpty()); // Select the content of the input field by ctrl+a QTest::keyClick(view.windowHandle(), Qt::Key_A, Qt::ControlModifier); - QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 3); + QTRY_COMPARE(selectionChangedSpy.size(), 3); QVERIFY(view.hasSelection()); QCOMPARE(view.page()->selectedText(), QString("QtWebEngine")); // Deselect input field's text by mouse click QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, view.geometry().center()); - QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 4); + QTRY_COMPARE(selectionChangedSpy.size(), 4); QVERIFY(!view.hasSelection()); QVERIFY(view.page()->selectedText().isEmpty()); } @@ -2365,7 +2525,7 @@ void tst_QWebEngineView::emptyInputMethodEvent() QVERIFY(QTest::qWaitForWindowExposed(&view)); evaluateJavaScriptSync(view.page(), "var inputEle = document.getElementById('input1'); inputEle.focus(); inputEle.select();"); - QTRY_COMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(selectionChangedSpy.size(), 1); // 1. Empty input method event does not clear text QInputMethodEvent emptyEvent; @@ -2414,7 +2574,7 @@ void tst_QWebEngineView::imeComposition() QVERIFY(QTest::qWaitForWindowExposed(&view)); evaluateJavaScriptSync(view.page(), "var inputEle = document.getElementById('input1'); inputEle.focus(); inputEle.select();"); - QTRY_COMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(selectionChangedSpy.size(), 1); // Clear the selection, also cancel the ongoing composition if there is one. { @@ -2424,7 +2584,7 @@ void tst_QWebEngineView::imeComposition() QInputMethodEvent event("", attributes); QApplication::sendEvent(view.focusProxy(), &event); selectionChangedSpy.wait(); - QCOMPARE(selectionChangedSpy.count(), 2); + QCOMPARE(selectionChangedSpy.size(), 2); } QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("QtWebEngine inputMethod")); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 0); @@ -2445,7 +2605,7 @@ void tst_QWebEngineView::imeComposition() QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 0); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 0); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); // Send temporary text, which makes the editor has composition 'n'. { @@ -2457,7 +2617,7 @@ void tst_QWebEngineView::imeComposition() QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 0); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 0); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); // Send commit text, which makes the editor conforms composition. { @@ -2470,7 +2630,7 @@ void tst_QWebEngineView::imeComposition() QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 1); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 1); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); // 2. insert a character to the middle of the line. @@ -2484,7 +2644,7 @@ void tst_QWebEngineView::imeComposition() QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 1); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 1); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); // Send commit text, which makes the editor conforms composition. { @@ -2497,7 +2657,7 @@ void tst_QWebEngineView::imeComposition() QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 2); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 2); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); // 3. Insert a character to the end of the line. @@ -2515,7 +2675,7 @@ void tst_QWebEngineView::imeComposition() QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 25); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 25); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); // Send commit text, which makes the editor conforms composition. { @@ -2528,7 +2688,7 @@ void tst_QWebEngineView::imeComposition() QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 26); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 26); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); // 4. Replace the selection. @@ -2538,7 +2698,7 @@ void tst_QWebEngineView::imeComposition() QTest::keyClick(view.focusProxy(), Qt::Key_Left, Qt::ShiftModifier | Qt::AltModifier); #endif QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 1); + QCOMPARE(selectionChangedSpy.size(), 1); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("oeQtWebEngine inputMethodt")); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 14); @@ -2552,7 +2712,7 @@ void tst_QWebEngineView::imeComposition() QApplication::sendEvent(view.focusProxy(), &event); // The new composition should clear the previous selection QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 2); + QCOMPARE(selectionChangedSpy.size(), 2); } QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("oeQtWebEngine ")); // The cursor should be positioned at the end of the composition text @@ -2572,7 +2732,7 @@ void tst_QWebEngineView::imeComposition() QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 15); QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 15); QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("")); - QCOMPARE(selectionChangedSpy.count(), 2); + QCOMPARE(selectionChangedSpy.size(), 2); selectionChangedSpy.clear(); @@ -2597,8 +2757,8 @@ void tst_QWebEngineView::imeComposition() QApplication::sendEvent(view.focusProxy(), &event); } QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QString("")); - QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 11); - QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 11); + QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 0); + QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 0); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("")); QCOMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), QString("QtWebEngine")); @@ -2614,7 +2774,7 @@ void tst_QWebEngineView::imeComposition() QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImAnchorPosition).toInt(), 11); QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QString("")); QCOMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), QString("QtWebEngine")); - QCOMPARE(selectionChangedSpy.count(), 0); + QCOMPARE(selectionChangedSpy.size(), 0); } void tst_QWebEngineView::newlineInTextarea() @@ -2769,7 +2929,7 @@ void tst_QWebEngineView::imeJSInputEvents() } // Simply committing text should not trigger any JS composition event. - QTRY_COMPARE(logLines().count(), 3); + QTRY_COMPARE(logLines().size(), 3); QCOMPARE(logLines()[0], QStringLiteral("[object InputEvent] beforeinput commit")); QCOMPARE(logLines()[1], QStringLiteral("[object TextEvent] textInput commit")); QCOMPARE(logLines()[2], QStringLiteral("[object InputEvent] input commit")); @@ -2785,7 +2945,7 @@ void tst_QWebEngineView::imeJSInputEvents() qApp->processEvents(); } - QTRY_COMPARE(logLines().count(), 4); + QTRY_COMPARE(logLines().size(), 4); QCOMPARE(logLines()[0], QStringLiteral("[object CompositionEvent] compositionstart ")); QCOMPARE(logLines()[1], QStringLiteral("[object InputEvent] beforeinput preedit")); QCOMPARE(logLines()[2], QStringLiteral("[object CompositionEvent] compositionupdate preedit")); @@ -2799,7 +2959,7 @@ void tst_QWebEngineView::imeJSInputEvents() qApp->processEvents(); } - QTRY_COMPARE(logLines().count(), 9); + QTRY_COMPARE(logLines().size(), 9); QCOMPARE(logLines()[4], QStringLiteral("[object InputEvent] beforeinput commit")); QCOMPARE(logLines()[5], QStringLiteral("[object CompositionEvent] compositionupdate commit")); QCOMPARE(logLines()[6], QStringLiteral("[object TextEvent] textInput commit")); @@ -2817,7 +2977,7 @@ void tst_QWebEngineView::imeJSInputEvents() qApp->processEvents(); } - QTRY_COMPARE(logLines().count(), 4); + QTRY_COMPARE(logLines().size(), 4); QCOMPARE(logLines()[0], QStringLiteral("[object CompositionEvent] compositionstart ")); QCOMPARE(logLines()[1], QStringLiteral("[object InputEvent] beforeinput preedit")); QCOMPARE(logLines()[2], QStringLiteral("[object CompositionEvent] compositionupdate preedit")); @@ -2830,7 +2990,7 @@ void tst_QWebEngineView::imeJSInputEvents() qApp->processEvents(); } - QTRY_COMPARE(logLines().count(), 9); + QTRY_COMPARE(logLines().size(), 9); QCOMPARE(logLines()[4], QStringLiteral("[object InputEvent] beforeinput ")); QCOMPARE(logLines()[5], QStringLiteral("[object CompositionEvent] compositionupdate ")); QCOMPARE(logLines()[6], QStringLiteral("[object TextEvent] textInput ")); @@ -2897,6 +3057,7 @@ void tst_QWebEngineView::imeCompositionQueryEvent() } QInputMethodQueryEvent srrndTextQuery(Qt::ImSurroundingText); + QInputMethodQueryEvent absolutePosQuery(Qt::ImAbsolutePosition); QInputMethodQueryEvent cursorPosQuery(Qt::ImCursorPosition); QInputMethodQueryEvent anchorPosQuery(Qt::ImAnchorPosition); @@ -2908,16 +3069,18 @@ void tst_QWebEngineView::imeCompositionQueryEvent() qApp->processEvents(); } QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), QString("composition")); - QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 11); + QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCursorPosition).toInt(), 0); QApplication::sendEvent(input, &srrndTextQuery); + QApplication::sendEvent(input, &absolutePosQuery); QApplication::sendEvent(input, &cursorPosQuery); QApplication::sendEvent(input, &anchorPosQuery); qApp->processEvents(); QTRY_COMPARE(srrndTextQuery.value(Qt::ImSurroundingText).toString(), QString("")); - QTRY_COMPARE(cursorPosQuery.value(Qt::ImCursorPosition).toInt(), 11); - QTRY_COMPARE(anchorPosQuery.value(Qt::ImAnchorPosition).toInt(), 11); + QTRY_COMPARE(absolutePosQuery.value(Qt::ImAbsolutePosition).toInt(), 0); + QTRY_COMPARE(cursorPosQuery.value(Qt::ImCursorPosition).toInt(), 0); + QTRY_COMPARE(anchorPosQuery.value(Qt::ImAnchorPosition).toInt(), 0); // Send commit { @@ -2931,16 +3094,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()) { @@ -2962,20 +3176,20 @@ void tst_QWebEngineView::globalMouseSelection() // Select text via JavaScript evaluateJavaScriptSync(view.page(), "var inputEle = document.getElementById('input1'); inputEle.focus(); inputEle.select();"); - QTRY_COMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(selectionChangedSpy.size(), 1); QVERIFY(QApplication::clipboard()->text(QClipboard::Selection).isEmpty()); // Deselect the selection (this moves the current cursor to the end of the text) QPoint textInputCenter = elementCenter(view.page(), "input1"); QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter); QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 2); + QCOMPARE(selectionChangedSpy.size(), 2); QVERIFY(QApplication::clipboard()->text(QClipboard::Selection).isEmpty()); // Select to the start of the line QTest::keyClick(view.focusProxy(), Qt::Key_Home, Qt::ShiftModifier); QVERIFY(selectionChangedSpy.wait()); - QCOMPARE(selectionChangedSpy.count(), 3); + QCOMPARE(selectionChangedSpy.size(), 3); QCOMPARE(QApplication::clipboard()->text(QClipboard::Selection), QStringLiteral("QtWebEngine")); } #endif @@ -3001,7 +3215,7 @@ void tst_QWebEngineView::noContextMenu() QTest::mouseMove(wrapper.windowHandle(), QPoint(10,10)); QTest::mouseClick(wrapper.windowHandle(), Qt::RightButton); - QTRY_COMPARE(wrapper.findChildren<QMenu *>().count(), 1); + QTRY_COMPARE(wrapper.findChildren<QMenu *>().size(), 1); QVERIFY(view.findChildren<QMenu *>().isEmpty()); } @@ -3041,7 +3255,7 @@ void tst_QWebEngineView::contextMenu() view.load(QUrl("about:blank")); view.resize(640, 480); view.show(); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); QVERIFY(view.findChildren<QMenu *>().isEmpty()); QTest::mouseMove(view.windowHandle(), QPoint(10,10)); @@ -3049,9 +3263,9 @@ void tst_QWebEngineView::contextMenu() // verify for zero children will always succeed, so should be tested with at least minor timeout if (childrenCount <= 0) { - QVERIFY(!QTest::qWaitFor([&view] () { return view.findChildren<QMenu *>().count() > 0; }, 500)); + QVERIFY(!QTest::qWaitFor([&view] () { return view.findChildren<QMenu *>().size() > 0; }, 500)); } else { - QTRY_COMPARE(view.findChildren<QMenu *>().count(), childrenCount); + QTRY_COMPARE(view.findChildren<QMenu *>().size(), childrenCount); if (isCustomMenu) { QCOMPARE(view.findChildren<QMenu *>().first(), customMenu); } @@ -3117,46 +3331,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; @@ -3165,7 +3383,7 @@ void tst_QWebEngineView::webUIURLs_data() QTest::newRow("process-internals") << QUrl("chrome://process-internals") << true; QTest::newRow("quota-internals") << QUrl("chrome://quota-internals") << true; QTest::newRow("safe-browsing") << QUrl("chrome://safe-browsing") << false; -#ifdef Q_OS_LINUX +#if defined(Q_OS_LINUX) || defined(Q_OS_WIN) QTest::newRow("sandbox") << QUrl("chrome://sandbox") << true; #else QTest::newRow("sandbox") << QUrl("chrome://sandbox") << false; @@ -3174,8 +3392,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; @@ -3185,12 +3401,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() @@ -3202,7 +3420,7 @@ void tst_QWebEngineView::webUIURLs() view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); view.load(url); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 90000); QCOMPARE(loadFinishedSpy.takeFirst().at(0).toBool(), supported); } @@ -3211,7 +3429,7 @@ void tst_QWebEngineView::visibilityState() QWebEngineView view; QSignalSpy spy(&view, &QWebEngineView::loadFinished); view.load(QStringLiteral("about:blank")); - QVERIFY(spy.count() || spy.wait()); + QVERIFY(spy.size() || spy.wait()); QVERIFY(spy.takeFirst().takeFirst().toBool()); QCOMPARE(evaluateJavaScriptSync(view.page(), "document.visibilityState").toString(), QStringLiteral("hidden")); view.show(); @@ -3226,7 +3444,7 @@ void tst_QWebEngineView::visibilityState2() view.show(); view.load(QStringLiteral("about:blank")); view.hide(); - QVERIFY(spy.count() || spy.wait()); + QVERIFY(spy.size() || spy.wait()); QVERIFY(spy.takeFirst().takeFirst().toBool()); QCOMPARE(evaluateJavaScriptSync(view.page(), "document.visibilityState").toString(), QStringLiteral("hidden")); } @@ -3239,8 +3457,8 @@ void tst_QWebEngineView::visibilityState3() QSignalSpy spy2(&page2, &QWebEnginePage::loadFinished); page1.load(QStringLiteral("about:blank")); page2.load(QStringLiteral("about:blank")); - QVERIFY(spy1.count() || spy1.wait()); - QVERIFY(spy2.count() || spy2.wait()); + QVERIFY(spy1.size() || spy1.wait()); + QVERIFY(spy2.size() || spy2.wait()); QWebEngineView view; view.setPage(&page1); view.show(); @@ -3304,7 +3522,7 @@ void tst_QWebEngineView::deletePage() QVERIFY(view.page()); QSignalSpy spy(view.page(), &QWebEnginePage::loadFinished); view.page()->load(QStringLiteral("about:blank")); - QTRY_VERIFY(spy.count()); + QTRY_VERIFY(spy.size()); } void tst_QWebEngineView::autoDeleteOnExternalPageDelete() @@ -3318,7 +3536,7 @@ void tst_QWebEngineView::autoDeleteOnExternalPageDelete() view->show(); view->resize(320, 240); page->load(QUrl("about:blank")); - QTRY_VERIFY(spy.count()); + QTRY_VERIFY(spy.size()); QVERIFY(page->parent() != view); auto sc = QObject::connect(page, &QWebEnginePage::destroyed, view, &QWebEngineView::deleteLater); @@ -3351,7 +3569,7 @@ void tst_QWebEngineView::closeOpenerTab() testView->settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true); QSignalSpy loadFinishedSpy(testView, SIGNAL(loadFinished(bool))); testView->setUrl(QStringLiteral("about:blank")); - QTRY_VERIFY(loadFinishedSpy.count()); + QTRY_VERIFY(loadFinishedSpy.size()); testView->page()->runJavaScript(QStringLiteral("window.open('about:blank','_blank')")); QTRY_COMPARE(testView->createdWindows.size(), 1); auto *newView = testView->createdWindows.at(0); @@ -3372,13 +3590,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(); @@ -3449,17 +3665,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); } @@ -3477,9 +3691,317 @@ 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::emptyUriListOnDrop() +{ + QWebEngineView view; + view.resize(640, 480); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QMimeData mimeData; + mimeData.setUrls({}); // creates an empty uri-list MIME type entry + QVERIFY(mimeData.hasUrls()); + + QDragEnterEvent dee(view.rect().center(), Qt::CopyAction, &mimeData, Qt::LeftButton, + Qt::NoModifier); + QApplication::sendEvent(&view, &dee); + QDropEvent de(view.rect().center(), Qt::CopyAction, &mimeData, Qt::LeftButton, Qt::NoModifier); + QApplication::sendEvent(&view, &de); + + QSignalSpy loadSpy(&view, &QWebEngineView::loadFinished); + view.setUrl(QUrl("about:blank")); + QTRY_COMPARE(loadSpy.size(), 1); +} + +void tst_QWebEngineView::datalist() +{ + QString html("<html><body>" + "<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/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/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/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 8e1a165c5..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,36 +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 ${CMAKE_COMMAND} -E env - $<IF:$<BOOL:${WEBENGINE_MODULE_BUILD}>,QT_WEBENGINE_ICU_DATA_DIR=${CMAKE_CURRENT_BINARY_DIR}/../../../../resources,CMAKE=AWESOME> - $<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/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/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/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" |