From 8a0090f31513e36384c2135e861033ceb2dfd3a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCri=20Valdmann?= Date: Wed, 22 Jan 2020 13:48:09 +0100 Subject: Add more tests for profile settings Add tests for changing httpUserAgent, httpAcceptLanguage, and persistentCookiesPolicy. Use local HttpServer instead of network in existing tests. Stabilize disableCache test and unblacklist. Stop actually downloading the test binary in downloadItem test. Register 'myscheme' to avoid warning from QWebEngineUrlScheme. Task-number: QTBUG-81558 Change-Id: I3178edd1eb241257e211855168ec4ca428a90d29 Reviewed-by: Allan Sandfeld Jensen --- tests/auto/widgets/qwebengineprofile/BLACKLIST | 3 - .../qwebengineprofile/qwebengineprofile.pro | 1 + .../qwebengineprofile/resources/hedgehog.html | 9 + .../qwebengineprofile/resources/hedgehog.png | Bin 0 -> 11273 bytes .../qwebengineprofile/tst_qwebengineprofile.cpp | 302 +++++++++++++++------ 5 files changed, 236 insertions(+), 79 deletions(-) delete mode 100644 tests/auto/widgets/qwebengineprofile/BLACKLIST create mode 100644 tests/auto/widgets/qwebengineprofile/resources/hedgehog.html create mode 100644 tests/auto/widgets/qwebengineprofile/resources/hedgehog.png diff --git a/tests/auto/widgets/qwebengineprofile/BLACKLIST b/tests/auto/widgets/qwebengineprofile/BLACKLIST deleted file mode 100644 index 55806eec4..000000000 --- a/tests/auto/widgets/qwebengineprofile/BLACKLIST +++ /dev/null @@ -1,3 +0,0 @@ -[disableCache] -* - diff --git a/tests/auto/widgets/qwebengineprofile/qwebengineprofile.pro b/tests/auto/widgets/qwebengineprofile/qwebengineprofile.pro index e56bbe8f7..ca16cee39 100644 --- a/tests/auto/widgets/qwebengineprofile/qwebengineprofile.pro +++ b/tests/auto/widgets/qwebengineprofile/qwebengineprofile.pro @@ -1,3 +1,4 @@ include(../tests.pri) +include(../../shared/http.pri) exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc QT *= core-private gui-private diff --git a/tests/auto/widgets/qwebengineprofile/resources/hedgehog.html b/tests/auto/widgets/qwebengineprofile/resources/hedgehog.html new file mode 100644 index 000000000..d8abbcd48 --- /dev/null +++ b/tests/auto/widgets/qwebengineprofile/resources/hedgehog.html @@ -0,0 +1,9 @@ + + + + BREAKING NEWS: 15 Hedgehogs With Things That Look Like Hedgehogs + + + + + diff --git a/tests/auto/widgets/qwebengineprofile/resources/hedgehog.png b/tests/auto/widgets/qwebengineprofile/resources/hedgehog.png new file mode 100644 index 000000000..4d56d8633 Binary files /dev/null and b/tests/auto/widgets/qwebengineprofile/resources/hedgehog.png differ diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp index a7a5ba62a..128cf7ab9 100644 --- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp +++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp @@ -28,6 +28,7 @@ #include "../util.h" #include +#include #include #include #include @@ -44,6 +45,9 @@ #include #endif +#include +#include + #include #include @@ -71,6 +75,10 @@ private Q_SLOTS: void httpAcceptLanguage(); void downloadItem(); void changePersistentPath(); + void changeHttpUserAgent(); + void changeHttpAcceptLanguage(); + void changeUseForGlobalCertificateVerification(); + void changePersistentCookiesPolicy(); void initiator(); void badDeleteOrder(); void qtbug_71895(); // this should be the last test @@ -82,6 +90,7 @@ void tst_QWebEngineProfile::initTestCase() QWebEngineUrlScheme stream("stream"); QWebEngineUrlScheme letterto("letterto"); QWebEngineUrlScheme aviancarrier("aviancarrier"); + QWebEngineUrlScheme myscheme("myscheme"); foo.setSyntax(QWebEngineUrlScheme::Syntax::Host); stream.setSyntax(QWebEngineUrlScheme::Syntax::HostAndPort); stream.setDefaultPort(8080); @@ -92,6 +101,7 @@ void tst_QWebEngineProfile::initTestCase() QWebEngineUrlScheme::registerScheme(stream); QWebEngineUrlScheme::registerScheme(letterto); QWebEngineUrlScheme::registerScheme(aviancarrier); + QWebEngineUrlScheme::registerScheme(myscheme); } void tst_QWebEngineProfile::init() @@ -151,75 +161,126 @@ void tst_QWebEngineProfile::testProfile() + QStringLiteral("/QtWebEngine/Test")); } -void tst_QWebEngineProfile::clearDataFromCache() +class AutoDir : public QDir { - QWebEnginePage page; +public: + AutoDir(const QString &p) : QDir(p) + { + makeAbsolute(); + removeRecursively(); + } - QDir cacheDir("./tst_QWebEngineProfile_cacheDir"); - cacheDir.makeAbsolute(); - if (cacheDir.exists()) - cacheDir.removeRecursively(); - cacheDir.mkpath(cacheDir.path()); + ~AutoDir() { removeRecursively(); } +}; - QWebEngineProfile *profile = page.profile(); - profile->setCachePath(cacheDir.path()); - profile->setHttpCacheType(QWebEngineProfile::DiskHttpCache); +qint64 totalSize(QDir dir) +{ + qint64 sum = 0; + const QDir::Filters filters{QDir::Dirs, QDir::Files, QDir::NoSymLinks, QDir::NoDotAndDotDot}; + for (const QFileInfo &entry : dir.entryInfoList(filters)) { + if (entry.isFile()) + sum += entry.size(); + else if (entry.isDir()) + sum += totalSize(entry.filePath()); + } + return sum; +} - QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); - page.load(QUrl("http://qt-project.org")); - if (!loadFinishedSpy.wait(10000) || !loadFinishedSpy.at(0).at(0).toBool()) - QSKIP("Couldn't load page from network, skipping test."); +class TestServer : public HttpServer +{ +public: + TestServer() + { + connect(this, &HttpServer::newRequest, this, &TestServer::onNewRequest); + } - cacheDir.refresh(); - QVERIFY(cacheDir.entryList().contains("Cache")); - cacheDir.cd("./Cache"); - int filesBeforeClear = cacheDir.entryList().count(); +private: + void onNewRequest(HttpReqRep *rr) + { + const QDir resourceDir(TESTS_SOURCE_DIR "qwebengineprofile/resources"); + QString path = rr->requestPath(); + path.remove(0, 1); + + if (rr->requestMethod() != "GET" || !resourceDir.exists(path)) + { + rr->setResponseStatus(404); + rr->sendResponse(); + return; + } - QFileSystemWatcher fileSystemWatcher; - fileSystemWatcher.addPath(cacheDir.path()); - QSignalSpy directoryChangedSpy(&fileSystemWatcher, SIGNAL(directoryChanged(const QString &))); + QFile file(resourceDir.filePath(path)); + file.open(QIODevice::ReadOnly); + QByteArray data = file.readAll(); + rr->setResponseBody(data); + QMimeDatabase db; + QMimeType mime = db.mimeTypeForFileNameAndData(file.fileName(), data); + rr->setResponseHeader(QByteArrayLiteral("content-type"), mime.name().toUtf8()); + if (!mime.inherits("text/html")) + rr->setResponseHeader(QByteArrayLiteral("cache-control"), + QByteArrayLiteral("public, max-age=31536000")); + rr->sendResponse(); + } +}; - // It deletes most of the files, but not all of them. - profile->clearHttpCache(); - QTest::qWait(1000); - QTRY_VERIFY(directoryChangedSpy.count() > 0); +static bool loadSync(QWebEnginePage *page, const QUrl &url, bool ok = true) +{ + QSignalSpy spy(page, &QWebEnginePage::loadFinished); + page->load(url); + return (!spy.empty() || spy.wait(20000)) && (spy.front().value(0).toBool() == ok); +} + +static bool loadSync(QWebEngineView *view, const QUrl &url, bool ok = true) +{ + return loadSync(view->page(), url, ok); +} + +void tst_QWebEngineProfile::clearDataFromCache() +{ + TestServer server; + QVERIFY(server.start()); + + AutoDir cacheDir("./tst_QWebEngineProfile_clearDataFromCache"); - cacheDir.refresh(); - QVERIFY(filesBeforeClear > cacheDir.entryList().count()); + QWebEngineProfile profile(QStringLiteral("Test")); + profile.setCachePath(cacheDir.path()); + profile.setHttpCacheType(QWebEngineProfile::DiskHttpCache); + + QWebEnginePage page(&profile); + QVERIFY(loadSync(&page, server.url("/hedgehog.html"))); - cacheDir.removeRecursively(); + QVERIFY(cacheDir.exists("Cache")); + qint64 sizeBeforeClear = totalSize(cacheDir); + profile.clearHttpCache(); + // Wait for cache to be cleared. + QTest::qWait(1000); + QVERIFY(sizeBeforeClear > totalSize(cacheDir)); + + QVERIFY(server.stop()); } void tst_QWebEngineProfile::disableCache() { - QWebEnginePage page; - QDir cacheDir("./tst_QWebEngineProfile_cacheDir"); - if (cacheDir.exists()) - cacheDir.removeRecursively(); - cacheDir.mkpath(cacheDir.path()); + TestServer server; + QVERIFY(server.start()); + AutoDir cacheDir("./tst_QWebEngineProfile_disableCache"); + + QWebEnginePage page; QWebEngineProfile *profile = page.profile(); profile->setCachePath(cacheDir.path()); - QVERIFY(!cacheDir.entryList().contains("Cache")); + QVERIFY(!cacheDir.exists("Cache")); profile->setHttpCacheType(QWebEngineProfile::NoCache); - QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); - page.load(QUrl("http://qt-project.org")); - if (!loadFinishedSpy.wait(10000) || !loadFinishedSpy.at(0).at(0).toBool()) - QSKIP("Couldn't load page from network, skipping test."); - - cacheDir.refresh(); - QVERIFY(!cacheDir.entryList().contains("Cache")); + // Wait for cache to be cleared. + QTest::qWait(1000); + QVERIFY(loadSync(&page, server.url("/hedgehog.html"))); + QVERIFY(!cacheDir.exists("Cache")); profile->setHttpCacheType(QWebEngineProfile::DiskHttpCache); - page.load(QUrl("http://qt-project.org")); - if (!loadFinishedSpy.wait(10000) || !loadFinishedSpy.at(1).at(0).toBool()) - QSKIP("Couldn't load page from network, skipping test."); - - cacheDir.refresh(); - QVERIFY(cacheDir.entryList().contains("Cache")); + QVERIFY(loadSync(&page, server.url("/hedgehog.html"))); + QVERIFY(cacheDir.exists("Cache")); - cacheDir.removeRecursively(); + QVERIFY(server.stop()); } class RedirectingUrlSchemeHandler : public QWebEngineUrlSchemeHandler @@ -331,21 +392,6 @@ public: } }; -static bool loadSync(QWebEngineView *view, const QUrl &url, int timeout = 5000) -{ - // Ripped off QTRY_VERIFY. - QSignalSpy loadFinishedSpy(view, SIGNAL(loadFinished(bool))); - view->load(url); - if (loadFinishedSpy.isEmpty()) - QTest::qWait(0); - for (int i = 0; i < timeout; i += 50) { - if (!loadFinishedSpy.isEmpty()) - return true; - QTest::qWait(50); - } - return false; -} - void tst_QWebEngineProfile::urlSchemeHandlers() { RedirectingUrlSchemeHandler lettertoHandler; @@ -368,7 +414,7 @@ void tst_QWebEngineProfile::urlSchemeHandlers() // Remove the letterto scheme, and check whether it is not handled anymore. profile.removeUrlScheme("letterto"); emailAddress = QStringLiteral("kjeld@olsen-banden.dk"); - QVERIFY(loadSync(&view, QUrl(QStringLiteral("letterto:") + emailAddress))); + QVERIFY(loadSync(&view, QUrl(QStringLiteral("letterto:") + emailAddress), false)); QVERIFY(toPlainTextSync(view.page()) != emailAddress); // Check if gopher is still working after removing letterto. @@ -379,7 +425,7 @@ void tst_QWebEngineProfile::urlSchemeHandlers() // Does removeAll work? profile.removeAllUrlSchemeHandlers(); url = QUrl(QStringLiteral("gopher://olsen-banden.dk/harry")); - QVERIFY(loadSync(&view, url)); + QVERIFY(loadSync(&view, url, false)); QVERIFY(toPlainTextSync(view.page()) != url.toString()); // Install a handler that is owned by the view. Make sure this doesn't crash on shutdown. @@ -775,28 +821,132 @@ void tst_QWebEngineProfile::downloadItem() QWebEngineProfile testProfile; QWebEnginePage page(&testProfile); QSignalSpy downloadSpy(&testProfile, SIGNAL(downloadRequested(QWebEngineDownloadItem *))); - connect(&testProfile, &QWebEngineProfile::downloadRequested, this, [=] (QWebEngineDownloadItem *item) { item->accept(); }); page.load(QUrl::fromLocalFile(QCoreApplication::applicationFilePath())); QTRY_COMPARE(downloadSpy.count(), 1); } void tst_QWebEngineProfile::changePersistentPath() { + TestServer server; + QVERIFY(server.start()); + + AutoDir dataDir1(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + + QStringLiteral("/QtWebEngine/Test")); + AutoDir dataDir2(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + + QStringLiteral("/QtWebEngine/Test2")); + QWebEngineProfile testProfile(QStringLiteral("Test")); - const QString oldPath = testProfile.persistentStoragePath(); - QVERIFY(oldPath.endsWith(QStringLiteral("Test"))); + QCOMPARE(testProfile.persistentStoragePath(), dataDir1.path()); - // Make sure the profile has been used and the url-request-context-getter instantiated: + // Make sure the profile has been used: QWebEnginePage page(&testProfile); - QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); - page.load(QUrl("http://qt-project.org")); - if (!loadFinishedSpy.wait(10000) || !loadFinishedSpy.at(0).at(0).toBool()) - QSKIP("Couldn't load page from network, skipping test."); + QVERIFY(loadSync(&page, server.url("/hedgehog.html"))); // Test we do not crash (QTBUG-55322): - testProfile.setPersistentStoragePath(oldPath + QLatin1Char('2')); - const QString newPath = testProfile.persistentStoragePath(); - QVERIFY(newPath.endsWith(QStringLiteral("Test2"))); + testProfile.setPersistentStoragePath(dataDir2.path()); + QCOMPARE(testProfile.persistentStoragePath(), dataDir2.path()); + QVERIFY(loadSync(&page, server.url("/hedgehog.html"))); + QVERIFY(dataDir2.exists()); + + QVERIFY(server.stop()); +} + +void tst_QWebEngineProfile::changeHttpUserAgent() +{ + TestServer server; + QVERIFY(server.start()); + + QVector userAgents; + connect(&server, &HttpServer::newRequest, [&](HttpReqRep *rr) { + if (rr->requestPath() == "/hedgehog.html") + userAgents.push_back(rr->requestHeader(QByteArrayLiteral("user-agent"))); + }); + + QWebEngineProfile profile(QStringLiteral("Test")); + std::unique_ptr page; + page.reset(new QWebEnginePage(&profile)); + QVERIFY(loadSync(page.get(), server.url("/hedgehog.html"))); + page.reset(); + profile.setHttpUserAgent("webturbine/42"); + page.reset(new QWebEnginePage(&profile)); + QVERIFY(loadSync(page.get(), server.url("/hedgehog.html"))); + + QCOMPARE(userAgents.size(), 2); + QCOMPARE(userAgents[1], "webturbine/42"); + QVERIFY(userAgents[0] != userAgents[1]); + + QVERIFY(server.stop()); +} + +void tst_QWebEngineProfile::changeHttpAcceptLanguage() +{ + TestServer server; + QVERIFY(server.start()); + + QVector languages; + connect(&server, &HttpServer::newRequest, [&](HttpReqRep *rr) { + if (rr->requestPath() == "/hedgehog.html") + languages.push_back(rr->requestHeader(QByteArrayLiteral("accept-language"))); + }); + + QWebEngineProfile profile(QStringLiteral("Test")); + std::unique_ptr page; + page.reset(new QWebEnginePage(&profile)); + QVERIFY(loadSync(page.get(), server.url("/hedgehog.html"))); + page.reset(); + profile.setHttpAcceptLanguage("fi"); + page.reset(new QWebEnginePage(&profile)); + QVERIFY(loadSync(page.get(), server.url("/hedgehog.html"))); + + QCOMPARE(languages.size(), 2); + QCOMPARE(languages[1], "fi"); + QVERIFY(languages[0] != languages[1]); + + QVERIFY(server.stop()); +} + +void tst_QWebEngineProfile::changeUseForGlobalCertificateVerification() +{ + QSKIP("Needs 3rdparty fix"); + + TestServer server; + QVERIFY(server.start()); + + // Check that we don't crash + + QWebEngineProfile profile(QStringLiteral("Test")); + std::unique_ptr page; + page.reset(new QWebEnginePage(&profile)); + QVERIFY(loadSync(page.get(), server.url("/hedgehog.html"))); + page.reset(); + profile.setUseForGlobalCertificateVerification(true); + page.reset(new QWebEnginePage(&profile)); + QVERIFY(loadSync(page.get(), server.url("/hedgehog.html"))); + QVERIFY(server.stop()); +} + +void tst_QWebEngineProfile::changePersistentCookiesPolicy() +{ + TestServer server; + QVERIFY(server.start()); + + AutoDir dataDir("./tst_QWebEngineProfile_dataDir"); + + QWebEngineProfile profile(QStringLiteral("Test")); + QWebEnginePage page(&profile); + + profile.setPersistentStoragePath(dataDir.path()); + profile.setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies); + + QVERIFY(loadSync(&page, server.url("/hedgehog.html"))); + QVERIFY(!dataDir.exists("Cookies")); + + profile.setPersistentCookiesPolicy(QWebEngineProfile::ForcePersistentCookies); + + QVERIFY(loadSync(&page, server.url("/hedgehog.html"))); + QVERIFY(dataDir.exists("Cookies")); + + QVERIFY(server.stop()); } class InitiatorSpy : public QWebEngineUrlSchemeHandler -- cgit v1.2.3 From 97bfaef8a39419d9524ef6ebbd073e5aee9c9c7d Mon Sep 17 00:00:00 2001 From: Michal Klocek Date: Thu, 13 Feb 2020 17:26:07 +0100 Subject: Fix docs for page url request interceptors Profile interceptors run also on ui thread. Change-Id: Iacfce46549e7ffd821033308077ba5f4fa410575 Reviewed-by: Allan Sandfeld Jensen --- src/webenginewidgets/api/qwebenginepage.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index 33a7721e7..850a29a16 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -1870,13 +1870,12 @@ void QWebEnginePagePrivate::visibleChanged(bool visible) Registers the request interceptor \a interceptor to intercept URL requests. The page does not take ownership of the pointer. This interceptor is called - after any interceptors on the profile, and unlike profile interceptors, is run - on the UI thread, making it thread-safer. Only URL requests from this page are - intercepted. + after any interceptors on the profile, and unlike profile interceptors, only + URL requests from this page are intercepted. To unset the request interceptor, set a \c nullptr. - \sa QWebEngineUrlRequestInfo, QWebEngineProfile::setRequestInterceptor() + \sa QWebEngineUrlRequestInfo, QWebEngineProfile::setUrlRequestInterceptor() */ void QWebEnginePage::setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) -- cgit v1.2.3 From 9691111bcb359fe39e97e13820b98385657ae03a Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 2 Dec 2019 21:36:46 -0800 Subject: Fix build: remove dependency on Qt private header The size of QJsonDocument's binary format is well known and we don't need the header to know what it is. This fixes the build with 5.15, where the contents of the previous QJsonPrivate namespace are now in QBinaryJsonPrivate. web_channel_ipc_transport_host.cpp:148:51: error: 'Header' is not a member of 'QJsonPrivate' Change-Id: Id7decde0c426479bbf61fffd15dcc5c20a9eca2c Reviewed-by: Allan Sandfeld Jensen --- src/core/renderer_host/web_channel_ipc_transport_host.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/core/renderer_host/web_channel_ipc_transport_host.cpp b/src/core/renderer_host/web_channel_ipc_transport_host.cpp index b1aab00a1..9758f73bf 100644 --- a/src/core/renderer_host/web_channel_ipc_transport_host.cpp +++ b/src/core/renderer_host/web_channel_ipc_transport_host.cpp @@ -52,10 +52,13 @@ #include #include -#include - namespace QtWebEngineCore { +enum { + // sizeof(QJsonPrivate::Header) + sizeof(QJsonPrivate::Base) + MinimumBinaryJsonSize = 8 + 12 +}; + Q_LOGGING_CATEGORY(log, "qt.webengine.webchanneltransport") inline QDebug operator<<(QDebug stream, content::RenderFrameHost *frame) @@ -145,7 +148,7 @@ void WebChannelIPCTransportHost::DispatchWebChannelMessage(const std::vector= sizeof(QJsonPrivate::Header) + sizeof(QJsonPrivate::Base)) + if (binaryJson.size() >= MinimumBinaryJsonSize) doc = QJsonDocument::fromRawData(reinterpret_cast(binaryJson.data()), binaryJson.size()); -- cgit v1.2.3 From db777c7bde2ad075f2a76cf6d5e9c20b04d62a2d Mon Sep 17 00:00:00 2001 From: Peter Varga Date: Fri, 21 Feb 2020 11:11:59 +0100 Subject: Fix event.key for Ctrl key combinations on Windows Fixes: QTBUG-81783 Change-Id: I107a4009630dc261013498a05987c0e8e29651eb Reviewed-by: Allan Sandfeld Jensen --- src/core/web_event_factory.cpp | 7 +++- .../widgets/qwebengineview/tst_qwebengineview.cpp | 40 ++++++++++++++++------ 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/core/web_event_factory.cpp b/src/core/web_event_factory.cpp index ba04806d5..f4940f305 100644 --- a/src/core/web_event_factory.cpp +++ b/src/core/web_event_factory.cpp @@ -157,8 +157,11 @@ static Qt::KeyboardModifiers qtModifiersForEvent(const QInputEvent *ev) // // On Linux, the Control modifier transformation is applied [1]. For example, // pressing Ctrl+@ generates the text "\u0000". We would like "@" instead. +// Windows also translates some control key combinations into ASCII control +// characters [2]. // // [1]: https://www.x.org/releases/current/doc/kbproto/xkbproto.html#Interpreting_the_Control_Modifier +// [2]: https://docs.microsoft.com/en-us/windows/win32/learnwin32/keyboard-input#character-messages // // On macOS, if the Control modifier is used, then no text is generated at all. // We need some text. @@ -171,8 +174,10 @@ static QString qtTextForKeyEvent(const QKeyEvent *ev, int qtKey, Qt::KeyboardMod { QString text = ev->text(); - if ((qtModifiers & Qt::ControlModifier) && keyboardDriver() == KeyboardDriver::Xkb) + if ((qtModifiers & Qt::ControlModifier) && + (keyboardDriver() == KeyboardDriver::Xkb || keyboardDriver() == KeyboardDriver::Windows)) { text.clear(); + } return text; } diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index b5f038bba..2862be1dd 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp @@ -199,6 +199,7 @@ private Q_SLOTS: void visibilityState(); void visibilityState2(); void visibilityState3(); + void jsKeyboardEvent_data(); void jsKeyboardEvent(); void deletePage(); void closeOpenerTab(); @@ -3364,6 +3365,28 @@ void tst_QWebEngineView::visibilityState3() QCOMPARE(evaluateJavaScriptSync(&page2, "document.visibilityState").toString(), QStringLiteral("visible")); } +void tst_QWebEngineView::jsKeyboardEvent_data() +{ + QTest::addColumn("key"); + QTest::addColumn("modifiers"); + QTest::addColumn("expected"); + +#if defined(Q_OS_MACOS) + // See Qt::AA_MacDontSwapCtrlAndMeta + Qt::KeyboardModifiers controlModifier = Qt::MetaModifier; +#else + Qt::KeyboardModifiers controlModifier = Qt::ControlModifier; +#endif + + QTest::newRow("Ctrl+Shift+A") << 'A' << (controlModifier | Qt::ShiftModifier) << QStringLiteral( + "16,ShiftLeft,Shift,false,true,false;" + "17,ControlLeft,Control,true,true,false;" + "65,KeyA,A,true,true,false;"); + QTest::newRow("Ctrl+z") << 'z' << controlModifier << QStringLiteral( + "17,ControlLeft,Control,true,false,false;" + "90,KeyZ,z,true,false,false;"); +} + void tst_QWebEngineView::jsKeyboardEvent() { QWebEngineView view; @@ -3373,18 +3396,13 @@ void tst_QWebEngineView::jsKeyboardEvent() "addEventListener('keydown', (ev) => {" " log += [ev.keyCode, ev.code, ev.key, ev.ctrlKey, ev.shiftKey, ev.altKey].join(',') + ';';" "});"); + + QFETCH(char, key); + QFETCH(Qt::KeyboardModifiers, modifiers); + QFETCH(QString, expected); + // Note that this only tests the fallback code path where native scan codes are not used. -#if defined(Q_OS_MACOS) - // See Qt::AA_MacDontSwapCtrlAndMeta - QTest::keyClick(view.focusProxy(), 'A', Qt::MetaModifier | Qt::ShiftModifier); -#else - QTest::keyClick(view.focusProxy(), 'A', Qt::ControlModifier | Qt::ShiftModifier); -#endif - QString expected = QStringLiteral( - "16,ShiftLeft,Shift,false,true,false;" - "17,ControlLeft,Control,true,true,false;" - "65,KeyA,A,true,true,false;" - ); + QTest::keyClick(view.focusProxy(), key, modifiers); QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "log") != QVariant(QString())); QCOMPARE(evaluateJavaScriptSync(view.page(), "log"), expected); } -- cgit v1.2.3 From 08d1d866107b82525f5c1ec83f179c58ee7e6b97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCri=20Valdmann?= Date: Tue, 25 Feb 2020 12:37:17 +0100 Subject: Update Chromium This pulls in the following changes: c1be521d4b0 Create an AudioOutputIPCFactory even without WebRTC 35b6d2c4838 third_party perfetto: add missing include for clang, asan and no_pch 6c2cf4c4571 Fix access after move 86de069171e FIXUP: Fix building with g++ 5 5c2d377121c Suppress racy DCHECK 458aa4294db Fix generation of attribution documentation a370b2f7a7e Don't force gpu process launch on macOS with vizdc and no GL 5b79320c013 Expose StoragePartitionImpl::InitNetworkContext bbc3a3082b4 [Backport] Fix input spinner double-increment. 432e1a9b1a0 Suppress DCHECK triggered by NGInlineNode::ComputeMinMaxSize caa20eed16d Add explicit dependencies on spellcheck buildflags c3737fb3824 [Backport] metatrace: remove memset and trivial-ctor assumption 12a57d9c943 Fix recursive deadlock in sandbox::InitLibcLocaltimeFunctions Change-Id: Id06aa2d5a148d3805ebd172ab21db2400f78f19a Reviewed-by: Allan Sandfeld Jensen --- src/3rdparty | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/3rdparty b/src/3rdparty index a9a20127e..12a57d9c9 160000 --- a/src/3rdparty +++ b/src/3rdparty @@ -1 +1 @@ -Subproject commit a9a20127e8adeb3f3cd7921b0bec32083103cc5c +Subproject commit 12a57d9c943eaa80d87481712fe58f7bf6678ba2 -- cgit v1.2.3 From df5d831bae99662fab43ed2628187113c18aac2c Mon Sep 17 00:00:00 2001 From: Kirill Burtsev Date: Wed, 12 Feb 2020 16:15:34 +0100 Subject: Clear previous page text selection on new navigation unconditionally Remove code duplication on triggering new url load, and use direct code to clear SelectedText instead of CollapseSelection as it assumes focused frame and might be ignored. Fixes: QTBUG-81574 Change-Id: I01cf02967e118f407c8a3997e176d5b258478a5a Reviewed-by: Peter Varga --- src/core/web_contents_adapter.cpp | 46 +++++++++++++++------- src/core/web_contents_adapter.h | 1 + .../widgets/qwebenginepage/tst_qwebenginepage.cpp | 46 +++++++++++++--------- 3 files changed, 61 insertions(+), 32 deletions(-) diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp index 8cc8179cf..a7579f916 100644 --- a/src/core/web_contents_adapter.cpp +++ b/src/core/web_contents_adapter.cpp @@ -67,6 +67,7 @@ #include "base/task/sequence_manager/thread_controller_with_message_pump_impl.h" #include "base/values.h" #include "content/browser/renderer_host/render_view_host_impl.h" +#include "content/browser/renderer_host/text_input_manager.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/child_process_security_policy.h" @@ -369,6 +370,23 @@ static void deserializeNavigationHistory(QDataStream &input, int *currentIndex, } } +static void Navigate(WebContentsAdapter *adapter, const content::NavigationController::LoadURLParams ¶ms) +{ + Q_ASSERT(adapter); + adapter->webContents()->GetController().LoadURLWithParams(params); + adapter->focusIfNecessary(); + adapter->resetSelection(); +} + +static void NavigateTask(QWeakPointer weakAdapter, const content::NavigationController::LoadURLParams ¶ms) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + const auto adapter = weakAdapter.toStrongRef(); + if (!adapter) + return; + Navigate(adapter.get(), params); +} + namespace { static QList recursive_guard_loading_adapters; @@ -705,21 +723,12 @@ void WebContentsAdapter::load(const QWebEngineHttpRequest &request) } } - auto navigate = [](QWeakPointer weakAdapter, const content::NavigationController::LoadURLParams ¶ms) { - const auto adapter = weakAdapter.toStrongRef(); - if (!adapter) - return; - adapter->webContents()->GetController().LoadURLWithParams(params); - adapter->focusIfNecessary(); - }; - - QWeakPointer weakThis(sharedFromThis()); if (resizeNeeded) { // Schedule navigation on the event loop. base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(navigate, std::move(weakThis), std::move(params))); + base::BindOnce(&NavigateTask, sharedFromThis().toWeakRef(), std::move(params))); } else { - navigate(std::move(weakThis), params); + Navigate(this, params); } } @@ -752,9 +761,7 @@ void WebContentsAdapter::setContent(const QByteArray &data, const QString &mimeT params.can_load_local_resources = true; params.transition_type = ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_API); params.override_user_agent = content::NavigationController::UA_OVERRIDE_TRUE; - m_webContents->GetController().LoadURLWithParams(params); - focusIfNecessary(); - m_webContents->CollapseSelection(); + Navigate(this, params); } void WebContentsAdapter::save(const QString &filePath, int savePageFormat) @@ -1676,6 +1683,17 @@ bool WebContentsAdapter::hasFocusedFrame() const return m_webContents->GetFocusedFrame() != nullptr; } +void WebContentsAdapter::resetSelection() +{ + CHECK_INITIALIZED(); + // unconditionally clears the selection in contrast to CollapseSelection, which checks focus state first + if (auto rwhv = static_cast(m_webContents->GetRenderWidgetHostView())) { + if (auto mgr = rwhv->GetTextInputManager()) + if (auto selection = const_cast(mgr->GetTextSelection(rwhv))) + selection->SetSelection(base::string16(), 0, gfx::Range(), false); + } +} + WebContentsAdapterClient::RenderProcessTerminationStatus WebContentsAdapterClient::renderProcessExitStatus(int terminationStatus) { auto status = WebContentsAdapterClient::RenderProcessTerminationStatus(-1); diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h index 11f8f9cb1..f8f147f9a 100644 --- a/src/core/web_contents_adapter.h +++ b/src/core/web_contents_adapter.h @@ -229,6 +229,7 @@ public: void focusIfNecessary(); bool isFindTextInProgress() const; bool hasFocusedFrame() const; + void resetSelection(); // meant to be used within WebEngineCore only void initialize(content::SiteInstance *site); diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index 8cdcc9f46..94b3f16c1 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -700,7 +700,7 @@ public: CursorTrackedPage(QWidget *parent = 0): QWebEnginePage(parent) { } - QString selectedText() { + QString jsSelectedText() { return evaluateJavaScriptSync(this, "window.getSelection().toString()").toString(); } @@ -716,42 +716,52 @@ 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("

The quick brown fox

" \ + CursorTrackedPage page; + + QString textToSelect("The quick brown fox"); + QString content = QString("

%1

" \ "

jumps over the lazy dog

" \ - "

May the source
be with you!

"); - page->setView(&view); - QSignalSpy loadSpy(&view, SIGNAL(loadFinished(bool))); - page->setHtml(content); + "

May the source
be with you!

").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()); } -- cgit v1.2.3