diff options
21 files changed, 314 insertions, 9 deletions
diff --git a/dist/changes-5.9.3 b/dist/changes-5.9.3 new file mode 100644 index 000000000..9144fae79 --- /dev/null +++ b/dist/changes-5.9.3 @@ -0,0 +1,52 @@ +Qt 5.9.3 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.9.0. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +http://doc.qt.io/qt-5/index.html + +The Qt version 5.9 series is binary compatible with the 5.8.x series. +Applications compiled for 5.8 will continue to run with 5.9. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Qt 5.9.3 Changes * +**************************************************************************** + + - This release contains only minor code improvements. + * Security fixes from Chromium up to version 62.0.3202.89 + Including: CVE-2017-5124, CVE-2017-5126, CVE-2017-5127, CVE-2017-5128, CVE-2017-5129, + CVE-2017-5132, CVE-2017-5133, CVE-2017-15386, CVE-2017-15387, CVE-2017-15388, + CVE-2017-15390, CVE-2017-15392, CVE-2017-15394, CVE-2017-15396, CVE-2017-15398. + + - QtWebEngineCore: + * [QTBUG-64032] Fix crash after resizing view to be empty. + + - QtWebEngine[QML]: + * Fix loading some favicons including qt.io's + + - QtWebEngineWidgets: + * [QTBUG-62147] Fix crash on shutdown if a QWebEngineProfile was child + of QApplication. + +**************************************************************************** +* Platform Specific Changes * +**************************************************************************** + + - Windows: + * Fix build issues with newer VS 2017 updates + + - Linux: + * Fix build issue with clang 3.9 + * [QTBUG-63675] Fix build issue with gcc 7.2 + + - macOS: + * Support for 10.13 High Sierra internals diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp index b30069c74..80c9121f5 100644 --- a/src/core/render_widget_host_view_qt.cpp +++ b/src/core/render_widget_host_view_qt.cpp @@ -1067,6 +1067,10 @@ bool RenderWidgetHostViewQt::forwardEvent(QEvent *event) case QEvent::InputMethodQuery: handleInputMethodQueryEvent(static_cast<QInputMethodQueryEvent*>(event)); break; + case QEvent::HoverLeave: + case QEvent::Leave: + m_host->ForwardMouseEvent(WebEventFactory::toWebMouseEvent(event)); + break; default: return false; } diff --git a/src/core/renderer/web_channel_ipc_transport.cpp b/src/core/renderer/web_channel_ipc_transport.cpp index 554bda2a8..94a7baa1d 100644 --- a/src/core/renderer/web_channel_ipc_transport.cpp +++ b/src/core/renderer/web_channel_ipc_transport.cpp @@ -186,7 +186,7 @@ void WebChannelIPCTransport::installWebChannel(uint worldId) void WebChannelIPCTransport::uninstallWebChannel(uint worldId) { - Q_ASSERT(worldId = m_installedWorldId); + Q_ASSERT(worldId == m_installedWorldId); blink::WebView *webView = render_view()->GetWebView(); if (!webView) return; diff --git a/src/core/url_request_qrc_job_qt.cpp b/src/core/url_request_qrc_job_qt.cpp index b4e960921..a2712653d 100644 --- a/src/core/url_request_qrc_job_qt.cpp +++ b/src/core/url_request_qrc_job_qt.cpp @@ -112,7 +112,7 @@ int URLRequestQrcJobQt::ReadRawData(IOBuffer *buf, int bufSize) void URLRequestQrcJobQt::startGetHead() { // Get qrc file path. - QString qrcFilePath = ':' + toQt(request_->url()).path(QUrl::RemovePath | QUrl::RemoveQuery); + QString qrcFilePath = ':' + toQt(request_->url()).path(); m_file.setFileName(qrcFilePath); QFileInfo qrcFileInfo(m_file); // Get qrc file mime type. diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp index ed13883c0..812a6f1c0 100644 --- a/src/core/web_contents_adapter.cpp +++ b/src/core/web_contents_adapter.cpp @@ -1465,6 +1465,12 @@ void WebContentsAdapter::focusIfNecessary() d->webContents->Focus(); } +bool WebContentsAdapter::isFindTextInProgress() const +{ + Q_D(const WebContentsAdapter); + return d->lastFindRequestId != d->webContentsDelegate->lastReceivedFindReply(); +} + 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 67fcbe7af..51fd2891d 100644 --- a/src/core/web_contents_adapter.h +++ b/src/core/web_contents_adapter.h @@ -184,6 +184,7 @@ public: void viewSource(); bool canViewSource(); void focusIfNecessary(); + bool isFindTextInProgress() const; private: diff --git a/src/core/web_event_factory.cpp b/src/core/web_event_factory.cpp index dcd81efe8..3193f885a 100644 --- a/src/core/web_event_factory.cpp +++ b/src/core/web_event_factory.cpp @@ -1019,12 +1019,14 @@ static ui::DomKey getDomKeyFromQKeyEvent(QKeyEvent *ev) } } -static inline double currentTimeForEvent(const QInputEvent* event) +static inline double currentTimeForEvent(const QEvent *event) { Q_ASSERT(event); - if (event->timestamp()) - return static_cast<double>(event->timestamp()) / 1000; + if (const QInputEvent *inputEvent = static_cast<const QInputEvent *>(event)) { + if (inputEvent->timestamp()) + return static_cast<double>(inputEvent->timestamp()) / 1000; + } static QElapsedTimer timer; if (!timer.isValid()) @@ -1210,6 +1212,16 @@ WebMouseEvent WebEventFactory::toWebMouseEvent(QHoverEvent *ev, double dpiScale) return webKitEvent; } +WebMouseEvent WebEventFactory::toWebMouseEvent(QEvent *ev) +{ + Q_ASSERT(ev->type() == QEvent::Leave || ev->type() == QEvent::HoverLeave); + + WebMouseEvent webKitEvent; + webKitEvent.SetTimeStampSeconds(currentTimeForEvent(ev)); + webKitEvent.SetType(WebInputEvent::kMouseLeave); + return webKitEvent; +} + #ifndef QT_NO_GESTURES WebGestureEvent WebEventFactory::toWebGestureEvent(QNativeGestureEvent *ev, double dpiScale) { diff --git a/src/core/web_event_factory.h b/src/core/web_event_factory.h index f4ce417cd..5b8f08968 100644 --- a/src/core/web_event_factory.h +++ b/src/core/web_event_factory.h @@ -50,6 +50,7 @@ #include <QtGlobal> QT_BEGIN_NAMESPACE +class QEvent; class QHoverEvent; class QKeyEvent; class QMouseEvent; @@ -64,6 +65,7 @@ class WebEventFactory { public: static blink::WebMouseEvent toWebMouseEvent(QMouseEvent*, double dpiScale); static blink::WebMouseEvent toWebMouseEvent(QHoverEvent*, double dpiScale); + static blink::WebMouseEvent toWebMouseEvent(QEvent *); #ifndef QT_NO_GESTURES static blink::WebGestureEvent toWebGestureEvent(QNativeGestureEvent *, double dpiScale); #endif diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp index f5d3646d8..addb7d6df 100644 --- a/src/webengine/api/qquickwebengineview.cpp +++ b/src/webengine/api/qquickwebengineview.cpp @@ -338,7 +338,7 @@ void QQuickWebEngineViewPrivate::navigationRequested(int navigationType, const Q Q_EMIT q->navigationRequested(&navigationRequest); navigationRequestAction = navigationRequest.action(); - if ((navigationRequestAction == WebContentsAdapterClient::AcceptRequest) && adapter) + if ((navigationRequestAction == WebContentsAdapterClient::AcceptRequest) && adapter && adapter->isFindTextInProgress()) adapter->stopFinding(); } diff --git a/src/webengine/render_widget_host_view_qt_delegate_quick.cpp b/src/webengine/render_widget_host_view_qt_delegate_quick.cpp index 0e06ddbce..0a31811d9 100644 --- a/src/webengine/render_widget_host_view_qt_delegate_quick.cpp +++ b/src/webengine/render_widget_host_view_qt_delegate_quick.cpp @@ -316,6 +316,11 @@ void RenderWidgetHostViewQtDelegateQuick::hoverMoveEvent(QHoverEvent *event) m_client->forwardEvent(event); } +void RenderWidgetHostViewQtDelegateQuick::hoverLeaveEvent(QHoverEvent *event) +{ + m_client->forwardEvent(event); +} + QVariant RenderWidgetHostViewQtDelegateQuick::inputMethodQuery(Qt::InputMethodQuery query) const { return m_client->inputMethodQuery(query); diff --git a/src/webengine/render_widget_host_view_qt_delegate_quick.h b/src/webengine/render_widget_host_view_qt_delegate_quick.h index d93fd539a..7426dc16d 100644 --- a/src/webengine/render_widget_host_view_qt_delegate_quick.h +++ b/src/webengine/render_widget_host_view_qt_delegate_quick.h @@ -90,6 +90,7 @@ protected: virtual void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; virtual void touchEvent(QTouchEvent *event) Q_DECL_OVERRIDE; virtual void hoverMoveEvent(QHoverEvent *event) Q_DECL_OVERRIDE; + virtual void hoverLeaveEvent(QHoverEvent *event) Q_DECL_OVERRIDE; virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const Q_DECL_OVERRIDE; virtual void inputMethodEvent(QInputMethodEvent *event) Q_DECL_OVERRIDE; virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; diff --git a/src/webengine/ui_delegates_manager.cpp b/src/webengine/ui_delegates_manager.cpp index 43e6e8817..fb23f5db7 100644 --- a/src/webengine/ui_delegates_manager.cpp +++ b/src/webengine/ui_delegates_manager.cpp @@ -257,7 +257,7 @@ QObject *UIDelegatesManager::addMenu(QObject *parentMenu, const QString &title, if (!title.isEmpty()) QQmlProperty(menu, QStringLiteral("title")).write(title); if (!pos.isNull()) - QQmlProperty(menu, QStringLiteral("pos")).write(pos); + menu->setProperty("pos", pos); menu->setParent(parentMenu); @@ -496,9 +496,38 @@ void UIDelegatesManager::showFilePicker(QSharedPointer<FilePickerController> con QMetaObject::invokeMethod(filePicker, "open"); } +class TemporaryCursorMove +{ +public: + TemporaryCursorMove(const QQuickItem *item, const QPoint &pos) + { + if (pos.isNull() || !item->contains(pos)) + return; + const QPoint oldPos = QCursor::pos(); + const QPoint globalPos = item->mapToGlobal(QPointF(pos)).toPoint(); + if (oldPos == globalPos) + return; + m_oldCursorPos = oldPos; + QCursor::setPos(globalPos); + } + + ~TemporaryCursorMove() + { + if (!m_oldCursorPos.isNull()) + QCursor::setPos(m_oldCursorPos); + } + +private: + QPoint m_oldCursorPos; +}; + void UIDelegatesManager::showMenu(QObject *menu) { - QMetaObject::invokeMethod(menu, "popup"); + // QtQuick.Controls.Menu.popup() always shows the menu under the mouse cursor, i.e. the menu's + // position we set above is ignored. Work around the problem by moving the mouse cursor + // temporarily to the right position. + TemporaryCursorMove tcm(m_view, menu->property("pos").toPoint()); + QMetaObject::invokeMethod(menu, "popup"); } void UIDelegatesManager::showMessageBubble(const QRect &anchor, const QString &mainText, const QString &subText) diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index c048a49f4..420057651 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -1489,7 +1489,7 @@ void QWebEnginePagePrivate::navigationRequested(int navigationType, const QUrl & { Q_Q(QWebEnginePage); bool accepted = q->acceptNavigationRequest(url, static_cast<QWebEnginePage::NavigationType>(navigationType), isMainFrame); - if (accepted && adapter) + if (accepted && adapter && adapter->isFindTextInProgress()) adapter->stopFinding(); navigationRequestAction = accepted ? WebContentsAdapterClient::AcceptRequest : WebContentsAdapterClient::IgnoreRequest; } diff --git a/tests/auto/quick/qmltests/data/tst_mouseMove.qml b/tests/auto/quick/qmltests/data/tst_mouseMove.qml new file mode 100644 index 000000000..adb937139 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_mouseMove.qml @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtWebEngine 1.4 + +Rectangle { + id: root + width: 200 + height: 200 + + Column { + anchors.fill: parent + Rectangle { + id: placeHolder + width: parent.width + height: 100 + color: "red" + } + + TestWebEngineView { + id: webEngineView + width: parent.width + height: 100 + + function getInnerText(element) { + var innerText; + runJavaScript("document.getElementById('" + element + "').innerText", function(result) { + innerText = result; + }); + testCase.tryVerify(function() { return innerText != undefined; }); + return innerText; + } + } + } + + TestCase { + id: testCase + name: "WebEngineViewMouseMove" + when: windowShown + + function test_mouseLeave() { + mouseMove(root, 0, 0); + webEngineView.loadHtml( + "<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>"); + verify(webEngineView.waitForLoadSucceeded()); + verify(!webEngineView.getInnerText("testDiv")); + + for (var i = 90; i < 110; ++i) + mouseMove(root, 50, i); + tryVerify(function() { return webEngineView.getInnerText("testDiv") == "Mouse IN" }); + + for (var i = 110; i > 90; --i) + mouseMove(root, 50, i); + tryVerify(function() { return webEngineView.getInnerText("testDiv") == "Mouse OUT" }); + } + } +} + diff --git a/tests/auto/quick/qmltests/qmltests.pro b/tests/auto/quick/qmltests/qmltests.pro index 56e61138d..bb209a70c 100644 --- a/tests/auto/quick/qmltests/qmltests.pro +++ b/tests/auto/quick/qmltests/qmltests.pro @@ -62,6 +62,7 @@ OTHER_FILES += \ $$PWD/data/tst_loadRecursionCrash.qml \ $$PWD/data/tst_loadUrl.qml \ $$PWD/data/tst_mouseClick.qml \ + $$PWD/data/tst_mouseMove.qml \ $$PWD/data/tst_navigationHistory.qml \ $$PWD/data/tst_navigationRequested.qml \ $$PWD/data/tst_newViewRequest.qml \ 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 47e83855c..1c9b668ae 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -202,6 +202,7 @@ private Q_SLOTS: void loadFinishedAfterNotFoundError(); void loadInSignalHandlers_data(); void loadInSignalHandlers(); + void loadFromQrc(); void restoreHistory(); void toPlainTextLoadFinishedRace_data(); @@ -4212,6 +4213,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/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index c3c8f9b28..8d2f92d96 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 @@ -2423,5 +2427,55 @@ 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()); + QVERIFY(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" |