summaryrefslogtreecommitdiffstats
path: root/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp')
-rw-r--r--tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp503
1 files changed, 275 insertions, 228 deletions
diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
index dbb15ba10..94ce92e40 100644
--- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
+++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp
@@ -158,6 +158,7 @@ private Q_SLOTS:
void setHtmlWithStylesheetResource();
void setHtmlWithBaseURL();
void setHtmlWithJSAlert();
+ void setHtmlWithModuleImport();
void baseUrl_data();
void baseUrl();
void scrollPosition();
@@ -224,9 +225,14 @@ private Q_SLOTS:
void editActionsWithoutSelection();
void customUserAgentInNewTab();
+ void renderProcessCrashed();
+ void backgroundColor();
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;
@@ -322,7 +328,7 @@ void tst_QWebEnginePage::acceptNavigationRequest()
page.setHtml(QString("<html><body><form name='tstform' action='data:text/html,foo'method='get'>"
"<input type='text'><input type='submit'></form></body></html>"), QUrl());
- QTRY_COMPARE(loadSpy.count(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000);
evaluateJavaScriptSync(&page, "tstform.submit();");
QTRY_COMPARE(loadSpy.count(), 2);
@@ -388,7 +394,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(spyLoadFinished.count(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(spyLoadFinished.count(), 1, 20000);
// Geolocation is only enabled for visible WebContents.
view.show();
@@ -415,7 +421,7 @@ 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(spyLoadFinished.count(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(spyLoadFinished.count(), 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);
@@ -475,9 +481,9 @@ void tst_QWebEnginePage::pasteImage()
QClipboard *clipboard = QGuiApplication::clipboard();
clipboard->setImage(origImage);
QWebEnginePage *page = m_view->page();
- page->load(QUrl("qrc:///resources/pasteimage.html"));
QSignalSpy spyFinished(m_view, &QWebEngineView::loadFinished);
- QVERIFY(spyFinished.wait());
+ page->load(QUrl("qrc:///resources/pasteimage.html"));
+ QTRY_VERIFY_WITH_TIMEOUT(!spyFinished.isEmpty(), 20000);
page->triggerAction(QWebEnginePage::Paste);
QTRY_VERIFY(evaluateJavaScriptSync(page,
"window.myImageDataURL ? window.myImageDataURL.length : 0").toInt() > 0);
@@ -568,11 +574,11 @@ void tst_QWebEnginePage::acceptNavigationRequestNavigationType()
QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
page.load(QUrl("qrc:///resources/script.html"));
- QTRY_COMPARE(loadSpy.count(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000);
QTRY_COMPARE(page.navigations.count(), 1);
page.load(QUrl("qrc:///resources/content.html"));
- QTRY_COMPARE(loadSpy.count(), 2);
+ QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 2, 20000);
QTRY_COMPARE(page.navigations.count(), 2);
page.triggerAction(QWebEnginePage::Stop);
@@ -587,7 +593,7 @@ void tst_QWebEnginePage::acceptNavigationRequestNavigationType()
QTRY_COMPARE(page.navigations.count(), 4);
page.load(QUrl("qrc:///resources/reload.html"));
- QTRY_COMPARE(loadSpy.count(), 6);
+ QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 6, 20000);
QTRY_COMPARE(page.navigations.count(), 6);
QList<QWebEnginePage::NavigationType> expectedList;
@@ -613,7 +619,7 @@ void tst_QWebEnginePage::popupFormSubmission()
page.setHtml("<form name='form1' method=get action='' target='myNewWin'>"
" <input type='hidden' name='foo' value='bar'>"
"</form>");
- QTRY_COMPARE(loadFinishedSpy.count(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 20000);
page.runJavaScript("window.open('', 'myNewWin', 'width=500,height=300,toolbar=0');");
evaluateJavaScriptSync(&page, "document.form1.submit();");
@@ -666,8 +672,8 @@ 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(loadSpy1.count(), 1);
- QTRY_COMPARE(loadSpy2.count(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(loadSpy1.count(), 1, 20000);
+ QTRY_COMPARE_WITH_TIMEOUT(loadSpy2.count(), 1, 20000);
evaluateJavaScriptSync(&page1, "localStorage.setItem('test', 'value1');");
evaluateJavaScriptSync(&page2, "localStorage.setItem('test', 'value2');");
@@ -695,7 +701,7 @@ public:
CursorTrackedPage(QWidget *parent = 0): QWebEnginePage(parent) {
}
- QString selectedText() {
+ QString jsSelectedText() {
return evaluateJavaScriptSync(this, "window.getSelection().toString()").toString();
}
@@ -711,62 +717,91 @@ public:
int isSelectionCollapsed() {
return evaluateJavaScriptSync(this, "window.getSelection().getRangeAt(0).collapsed").toBool();
}
- bool hasSelection()
- {
- return !selectedText().isEmpty();
- }
};
void tst_QWebEnginePage::textSelection()
{
- QWebEngineView view;
- CursorTrackedPage *page = new CursorTrackedPage(&view);
- QString content("<html><body><p id=one>The quick brown fox</p>" \
+ CursorTrackedPage page;
+
+ QString textToSelect("The quick brown fox");
+ QString content = QString("<html><body><p id=one>%1</p>" \
"<p id=two>jumps over the lazy dog</p>" \
- "<p>May the source<br/>be with you!</p></body></html>");
- page->setView(&view);
- QSignalSpy loadSpy(&view, SIGNAL(loadFinished(bool)));
- page->setHtml(content);
- QTRY_COMPARE(loadSpy.count(), 1);
+ "<p>May the source<br/>be with you!</p></body></html>").arg(textToSelect);
+
+ QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
+ page.setHtml(content);
+ QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000);
// these actions must exist
- QVERIFY(page->action(QWebEnginePage::SelectAll) != 0);
+ QVERIFY(page.action(QWebEnginePage::SelectAll) != 0);
// ..but SelectAll is disabled because the page has no focus due to disabled FocusOnNavigationEnabled.
- QCOMPARE(page->action(QWebEnginePage::SelectAll)->isEnabled(), false);
+ QCOMPARE(page.action(QWebEnginePage::SelectAll)->isEnabled(), false);
// Verify hasSelection returns false since there is no selection yet...
- QCOMPARE(page->hasSelection(), false);
+ QVERIFY(!page.hasSelection());
+ QVERIFY(page.jsSelectedText().isEmpty());
// this will select the first paragraph
QString selectScript = "var range = document.createRange(); " \
"var node = document.getElementById(\"one\"); " \
"range.selectNode(node); " \
"getSelection().addRange(range);";
- evaluateJavaScriptSync(page, selectScript);
- QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox"));
+ evaluateJavaScriptSync(&page, selectScript);
+
// Make sure hasSelection returns true, since there is selected text now...
- QCOMPARE(page->hasSelection(), true);
+ QTRY_VERIFY(page.hasSelection());
+ QCOMPARE(page.selectedText().trimmed(), textToSelect);
+
+ QCOMPARE(page.jsSelectedText().trimmed(), textToSelect);
+
+ // navigate away and check that selection is cleared
+ page.load(QUrl("about:blank"));
+ QTRY_COMPARE(loadSpy.count(), 2);
+
+ QVERIFY(!page.hasSelection());
+ QVERIFY(page.selectedText().isEmpty());
+
+ QVERIFY(page.jsSelectedText().isEmpty());
}
void tst_QWebEnginePage::backActionUpdate()
{
QWebEngineView view;
+ view.resize(640, 480);
+ view.show();
+
QWebEnginePage *page = view.page();
+ QSignalSpy loadSpy(page, &QWebEnginePage::loadFinished);
QAction *action = page->action(QWebEnginePage::Back);
QVERIFY(!action->isEnabled());
- QSignalSpy loadSpy(page, SIGNAL(loadFinished(bool)));
- QUrl url = QUrl("qrc:///resources/framedindex.html");
- page->load(url);
- QTRY_COMPARE(loadSpy.count(), 1);
+
+ page->load(QUrl("qrc:///resources/framedindex.html"));
+ QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000);
QVERIFY(!action->isEnabled());
- QTest::mouseClick(&view, Qt::LeftButton, 0, QPoint(10, 10));
- QEXPECT_FAIL("", "Behavior change: Load signals are emitted only for the main frame in QtWebEngine.", Continue);
- QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 2, 100);
- QEXPECT_FAIL("", "FIXME: Mouse events aren't passed from the QWebEngineView down to the RWHVQtDelegateWidget", Continue);
- QVERIFY(action->isEnabled());
+ auto firstAnchorCenterInFrame = [](QWebEnginePage *page, const QString &frameName) {
+ QVariantList rectList = evaluateJavaScriptSync(page,
+ "(function(){"
+ "var frame = document.getElementsByName('" + frameName + "')[0];"
+ "var anchor = frame.contentDocument.getElementsByTagName('a')[0];"
+ "var rect = anchor.getBoundingClientRect();"
+ "return [(rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2];"
+ "})()").toList();
+
+ if (rectList.count() != 2) {
+ qWarning("firstAnchorCenterInFrame failed.");
+ return QPoint();
+ }
+
+ return QPoint(rectList.at(0).toInt(), rectList.at(1).toInt());
+ };
+
+ QVERIFY(evaluateJavaScriptSync(page, "document.getElementsByName('frame_b')[0].contentDocument == undefined").toBool());
+ QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, firstAnchorCenterInFrame(page, "frame_c"));
+ QTRY_VERIFY(evaluateJavaScriptSync(page, "document.getElementsByName('frame_b')[0].contentDocument != undefined").toBool());
+ QTRY_VERIFY(action->isEnabled());
}
void tst_QWebEnginePage::localStorageVisibility()
@@ -782,18 +817,20 @@ 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(loadSpy1.count(), 1);
- QTRY_COMPARE(loadSpy2.count(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(loadSpy1.count(), 1, 20000);
+ QTRY_COMPARE_WITH_TIMEOUT(loadSpy2.count(), 1, 20000);
// The attribute determines the visibility of the window.localStorage object.
QVERIFY(evaluateJavaScriptSync(&webPage1, QString("(window.localStorage != undefined)")).toBool());
QVERIFY(!evaluateJavaScriptSync(&webPage2, QString("(window.localStorage != undefined)")).toBool());
- // Switching the feature off does not actively remove the object from webPage1.
+ // Toggle local setting for every page and...
webPage1.settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, false);
webPage2.settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, true);
+ // ...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());
- QVERIFY(evaluateJavaScriptSync(&webPage2, QString("(window.localStorage != undefined)")).toBool());
// The object disappears only after reloading.
webPage1.triggerAction(QWebEnginePage::Reload);
@@ -900,7 +937,7 @@ void tst_QWebEnginePage::testJSPrompt()
bool res;
QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
page.setHtml(QStringLiteral("<html><body></body></html>"));
- QTRY_COMPARE(loadSpy.count(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000);
// OK + QString()
res = evaluateJavaScriptSync(&page,
@@ -990,6 +1027,19 @@ void tst_QWebEnginePage::findText()
QTRY_COMPARE(signalSpy.count(), 1);
QTRY_COMPARE(m_view->selectedText(), QString("foo"));
}
+
+ // Invoking startFinding operation for the same text twice. Without any wait, the second one
+ // should interrupt the first one.
+ {
+ QSignalSpy signalSpy(m_view->page(), &QWebEnginePage::findTextFinished);
+ m_view->findText("foo", 0);
+ m_view->findText("foo", 0);
+ QTRY_COMPARE(signalSpy.count(), 2);
+ QTRY_VERIFY(m_view->selectedText().isEmpty());
+
+ QCOMPARE(signalSpy.at(0).value(0).value<QWebEngineFindTextResult>().numberOfMatches(), 0);
+ QCOMPARE(signalSpy.at(1).value(0).value<QWebEngineFindTextResult>().numberOfMatches(), 1);
+ }
}
void tst_QWebEnginePage::findTextResult()
@@ -999,7 +1049,7 @@ void tst_QWebEnginePage::findTextResult()
if (findTextSpy.count() != 1)
return QVector<int>({-1, -1});
auto r = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>();
- return QVector<int>({ r.numberOfMatches(), r.activeMatchOrdinal() });
+ return QVector<int>({ r.numberOfMatches(), r.activeMatch() });
};
// findText will abort in blink if the view has an empty size.
@@ -1037,7 +1087,7 @@ void tst_QWebEnginePage::findTextSuccessiveShouldCallAllCallbacks()
CallbackSpy<bool> 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(loadSpy.count(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(loadSpy.count(), 1, 20000);
m_page->findText("abcde", 0, spy1.ref());
m_page->findText("abcd", 0, spy2.ref());
m_page->findText("abc", 0, spy3.ref());
@@ -1104,7 +1154,7 @@ void tst_QWebEnginePage::findTextActiveMatchOrdinal()
QTRY_COMPARE(findTextSpy.count(), 1);
result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>();
QCOMPARE(result.numberOfMatches(), 3);
- QCOMPARE(result.activeMatchOrdinal(), i);
+ QCOMPARE(result.activeMatch(), i);
}
// The last match is followed by the fist one.
@@ -1112,28 +1162,28 @@ void tst_QWebEnginePage::findTextActiveMatchOrdinal()
QTRY_COMPARE(findTextSpy.count(), 1);
result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>();
QCOMPARE(result.numberOfMatches(), 3);
- QCOMPARE(result.activeMatchOrdinal(), 1);
+ 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);
result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>();
QCOMPARE(result.numberOfMatches(), 3);
- QCOMPARE(result.activeMatchOrdinal(), 3);
+ QCOMPARE(result.activeMatch(), 3);
- // Finding another word resets the activeMatchOrdinal.
+ // Finding another word resets the activeMatch.
m_view->page()->findText("bar", 0);
QTRY_COMPARE(findTextSpy.count(), 1);
result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>();
QCOMPARE(result.numberOfMatches(), 2);
- QCOMPARE(result.activeMatchOrdinal(), 1);
+ QCOMPARE(result.activeMatch(), 1);
- // If no match activeMatchOrdinal is 0.
+ // If no match activeMatch is 0.
m_view->page()->findText("bla", 0);
QTRY_COMPARE(findTextSpy.count(), 1);
result = findTextSpy.takeFirst().value(0).value<QWebEngineFindTextResult>();
QCOMPARE(result.numberOfMatches(), 0);
- QCOMPARE(result.activeMatchOrdinal(), 0);
+ QCOMPARE(result.activeMatch(), 0);
}
static QWindow *findNewTopLevelWindow(const QWindowList &oldTopLevelWindows)
@@ -1177,6 +1227,12 @@ void tst_QWebEnginePage::comboBoxPopupPositionAfterMove()
auto jsViewPosition = [&view]() {
QLatin1String script("(function() { return [window.screenX, window.screenY]; })()");
QVariantList posList = evaluateJavaScriptSync(view.page(), script).toList();
+
+ if (posList.count() != 2) {
+ qWarning("jsViewPosition failed.");
+ return QPoint();
+ }
+
return QPoint(posList.at(0).toInt(), posList.at(1).toInt());
};
@@ -1190,6 +1246,8 @@ void tst_QWebEnginePage::comboBoxPopupPositionAfterMove()
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));
+ QTRY_VERIFY(!QGuiApplication::topLevelWindows().contains(popup));
}
void tst_QWebEnginePage::comboBoxPopupPositionAfterChildMove()
@@ -1331,7 +1389,7 @@ void tst_QWebEnginePage::loadSignalsOrder()
QSignalSpy spyLoadSpy(&loadSpy, &SpyForLoadSignalsOrder::started);
QVERIFY(spyLoadSpy.wait(500));
page.load(url);
- QTRY_VERIFY(loadSpy.isFinished());
+ QTRY_VERIFY_WITH_TIMEOUT(loadSpy.isFinished(), 20000);
}
void tst_QWebEnginePage::renderWidgetHostViewNotShowTopLevel()
@@ -1373,16 +1431,21 @@ public:
load(QUrl("qrc:///resources/content.html"));
}
- void jsGetUserMedia(const QString & constraints)
+ void jsGetMedia(const QString &call)
{
evaluateJavaScriptSync(this,
QStringLiteral(
"var promiseFulfilled = false;"
"var promiseRejected = false;"
- "navigator.mediaDevices.getUserMedia(%1)"
+ "navigator.mediaDevices.%1"
".then(stream => { promiseFulfilled = true})"
".catch(err => { promiseRejected = true})")
- .arg(constraints));
+ .arg(call));
+ }
+
+ void jsGetUserMedia(const QString &constraints)
+ {
+ jsGetMedia(QStringLiteral("getUserMedia(%1)").arg(constraints));
}
bool jsPromiseFulfilled()
@@ -1439,43 +1502,45 @@ private:
void tst_QWebEnginePage::getUserMediaRequest_data()
{
- QTest::addColumn<QString>("constraints");
+ QTest::addColumn<QString>("call");
QTest::addColumn<QWebEnginePage::Feature>("feature");
QTest::addRow("device audio")
- << "{audio: true}" << QWebEnginePage::MediaAudioCapture;
+ << "getUserMedia({audio: true})" << QWebEnginePage::MediaAudioCapture;
QTest::addRow("device video")
- << "{video: true}" << QWebEnginePage::MediaVideoCapture;
+ << "getUserMedia({video: true})" << QWebEnginePage::MediaVideoCapture;
QTest::addRow("device audio+video")
- << "{audio: true, video: true}" << QWebEnginePage::MediaAudioVideoCapture;
+ << "getUserMedia({audio: true, video: true})" << QWebEnginePage::MediaAudioVideoCapture;
QTest::addRow("desktop video")
- << "{video: { mandatory: { chromeMediaSource: 'desktop' }}}"
+ << "getUserMedia({video: { mandatory: { chromeMediaSource: 'desktop' }}})"
<< QWebEnginePage::DesktopVideoCapture;
QTest::addRow("desktop audio+video")
- << "{audio: { mandatory: { chromeMediaSource: 'desktop' }}, video: { mandatory: { chromeMediaSource: 'desktop' }}}"
+ << "getUserMedia({audio: { mandatory: { chromeMediaSource: 'desktop' }}, video: { mandatory: { chromeMediaSource: 'desktop' }}})"
<< QWebEnginePage::DesktopAudioVideoCapture;
+ QTest::addRow("display video")
+ << "getDisplayMedia()" << QWebEnginePage::DesktopVideoCapture;
}
void tst_QWebEnginePage::getUserMediaRequest()
{
- QFETCH(QString, constraints);
+ QFETCH(QString, call);
QFETCH(QWebEnginePage::Feature, feature);
GetUserMediaTestPage page;
+ QWebEngineView view;
if (feature == QWebEnginePage::DesktopVideoCapture || feature == QWebEnginePage::DesktopAudioVideoCapture) {
// Desktop capture needs to be on a desktop.
- QWebEngineView view;
view.setPage(&page);
view.resize(640, 480);
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
}
- QTRY_VERIFY_WITH_TIMEOUT(page.loadSucceeded(), 20000);
+ QTRY_VERIFY_WITH_TIMEOUT(page.loadSucceeded(), 60000);
page.settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true);
// 1. Rejecting request on C++ side should reject promise on JS side.
- page.jsGetUserMedia(constraints);
+ page.jsGetMedia(call);
QTRY_VERIFY(page.gotFeatureRequest(feature));
page.rejectPendingRequest();
QTRY_VERIFY(!page.jsPromiseFulfilled() && page.jsPromiseRejected());
@@ -1485,13 +1550,13 @@ void tst_QWebEnginePage::getUserMediaRequest()
// deeper in the content layer we cannot guarantee that the promise will
// always be fulfilled, however in this case an error should be returned to
// JS instead of leaving the Promise in limbo.
- page.jsGetUserMedia(constraints);
+ page.jsGetMedia(call);
QTRY_VERIFY(page.gotFeatureRequest(feature));
page.acceptPendingRequest();
QTRY_VERIFY(page.jsPromiseFulfilled() || page.jsPromiseRejected());
// 3. Media feature permissions are not remembered.
- page.jsGetUserMedia(constraints);
+ page.jsGetMedia(call);
QTRY_VERIFY(page.gotFeatureRequest(feature));
page.acceptPendingRequest();
QTRY_VERIFY(page.jsPromiseFulfilled() || page.jsPromiseRejected());
@@ -1652,156 +1717,51 @@ void tst_QWebEnginePage::openWindowDefaultSize()
QCOMPARE(requestedGeometry.height(), 100);
}
-class JavaScriptCallbackBase
+bool tst_QWebEnginePage::isFalseJavaScriptResult(QWebEnginePage *page, const QString &javaScript)
{
-public:
- JavaScriptCallbackBase()
- {
- if (watcher)
- QMetaObject::invokeMethod(watcher, "add");
- }
-
- void operator() (const QVariant &result)
- {
- check(result);
- if (watcher)
- QMetaObject::invokeMethod(watcher, "notify");
- }
-
-protected:
- virtual void check(const QVariant &result) = 0;
-
-private:
- friend class JavaScriptCallbackWatcher;
- static QPointer<QObject> watcher;
-};
-
-QPointer<QObject> JavaScriptCallbackBase::watcher = 0;
-
-class JavaScriptCallback : public JavaScriptCallbackBase
-{
-public:
- JavaScriptCallback() { }
- JavaScriptCallback(const QVariant& _expected) : expected(_expected) { }
-
- void check(const QVariant& result) override
- {
- QVERIFY(result.isValid());
- QCOMPARE(result, expected);
- }
-
-private:
- QVariant expected;
-};
-
-class JavaScriptCallbackNull : public JavaScriptCallbackBase
-{
-public:
- void check(const QVariant& result) override
- {
- QVERIFY(result.isNull());
-// FIXME: Returned null values are currently invalid QVariants.
-// QVERIFY(result.isValid());
- }
-};
+ QVariant result = evaluateJavaScriptSync(page, javaScript);
+ return !result.isNull() && result.isValid() && result == QVariant(false);
+}
-class JavaScriptCallbackUndefined : public JavaScriptCallbackBase
+bool tst_QWebEnginePage::isTrueJavaScriptResult(QWebEnginePage *page, const QString &javaScript)
{
-public:
- void check(const QVariant& result) override
- {
- QVERIFY(result.isNull());
- QVERIFY(!result.isValid());
- }
-};
+ QVariant result = evaluateJavaScriptSync(page, javaScript);
+ return !result.isNull() && result.isValid() && result == QVariant(true);
+}
-class JavaScriptCallbackWatcher : public QObject
+bool tst_QWebEnginePage::isEmptyListJavaScriptResult(QWebEnginePage *page, const QString &javaScript)
{
- Q_OBJECT
-public:
- JavaScriptCallbackWatcher()
- {
- Q_ASSERT(!JavaScriptCallbackBase::watcher);
- JavaScriptCallbackBase::watcher = this;
- }
-
- Q_INVOKABLE void add()
- {
- available++;
- }
-
- Q_INVOKABLE void notify()
- {
- called++;
- if (called == available)
- emit allCalled();
- }
-
- bool wait(int maxSeconds = 30)
- {
- if (called == available)
- return true;
-
- QTestEventLoop loop;
- connect(this, SIGNAL(allCalled()), &loop, SLOT(exitLoop()));
- loop.enterLoop(maxSeconds);
- return !loop.timeout();
- }
-
-signals:
- void allCalled();
-
-private:
- int available = 0;
- int called = 0;
-};
-
+ QVariant result = evaluateJavaScriptSync(page, javaScript);
+ return !result.isNull() && result.isValid() && result == QList<QVariant>();
+}
void tst_QWebEnginePage::runJavaScript()
{
TestPage page;
- JavaScriptCallbackWatcher watcher;
-
- JavaScriptCallback callbackBool(QVariant(false));
- page.runJavaScript("false", QWebEngineCallback<const QVariant&>(callbackBool));
-
- JavaScriptCallback callbackInt(QVariant(2));
- page.runJavaScript("2", QWebEngineCallback<const QVariant&>(callbackInt));
-
- JavaScriptCallback callbackDouble(QVariant(2.5));
- page.runJavaScript("2.5", QWebEngineCallback<const QVariant&>(callbackDouble));
-
- JavaScriptCallback callbackString(QVariant(QStringLiteral("Test")));
- page.runJavaScript("\"Test\"", QWebEngineCallback<const QVariant&>(callbackString));
-
- QVariantList list;
- JavaScriptCallback callbackList(list);
- page.runJavaScript("[]", QWebEngineCallback<const QVariant&>(callbackList));
-
+ QVariant result;
QVariantMap map;
- map.insert(QStringLiteral("test"), QVariant(2));
- JavaScriptCallback callbackMap(map);
- page.runJavaScript("var el = {\"test\": 2}; el", QWebEngineCallback<const QVariant&>(callbackMap));
- JavaScriptCallbackNull callbackNull;
- page.runJavaScript("null", QWebEngineCallback<const QVariant&>(callbackNull));
+ QVERIFY(isFalseJavaScriptResult(&page, "false"));
+ QCOMPARE(evaluateJavaScriptSync(&page, "2").toInt(), 2);
+ QCOMPARE(evaluateJavaScriptSync(&page, "2.5").toDouble(), 2.5);
+ QCOMPARE(evaluateJavaScriptSync(&page, "\"Test\"").toString(), "Test");
+ QVERIFY(isEmptyListJavaScriptResult(&page, "[]"));
- JavaScriptCallbackUndefined callbackUndefined;
- page.runJavaScript("undefined", QWebEngineCallback<const QVariant&>(callbackUndefined));
+ map.insert(QStringLiteral("test"), QVariant(2));
+ QCOMPARE(evaluateJavaScriptSync(&page, "var el = {\"test\": 2}; el").toMap(), map);
- JavaScriptCallback callbackDate(QVariant(42.0));
- page.runJavaScript("new Date(42000)", QWebEngineCallback<const QVariant&>(callbackDate));
+ QVERIFY(evaluateJavaScriptSync(&page, "null").isNull());
- JavaScriptCallback callbackBlob(QVariant(QByteArray(8, 0)));
- page.runJavaScript("new ArrayBuffer(8)", QWebEngineCallback<const QVariant&>(callbackBlob));
+ result = evaluateJavaScriptSync(&page, "undefined");
+ QVERIFY(result.isNull() && !result.isValid());
- JavaScriptCallbackUndefined callbackFunction;
- page.runJavaScript("(function(){})", QWebEngineCallback<const QVariant&>(callbackFunction));
+ QCOMPARE(evaluateJavaScriptSync(&page, "new Date(42000)").toDate(), QVariant(42.0).toDate());
+ QCOMPARE(evaluateJavaScriptSync(&page, "new ArrayBuffer(8)").toByteArray(), QByteArray(8, 0));
- JavaScriptCallback callbackPromise(QVariant(QVariantMap{}));
- page.runJavaScript("new Promise(function(){})", QWebEngineCallback<const QVariant&>(callbackPromise));
+ result = evaluateJavaScriptSync(&page, "(function(){})");
+ QVERIFY(result.isNull() && !result.isValid());
- QVERIFY(watcher.wait());
+ QCOMPARE(evaluateJavaScriptSync(&page, "new Promise(function(){})"), QVariant(QVariantMap{}));
}
void tst_QWebEnginePage::runJavaScriptDisabled()
@@ -1812,7 +1772,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(spy.count(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 20000);
QCOMPARE(evaluateJavaScriptSyncInWorld(&page, QStringLiteral("1+1"), QWebEngineScript::MainWorld),
QVariant());
QCOMPARE(evaluateJavaScriptSyncInWorld(&page, QStringLiteral("1+1"), QWebEngineScript::ApplicationWorld),
@@ -1824,29 +1784,26 @@ void tst_QWebEnginePage::runJavaScriptFromSlot()
{
QWebEngineProfile profile;
QWebEnginePage page(&profile);
- page.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true);
QSignalSpy loadFinishedSpy(&page, &QWebEnginePage::loadFinished);
page.setHtml("<html><body>"
" <input type='text' id='input1' value='QtWebEngine' size='50' />"
"</body></html>");
QTRY_COMPARE(loadFinishedSpy.count(), 1);
- // Workaround for QTBUG-74718
- QTRY_VERIFY(page.action(QWebEnginePage::SelectAll)->isEnabled());
- QVariant result(-1);
+ bool done = false;
connect(&page, &QWebEnginePage::selectionChanged, [&]() {
- result = evaluateJavaScriptSync(&page, QStringLiteral("2+2"));
+ QTRY_COMPARE(evaluateJavaScriptSync(&page, QStringLiteral("2+2")), QVariant(4));
+ done = true;
});
evaluateJavaScriptSync(&page, QStringLiteral("const input = document.getElementById('input1');"
"input.focus();"
"input.select();"));
- QTRY_COMPARE(result, QVariant(4));
+ QTRY_VERIFY(done);
}
void tst_QWebEnginePage::fullScreenRequested()
{
- JavaScriptCallbackWatcher watcher;
QWebEngineView view;
QWebEnginePage* page = view.page();
view.show();
@@ -1857,9 +1814,8 @@ void tst_QWebEnginePage::fullScreenRequested()
page->load(QUrl("qrc:///resources/fullscreen.html"));
QTRY_COMPARE(loadSpy.count(), 1);
- page->runJavaScript("document.webkitFullscreenEnabled", JavaScriptCallback(true));
- page->runJavaScript("document.webkitIsFullScreen", JavaScriptCallback(false));
- QVERIFY(watcher.wait());
+ QTRY_VERIFY(isTrueJavaScriptResult(page, "document.webkitFullscreenEnabled"));
+ QVERIFY(isFalseJavaScriptResult(page, "document.webkitIsFullScreen"));
// FullscreenRequest must be a user gesture
bool acceptRequest = true;
@@ -1869,17 +1825,15 @@ void tst_QWebEnginePage::fullScreenRequested()
});
QTest::keyPress(view.focusProxy(), Qt::Key_Space);
- QTRY_VERIFY(evaluateJavaScriptSync(page, "document.webkitIsFullScreen").toBool());
- page->runJavaScript("document.webkitExitFullscreen()", JavaScriptCallbackUndefined());
- QVERIFY(watcher.wait());
+ QTRY_VERIFY(isTrueJavaScriptResult(page, "document.webkitIsFullScreen"));
+ page->runJavaScript("document.webkitExitFullscreen()");
+ QTRY_VERIFY(isFalseJavaScriptResult(page, "document.webkitIsFullScreen"));
acceptRequest = false;
- page->runJavaScript("document.webkitFullscreenEnabled", JavaScriptCallback(true));
+ QVERIFY(isTrueJavaScriptResult(page, "document.webkitFullscreenEnabled"));
QTest::keyPress(view.focusProxy(), Qt::Key_Space);
- QVERIFY(watcher.wait());
- page->runJavaScript("document.webkitIsFullScreen", JavaScriptCallback(false));
- QVERIFY(watcher.wait());
+ QTRY_VERIFY(isFalseJavaScriptResult(page, "document.webkitIsFullScreen"));
}
void tst_QWebEnginePage::quotaRequested()
@@ -1940,7 +1894,7 @@ void tst_QWebEnginePage::symmetricUrl()
// loading is _not_ immediate, so the text isn't set just yet.
QVERIFY(toPlainTextSync(view.page()).isEmpty());
- QTRY_COMPARE(loadFinishedSpy.count(), 1);
+ QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 20000);
QCOMPARE(view.history()->count(), 1);
QCOMPARE(toPlainTextSync(view.page()), QString("Test"));
@@ -2012,7 +1966,8 @@ void tst_QWebEnginePage::urlChange()
QUrl testUrl("http://test.qt.io/");
m_view->setHtml(QStringLiteral("<h1>Test</h1"), testUrl);
- QTRY_COMPARE(urlSpy.size(), 1);
+ QTRY_COMPARE(urlSpy.size(), 2);
+ QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), QUrl("data:text/html;charset=UTF-8,%3Ch1%3ETest%3C%2Fh1"));
QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), testUrl);
}
@@ -2270,6 +2225,41 @@ void tst_QWebEnginePage::setHtmlWithJSAlert()
QCOMPARE(toHtmlSync(&page), html);
}
+void tst_QWebEnginePage::setHtmlWithModuleImport()
+{
+ HttpServer server;
+ connect(&server, &HttpServer::newRequest, [&](HttpReqRep *rr) {
+ if (rr->requestMethod() == "GET" && rr->requestPath() == "/fibonacci.mjs") {
+ rr->setResponseBody("export function fib(n) {\n"
+ " return n < 2 ? n : fib(n-1) + fib(n-2)\n"
+ "}\n");
+ rr->setResponseHeader("Content-Type", "text/javascript");
+ rr->sendResponse();
+ } else {
+ rr->setResponseStatus(404);
+ rr->sendResponse();
+ }
+ });
+ QVERIFY(server.start());
+
+ QString html("<html>\n"
+ " <head>\n"
+ " <script type='module'>\n"
+ " import {fib} from './fibonacci.mjs'\n"
+ " window.fib7 = fib(7)\n"
+ " </script>\n"
+ " </head>\n"
+ " <body></body>\n"
+ "</html>\n");
+
+ QWebEnginePage page;
+ QSignalSpy spy(&page, &QWebEnginePage::loadFinished);
+ page.setHtml(html, server.url());
+ QVERIFY(spy.count() || spy.wait());
+
+ QCOMPARE(evaluateJavaScriptSync(&page, "fib7"), QVariant(13));
+}
+
void tst_QWebEnginePage::baseUrl_data()
{
QTest::addColumn<QString>("html");
@@ -2718,14 +2708,14 @@ void tst_QWebEnginePage::setUrlUsingStateObject()
evaluateJavaScriptSync(m_page, "window.history.pushState(null, 'push', 'navigate/to/here')");
expectedUrlChangeCount++;
- QCOMPARE(urlChangedSpy.count(), expectedUrlChangeCount);
+ QTRY_COMPARE(urlChangedSpy.count(), 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++;
- QCOMPARE(urlChangedSpy.count(), expectedUrlChangeCount);
+ QTRY_COMPARE(urlChangedSpy.count(), expectedUrlChangeCount);
QCOMPARE(m_page->url(), QUrl("qrc:/resources/navigate/to/another/location"));
QCOMPARE(m_page->history()->count(), 2);
QVERIFY(!m_page->history()->canGoForward());
@@ -2774,8 +2764,8 @@ void tst_QWebEnginePage::setUrlThenLoads()
const QUrl urlToLoad2("qrc:/resources/test1.html");
m_page->load(urlToLoad1);
- QCOMPARE(m_page->url(), urlToLoad1);
- QCOMPARE(m_page->requestedUrl(), urlToLoad1);
+ QTRY_COMPARE(m_page->url(), urlToLoad1);
+ 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);
@@ -2789,8 +2779,8 @@ void tst_QWebEnginePage::setUrlThenLoads()
QCOMPARE(baseUrlSync(m_page), extractBaseUrl(urlToLoad1));
m_page->load(urlToLoad2);
- QCOMPARE(m_page->url(), urlToLoad2);
- QCOMPARE(m_page->requestedUrl(), urlToLoad2);
+ QTRY_COMPARE(m_page->url(), urlToLoad2);
+ QTRY_COMPARE(m_page->requestedUrl(), urlToLoad2);
QCOMPARE(baseUrlSync(m_page), extractBaseUrl(urlToLoad1));
QTRY_COMPARE(startedSpy.count(), 3);
@@ -4361,6 +4351,63 @@ void tst_QWebEnginePage::customUserAgentInNewTab()
QCOMPARE(lastUserAgent, profile2.httpUserAgent().toUtf8());
}
+void tst_QWebEnginePage::renderProcessCrashed()
+{
+ using Status = QWebEnginePage::RenderProcessTerminationStatus;
+ QWebEngineProfile profile;
+ QWebEnginePage page(&profile);
+ bool done = false;
+ Status status;
+ connect(&page, &QWebEnginePage::renderProcessTerminated, [&](Status newStatus) {
+ status = newStatus;
+ done = true;
+ });
+ page.load(QUrl("chrome://crash"));
+ QTRY_VERIFY_WITH_TIMEOUT(done, 20000);
+ // The status depends on whether stack traces are enabled. With
+ // --disable-in-process-stack-traces we get an AbnormalTerminationStatus,
+ // otherwise a CrashedTerminationStatus.
+ QVERIFY(status == QWebEnginePage::CrashedTerminationStatus ||
+ status == QWebEnginePage::AbnormalTerminationStatus);
+}
+
+void tst_QWebEnginePage::backgroundColor()
+{
+ QWebEngineProfile profile;
+ QWebEngineView view;
+ QWebEnginePage *page = new QWebEnginePage(&profile, &view);
+
+ view.resize(640, 480);
+ view.show();
+ QPoint center(view.size().width() / 2, view.size().height() / 2);
+
+ QCOMPARE(page->backgroundColor(), Qt::white);
+ QTRY_COMPARE(view.grab().toImage().pixelColor(center), Qt::white);
+
+ page->setBackgroundColor(Qt::red);
+ view.setPage(page);
+
+ QCOMPARE(page->backgroundColor(), Qt::red);
+ QTRY_COMPARE(view.grab().toImage().pixelColor(center), Qt::red);
+
+ page->setHtml(QString("<html>"
+ "<head><style>html, body { margin:0; padding:0; }</style></head>"
+ "<body><div style=\"width:100%; height:10px; background-color:black\"/></body>"
+ "</html>"));
+ QSignalSpy spyFinished(page, &QWebEnginePage::loadFinished);
+ QVERIFY(spyFinished.wait());
+ // Make sure the page is rendered and the test is not grabbing the color of the RenderWidgetHostViewQtDelegateWidget.
+ QTRY_COMPARE(view.grab().toImage().pixelColor(QPoint(5, 5)), Qt::black);
+
+ QCOMPARE(page->backgroundColor(), Qt::red);
+ QCOMPARE(view.grab().toImage().pixelColor(center), Qt::red);
+
+ page->setBackgroundColor(Qt::green);
+
+ QCOMPARE(page->backgroundColor(), Qt::green);
+ QTRY_COMPARE(view.grab().toImage().pixelColor(center), Qt::green);
+}
+
static QByteArrayList params = {QByteArrayLiteral("--use-fake-device-for-media-stream")};
W_QTEST_MAIN(tst_QWebEnginePage, params)