From 9202da6fac5c9f818ba8c70f3902cc03f6bc1d16 Mon Sep 17 00:00:00 2001 From: Kirill Burtsev Date: Tue, 12 May 2020 20:18:47 +0200 Subject: Allow to set feature permission before first ever navigation Since PermissionManagerQt functions independent of webcontents initialization, permission installment may be done earlier (right after page base constructor). Practically, this allows to grant/deny permission before first ever navigation to avoid permission feature request from well-behaving web application. Unfortunately, this only applies to Web Notifications: there is a way to check without request: Notification.permission javascript static object. Other supported features lack similar mechanism: they operate through success/failure callbacks, which unconditionally invoke permission requests, and Page/View API doesn't automatically answer with remembered permissions. Task-number: QTBUG-83476 Change-Id: I63a3cbca25498d5026975073e125f2ab4f9ab2ad Reviewed-by: Allan Sandfeld Jensen --- .../widgets/qwebenginepage/tst_qwebenginepage.cpp | 66 ++++++++++++++-------- 1 file changed, 42 insertions(+), 24 deletions(-) (limited to 'tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp') diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index 3f3534931..515aa4ebb 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -203,11 +203,10 @@ private Q_SLOTS: void triggerActionWithoutMenu(); void dynamicFrame(); - void notificationRequest_data(); - void notificationRequest(); + void notificationPermission_data(); + void notificationPermission(); void sendNotification(); void contentsSize(); - void notificationPermission(); void setLifecycleState(); void setVisible(); @@ -3590,42 +3589,61 @@ public: } }; -void tst_QWebEnginePage::notificationRequest_data() +void tst_QWebEnginePage::notificationPermission_data() { + QTest::addColumn("setOnInit"); QTest::addColumn("policy"); QTest::addColumn("permission"); - QTest::newRow("deny") << QWebEnginePage::PermissionDeniedByUser << "denied"; - QTest::newRow("grant") << QWebEnginePage::PermissionGrantedByUser << "granted"; + QTest::newRow("denyOnInit") << true << QWebEnginePage::PermissionDeniedByUser << "denied"; + QTest::newRow("deny") << false << QWebEnginePage::PermissionDeniedByUser << "denied"; + QTest::newRow("grant") << false << QWebEnginePage::PermissionGrantedByUser << "granted"; + QTest::newRow("grantOnInit") << true << QWebEnginePage::PermissionGrantedByUser << "granted"; } -void tst_QWebEnginePage::notificationRequest() +void tst_QWebEnginePage::notificationPermission() { + QFETCH(bool, setOnInit); QFETCH(QWebEnginePage::PermissionPolicy, policy); QFETCH(QString, permission); - NotificationPage page(policy); - QVERIFY(page.spyLoad.waitForResult()); + QWebEngineProfile otr; + QWebEnginePage page(&otr, nullptr); - page.resetPermission(); - QCOMPARE(page.getPermission(), "default"); + QUrl baseUrl("https://www.example.com/somepage.html"); - page.requestPermission(); - page.spyRequest.waitForResult(); - QVERIFY(page.spyRequest.wasCalled()); + bool permissionRequested = false, errorState = false; + connect(&page, &QWebEnginePage::featurePermissionRequested, &page, [&] (const QUrl &o, QWebEnginePage::Feature f) { + if (f != QWebEnginePage::Notifications) + return; + if (permissionRequested || o != baseUrl.url(QUrl::RemoveFilename)) { + qWarning() << "Unexpected case. Can't proceed." << setOnInit << permissionRequested << o; + errorState = true; + return; + } + permissionRequested = true; + page.setFeaturePermission(o, f, policy); + }); - QCOMPARE(page.getPermission(), permission); -} + if (setOnInit) + page.setFeaturePermission(baseUrl, QWebEnginePage::Notifications, policy); -void tst_QWebEnginePage::notificationPermission() -{ - QWebEngineProfile otr; - QWebEnginePage page(&otr, nullptr); QSignalSpy spy(&page, &QWebEnginePage::loadFinished); - page.setHtml(QString("Test"), QUrl("https://www.example.com")); + page.setHtml(QString("Test"), baseUrl); QTRY_COMPARE(spy.count(), 1); - QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), QLatin1String("default")); - page.setFeaturePermission(QUrl("https://www.example.com"), QWebEnginePage::Notifications, QWebEnginePage::PermissionGrantedByUser); - QTRY_COMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), QLatin1String("granted")); + + QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), setOnInit ? permission : QLatin1String("default")); + + if (!setOnInit) { + page.setFeaturePermission(baseUrl, QWebEnginePage::Notifications, policy); + QTRY_COMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), permission); + } + + auto js = QStringLiteral("var permission; Notification.requestPermission().then(p => { permission = p })"); + evaluateJavaScriptSync(&page, js); + QTRY_COMPARE(evaluateJavaScriptSync(&page, "permission").toString(), permission); + // permission is not 'remembered' from api standpoint, hence is not suppressed on explicit call from JS + QVERIFY(permissionRequested); + QVERIFY(!errorState); } void tst_QWebEnginePage::sendNotification() -- cgit v1.2.3 From 17cac776b2ec30af8f928d648f4708f8e1a25b5e Mon Sep 17 00:00:00 2001 From: Peter Varga Date: Thu, 21 May 2020 16:28:02 +0200 Subject: Fix crash on WebContentsDelegateQt::CloseContents() CloseContents() triggers a windowCloseRequested() signal. The handler of the signal might close the QWebEngineView and it would result the destruction of the WebContentsDelegateQt. Thus, any operation on WebContentsDelegateQt is not safe after a WebContentsAdapterClient::close() call. For example, calling QWebEngineView::close() on QWebEnginePage::windowCloseRequested() would result crash because it sets life cycle state to discarded which destructs web contents. Fixes: QTBUG-84355 Change-Id: I5da404bc9b1923cc19085ee899b550da49d1416b Reviewed-by: Allan Sandfeld Jensen --- .../widgets/qwebenginepage/tst_qwebenginepage.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp') diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index 515aa4ebb..e4e451a55 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -231,6 +231,7 @@ private Q_SLOTS: void renderProcessPid(); void backgroundColor(); void audioMuted(); + void closeContents(); private: static QPoint elementCenter(QWebEnginePage *page, const QString &id); @@ -4641,6 +4642,27 @@ void tst_QWebEnginePage::audioMuted() QCOMPARE(spy[1][0], QVariant(false)); } +void tst_QWebEnginePage::closeContents() +{ + TestPage page; + QSignalSpy windowCreatedSpy(&page, &TestPage::windowCreated); + page.runJavaScript("var dialog = window.open('', '', 'width=100, height=100');"); + QTRY_COMPARE(windowCreatedSpy.count(), 1); + + QWebEngineView *dialogView = new QWebEngineView; + QWebEnginePage *dialogPage = page.createdWindows[0]; + dialogPage->setView(dialogView); + QCOMPARE(dialogPage->lifecycleState(), QWebEnginePage::LifecycleState::Active); + + // This should not crash. + connect(dialogPage, &QWebEnginePage::windowCloseRequested, dialogView, &QWebEngineView::close); + page.runJavaScript("dialog.close();"); + + // QWebEngineView::closeEvent() sets the life cycle state to discarded. + QTRY_COMPARE(dialogPage->lifecycleState(), QWebEnginePage::LifecycleState::Discarded); + delete dialogView; +} + static QByteArrayList params = {QByteArrayLiteral("--use-fake-device-for-media-stream")}; W_QTEST_MAIN(tst_QWebEnginePage, params) -- cgit v1.2.3 From e63d27ca775429df8c3f03dc2df001e502119db1 Mon Sep 17 00:00:00 2001 From: Peter Varga Date: Wed, 20 May 2020 08:55:43 +0200 Subject: Remove leftover of tst_QWebEnginePage::inputFieldFocus auto test The test has been removed by: cfef4615 Delete QtWebKit tests from tst_qwebenginepage.cpp Pick-to: 5.15 Change-Id: I677772f00c99ae3b6e33a2d61c7135d4306d3674 Reviewed-by: Allan Sandfeld Jensen (cherry picked from commit 0891ac9c05b96471a0858011ef4f84b353337d16) --- tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp') diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index e4e451a55..0368e7af5 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -81,8 +81,6 @@ public: tst_QWebEnginePage(); virtual ~tst_QWebEnginePage(); - bool eventFilter(QObject *watched, QEvent *event); - public Q_SLOTS: void init(); void cleanup(); @@ -241,8 +239,6 @@ private: QWebEngineView* m_view; QWebEnginePage* m_page; - QWebEngineView* m_inputFieldsTestView; - int m_inputFieldTestPaintCount; QString tmpDirPath() const { static QString tmpd = QDir::tempPath() + "/tst_qwebenginepage-" @@ -259,16 +255,6 @@ tst_QWebEnginePage::~tst_QWebEnginePage() { } -bool tst_QWebEnginePage::eventFilter(QObject* watched, QEvent* event) -{ - // used on the inputFieldFocus test - if (watched == m_inputFieldsTestView) { - if (event->type() == QEvent::Paint) - m_inputFieldTestPaintCount++; - } - return QObject::eventFilter(watched, event); -} - void tst_QWebEnginePage::init() { m_view = new QWebEngineView(); -- cgit v1.2.3