diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-11-29 09:44:38 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-11-29 09:44:38 +0100 |
commit | 8a164aa2cd83d728ce620c2b7199ebc6222b55cb (patch) | |
tree | e63e01795baafd41e2a65641d21eb00469bed0c5 /tests/auto/widgets | |
parent | 5480a26d2f16dacfbce47da337559b5bcfb3d45e (diff) | |
parent | f9eb2b95fcdb4df82d3ba8c4aab44347a2bad452 (diff) |
Merge remote-tracking branch 'origin/5.10' into dev
Conflicts:
src/core/web_event_factory.cpp
src/core/web_event_factory.h
src/webengine/render_widget_host_view_qt_delegate_quick.h
Change-Id: Ic43787e2689c81b501ed395a990190eb67d83a2b
Diffstat (limited to 'tests/auto/widgets')
11 files changed, 323 insertions, 58 deletions
diff --git a/tests/auto/widgets/qwebenginedownloads/tst_qwebenginedownloads.cpp b/tests/auto/widgets/qwebenginedownloads/tst_qwebenginedownloads.cpp index 9531d0cf4..7094c8e4b 100644 --- a/tests/auto/widgets/qwebenginedownloads/tst_qwebenginedownloads.cpp +++ b/tests/auto/widgets/qwebenginedownloads/tst_qwebenginedownloads.cpp @@ -27,6 +27,7 @@ ****************************************************************************/ #include <QCoreApplication> +#include <QSignalSpy> #include <QStandardPaths> #include <QTemporaryDir> #include <QTest> @@ -37,6 +38,18 @@ #include <httpserver.h> #include <waitforsignal.h> +static std::unique_ptr<HttpReqRep> waitForFaviconRequest(HttpServer *server) +{ + auto rr = waitForRequest(server); + if (!rr || + rr->requestMethod() != QByteArrayLiteral("GET") || + rr->requestPath() != QByteArrayLiteral("/favicon.ico")) + return nullptr; + rr->setResponseStatus(404); + rr->sendResponse(); + return std::move(rr); +} + class tst_QWebEngineDownloads : public QObject { Q_OBJECT @@ -46,6 +59,7 @@ private Q_SLOTS: void downloadTwoLinks(); void downloadPage_data(); void downloadPage(); + void downloadViaSetUrl(); }; enum DownloadTestUserAction { @@ -317,12 +331,8 @@ void tst_QWebEngineDownloads::downloadLink() QVERIFY(loadOk); // 1.1. Ignore favicon request - auto favIconRR = waitForRequest(&server); + auto favIconRR = waitForFaviconRequest(&server); QVERIFY(favIconRR); - QCOMPARE(favIconRR->requestMethod(), QByteArrayLiteral("GET")); - QCOMPARE(favIconRR->requestPath(), QByteArrayLiteral("/favicon.ico")); - favIconRR->setResponseStatus(404); - favIconRR->sendResponse(); // 2. Simulate user action // @@ -439,12 +449,8 @@ void tst_QWebEngineDownloads::downloadTwoLinks() QVERIFY(waitForSignal(&page, &QWebEnginePage::loadFinished, [&](bool ok){ loadOk = ok; })); QVERIFY(loadOk); - auto favIconRR = waitForRequest(&server); + auto favIconRR = waitForFaviconRequest(&server); QVERIFY(favIconRR); - QCOMPARE(favIconRR->requestMethod(), QByteArrayLiteral("GET")); - QCOMPARE(favIconRR->requestPath(), QByteArrayLiteral("/favicon.ico")); - favIconRR->setResponseStatus(404); - favIconRR->sendResponse(); QTRY_VERIFY(view.focusWidget()); QWidget *renderWidget = view.focusWidget(); @@ -541,12 +547,8 @@ void tst_QWebEngineDownloads::downloadPage() QVERIFY(waitForSignal(&page, &QWebEnginePage::loadFinished, [&](bool ok){ loadOk = ok; })); QVERIFY(loadOk); - auto favIconRR = waitForRequest(&server); + auto favIconRR = waitForFaviconRequest(&server); QVERIFY(favIconRR); - QCOMPARE(favIconRR->requestMethod(), QByteArrayLiteral("GET")); - QCOMPARE(favIconRR->requestPath(), QByteArrayLiteral("/favicon.ico")); - favIconRR->setResponseStatus(404); - favIconRR->sendResponse(); QTemporaryDir tmpDir; QVERIFY(tmpDir.isValid()); @@ -593,5 +595,70 @@ void tst_QWebEngineDownloads::downloadPage() QVERIFY(file.exists()); } +void tst_QWebEngineDownloads::downloadViaSetUrl() +{ + // Reproduce the scenario described in QTBUG-63388 by triggering downloads + // of the same file multiple times via QWebEnginePage::setUrl + + HttpServer server; + QWebEngineProfile profile; + QWebEnginePage page(&profile); + QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); + QSignalSpy urlSpy(&page, &QWebEnginePage::urlChanged); + const QUrl indexUrl = server.url(); + const QUrl fileUrl = server.url(QByteArrayLiteral("/file")); + + // Set up the test scenario by trying to load some unrelated HTML. + + page.setUrl(indexUrl); + + auto indexRR = waitForRequest(&server); + QVERIFY(indexRR); + QCOMPARE(indexRR->requestMethod(), QByteArrayLiteral("GET")); + QCOMPARE(indexRR->requestPath(), QByteArrayLiteral("/")); + indexRR->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/html")); + indexRR->setResponseBody(QByteArrayLiteral("<html><body>Hello</body></html>")); + indexRR->sendResponse(); + + auto indexFavRR = waitForFaviconRequest(&server); + QVERIFY(indexFavRR); + + QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(urlSpy.count(), 1); + QCOMPARE(loadSpy.takeFirst().value(0).toBool(), true); + QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), indexUrl); + + // Download files via setUrl. With QTBUG-63388 after the first iteration the + // downloads would be triggered for indexUrl and not fileUrl. + + QVector<QUrl> downloadUrls; + QObject::connect(&profile, &QWebEngineProfile::downloadRequested, [&](QWebEngineDownloadItem *item) { + downloadUrls.append(item->url()); + }); + + for (int i = 0; i != 3; ++i) { + page.setUrl(fileUrl); + QCOMPARE(page.url(), fileUrl); + + auto fileRR = waitForRequest(&server); + QVERIFY(fileRR); + fileRR->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment")); + fileRR->setResponseBody(QByteArrayLiteral("redacted")); + fileRR->sendResponse(); + + auto fileFavRR = waitForFaviconRequest(&server); + QVERIFY(fileFavRR); + + QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(urlSpy.count(), 2); + QTRY_COMPARE(downloadUrls.count(), 1); + QCOMPARE(loadSpy.takeFirst().value(0).toBool(), false); + QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), fileUrl); + QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), indexUrl); + QCOMPARE(downloadUrls.takeFirst(), fileUrl); + QCOMPARE(page.url(), indexUrl); + } +} + QTEST_MAIN(tst_QWebEngineDownloads) #include "tst_qwebenginedownloads.moc" diff --git a/tests/auto/widgets/qwebenginepage/qwebenginepage.pro b/tests/auto/widgets/qwebenginepage/qwebenginepage.pro index a2dbd4d70..47c09e1ce 100644 --- a/tests/auto/widgets/qwebenginepage/qwebenginepage.pro +++ b/tests/auto/widgets/qwebenginepage/qwebenginepage.pro @@ -1,4 +1,4 @@ include(../tests.pri) QT *= core-private -qtConfig(printing-and-pdf): DEFINES+=QWEBENGINEPAGE_PDFPRINTINGENABLED +qtConfig(webengine-printing-and-pdf): DEFINES+=QWEBENGINEPAGE_PDFPRINTINGENABLED diff --git a/tests/auto/widgets/qwebenginepage/resources/bar.txt b/tests/auto/widgets/qwebenginepage/resources/bar.txt new file mode 100644 index 000000000..5716ca598 --- /dev/null +++ b/tests/auto/widgets/qwebenginepage/resources/bar.txt @@ -0,0 +1 @@ +bar diff --git a/tests/auto/widgets/qwebenginepage/resources/foo.txt b/tests/auto/widgets/qwebenginepage/resources/foo.txt new file mode 100644 index 000000000..257cc5642 --- /dev/null +++ b/tests/auto/widgets/qwebenginepage/resources/foo.txt @@ -0,0 +1 @@ +foo diff --git a/tests/auto/widgets/qwebenginepage/resources/path with spaces.txt b/tests/auto/widgets/qwebenginepage/resources/path with spaces.txt new file mode 100644 index 000000000..4f79cb0dd --- /dev/null +++ b/tests/auto/widgets/qwebenginepage/resources/path with spaces.txt @@ -0,0 +1 @@ +contents with spaces diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index 63569bd4b..f0ab74d6c 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -195,6 +195,8 @@ private Q_SLOTS: void setUrlWithPendingLoads(); void setUrlToEmpty(); void setUrlToInvalid(); + void setUrlToBadDomain(); + void setUrlToBadPort(); void setUrlHistory(); void setUrlUsingStateObject(); void setUrlThenLoads_data(); @@ -202,6 +204,7 @@ private Q_SLOTS: void loadFinishedAfterNotFoundError(); void loadInSignalHandlers_data(); void loadInSignalHandlers(); + void loadFromQrc(); void restoreHistory(); void toPlainTextLoadFinishedRace_data(); @@ -2657,18 +2660,19 @@ Q_OBJECT public: GetUserMediaTestPage() : m_gotRequest(false) + , m_loadSucceeded(false) { connect(this, &QWebEnginePage::featurePermissionRequested, this, &GetUserMediaTestPage::onFeaturePermissionRequested); - + connect(this, &QWebEnginePage::loadFinished, [this](bool success){ + m_loadSucceeded = success; + }); // We need to load content from a resource in order for the securityOrigin to be valid. - QSignalSpy loadSpy(this, SIGNAL(loadFinished(bool))); load(QUrl("qrc:///resources/content.html")); - QTRY_COMPARE(loadSpy.count(), 1); } void jsGetUserMedia(const QString & constraints) { - runJavaScript( + evaluateJavaScriptSync(this, QStringLiteral( "var promiseFulfilled = false;" "var promiseRejected = false;" @@ -2709,6 +2713,11 @@ public: return m_gotRequest; } + bool loadSucceeded() const + { + return m_loadSucceeded; + } + private Q_SLOTS: void onFeaturePermissionRequested(const QUrl &securityOrigin, QWebEnginePage::Feature feature) { @@ -2719,6 +2728,7 @@ private Q_SLOTS: private: bool m_gotRequest; + bool m_loadSucceeded; QWebEnginePage::Feature m_requestedFeature; QUrl m_requestSecurityOrigin; @@ -2749,6 +2759,7 @@ void tst_QWebEnginePage::getUserMediaRequest() QFETCH(QWebEnginePage::Feature, feature); GetUserMediaTestPage page; + QTRY_VERIFY_WITH_TIMEOUT(page.loadSucceeded(), 20000); page.settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true); // 1. Rejecting request on C++ side should reject promise on JS side. @@ -2777,6 +2788,7 @@ void tst_QWebEnginePage::getUserMediaRequest() void tst_QWebEnginePage::getUserMediaRequestDesktopAudio() { GetUserMediaTestPage page; + QTRY_VERIFY_WITH_TIMEOUT(page.loadSucceeded(), 20000); page.settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true); // Audio-only desktop capture is not supported. JS Promise should be @@ -2794,6 +2806,7 @@ void tst_QWebEnginePage::getUserMediaRequestDesktopAudio() void tst_QWebEnginePage::getUserMediaRequestSettingDisabled() { GetUserMediaTestPage page; + QTRY_VERIFY_WITH_TIMEOUT(page.loadSucceeded(), 20000); page.settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, false); // With the setting disabled, the JS Promise should be rejected without @@ -3222,21 +3235,19 @@ void tst_QWebEnginePage::progressSignal() void tst_QWebEnginePage::urlChange() { - QSignalSpy urlSpy(m_page, SIGNAL(urlChanged(QUrl))); + QSignalSpy urlSpy(m_page, &QWebEnginePage::urlChanged); QUrl dataUrl("data:text/html,<h1>Test"); m_view->setUrl(dataUrl); - QVERIFY(urlSpy.wait()); - - QCOMPARE(urlSpy.size(), 1); + QTRY_COMPARE(urlSpy.size(), 1); + QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), dataUrl); QUrl dataUrl2("data:text/html,<html><head><title>title</title></head><body><h1>Test</body></html>"); m_view->setUrl(dataUrl2); - QVERIFY(urlSpy.wait()); - - QCOMPARE(urlSpy.size(), 2); + QTRY_COMPARE(urlSpy.size(), 1); + QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), dataUrl2); } class FakeReply : public QNetworkReply { @@ -3347,7 +3358,7 @@ void tst_QWebEnginePage::requestedUrlAfterSetAndLoadFailures() page.load(second); QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 2, 12000); - QCOMPARE(page.url(), first); + QCOMPARE(page.url(), second); QCOMPARE(page.requestedUrl(), second); QVERIFY(!spy.at(1).first().toBool()); } @@ -3861,6 +3872,90 @@ void tst_QWebEnginePage::setUrlToInvalid() QCOMPARE(baseUrlSync(&page), aboutBlank); } +void tst_QWebEnginePage::setUrlToBadDomain() +{ + // Failing to load a URL should still emit a urlChanged signal. + // + // This test is based on the scenario in QTBUG-48995 where the second setUrl + // call first triggers an unexpected additional urlChanged signal with the + // original url before the expected signal with the new url. + + // RFC 2606 says the .invalid TLD should be invalid. + const QUrl url1 = QStringLiteral("http://this.is.definitely.invalid/"); + const QUrl url2 = QStringLiteral("http://this.is.also.invalid/"); + QWebEnginePage page; + QSignalSpy urlSpy(&page, &QWebEnginePage::urlChanged); + QSignalSpy titleSpy(&page, &QWebEnginePage::titleChanged); + QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); + + page.setUrl(url1); + + QTRY_COMPARE(urlSpy.count(), 1); + QTRY_COMPARE(titleSpy.count(), 1); + QTRY_COMPARE(loadSpy.count(), 1); + + QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), url1); + QCOMPARE(titleSpy.takeFirst().value(0).toString(), url1.host()); + QCOMPARE(loadSpy.takeFirst().value(0).toBool(), false); + + QCOMPARE(page.url(), url1); + QCOMPARE(page.title(), url1.host()); + + page.setUrl(url2); + + QTRY_COMPARE(urlSpy.count(), 1); + QTRY_COMPARE(titleSpy.count(), 1); + QTRY_COMPARE(loadSpy.count(), 1); + + QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), url2); + QCOMPARE(titleSpy.takeFirst().value(0).toString(), url2.host()); + QCOMPARE(loadSpy.takeFirst().value(0).toBool(), false); + + QCOMPARE(page.url(), url2); + QCOMPARE(page.title(), url2.host()); +} + +void tst_QWebEnginePage::setUrlToBadPort() +{ + // Failing to load a URL should still emit a urlChanged signal. + + // Ports 244-245 are hopefully unbound (marked unassigned in RFC1700). + const QUrl url1 = QStringLiteral("http://127.0.0.1:244/"); + const QUrl url2 = QStringLiteral("http://127.0.0.1:245/"); + QWebEnginePage page; + QSignalSpy urlSpy(&page, &QWebEnginePage::urlChanged); + QSignalSpy titleSpy(&page, &QWebEnginePage::titleChanged); + QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); + + page.setUrl(url1); + + QTRY_COMPARE(urlSpy.count(), 1); + QTRY_COMPARE(titleSpy.count(), 2); + QTRY_COMPARE(loadSpy.count(), 1); + + QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), url1); + QCOMPARE(titleSpy.takeFirst().value(0).toString(), url1.authority()); + QCOMPARE(titleSpy.takeFirst().value(0).toString(), url1.host()); + QCOMPARE(loadSpy.takeFirst().value(0).toBool(), false); + + QCOMPARE(page.url(), url1); + QCOMPARE(page.title(), url1.host()); + + page.setUrl(url2); + + QTRY_COMPARE(urlSpy.count(), 1); + QTRY_COMPARE(titleSpy.count(), 2); + QTRY_COMPARE(loadSpy.count(), 1); + + QCOMPARE(urlSpy.takeFirst().value(0).toUrl(), url2); + QCOMPARE(titleSpy.takeFirst().value(0).toString(), url2.authority()); + QCOMPARE(titleSpy.takeFirst().value(0).toString(), url2.host()); + QCOMPARE(loadSpy.takeFirst().value(0).toBool(), false); + + QCOMPARE(page.url(), url2); + QCOMPARE(page.title(), url2.host()); +} + static QStringList collectHistoryUrls(QWebEngineHistory *history) { QStringList urls; @@ -4021,9 +4116,8 @@ void tst_QWebEnginePage::setUrlThenLoads() const QUrl urlToLoad1("qrc:/resources/test2.html"); const QUrl urlToLoad2("qrc:/resources/test1.html"); - // Just after first load. URL didn't changed yet. m_page->load(urlToLoad1); - QCOMPARE(m_page->url(), url); + QCOMPARE(m_page->url(), urlToLoad1); QCOMPARE(m_page->requestedUrl(), urlToLoad1); // baseUrlSync spins an event loop and this sometimes return the next result. // QCOMPARE(baseUrlSync(m_page), baseUrl); @@ -4037,9 +4131,8 @@ void tst_QWebEnginePage::setUrlThenLoads() QCOMPARE(m_page->requestedUrl(), urlToLoad1); QCOMPARE(baseUrlSync(m_page), extractBaseUrl(urlToLoad1)); - // Just after second load. URL didn't changed yet. m_page->load(urlToLoad2); - QCOMPARE(m_page->url(), urlToLoad1); + QCOMPARE(m_page->url(), urlToLoad2); QCOMPARE(m_page->requestedUrl(), urlToLoad2); QCOMPARE(baseUrlSync(m_page), extractBaseUrl(urlToLoad1)); QTRY_COMPARE(startedSpy.count(), 3); @@ -4162,6 +4255,41 @@ void tst_QWebEnginePage::loadInSignalHandlers() QCOMPARE(m_page->url(), urlForSetter); } +void tst_QWebEnginePage::loadFromQrc() +{ + QWebEnginePage page; + QSignalSpy spy(&page, &QWebEnginePage::loadFinished); + + // Standard case. + page.load(QStringLiteral("qrc:///resources/foo.txt")); + QTRY_COMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst().value(0).toBool(), true); + QCOMPARE(toPlainTextSync(&page), QStringLiteral("foo\n")); + + // Query and fragment parts are ignored. + page.load(QStringLiteral("qrc:///resources/bar.txt?foo=1#bar")); + QTRY_COMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst().value(0).toBool(), true); + QCOMPARE(toPlainTextSync(&page), QStringLiteral("bar\n")); + + // Literal spaces are OK. + page.load(QStringLiteral("qrc:///resources/path with spaces.txt")); + QTRY_COMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst().value(0).toBool(), true); + QCOMPARE(toPlainTextSync(&page), QStringLiteral("contents with spaces\n")); + + // Escaped spaces are OK too. + page.load(QStringLiteral("qrc:///resources/path%20with%20spaces.txt")); + QTRY_COMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst().value(0).toBool(), true); + QCOMPARE(toPlainTextSync(&page), QStringLiteral("contents with spaces\n")); + + // Resource not found, loading fails. + page.load(QStringLiteral("qrc:///nope")); + QTRY_COMPARE(spy.count(), 1); + QCOMPARE(spy.takeFirst().value(0).toBool(), false); +} + void tst_QWebEnginePage::restoreHistory() { QWebChannel channel; diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc index 4fddd7a3f..fc83aefa5 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc @@ -19,5 +19,8 @@ <file>resources/test2.html</file> <file>resources/testiframe.html</file> <file>resources/testiframe2.html</file> + <file>resources/foo.txt</file> + <file>resources/bar.txt</file> + <file>resources/path with spaces.txt</file> </qresource> </RCC> diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp index 400105152..294cc52b1 100644 --- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp +++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp @@ -187,6 +187,12 @@ public: setOpenMode(QIODevice::ReadOnly); m_timer.start(100, this); } + void close() override + { + QMutexLocker lock(&m_mutex); + QIODevice::close(); + deleteLater(); + } bool isSequential() const override { return true; } qint64 bytesAvailable() const override { return m_bytesAvailable; } @@ -243,7 +249,7 @@ public: void requestStarted(QWebEngineUrlRequestJob *job) { - job->reply("text/plain;charset=utf-8", new StreamingIODevice(job)); + job->reply("text/plain;charset=utf-8", new StreamingIODevice(this)); } }; diff --git a/tests/auto/widgets/qwebengineview/BLACKLIST b/tests/auto/widgets/qwebengineview/BLACKLIST index a2ecd05b7..0a909d0f6 100644 --- a/tests/auto/widgets/qwebengineview/BLACKLIST +++ b/tests/auto/widgets/qwebengineview/BLACKLIST @@ -1,5 +1,2 @@ [doNotSendMouseKeyboardEventsWhenDisabled] windows - -[inputFieldOverridesShortcuts] -osx diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index f46d9f455..48b12e18b 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp @@ -35,6 +35,7 @@ #include <qtemporarydir.h> #include <QClipboard> #include <QCompleter> +#include <QLabel> #include <QLineEdit> #include <QHBoxLayout> #include <QMenu> @@ -172,6 +173,9 @@ private Q_SLOTS: void imeCompositionQueryEvent_data(); void imeCompositionQueryEvent(); void newlineInTextarea(); + + void mouseLeave(); + #ifndef QT_NO_CLIPBOARD void globalMouseSelection(); #endif @@ -1487,7 +1491,7 @@ void tst_QWebEngineView::inputFieldOverridesShortcuts() view.addAction(action); QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); - view.setHtml(QString("<html><body onload=\"input1=document.getElementById('input1')\">" + view.setHtml(QString("<html><body>" "<button id=\"btn1\" type=\"button\">push it real good</button>" "<input id=\"input1\" type=\"text\" value=\"x\">" "</body></html>")); @@ -1498,7 +1502,7 @@ void tst_QWebEngineView::inputFieldOverridesShortcuts() auto inputFieldValue = [&view] () -> QString { return evaluateJavaScriptSync(view.page(), - "input1.value").toString(); + "document.getElementById('input1').value").toString(); }; // The input form is not focused. The action is triggered on pressing Shift+Delete. @@ -1515,10 +1519,13 @@ void tst_QWebEngineView::inputFieldOverridesShortcuts() QCOMPARE(inputFieldValue(), QString("x")); // The input form is focused. The action is not triggered, and the form's text changed. - evaluateJavaScriptSync(view.page(), "input1.focus();"); + evaluateJavaScriptSync(view.page(), "document.getElementById('input1').focus();"); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input1")); actionTriggered = false; QTest::keyClick(view.windowHandle(), Qt::Key_Y); QTRY_COMPARE(inputFieldValue(), QString("yx")); + QTest::keyClick(view.windowHandle(), Qt::Key_X); + QTRY_COMPARE(inputFieldValue(), QString("yxx")); QVERIFY(!actionTriggered); // The input form is focused. Make sure we don't override all short cuts. @@ -1526,10 +1533,20 @@ void tst_QWebEngineView::inputFieldOverridesShortcuts() action->setShortcut(Qt::CTRL + Qt::Key_1); QTest::keyClick(view.windowHandle(), Qt::Key_1, Qt::ControlModifier); QTRY_VERIFY(actionTriggered); - QCOMPARE(inputFieldValue(), QString("yx")); + QCOMPARE(inputFieldValue(), QString("yxx")); + + // The input form is focused. The following shortcuts are not overridden + // thus handled by Qt WebEngine. Make sure the subsequent shortcuts with text + // character don't cause assert due to an unconsumed editor command. + QTest::keyClick(view.windowHandle(), Qt::Key_A, Qt::ControlModifier); + QTest::keyClick(view.windowHandle(), Qt::Key_C, Qt::ControlModifier); + QTest::keyClick(view.windowHandle(), Qt::Key_V, Qt::ControlModifier); + QTest::keyClick(view.windowHandle(), Qt::Key_V, Qt::ControlModifier); + QTRY_COMPARE(inputFieldValue(), QString("yxxyxx")); // Remove focus from the input field. A QKeySequence::Copy action must be triggerable. evaluateJavaScriptSync(view.page(), "document.getElementById('btn1').focus();"); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("btn1")); action->setShortcut(QKeySequence::Copy); actionTriggered = false; QTest::keyClick(view.windowHandle(), Qt::Key_C, Qt::ControlModifier); @@ -1824,13 +1841,8 @@ void tst_QWebEngineView::textSelectionOutOfInputField() QVERIFY(!view.hasSelection()); QVERIFY(view.page()->selectedText().isEmpty()); - // Workaround for macOS: press ctrl+a without key text - QKeyEvent keyPressCtrlA(QEvent::KeyPress, Qt::Key_A, Qt::ControlModifier); - QKeyEvent keyReleaseCtrlA(QEvent::KeyRelease, Qt::Key_A, Qt::ControlModifier); - // Select text by ctrl+a - QApplication::sendEvent(view.focusProxy(), &keyPressCtrlA); - QApplication::sendEvent(view.focusProxy(), &keyReleaseCtrlA); + QTest::keyClick(view.windowHandle(), Qt::Key_A, Qt::ControlModifier); QVERIFY(selectionChangedSpy.wait()); QCOMPARE(selectionChangedSpy.count(), 1); QVERIFY(view.hasSelection()); @@ -1860,8 +1872,7 @@ void tst_QWebEngineView::textSelectionOutOfInputField() QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); // Select the whole page by ctrl+a - QApplication::sendEvent(view.focusProxy(), &keyPressCtrlA); - QApplication::sendEvent(view.focusProxy(), &keyReleaseCtrlA); + QTest::keyClick(view.windowHandle(), Qt::Key_A, Qt::ControlModifier); QVERIFY(selectionChangedSpy.wait()); QCOMPARE(selectionChangedSpy.count(), 1); QVERIFY(view.hasSelection()); @@ -1877,8 +1888,7 @@ void tst_QWebEngineView::textSelectionOutOfInputField() QVERIFY(view.page()->selectedText().isEmpty()); // Select the content of the input field by ctrl+a - QApplication::sendEvent(view.focusProxy(), &keyPressCtrlA); - QApplication::sendEvent(view.focusProxy(), &keyReleaseCtrlA); + QTest::keyClick(view.windowHandle(), Qt::Key_A, Qt::ControlModifier); QVERIFY(selectionChangedSpy.wait()); QCOMPARE(selectionChangedSpy.count(), 3); QVERIFY(view.hasSelection()); @@ -1934,10 +1944,9 @@ void tst_QWebEngineView::emptyInputMethodEvent() // 1. Empty input method event does not clear text QInputMethodEvent emptyEvent; - QApplication::sendEvent(view.focusProxy(), &emptyEvent); - - QString inputValue = evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(); - QTRY_COMPARE(inputValue, QStringLiteral("QtWebEngine")); + QVERIFY(QApplication::sendEvent(view.focusProxy(), &emptyEvent)); + qApp->processEvents(); + QCOMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), QStringLiteral("QtWebEngine")); QTRY_COMPARE(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString(), QStringLiteral("QtWebEngine")); // Reset: clear input field @@ -1949,12 +1958,12 @@ void tst_QWebEngineView::emptyInputMethodEvent() // Start IME composition QList<QInputMethodEvent::Attribute> attributes; QInputMethodEvent eventComposition("a", attributes); - QApplication::sendEvent(view.focusProxy(), &eventComposition); + QVERIFY(QApplication::sendEvent(view.focusProxy(), &eventComposition)); QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString(), QStringLiteral("a")); QTRY_VERIFY(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString().isEmpty()); // Cancel IME composition - QApplication::sendEvent(view.focusProxy(), &emptyEvent); + QVERIFY(QApplication::sendEvent(view.focusProxy(), &emptyEvent)); QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString().isEmpty()); QTRY_VERIFY(view.focusProxy()->inputMethodQuery(Qt::ImSurroundingText).toString().isEmpty()); @@ -2417,5 +2426,57 @@ void tst_QWebEngineView::contextMenu() QTRY_COMPARE(view.findChildren<QMenu *>().count(), childrenCount); } +void tst_QWebEngineView::mouseLeave() +{ + QScopedPointer<QWidget> containerWidget(new QWidget); + + QLabel *label = new QLabel(containerWidget.data()); + label->setStyleSheet("background-color: red;"); + label->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); + label->setMinimumHeight(100); + + QWebEngineView *view = new QWebEngineView(containerWidget.data()); + view->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); + view->setMinimumHeight(100); + + QVBoxLayout *layout = new QVBoxLayout; + layout->setAlignment(Qt::AlignTop); + layout->setSpacing(0); + layout->setMargin(0); + layout->addWidget(label); + layout->addWidget(view); + containerWidget->setLayout(layout); + containerWidget->show(); + QVERIFY(QTest::qWaitForWindowExposed(containerWidget.data())); + QTest::mouseMove(containerWidget->windowHandle(), QPoint(0, 0)); + + auto innerText = [view]() -> QString { + return evaluateJavaScriptSync(view->page(), "document.getElementById('testDiv').innerText").toString(); + }; + + QSignalSpy loadFinishedSpy(view, SIGNAL(loadFinished(bool))); + view->setHtml("<html>" + "<head><script>" + "function init() {" + " var div = document.getElementById('testDiv');" + " div.onmouseenter = function(e) { div.innerText = 'Mouse IN' };" + " div.onmouseleave = function(e) { div.innerText = 'Mouse OUT' };" + "}" + "</script></head>" + "<body onload='init()' style='margin: 0px; padding: 0px'>" + " <div id='testDiv' style='width: 100%; height: 100%; background-color: green' />" + "</body>" + "</html>"); + QVERIFY(loadFinishedSpy.wait()); + // Make sure the testDiv text is empty. + evaluateJavaScriptSync(view->page(), "document.getElementById('testDiv').innerText = ''"); + QTRY_VERIFY(innerText().isEmpty()); + + QTest::mouseMove(containerWidget->windowHandle(), QPoint(50, 150)); + QTRY_COMPARE(innerText(), QStringLiteral("Mouse IN")); + QTest::mouseMove(containerWidget->windowHandle(), QPoint(50, 50)); + QTRY_COMPARE(innerText(), QStringLiteral("Mouse OUT")); +} + QTEST_MAIN(tst_QWebEngineView) #include "tst_qwebengineview.moc" diff --git a/tests/auto/widgets/widgets.pro b/tests/auto/widgets/widgets.pro index fbabdeaad..28f8f56c1 100644 --- a/tests/auto/widgets/widgets.pro +++ b/tests/auto/widgets/widgets.pro @@ -20,8 +20,8 @@ qtConfig(accessibility) { SUBDIRS += qwebengineaccessibility } -qtConfig(spellchecker):!cross_compile { - !qtConfig(native-spellchecker) { +qtConfig(webengine-spellchecker):!cross_compile { + !qtConfig(webengine-native-spellchecker) { SUBDIRS += qwebenginespellcheck } else { message("Spellcheck test will not be built because it depends on usage of Hunspell dictionaries.") |