summaryrefslogtreecommitdiffstats
path: root/tests/auto/widgets/qwebenginepage
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/widgets/qwebenginepage')
-rw-r--r--tests/auto/widgets/qwebenginepage/BLACKLIST10
-rw-r--r--tests/auto/widgets/qwebenginepage/CMakeLists.txt1
-rw-r--r--tests/auto/widgets/qwebenginepage/resources/fontaccess.html14
-rw-r--r--tests/auto/widgets/qwebenginepage/resources/reload.html2
-rw-r--r--tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp931
5 files changed, 833 insertions, 125 deletions
diff --git a/tests/auto/widgets/qwebenginepage/BLACKLIST b/tests/auto/widgets/qwebenginepage/BLACKLIST
index 7eb97b3bb..01d1ffe3d 100644
--- a/tests/auto/widgets/qwebenginepage/BLACKLIST
+++ b/tests/auto/widgets/qwebenginepage/BLACKLIST
@@ -5,11 +5,15 @@ osx
windows
macos # Can't move cursor (QTBUG-76312)
-[acceptNavigationRequestNavigationType]
-b2qt arm
-
[comboBoxPopupPositionAfterMove]
macos
[comboBoxPopupPositionAfterChildMove]
macos
+
+[backgroundColor]
+macos
+
+[dynamicFrame]
+ubuntu-22.04
+
diff --git a/tests/auto/widgets/qwebenginepage/CMakeLists.txt b/tests/auto/widgets/qwebenginepage/CMakeLists.txt
index a15bb6e06..f63d6211c 100644
--- a/tests/auto/widgets/qwebenginepage/CMakeLists.txt
+++ b/tests/auto/widgets/qwebenginepage/CMakeLists.txt
@@ -24,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 64acdb3d5..976a650ec 100644
--- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
+++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2016 The Qt Company Ltd.
+ Copyright (C) 2023 The Qt Company Ltd.
Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in>
Copyright (C) 2010 Holger Hans Peter Freyther
@@ -36,6 +36,7 @@
#include <QPaintEngine>
#include <QPushButton>
#include <QScreen>
+#include <QWheelEvent>
#if defined(QT_STATEMACHINE_LIB)
# include <QStateMachine>
#endif
@@ -49,7 +50,9 @@
#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>
@@ -64,10 +67,12 @@
#include <qwebenginescript.h>
#include <qwebenginescriptcollection.h>
#include <qwebenginesettings.h>
+#include <qwebengineurlrequestinterceptor.h>
#include <qwebengineurlrequestjob.h>
#include <qwebengineurlscheme.h>
#include <qwebengineurlschemehandler.h>
#include <qwebengineview.h>
+#include <qwebenginepermission.h>
#include <qimagewriter.h>
#include <QColorSpace>
#include <QQuickRenderControl>
@@ -113,10 +118,13 @@ private Q_SLOTS:
void comboBoxPopupPositionAfterChildMove_data();
void comboBoxPopupPositionAfterChildMove();
void acceptNavigationRequest();
+ void acceptNavigationRequestWithFormData();
void acceptNavigationRequestNavigationType();
void acceptNavigationRequestRelativeToNothing();
+#ifndef Q_OS_MACOS
void geolocationRequestJS_data();
void geolocationRequestJS();
+#endif
void loadFinished();
void actionStates();
void pasteImage();
@@ -165,10 +173,10 @@ private Q_SLOTS:
void runJavaScriptDisabled();
void runJavaScriptFromSlot();
void fullScreenRequested();
- void quotaRequested();
-
+ void requestQuota_data();
+ void requestQuota();
- // Tests from tst_QWebEngineFrame
+ // Tests from pre-6.8 tst_QWebEngineFrame
void symmetricUrl();
void progressSignal();
void urlChange();
@@ -227,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();
@@ -263,15 +277,24 @@ private Q_SLOTS:
void fileSystemAccessDialog();
void localToRemoteNavigation();
+ void clientHints_data();
+ void clientHints();
+ void childFrameInput();
+ void openLinkInNewPageWithWebWindowType_data();
+ void openLinkInNewPageWithWebWindowType();
+ void keepInterceptorAfterNewWindowRequested();
+ void chooseDesktopMedia();
private:
- static QPoint elementCenter(QWebEnginePage *page, const QString &id);
static bool isFalseJavaScriptResult(QWebEnginePage *page, const QString &javaScript);
static bool isTrueJavaScriptResult(QWebEnginePage *page, const QString &javaScript);
static bool isEmptyListJavaScriptResult(QWebEnginePage *page, const QString &javaScript);
QWebEngineView* m_view;
QWebEnginePage* m_page;
+ QScopedPointer<QPointingDevice> s_touchDevice =
+ QScopedPointer<QPointingDevice>(QTest::createTouchDevice());
+
QString tmpDirPath() const
{
static QString tmpd = QDir::tempPath() + "/tst_qwebenginepage-"
@@ -279,17 +302,24 @@ private:
return tmpd;
}
- QScopedPointer<QPointingDevice> s_touchDevice;
- void makeClick(QWindow *window, bool withTouch = false, const QPoint &p = QPoint()) {
+ void makeClick(const QPointer<QWindow> window, bool withTouch = false,
+ const QPoint &p = QPoint())
+ {
+ QVERIFY2(window, "window is gone");
if (!withTouch) {
QTest::mouseClick(window, Qt::LeftButton, Qt::KeyboardModifiers(), p);
} else {
- if (!s_touchDevice)
- s_touchDevice.reset(QTest::createTouchDevice());
QTest::touchEvent(window, s_touchDevice.get()).press(1, p);
QTest::touchEvent(window, s_touchDevice.get()).release(1, p);
}
};
+
+ void makeScroll(QWidget *target, QPointF pos, QPoint globalPos, QPoint angleDelta)
+ {
+ QWheelEvent ev(pos, globalPos, QPoint(0, 0), angleDelta, Qt::NoButton, Qt::NoModifier,
+ Qt::NoScrollPhase, false);
+ QGuiApplication::sendEvent(target, &ev);
+ }
};
tst_QWebEnginePage::tst_QWebEnginePage()
@@ -421,12 +451,12 @@ public:
return true;
}
public Q_SLOTS:
- void requestPermission(const QUrl &origin, QWebEnginePage::Feature feature)
+ void requestPermission(QWebEnginePermission permission)
{
if (m_allowGeolocation)
- setFeaturePermission(origin, feature, PermissionGrantedByUser);
+ permission.grant();
else
- setFeaturePermission(origin, feature, PermissionDeniedByUser);
+ permission.deny();
}
public:
@@ -439,6 +469,7 @@ private:
bool m_allowGeolocation;
};
+#ifndef Q_OS_MACOS
void tst_QWebEnginePage::geolocationRequestJS_data()
{
QTest::addColumn<bool>("allowed");
@@ -454,10 +485,11 @@ void tst_QWebEnginePage::geolocationRequestJS()
QWebEngineView view;
JSTestPage *newPage = new JSTestPage(&view);
view.setPage(newPage);
+ newPage->profile()->setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions);
newPage->setGeolocationPermission(allowed);
- connect(newPage, SIGNAL(featurePermissionRequested(const QUrl&, QWebEnginePage::Feature)),
- newPage, SLOT(requestPermission(const QUrl&, QWebEnginePage::Feature)));
+ connect(newPage, SIGNAL(permissionRequested(QWebEnginePermission)),
+ newPage, SLOT(requestPermission(QWebEnginePermission)));
QSignalSpy spyLoadFinished(newPage, SIGNAL(loadFinished(bool)));
newPage->setHtml(QString("<html><body>test</body></html>"), QUrl("qrc://secure/origin"));
@@ -478,6 +510,7 @@ void tst_QWebEnginePage::geolocationRequestJS()
QEXPECT_FAIL("", "No location service available.", Continue);
QCOMPARE(result, errorCode);
}
+#endif
void tst_QWebEnginePage::loadFinished()
{
@@ -605,6 +638,7 @@ public:
QWebEngineNavigationRequest::NavigationType type;
QUrl url;
bool isMainFrame;
+ bool hasFormData;
};
QList<Navigation> navigations;
@@ -622,6 +656,7 @@ private Q_SLOTS:
n.url = request.url();
n.type = request.navigationType();
n.isMainFrame = request.isMainFrame();
+ n.hasFormData = request.hasFormData();
navigations.append(n);
request.accept();
}
@@ -639,9 +674,33 @@ 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)));
@@ -664,19 +723,19 @@ void tst_QWebEnginePage::acceptNavigationRequestNavigationType()
QTRY_COMPARE(loadSpy.size(), 4);
QTRY_COMPARE(page.navigations.size(), 4);
- page.load(QUrl("qrc:///resources/reload.html"));
- QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 6, 20000);
- QTRY_COMPARE(page.navigations.size(), 6);
-
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.size(), 7, 20000);
QTRY_COMPARE(page.navigations.size(), 8);
@@ -1345,7 +1404,11 @@ void tst_QWebEnginePage::comboBoxPopupPositionAfterMove_data()
void tst_QWebEnginePage::comboBoxPopupPositionAfterMove()
{
+#if defined(Q_OS_MACOS) && (defined(__arm64__) || defined(__aarch64__))
+ QSKIP("This test crashes for Apple M1");
+#endif
QWebEngineView view;
+ QTRY_VERIFY(QGuiApplication::primaryScreen());
view.move(QGuiApplication::primaryScreen()->availableGeometry().topLeft());
view.resize(640, 480);
view.show();
@@ -1357,7 +1420,7 @@ void tst_QWebEnginePage::comboBoxPopupPositionAfterMove()
QTRY_COMPARE(spyLoadFinished.size(), 1);
const auto oldTlws = QGuiApplication::topLevelWindows();
QFETCH(bool, withTouch);
- QWindow *window = view.windowHandle();
+ QPointer<QWindow> window = view.windowHandle();
auto pos = elementCenter(view.page(), "foo");
makeClick(window, withTouch, pos);
QWindow *popup = nullptr;
@@ -1407,6 +1470,9 @@ void tst_QWebEnginePage::comboBoxPopupPositionAfterChildMove_data()
void tst_QWebEnginePage::comboBoxPopupPositionAfterChildMove()
{
+#if defined(Q_OS_MACOS) && (defined(__arm64__) || defined(__aarch64__))
+ QSKIP("This test crashes for Apple M1");
+#endif
QWidget mainWidget;
mainWidget.setLayout(new QHBoxLayout);
@@ -1417,6 +1483,7 @@ void tst_QWebEnginePage::comboBoxPopupPositionAfterChildMove()
mainWidget.layout()->addWidget(&view);
QScreen *screen = QGuiApplication::primaryScreen();
+ Q_ASSERT(screen);
mainWidget.move(screen->availableGeometry().topLeft());
mainWidget.resize(640, 480);
mainWidget.show();
@@ -1430,7 +1497,7 @@ void tst_QWebEnginePage::comboBoxPopupPositionAfterChildMove()
const auto oldTlws = QGuiApplication::topLevelWindows();
QFETCH(bool, withTouch);
- QWindow *window = view.window()->windowHandle();
+ QPointer<QWindow> window = view.window()->windowHandle();
makeClick(window, withTouch, view.mapTo(view.window(), elementCenter(view.page(), "foo")));
QWindow *popup = nullptr;
@@ -1589,11 +1656,13 @@ public:
GetUserMediaTestPage()
: m_gotRequest(false)
, m_loadSucceeded(false)
+ , m_permission(nullptr)
{
- connect(this, &QWebEnginePage::featurePermissionRequested, this, &GetUserMediaTestPage::onFeaturePermissionRequested);
+ connect(this, &QWebEnginePage::permissionRequested, this, &GetUserMediaTestPage::onPermissionRequested);
connect(this, &QWebEnginePage::loadFinished, [this](bool success){
m_loadSucceeded = success;
});
+ profile()->setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions);
// We need to load content from a resource in order for the securityOrigin to be valid.
load(QUrl("qrc:///resources/content.html"));
}
@@ -1627,18 +1696,20 @@ public:
void rejectPendingRequest()
{
- setFeaturePermission(m_requestSecurityOrigin, m_requestedFeature, QWebEnginePage::PermissionDeniedByUser);
+ QVERIFY(m_permission);
+ m_permission->deny();
m_gotRequest = false;
}
void acceptPendingRequest()
{
- setFeaturePermission(m_requestSecurityOrigin, m_requestedFeature, QWebEnginePage::PermissionGrantedByUser);
+ QVERIFY(m_permission);
+ m_permission->grant();
m_gotRequest = false;
}
- bool gotFeatureRequest(QWebEnginePage::Feature feature)
+ bool gotFeatureRequest(QWebEnginePermission::Feature feature)
{
- return m_gotRequest && m_requestedFeature == feature;
+ return m_gotRequest && m_permission && m_permission->feature() == feature;
}
bool gotFeatureRequest() const
@@ -1652,50 +1723,47 @@ public:
}
private Q_SLOTS:
- void onFeaturePermissionRequested(const QUrl &securityOrigin, QWebEnginePage::Feature feature)
+ void onPermissionRequested(QWebEnginePermission permission)
{
- m_requestedFeature = feature;
- m_requestSecurityOrigin = securityOrigin;
+ m_permission.reset(new QWebEnginePermission(permission));
m_gotRequest = true;
}
private:
bool m_gotRequest;
bool m_loadSucceeded;
- QWebEnginePage::Feature m_requestedFeature;
- QUrl m_requestSecurityOrigin;
-
+ std::unique_ptr<QWebEnginePermission> m_permission;
};
void tst_QWebEnginePage::getUserMediaRequest_data()
{
QTest::addColumn<QString>("call");
- QTest::addColumn<QWebEnginePage::Feature>("feature");
+ QTest::addColumn<QWebEnginePermission::Feature>("feature");
QTest::addRow("device audio")
- << "getUserMedia({audio: true})" << QWebEnginePage::MediaAudioCapture;
+ << "getUserMedia({audio: true})" << QWebEnginePermission::MediaAudioCapture;
QTest::addRow("device video")
- << "getUserMedia({video: true})" << QWebEnginePage::MediaVideoCapture;
+ << "getUserMedia({video: true})" << QWebEnginePermission::MediaVideoCapture;
QTest::addRow("device audio+video")
- << "getUserMedia({audio: true, video: true})" << QWebEnginePage::MediaAudioVideoCapture;
+ << "getUserMedia({audio: true, video: true})" << QWebEnginePermission::MediaAudioVideoCapture;
QTest::addRow("desktop video")
<< "getUserMedia({video: { mandatory: { chromeMediaSource: 'desktop' }}})"
- << QWebEnginePage::DesktopVideoCapture;
+ << QWebEnginePermission::DesktopVideoCapture;
QTest::addRow("desktop audio+video")
<< "getUserMedia({audio: { mandatory: { chromeMediaSource: 'desktop' }}, video: { mandatory: { chromeMediaSource: 'desktop' }}})"
- << QWebEnginePage::DesktopAudioVideoCapture;
+ << QWebEnginePermission::DesktopAudioVideoCapture;
QTest::addRow("display video")
- << "getDisplayMedia()" << QWebEnginePage::DesktopVideoCapture;
+ << "getDisplayMedia()" << QWebEnginePermission::DesktopVideoCapture;
}
void tst_QWebEnginePage::getUserMediaRequest()
{
QFETCH(QString, call);
- QFETCH(QWebEnginePage::Feature, feature);
+ QFETCH(QWebEnginePermission::Feature, feature);
GetUserMediaTestPage page;
QWebEngineView view;
- if (feature == QWebEnginePage::DesktopVideoCapture || feature == QWebEnginePage::DesktopAudioVideoCapture) {
+ if (feature == QWebEnginePermission::DesktopVideoCapture || feature == QWebEnginePermission::DesktopAudioVideoCapture) {
// Desktop capture needs to be on a desktop.
view.setPage(&page);
view.resize(640, 480);
@@ -1765,7 +1833,7 @@ void tst_QWebEnginePage::getUserMediaRequestSettingDisabled()
void tst_QWebEnginePage::getUserMediaRequestDesktopVideoManyPages()
{
const QString constraints = QStringLiteral("{video: { mandatory: { chromeMediaSource: 'desktop' }}}");
- const QWebEnginePage::Feature feature = QWebEnginePage::DesktopVideoCapture;
+ const QWebEnginePermission::Feature feature = QWebEnginePermission::DesktopVideoCapture;
std::vector<GetUserMediaTestPage> pages(10);
// Desktop capture needs to be on a desktop
@@ -1798,7 +1866,7 @@ void tst_QWebEnginePage::getUserMediaRequestDesktopVideoManyPages()
void tst_QWebEnginePage::getUserMediaRequestDesktopVideoManyRequests()
{
const QString constraints = QStringLiteral("{video: { mandatory: { chromeMediaSource: 'desktop' }}}");
- const QWebEnginePage::Feature feature = QWebEnginePage::DesktopVideoCapture;
+ const QWebEnginePermission::Feature feature = QWebEnginePermission::DesktopVideoCapture;
GetUserMediaTestPage page;
// Desktop capture needs to be on a desktop
@@ -2014,8 +2082,18 @@ void tst_QWebEnginePage::fullScreenRequested()
QTRY_VERIFY(isFalseJavaScriptResult(page, "document.webkitIsFullScreen"));
}
-void tst_QWebEnginePage::quotaRequested()
+void tst_QWebEnginePage::requestQuota_data()
+{
+ QTest::addColumn<QString>("storage");
+ QTest::addRow("webkitPersistentStorage") << "navigator.webkitPersistentStorage";
+ QTest::addRow("webkitTemporaryStorage") << "navigator.webkitTemporaryStorage";
+
+}
+
+void tst_QWebEnginePage::requestQuota()
{
+ QFETCH(QString, storage);
+
ConsolePage page;
QWebEngineView view;
view.setPage(&page);
@@ -2023,35 +2101,23 @@ void tst_QWebEnginePage::quotaRequested()
page.load(QUrl("qrc:///resources/content.html"));
QVERIFY(loadFinishedSpy.wait());
- connect(&page, &QWebEnginePage::quotaRequested,
- [] (QWebEngineQuotaRequest request)
- {
- if (request.requestedSize() <= 5000)
- request.accept();
- else
- request.reject();
- });
-
- evaluateJavaScriptSync(&page,
- "navigator.webkitPersistentStorage.requestQuota(1024, function(grantedSize) {" \
- "console.log(grantedSize);" \
- "});");
+ 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.size(), 2);
- QTRY_COMPARE(page.messages[1], QString("1024"));
-
- evaluateJavaScriptSync(&page,
- "navigator.webkitPersistentStorage.queryUsageAndQuota(function(usedBytes, grantedBytes) {" \
- "console.log(usedBytes + ', ' + grantedBytes);" \
- "});");
+ 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[2], QString("0, 1024"));
+ QTRY_COMPARE(page.messages[1], QString("0"));
+ QTRY_VERIFY(page.messages[2].toLongLong() >= 1024);
}
void tst_QWebEnginePage::symmetricUrl()
@@ -2061,14 +2127,14 @@ void tst_QWebEnginePage::symmetricUrl()
QVERIFY(view.url().isEmpty());
- QCOMPARE(view.history()->count(), 1);
+ QCOMPARE(view.history()->count(), 0);
QUrl dataUrl("data:text/html,<h1>Test");
view.setUrl(dataUrl);
view.show();
QCOMPARE(view.url(), dataUrl);
- QCOMPARE(view.history()->count(), 1);
+ QCOMPARE(view.history()->count(), 0);
// loading is _not_ immediate, so the text isn't set just yet.
QVERIFY(toPlainTextSync(view.page()).isEmpty());
@@ -2381,7 +2447,7 @@ void tst_QWebEnginePage::setHtmlWithBaseURL()
QCOMPARE(evaluateJavaScriptSync(&page, "document.images[0].height").toInt(), 128);
// no history item has to be added.
- QCOMPARE(m_view->page()->history()->count(), 1);
+ QCOMPARE(m_view->page()->history()->count(), 0);
}
class MyPage : public QWebEnginePage
@@ -2482,7 +2548,8 @@ void tst_QWebEnginePage::baseUrl()
void tst_QWebEnginePage::scrollPosition()
{
// enlarged image in a small viewport, to provoke the scrollbars to appear
- QString html("<html><body><img src='qrc:/image.png' height=500 width=500/></body></html>");
+ QString html(
+ "<html><body><img src='qrc:/resources/image.png' height=500 width=500/></body></html>");
QWebEngineView view;
view.setFixedSize(200,200);
@@ -2496,8 +2563,8 @@ void tst_QWebEnginePage::scrollPosition()
// 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();
@@ -2811,7 +2878,7 @@ void tst_QWebEnginePage::setUrlHistory()
int expectedLoadFinishedCount = 0;
QSignalSpy spy(m_page, SIGNAL(loadFinished(bool)));
- QCOMPARE(m_page->history()->count(), 1);
+ QCOMPARE(m_page->history()->count(), 0);
m_page->setUrl(QUrl());
expectedLoadFinishedCount++;
@@ -2885,7 +2952,7 @@ void tst_QWebEnginePage::setUrlUsingStateObject()
QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool)));
int expectedUrlChangeCount = 0;
- QCOMPARE(m_page->history()->count(), 1);
+ QCOMPARE(m_page->history()->count(), 0);
url = QUrl("qrc:/resources/test1.html");
m_page->setUrl(url);
@@ -3064,7 +3131,7 @@ void tst_QWebEnginePage::loadInSignalHandlers()
URLSetter setter(m_page, signal, type, urlForSetter);
QSignalSpy spy(&setter, &URLSetter::finished);
m_page->load(url);
- QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 20000);
+ QTRY_VERIFY_WITH_TIMEOUT(spy.size() >= 1, 20000);
QCOMPARE(m_page->url(), urlForSetter);
}
@@ -3274,23 +3341,6 @@ void tst_QWebEnginePage::mouseMovementProperties()
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.size() != 2) {
- qWarning("elementCenter failed.");
- return QPoint();
- }
-
- return QPoint(rectList.at(0).toInt(), rectList.at(1).toInt());
-}
-
void tst_QWebEnginePage::viewSource()
{
TestPage page;
@@ -3533,6 +3583,8 @@ void tst_QWebEnginePage::devTools()
QCOMPARE(inspectedPage2.inspectedPage(), nullptr);
QCOMPARE(devToolsPage.devToolsPage(), nullptr);
QCOMPARE(devToolsPage.inspectedPage(), nullptr);
+
+ QVERIFY(!inspectedPage1.devToolsId().isEmpty());
}
void tst_QWebEnginePage::openLinkInDifferentProfile()
@@ -3710,7 +3762,7 @@ void tst_QWebEnginePage::openLinkInNewPage()
QCOMPARE(page1.history()->count(), 1);
else
QCOMPARE(page1.history()->count(), 2);
- QCOMPARE(page2.history()->count(), 1);
+ QCOMPARE(page2.history()->count(), 0);
break;
case Effect::LoadInOther:
QTRY_COMPARE(page2.spy.size(), 1);
@@ -3743,20 +3795,32 @@ void tst_QWebEnginePage::dynamicFrame()
struct NotificationPage : ConsolePage {
Q_OBJECT
- const QWebEnginePage::PermissionPolicy policy;
+ const QWebEnginePermission::State policy;
public:
- NotificationPage(QWebEnginePage::PermissionPolicy ppolicy) : policy(ppolicy) {
+ NotificationPage(QWebEnginePermission::State ppolicy) : policy(ppolicy) {
connect(this, &QWebEnginePage::loadFinished, [load = spyLoad.ref()] (bool result) mutable { load(result); });
- connect(this, &QWebEnginePage::featurePermissionRequested,
- [this] (const QUrl &origin, QWebEnginePage::Feature feature) {
- if (feature != QWebEnginePage::Notifications)
+ connect(this, &QWebEnginePage::permissionRequested,
+ [this] (QWebEnginePermission permission) {
+ if (permission.feature() != QWebEnginePermission::Notifications)
return;
if (spyRequest.wasCalled())
QFAIL("request executed twise!");
- setFeaturePermission(origin, feature, policy);
- spyRequest.ref()(origin);
+ switch (policy) {
+ case QWebEnginePermission::Granted:
+ permission.grant();
+ break;
+ case QWebEnginePermission::Denied:
+ permission.deny();
+ break;
+ case QWebEnginePermission::Ask:
+ permission.reset();
+ break;
+ default:
+ break;
+ }
+ spyRequest.ref()(permission.origin());
});
load(QStringLiteral("qrc:///shared/notification.html"));
@@ -3776,40 +3840,50 @@ public:
void tst_QWebEnginePage::notificationPermission_data()
{
QTest::addColumn<bool>("setOnInit");
- QTest::addColumn<QWebEnginePage::PermissionPolicy>("policy");
+ QTest::addColumn<QWebEnginePermission::State>("policy");
QTest::addColumn<QString>("permission");
- QTest::newRow("denyOnInit") << true << QWebEnginePage::PermissionDeniedByUser << "denied";
- QTest::newRow("deny") << false << QWebEnginePage::PermissionDeniedByUser << "denied";
- QTest::newRow("grant") << false << QWebEnginePage::PermissionGrantedByUser << "granted";
- QTest::newRow("grantOnInit") << true << QWebEnginePage::PermissionGrantedByUser << "granted";
+ QTest::newRow("denyOnInit") << true << QWebEnginePermission::Denied << "denied";
+ QTest::newRow("deny") << false << QWebEnginePermission::Denied << "denied";
+ QTest::newRow("grant") << false << QWebEnginePermission::Granted << "granted";
+ QTest::newRow("grantOnInit") << true << QWebEnginePermission::Granted << "granted";
}
void tst_QWebEnginePage::notificationPermission()
{
QFETCH(bool, setOnInit);
- QFETCH(QWebEnginePage::PermissionPolicy, policy);
+ QFETCH(QWebEnginePermission::State, policy);
QFETCH(QString, permission);
QWebEngineProfile otr;
+ otr.setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions);
QWebEnginePage page(&otr, nullptr);
QUrl baseUrl("https://www.example.com/somepage.html");
bool permissionRequested = false, errorState = false;
- connect(&page, &QWebEnginePage::featurePermissionRequested, &page, [&] (const QUrl &o, QWebEnginePage::Feature f) {
- if (f != QWebEnginePage::Notifications)
+ connect(&page, &QWebEnginePage::permissionRequested, &page, [&] (QWebEnginePermission permission) {
+ if (permission.feature() != QWebEnginePermission::Notifications)
return;
- if (permissionRequested || o != baseUrl.url(QUrl::RemoveFilename)) {
- qWarning() << "Unexpected case. Can't proceed." << setOnInit << permissionRequested << o;
+ if (permissionRequested || permission.origin() != baseUrl.url(QUrl::RemoveFilename)) {
+ qWarning() << "Unexpected case. Can't proceed." << setOnInit << permissionRequested << permission.origin();
errorState = true;
return;
}
permissionRequested = true;
- page.setFeaturePermission(o, f, policy);
+
+ if (policy == QWebEnginePermission::Granted)
+ permission.grant();
+ else
+ permission.deny();
});
- if (setOnInit)
- page.setFeaturePermission(baseUrl, QWebEnginePage::Notifications, policy);
+ QWebEnginePermission permissionObject = otr.getPermission(baseUrl, QWebEnginePermission::Notifications);
+ if (setOnInit) {
+ if (policy == QWebEnginePermission::Granted)
+ permissionObject.grant();
+ else
+ permissionObject.deny();
+ }
QSignalSpy spy(&page, &QWebEnginePage::loadFinished);
page.setHtml(QString("<html><body>Test</body></html>"), baseUrl);
@@ -3818,7 +3892,10 @@ void tst_QWebEnginePage::notificationPermission()
QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), setOnInit ? permission : QLatin1String("default"));
if (!setOnInit) {
- page.setFeaturePermission(baseUrl, QWebEnginePage::Notifications, policy);
+ if (policy == QWebEnginePermission::Granted)
+ permissionObject.grant();
+ else
+ permissionObject.deny();
QTRY_COMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), permission);
}
@@ -3832,7 +3909,7 @@ void tst_QWebEnginePage::notificationPermission()
void tst_QWebEnginePage::sendNotification()
{
- NotificationPage page(QWebEnginePage::PermissionGrantedByUser);
+ NotificationPage page(QWebEnginePermission::Granted);
QVERIFY(page.spyLoad.waitForResult());
page.resetPermission();
@@ -3871,6 +3948,176 @@ void tst_QWebEnginePage::sendNotification()
QTRY_VERIFY2(page.messages.contains("onclose"), page.messages.join("\n").toLatin1().constData());
}
+static QString clipboardPermissionQuery(QString variableName, QString permissionName)
+{
+ return QString("var %1; navigator.permissions.query({ name:'%2' }).then((p) => { %1 = p.state; "
+ "});")
+ .arg(variableName)
+ .arg(permissionName);
+}
+
+
+void tst_QWebEnginePage::clipboardReadWritePermissionInitialState_data()
+{
+ QTest::addColumn<bool>("canAccessClipboard");
+ QTest::addColumn<bool>("canPaste");
+ QTest::addColumn<QString>("permission");
+ QTest::newRow("access and paste should grant") << true << true << "granted";
+ QTest::newRow("no access should prompt") << false << true << "prompt";
+ QTest::newRow("no paste should prompt") << true << false << "prompt";
+ QTest::newRow("no access or paste should prompt") << false << false << "prompt";
+}
+
+void tst_QWebEnginePage::clipboardReadWritePermissionInitialState()
+{
+ QFETCH(bool, canAccessClipboard);
+ QFETCH(bool, canPaste);
+ QFETCH(QString, permission);
+
+ QWebEngineProfile otr;
+ otr.setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions);
+ QWebEngineView view(&otr);
+ QWebEnginePage &page = *view.page();
+ view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true);
+ page.settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard,
+ canAccessClipboard);
+ page.settings()->setAttribute(QWebEngineSettings::JavascriptCanPaste, canPaste);
+
+ QSignalSpy spy(&page, &QWebEnginePage::loadFinished);
+ QUrl baseUrl("https://www.example.com/somepage.html");
+ page.setHtml(QString("<html><body>Test</body></html>"), baseUrl);
+ QTRY_COMPARE(spy.size(), 1);
+
+ evaluateJavaScriptSync(&page, clipboardPermissionQuery("readPermission", "clipboard-read"));
+ QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("readPermission")), permission);
+ evaluateJavaScriptSync(&page, clipboardPermissionQuery("writePermission", "clipboard-write"));
+ QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("writePermission")), permission);
+}
+
+void tst_QWebEnginePage::clipboardReadWritePermission_data()
+{
+ QTest::addColumn<bool>("canAccessClipboard");
+ QTest::addColumn<QWebEnginePermission::State>("initialPolicy");
+ QTest::addColumn<QString>("initialPermission");
+ QTest::addColumn<QWebEnginePermission::State>("requestPolicy");
+ QTest::addColumn<QString>("finalPermission");
+
+ QTest::newRow("noAccessGrantGrant")
+ << false << QWebEnginePermission::Granted << "granted"
+ << QWebEnginePermission::Granted << "granted";
+ QTest::newRow("noAccessGrantDeny")
+ << false << QWebEnginePermission::Granted << "granted"
+ << QWebEnginePermission::Denied << "denied";
+ QTest::newRow("noAccessDenyGrant")
+ << false << QWebEnginePermission::Denied << "denied"
+ << QWebEnginePermission::Granted << "granted";
+ QTest::newRow("noAccessDenyDeny") << false << QWebEnginePermission::Denied << "denied"
+ << QWebEnginePermission::Denied << "denied";
+ QTest::newRow("noAccessAskGrant") << false << QWebEnginePermission::Ask << "prompt"
+ << QWebEnginePermission::Granted << "granted";
+
+ // All policies are ignored and overridden by setting JsCanAccessClipboard and JsCanPaste to
+ // true
+ QTest::newRow("accessGrantGrant")
+ << true << QWebEnginePermission::Granted << "granted"
+ << QWebEnginePermission::Granted << "granted";
+ QTest::newRow("accessDenyDeny") << true << QWebEnginePermission::Denied << "granted"
+ << QWebEnginePermission::Denied << "granted";
+ QTest::newRow("accessAskAsk") << true << QWebEnginePermission::Ask << "granted"
+ << QWebEnginePermission::Ask << "granted";
+}
+
+void tst_QWebEnginePage::clipboardReadWritePermission()
+{
+ QFETCH(bool, canAccessClipboard);
+ QFETCH(QWebEnginePermission::State, initialPolicy);
+ QFETCH(QString, initialPermission);
+ QFETCH(QWebEnginePermission::State, requestPolicy);
+ QFETCH(QString, finalPermission);
+
+ QWebEngineProfile otr;
+ otr.setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions);
+ QWebEngineView view(&otr);
+ QWebEnginePage &page = *view.page();
+ view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true);
+ page.settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard,
+ canAccessClipboard);
+ page.settings()->setAttribute(QWebEngineSettings::JavascriptCanPaste, true);
+
+ QUrl baseUrl("https://www.example.com/somepage.html");
+
+ int permissionRequestCount = 0;
+ bool errorState = false;
+
+ // if JavascriptCanAccessClipboard is true, this never fires
+ connect(&page, &QWebEnginePage::permissionRequested, &page,
+ [&](QWebEnginePermission permission) {
+ if (permission.feature() != QWebEnginePermission::ClipboardReadWrite)
+ return;
+ if (permission.origin() != baseUrl.url(QUrl::RemoveFilename)) {
+ qWarning() << "Unexpected case. Can't proceed." << permission.origin();
+ errorState = true;
+ return;
+ }
+ permissionRequestCount++;
+ switch (requestPolicy) {
+ case QWebEnginePermission::Granted:
+ permission.grant();
+ break;
+ case QWebEnginePermission::Denied:
+ permission.deny();
+ break;
+ case QWebEnginePermission::Ask:
+ permission.reset();
+ break;
+ default:
+ break;
+ }
+ });
+
+ QWebEnginePermission permissionObject = otr.getPermission(baseUrl, QWebEnginePermission::ClipboardReadWrite);
+ switch (initialPolicy) {
+ case QWebEnginePermission::Granted:
+ permissionObject.grant();
+ break;
+ case QWebEnginePermission::Denied:
+ permissionObject.deny();
+ break;
+ case QWebEnginePermission::Ask:
+ permissionObject.reset();
+ break;
+ default:
+ break;
+ }
+
+ QSignalSpy spy(&page, &QWebEnginePage::loadFinished);
+ page.setHtml(QString("<html><body>Test</body></html>"), baseUrl);
+ QTRY_COMPARE(spy.size(), 1);
+
+ evaluateJavaScriptSync(&page, clipboardPermissionQuery("readPermission", "clipboard-read"));
+ QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("readPermission")), initialPermission);
+ evaluateJavaScriptSync(&page, clipboardPermissionQuery("writePermission", "clipboard-write"));
+ QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("writePermission")), initialPermission);
+
+ auto triggerRequest = [&page](QString variableName, QString apiCall)
+ {
+ auto js = QString("var %1; navigator.clipboard.%2.then((v) => { %1 = 'granted' }, (v) => { %1 = "
+ "'denied' });")
+ .arg(variableName)
+ .arg(apiCall);
+ evaluateJavaScriptSync(&page, js);
+ };
+
+ // permission is not 'remembered' from api standpoint, hence is not suppressed on explicit call
+ // from JS
+ triggerRequest("readState", "readText()");
+ QTRY_COMPARE(evaluateJavaScriptSync(&page, "readState"), finalPermission);
+ triggerRequest("writeState", "writeText('foo')");
+ QTRY_COMPARE(evaluateJavaScriptSync(&page, "writeState"), finalPermission);
+ QCOMPARE(permissionRequestCount, canAccessClipboard ? 0 : 2);
+ QVERIFY(!errorState);
+}
+
void tst_QWebEnginePage::contentsSize()
{
m_view->resize(800, 600);
@@ -3899,6 +4146,62 @@ void tst_QWebEnginePage::contentsSize()
QCOMPARE(m_page->contentsSize().height(), 1216);
}
+void tst_QWebEnginePage::localFontAccessPermission_data()
+{
+ QTest::addColumn<QWebEnginePermission::State>("policy");
+ QTest::addColumn<bool>("ignore");
+ QTest::addColumn<bool>("shouldBeEmpty");
+
+ QTest::newRow("ignore") << QWebEnginePermission::Denied << true << true;
+ QTest::newRow("setDeny") << QWebEnginePermission::Denied << false << true;
+ QTest::newRow("setGrant") << QWebEnginePermission::Granted << false << false;
+}
+
+void tst_QWebEnginePage::localFontAccessPermission() {
+ QFETCH(QWebEnginePermission::State, policy);
+ QFETCH(bool, ignore);
+ QFETCH(bool, shouldBeEmpty);
+
+ QWebEngineView view;
+ QWebEnginePage page(&view);
+ page.profile()->setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions);
+ view.setPage(&page);
+
+ connect(&page, &QWebEnginePage::permissionRequested, &page, [&] (QWebEnginePermission permission) {
+ if (permission.feature() != QWebEnginePermission::LocalFontsAccess)
+ return;
+
+ if (!ignore) {
+ if (policy == QWebEnginePermission::Granted)
+ permission.grant();
+ else
+ permission.deny();
+ }
+ });
+
+ QSignalSpy spy(&page, &QWebEnginePage::loadFinished);
+ page.load(QUrl("qrc:///resources/fontaccess.html"));
+ QTRY_COMPARE(spy.size(), 1);
+
+ // Font access is only enabled for visible WebContents.
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ if (evaluateJavaScriptSync(&page, QStringLiteral("!window.queryLocalFonts")).toBool())
+ W_QSKIP("Local fonts access is not supported.", SkipSingle);
+
+ // Access to the API requires recent user interaction
+ QTest::keyPress(view.focusProxy(), Qt::Key_Space);
+ QTRY_COMPARE(evaluateJavaScriptSync(&page, QStringLiteral("activated")).toBool(), true);
+
+ if (ignore) {
+ QTRY_COMPARE_NE_WITH_TIMEOUT(evaluateJavaScriptSync(&page, QStringLiteral("done")).toBool(), true, 1000);
+ } else {
+ QTRY_VERIFY_WITH_TIMEOUT(evaluateJavaScriptSync(&page, QStringLiteral("done")).toBool() == true, 1000);
+ QVERIFY((evaluateJavaScriptSync(&page, QStringLiteral("fonts.length")).toInt() == 0) == shouldBeEmpty);
+ }
+}
+
void tst_QWebEnginePage::setLifecycleState()
{
qRegisterMetaType<QWebEnginePage::LifecycleState>("LifecycleState");
@@ -5119,6 +5422,392 @@ void tst_QWebEnginePage::localToRemoteNavigation()
QVERIFY(!remote.loaded);
}
+void tst_QWebEnginePage::clientHints_data()
+{
+ QTest::addColumn<bool>("clientHintsEnabled");
+ QTest::addColumn<QString>("arch");
+ QTest::addColumn<QString>("platform");
+ QTest::addColumn<QString>("model");
+ QTest::addColumn<bool>("isMobile");
+ QTest::addColumn<QString>("fullVersion");
+ QTest::addColumn<QString>("platformVersion");
+ QTest::addColumn<QString>("bitness");
+ QTest::addColumn<bool>("isWOW64");
+ QTest::addColumn<QHash<QString, QString>>("fullVersionList");
+
+ QTest::newRow("Modify values") << true << "Abc" << "AmigaOS" << "Ultra" << true << "1.99" << "3" << "x64" << true << QHash<QString, QString>({{"APITest", "1.0.0"}, {"App", "5.0"}});
+ QTest::newRow("Empty values") << true << "" << "" << "" << false << "" << "" << "" << false << QHash<QString, QString>();
+ QTest::newRow("Disable headers") << false << "" << "" << "" << false << "" << "" << "" << false << QHash<QString, QString>();
+}
+
+void tst_QWebEnginePage::clientHints()
+{
+ QFETCH(bool, clientHintsEnabled);
+ QFETCH(QString, arch);
+ QFETCH(QString, platform);
+ QFETCH(QString, model);
+ QFETCH(bool, isMobile);
+ QFETCH(QString, fullVersion);
+ QFETCH(QString, platformVersion);
+ QFETCH(QString, bitness);
+ QFETCH(bool, isWOW64);
+ typedef QHash<QString, QString> brandVersionPairs;
+ QFETCH(brandVersionPairs, fullVersionList);
+
+ QWebEnginePage page;
+ QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
+
+ QWebEngineClientHints *clientHints = page.profile()->clientHints();
+ clientHints->setAllClientHintsEnabled(clientHintsEnabled);
+
+ HttpServer server;
+ int requestCount = 0;
+ connect(&server, &HttpServer::newRequest, [&] (HttpReqRep *r) {
+ // Platform and Mobile hints are always sent and can't be disabled with this API
+ QVERIFY(r->hasRequestHeader("Sec-CH-UA-Platform"));
+ QVERIFY(r->hasRequestHeader("Sec-CH-UA-Mobile"));
+ if (!clientHintsEnabled) {
+ QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Arch"));
+ QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Model"));
+ QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Full-Version"));
+ QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Platform-Version"));
+ QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Bitness"));
+ QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Wow64"));
+ QVERIFY(!r->hasRequestHeader("Sec-CH-UA-Full-Version-List"));
+ }
+
+ // The first request header won't contain any hints, only after a response with "Accept-CH"
+ if (requestCount > 1 && clientHintsEnabled) {
+ // All hint values are lower case in the headers
+ QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Arch")).remove("\""), arch.toLower());
+ QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Platform")).remove("\""), platform.toLower());
+ QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Model")).remove("\""), model.toLower());
+ QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Mobile")).remove("\""), isMobile ? "?1" : "?0");
+ QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Full-Version")).remove("\""), fullVersion.toLower());
+ QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Platform-Version")).remove("\""), platformVersion.toLower());
+ QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Bitness")).remove("\""), bitness.toLower());
+ QCOMPARE(QString(r->requestHeader("Sec-CH-UA-Wow64")).remove("\""), isWOW64 ? "?1" : "?0");
+ for (auto i = fullVersionList.cbegin(), end = fullVersionList.cend(); i != end; ++i)
+ QVERIFY(QString(r->requestHeader("Sec-CH-UA-Full-Version-List")).contains(i.key().toLower()));
+ }
+
+ r->setResponseHeader("Accept-CH", "Sec-CH-UA-Arch, Sec-CH-UA-Bitness, Sec-CH-UA-Full-Version, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform-Version, Sec-CH-UA-Platform, Sec-CH-UA-Wow64, Sec-CH-UA");
+ r->sendResponse();
+ requestCount++;
+ });
+ QVERIFY(server.start());
+
+ clientHints->setArch(arch);
+ clientHints->setPlatform(platform);
+ clientHints->setModel(model);
+ clientHints->setIsMobile(isMobile);
+ clientHints->setFullVersion(fullVersion);
+ clientHints->setPlatformVersion(platformVersion);
+ clientHints->setBitness(bitness);
+ clientHints->setIsWow64(isWOW64);
+ clientHints->setFullVersionList(fullVersionList);
+
+ page.setUrl(server.url());
+ QTRY_COMPARE(loadSpy.size(), 1);
+ QVERIFY(loadSpy.takeFirst().value(0).toBool());
+
+ QCOMPARE(clientHints->arch(), arch);
+ QCOMPARE(clientHints->platform(), platform);
+ QCOMPARE(clientHints->model(), model);
+ QCOMPARE(clientHints->isMobile(), isMobile);
+ QCOMPARE(clientHints->fullVersion(), fullVersion);
+ QCOMPARE(clientHints->platformVersion(), platformVersion);
+ QCOMPARE(clientHints->bitness(), bitness);
+ QCOMPARE(clientHints->isWow64(), isWOW64);
+ for (auto i = fullVersionList.cbegin(), end = fullVersionList.cend(); i != end; ++i)
+ QCOMPARE(clientHints->fullVersionList()[i.key()], i.value());
+
+ // A new user agent string should not override/disable client hints
+ page.profile()->setHttpUserAgent(QStringLiteral("Custom user agent"));
+ page.triggerAction(QWebEnginePage::Reload);
+ QTRY_COMPARE(loadSpy.size(), 1);
+
+ // Reset all to default values
+ clientHints->resetAll();
+ QCOMPARE_NE(clientHints->arch(), arch);
+#ifdef Q_OS_LINUX
+ QCOMPARE(clientHints->platform().toLower(), "linux");
+#elif defined (Q_OS_MACOS)
+ QCOMPARE(clientHints->platform().toLower(), "macos");
+#elif defined (Q_OS_WIN)
+ QCOMPARE(clientHints->platform().toLower(), "windows");
+#endif
+ QCOMPARE_NE(clientHints->fullVersion(), fullVersion);
+ QCOMPARE_NE(clientHints->platformVersion(), platformVersion);
+ QCOMPARE_NE(clientHints->bitness(), bitness);
+ for (auto i = fullVersionList.cbegin(), end = fullVersionList.cend(); i != end; ++i)
+ QVERIFY(!clientHints->fullVersionList().contains(i.key()));
+ QVERIFY(clientHints->fullVersionList().contains("Chromium"));
+}
+
+void tst_QWebEnginePage::childFrameInput()
+{
+ HttpServer server;
+ server.setHostDomain("localhost");
+
+ // The cross-origin policy blocks scripting this frame with QWebEnginePage::runJavaScript.
+ // Use console messages to validate events.
+ QString innerHtml(
+ "<html><head><style>body{height:1200px;width:1200px;}</style></head><body>test<script>"
+ " let lastX, lastY = 0;"
+ " document.onscroll = (e) => {"
+ " if (window.scrollY > lastY) console.log(\"Down\");"
+ " if (window.scrollY < lastY) console.log(\"Up\");"
+ " if (window.scrollX > lastX) console.log(\"Right\");"
+ " if (window.scrollX < lastX) console.log(\"Left\");"
+ " lastX = window.scrollX;"
+ " lastY = window.scrollY;"
+ " };"
+ " window.onload = () => {console.log('loaded');};"
+ "</script></body></html>");
+
+ QVERIFY(server.start());
+ connect(&server, &HttpServer::newRequest, [&](HttpReqRep *rr) {
+ if (rr->requestPath() == "/main.html") {
+ // the Origin-Agent-Cluster header enables dedicated processes for origins
+ rr->setResponseHeader("Origin-Agent-Cluster", "?1");
+ // the same-site-cross-origin page forces to create the frame in a different process
+ server.setHostDomain("sub.localhost");
+ rr->setResponseBody(("<html><body>"
+ "<iframe id=\"iframe\" width=90% height=90% src=\""
+ + server.url().toString().toUtf8()
+ + "inner.html\"></iframe>"
+ "</body></html>"));
+ }
+ if (rr->requestPath() == "/inner.html")
+ rr->setResponseBody(innerHtml.toUtf8());
+ rr->sendResponse();
+ });
+
+ QWebEngineView view;
+ ConsolePage page;
+ view.setPage(&page);
+ view.resize(640, 480);
+ QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished);
+ page.load(server.url("/main.html"));
+ QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000);
+
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+ QTRY_VERIFY(evaluateJavaScriptSync(&page, "window.originAgentCluster").toBool());
+
+ // make sure the frame is loaded
+ QTRY_COMPARE(page.messages.size(), 1);
+ QTRY_COMPARE(page.messages[0], QString("loaded"));
+
+ // focus
+ evaluateJavaScriptSync(&page, "document.getElementById('iframe').contentWindow.focus()");
+ QTRY_COMPARE(evaluateJavaScriptSync(&page, "document.activeElement.id").toString(),
+ QStringLiteral("iframe"));
+
+ QPoint globalPos = view.windowHandle()->position();
+ QPoint p = elementCenter(&page, QString("iframe"));
+
+ // Even if the document is loaded, it is not necessarily drawn.
+ // Hit-testing (in Viz) for pointer events will be flacky in this scenario.
+ // Send keyClick events first so the target frame will be cached for wheel events.
+ QTest::keyClick(view.focusProxy(), Qt::Key_Down);
+ QTRY_COMPARE(page.messages.size(), 2);
+ QTRY_COMPARE(page.messages[1], QString("Down"));
+
+ QTest::keyClick(view.focusProxy(), Qt::Key_Up);
+ QTRY_COMPARE(page.messages.size(), 3);
+ QTRY_COMPARE(page.messages[2], QString("Up"));
+
+ QTest::keyClick(view.focusProxy(), Qt::Key_Right);
+ QTRY_COMPARE(page.messages.size(), 4);
+ QTRY_COMPARE(page.messages[3], QString("Right"));
+
+ QTest::keyClick(view.focusProxy(), Qt::Key_Left);
+ QTRY_COMPARE(page.messages.size(), 5);
+ QTRY_COMPARE(page.messages[4], QString("Left"));
+
+ makeScroll(view.focusProxy(), p, globalPos, QPoint(0, -120));
+ QTRY_COMPARE(page.messages.size(), 6);
+ QTRY_COMPARE(page.messages[5], QString("Down"));
+
+ makeScroll(view.focusProxy(), p, globalPos, QPoint(0, 120));
+ QTRY_COMPARE(page.messages.size(), 7);
+ QTRY_COMPARE(page.messages[6], QString("Up"));
+
+ makeScroll(view.focusProxy(), p, globalPos, QPoint(-120, 0));
+ QTRY_COMPARE(page.messages.size(), 8);
+ QTRY_COMPARE(page.messages[7], QString("Right"));
+
+ makeScroll(view.focusProxy(), p, globalPos, QPoint(120, 0));
+ QTRY_COMPARE(page.messages.size(), 9);
+ QTRY_COMPARE(page.messages[8], QString("Left"));
+}
+
+void tst_QWebEnginePage::openLinkInNewPageWithWebWindowType_data()
+{
+ QTest::addColumn<QWebEnginePage::WebWindowType>("webWindowType");
+ QTest::addColumn<QString>("elementId");
+ QTest::addColumn<Qt::MouseButton>("button");
+ QTest::addColumn<Qt::KeyboardModifier>("keyboardModififer");
+ QTest::newRow("webBrowserWindow")
+ << QWebEnginePage::WebBrowserWindow << "link" << Qt::LeftButton << Qt::ShiftModifier;
+ QTest::newRow("webBrowserTab")
+ << QWebEnginePage::WebBrowserTab << "link" << Qt::LeftButton << Qt::NoModifier;
+ QTest::newRow("webDialog") << QWebEnginePage::WebDialog << "openWindow" << Qt::LeftButton
+ << Qt::NoModifier;
+ QTest::newRow("webBrowserBackgroundTab") << QWebEnginePage::WebBrowserBackgroundTab << "link"
+ << Qt::MiddleButton << Qt::NoModifier;
+}
+
+class WebWindowTypeTestPage : public QWebEnginePage
+{
+ Q_OBJECT
+
+public:
+ WebWindowType windowType;
+
+signals:
+ void windowCreated();
+
+private:
+ QWebEnginePage *createWindow(WebWindowType type) override
+ {
+ windowType = type;
+ emit windowCreated();
+ return nullptr;
+ }
+};
+
+void tst_QWebEnginePage::openLinkInNewPageWithWebWindowType()
+{
+ QFETCH(QWebEnginePage::WebWindowType, webWindowType);
+ QFETCH(QString, elementId);
+ QFETCH(Qt::MouseButton, button);
+ QFETCH(Qt::KeyboardModifier, keyboardModififer);
+
+ WebWindowTypeTestPage page;
+ QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool)));
+ QSignalSpy windowCreatedSpy(&page, &WebWindowTypeTestPage::windowCreated);
+ QWebEngineView view(&page);
+ view.resize(640, 480);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ page.settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, true);
+ page.settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true);
+ QString html = "<html><body>"
+ "<a id='link' href='hello' target='_blank'>link</a>"
+ "<br><br>"
+ "<button id='openWindow' onclick='myFunction()'>Try it</button>"
+ "<script>"
+ "function myFunction() {"
+ " const myWindow = window.open('', '', 'width=300,height=300');"
+ "}"
+ "</script>"
+ "</body></html>";
+
+ page.setHtml(html);
+ QVERIFY(loadFinishedSpy.wait());
+
+ QTest::mouseClick(view.focusProxy(), button, keyboardModififer,
+ elementCenter(&page, elementId));
+ QVERIFY(windowCreatedSpy.wait());
+ QCOMPARE(page.windowType, webWindowType);
+}
+
+class DoNothingInterceptor : public QWebEngineUrlRequestInterceptor
+{
+public:
+ DoNothingInterceptor() { }
+
+ void interceptRequest(QWebEngineUrlRequestInfo &) override
+ {
+ ran = true;
+ }
+ bool ran = false;
+};
+
+void tst_QWebEnginePage::keepInterceptorAfterNewWindowRequested()
+{
+ DoNothingInterceptor interceptor;
+ QWebEnginePage page;
+ page.setUrlRequestInterceptor(&interceptor);
+ connect(&page, &QWebEnginePage::newWindowRequested, [&](QWebEngineNewWindowRequest &request) {
+ request.openIn(&page);
+ });
+ QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool)));
+
+ QWebEngineView view;
+ view.resize(500, 500);
+ view.setPage(&page);
+ view.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&view));
+
+ page.setHtml("<html><body>"
+ "<a id='link' href='hello' target='_blank'>link</a>"
+ "</body></html>");
+ QTRY_COMPARE(loadFinishedSpy.size(), 1);
+ QVERIFY(loadFinishedSpy.takeFirst().value(0).toBool());
+ QVERIFY(interceptor.ran);
+ interceptor.ran = false;
+
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, elementCenter(&page, "link"));
+ QTRY_COMPARE(loadFinishedSpy.size(), 1);
+ QVERIFY(loadFinishedSpy.takeFirst().value(0).toBool());
+ QVERIFY(!interceptor.ran);
+
+ page.setHtml("<html><body></body></html>");
+ QTRY_COMPARE(loadFinishedSpy.size(), 1);
+ QVERIFY(loadFinishedSpy.takeFirst().value(0).toBool());
+ QVERIFY(interceptor.ran);
+}
+
+void tst_QWebEnginePage::chooseDesktopMedia()
+{
+#if QT_CONFIG(webengine_extensions) && QT_CONFIG(webengine_webrtc)
+ HttpServer server;
+ server.setHostDomain("localhost");
+ connect(&server, &HttpServer::newRequest, &server, [&] (HttpReqRep *r) {
+ if (r->requestMethod() == "GET")
+ r->setResponseBody("<html></html>");
+ });
+ QVERIFY(server.start());
+
+ QWebEnginePage page;
+ QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool)));
+ page.settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true);
+ page.profile()->setPersistentPermissionsPolicy(QWebEngineProfile::NoPersistentPermissions);
+
+ bool desktopMediaRequested = false;
+ bool permissionRequested = false;
+
+ connect(&page, &QWebEnginePage::desktopMediaRequested,
+ [&](const QWebEngineDesktopMediaRequest &) {
+ desktopMediaRequested = true;
+ });
+
+ connect(&page, &QWebEnginePage::permissionRequested,
+ [&](QWebEnginePermission permission) {
+ permissionRequested = true;
+ // Handle permission to 'complete' the media request
+ permission.grant();
+ });
+
+ page.load(QUrl(server.url()));
+ QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 20000);
+
+ const QString extensionId("nkeimhogjdpnpccoofpliimaahmaaome");
+ page.runJavaScript(QString("(() => {"
+ " let port = chrome.runtime.connect(\"%1\", {name: \"chooseDesktopMedia\"});"
+ " port.postMessage({method: \"chooseDesktopMedia\"});"
+ "})()").arg(extensionId));
+
+ QTRY_VERIFY(desktopMediaRequested);
+ QTRY_VERIFY(permissionRequested);
+#endif // QT_CONFIG(webengine_extensions) && QT_CONFIG(webengine_webrtc)
+}
+
static QByteArrayList params = {QByteArrayLiteral("--use-fake-device-for-media-stream")};
W_QTEST_MAIN(tst_QWebEnginePage, params)