diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-04-30 12:03:36 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-04-30 12:03:36 +0200 |
commit | 3a35e38c4f0110349ff3413efa3cbaaef5d8826d (patch) | |
tree | 10a70344f50fba28932d4a91b08f9b5b48fc4b79 | |
parent | 685430b66ab2830d5e0e5ebafc17294ff1ce1f48 (diff) | |
parent | 950e462b1d369f8140a1d54078d42b5f057adfdb (diff) | |
download | qtwebengine-3a35e38c4f0110349ff3413efa3cbaaef5d8826d.tar.gz |
Merge remote-tracking branch 'origin/5.15.0' into 5.15
Change-Id: I916838caf8c981d5dac876631a6e510ed2ffbe0e
20 files changed, 409 insertions, 53 deletions
diff --git a/dist/changes-5.15.0 b/dist/changes-5.15.0 new file mode 100644 index 000000000..a5e3da7a1 --- /dev/null +++ b/dist/changes-5.15.0 @@ -0,0 +1,91 @@ +Qt 5.15 introduces many new features and improvements as well as bugfixes +over the 5.14.x series. For more details, refer to the online documentation +included in this distribution. The documentation is also available online: + +https://doc.qt.io/qt-5/index.html + +The Qt version 5.15 series is binary compatible with the 5.14.x series. +Applications compiled for 5.14 will continue to run with 5.15. + +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. + + +**************************************************************************** +* General * +**************************************************************************** + +Behavior Changes +---------------- + + - XSS Auditing has been removed, and the XSSAuditingEnabled setting no + longer has any effect. + - [QTBUG-79864] The viz display compositor is now used by default on all + platforms, but can be disabled with --disable-viz-display-compositor. + - The network layer integration has been rewritten to use Chromium's network + service, and now runs in a separate sandboxed process by default. + - [QTBUG-83656] CTRL+mouse wheel page zoom fixed, and now works by default. + + +Chromium Snapshot +----------------- + + - Updated the Chromium version to 80.0.3987.163 + - Applied security fixes from Chrome up to version 81.0.4044.129 + + +General +------- + + - Fixed hardware accelerated video decoding on Windows and macOS. + - Updated where to look for Google Chrome's CDM plugin. + - [QTBUG-82390] Disabled picture-in-picture to avoid non-functional button + showing up. + - Command-line specified PAC files can now be loaded from QRC. + - [QTBUG-82012] Placeholder for missing PPAPI plugins added. + + +Libraries +--------- + + - Added a renderProcessPid() getter to QWebEnginePage and WebEngineView + which allows reading the process ID of the underlying render process. + - [QTBUG-83338] Avoid decoding HTML in default JavaScript message handlers. + + +**************************************************************************** +* Qt PDF * +**************************************************************************** + +General +------- + + - The qt-labs/qtpdf module was using an out-of-date version of pdfium. + Development will now continue in the qtwebengine repository in order to + reuse Qt WebEngine's pdfium build system integration. + - QtPdf is still in Tech Preview. + - It's now possible to build QtPDF for iOS (even though Qt WebEngine does not). + - Added a Qt Quick API (import QtQuick.Pdf): + * Added an image plugin: Image { source: "my.pdf"; currentFrame: 2 } is + enough to view the third page in a PDF file. + * High-level API for full-featured viewers: PdfScrollablePageView shows + one page at a time; PdfMultiPageView allows flicking vertically from + page to page. These are implemented in QML and packaged with the module. + - Low-level QtQuick API: + * PdfDocument provides API for the document and its metadata. + * PdfSearchModel can find a text string and provides information about + the locations where it is found on each page, which can be listed in + a ListView and visualized by using a Repeater to instantiate + QtQuick.Shapes for the bounding boxes. + * PdfSelection allows selecting text and copying to the clipboard; + visualization of the selected region is done via QtQuick.Shapes. + * PdfLinkModel provides information about the links in the document; + visual feedback and clicking can be handled with QtQuick.Shapes + and TapHandler, respectively. + * PdfNavigationStack is a model for implementing forward/back navigation. + diff --git a/src/3rdparty b/src/3rdparty -Subproject 757b9f459d3770644ad83d2faf26a7539c65023 +Subproject 6f260e5b2f717b9977045419f12c7c35d31f9a9 diff --git a/src/buildtools/configure.json b/src/buildtools/configure.json index 1298ab916..a80ef32eb 100644 --- a/src/buildtools/configure.json +++ b/src/buildtools/configure.json @@ -34,8 +34,9 @@ }, "webengine-xkbcommon": { "label": "xkbcommon", + "headers": "xkbcommon/xkbcommon.h", "sources": [ - { "type": "pkgConfig", "args": "xkbcommon" } + "-lxkbcommon" ] }, "webengine-xcomposite": { @@ -296,13 +297,6 @@ ] } }, - "webengine-xkbcommon": { - "label": "system xkbcommon", - "type": "compile", - "test": { - "include": "xkbcommon/xkbcommon.h" - } - }, "webengine-ninja": { "label": "system ninja", "type": "detectNinja" @@ -455,7 +449,7 @@ }, "webengine-system-xkbcommon": { "label": "xkbcommon", - "condition": "libs.webengine-xkbcommon && tests.webengine-xkbcommon", + "condition": "libs.webengine-xkbcommon", "output": [ "privateFeature" ] }, "webengine-system-xcomposite": { diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp index c99c560a3..0af26314a 100644 --- a/src/core/render_widget_host_view_qt.cpp +++ b/src/core/render_widget_host_view_qt.cpp @@ -1530,7 +1530,21 @@ void RenderWidgetHostViewQt::WheelEventAck(const blink::WebMouseWheelEvent &even m_mouseWheelPhaseHandler.AddPhaseIfNeededAndScheduleEndEvent(webEvent, false); host()->ForwardWheelEvent(webEvent); } - // TODO: We could forward unhandled wheelevents to our parent. +} + +void RenderWidgetHostViewQt::GestureEventAck(const blink::WebGestureEvent &event, content::InputEventAckState ack_result) +{ + // Forward unhandled scroll events back as wheel events + if (event.GetType() != blink::WebInputEvent::kGestureScrollUpdate) + return; + switch (ack_result) { + case content::INPUT_EVENT_ACK_STATE_NOT_CONSUMED: + case content::INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS: + WebEventFactory::sendUnhandledWheelEvent(event, delegate()); + break; + default: + break; + } } content::MouseWheelPhaseHandler *RenderWidgetHostViewQt::GetMouseWheelPhaseHandler() diff --git a/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h index 41ce50b34..a07d21468 100644 --- a/src/core/render_widget_host_view_qt.h +++ b/src/core/render_widget_host_view_qt.h @@ -155,6 +155,7 @@ public: void DidCreateNewRendererCompositorFrameSink(viz::mojom::CompositorFrameSinkClient* renderer_compositor_frame_sink) override; void SubmitCompositorFrame(const viz::LocalSurfaceId&, viz::CompositorFrame, base::Optional<viz::HitTestRegionList>) override; void WheelEventAck(const blink::WebMouseWheelEvent &event, content::InputEventAckState ack_result) override; + void GestureEventAck(const blink::WebGestureEvent &event, content::InputEventAckState ack_result) override; content::MouseWheelPhaseHandler *GetMouseWheelPhaseHandler() override; viz::ScopedSurfaceIdAllocator DidUpdateVisualProperties(const cc::RenderFrameMetadata &metadata) override; void OnDidUpdateVisualPropertiesComplete(const cc::RenderFrameMetadata &metadata); diff --git a/src/core/render_widget_host_view_qt_delegate.h b/src/core/render_widget_host_view_qt_delegate.h index 4ee790ce9..46f1802a6 100644 --- a/src/core/render_widget_host_view_qt_delegate.h +++ b/src/core/render_widget_host_view_qt_delegate.h @@ -58,12 +58,13 @@ QT_BEGIN_NAMESPACE class QEvent; +class QInputMethodEvent; class QSGLayer; class QSGNode; class QSGRectangleNode; class QSGTexture; class QVariant; -class QInputMethodEvent; +class QWheelEvent; class QSGImageNode; @@ -111,6 +112,7 @@ public: virtual void setInputMethodHints(Qt::InputMethodHints hints) = 0; virtual void setClearColor(const QColor &color) = 0; virtual bool copySurface(const QRect &, const QSize &, QImage &) = 0; + virtual void unhandledWheelEvent(QWheelEvent *) {} }; } // namespace QtWebEngineCore diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h index 250801f51..04df99f0e 100644 --- a/src/core/web_contents_adapter_client.h +++ b/src/core/web_contents_adapter_client.h @@ -469,7 +469,10 @@ public: virtual void loadFinished(bool success, const QUrl &url, bool isErrorPage = false, int errorCode = 0, const QString &errorDescription = QString()) = 0; virtual void focusContainer() = 0; virtual void unhandledKeyEvent(QKeyEvent *event) = 0; - virtual void adoptNewWindow(QSharedPointer<WebContentsAdapter> newWebContents, WindowOpenDisposition disposition, bool userGesture, const QRect & initialGeometry, const QUrl &targetUrl) = 0; + virtual QSharedPointer<WebContentsAdapter> + adoptNewWindow(QSharedPointer<WebContentsAdapter> newWebContents, + WindowOpenDisposition disposition, bool userGesture, + const QRect &initialGeometry, const QUrl &targetUrl) = 0; virtual bool isBeingAdopted() = 0; virtual void close() = 0; virtual void windowCloseRejected() = 0; diff --git a/src/core/web_contents_delegate_qt.cpp b/src/core/web_contents_delegate_qt.cpp index fada874a3..216e4faf1 100644 --- a/src/core/web_contents_delegate_qt.cpp +++ b/src/core/web_contents_delegate_qt.cpp @@ -663,14 +663,17 @@ void WebContentsDelegateQt::overrideWebPreferences(content::WebContents *webCont m_viewClient->webEngineSettings()->overrideWebPreferences(webContents, webPreferences); } -QWeakPointer<WebContentsAdapter> WebContentsDelegateQt::createWindow(std::unique_ptr<content::WebContents> new_contents, WindowOpenDisposition disposition, const gfx::Rect& initial_pos, bool user_gesture) +QSharedPointer<WebContentsAdapter> +WebContentsDelegateQt::createWindow(std::unique_ptr<content::WebContents> new_contents, + WindowOpenDisposition disposition, const gfx::Rect &initial_pos, + bool user_gesture) { QSharedPointer<WebContentsAdapter> newAdapter = QSharedPointer<WebContentsAdapter>::create(std::move(new_contents)); - m_viewClient->adoptNewWindow(newAdapter, static_cast<WebContentsAdapterClient::WindowOpenDisposition>(disposition), user_gesture, toQt(initial_pos), m_initialTargetUrl); - - // If the client didn't reference the adapter, it will be deleted now, and the weak pointer zeroed. - return newAdapter; + return m_viewClient->adoptNewWindow( + std::move(newAdapter), + static_cast<WebContentsAdapterClient::WindowOpenDisposition>(disposition), user_gesture, + toQt(initial_pos), m_initialTargetUrl); } void WebContentsDelegateQt::allowCertificateError(const QSharedPointer<CertificateErrorController> &errorController) @@ -797,6 +800,15 @@ bool WebContentsDelegateQt::TakeFocus(content::WebContents *source, bool reverse return m_viewClient->passOnFocus(reverse); } +void WebContentsDelegateQt::ContentsZoomChange(bool zoom_in) +{ + WebContentsAdapter *adapter = webContentsAdapter(); + if (zoom_in) + adapter->setZoomFactor(adapter->currentZoomFactor() + 0.1f); + else + adapter->setZoomFactor(adapter->currentZoomFactor() - 0.1f); +} + FaviconManager *WebContentsDelegateQt::faviconManager() { return m_faviconManager.data(); diff --git a/src/core/web_contents_delegate_qt.h b/src/core/web_contents_delegate_qt.h index f32b02caf..bfef9a1df 100644 --- a/src/core/web_contents_delegate_qt.h +++ b/src/core/web_contents_delegate_qt.h @@ -148,6 +148,7 @@ public: void RegisterProtocolHandler(content::WebContents* web_contents, const std::string& protocol, const GURL& url, bool user_gesture) override; void UnregisterProtocolHandler(content::WebContents* web_contents, const std::string& protocol, const GURL& url, bool user_gesture) override; bool TakeFocus(content::WebContents *source, bool reverse) override; + void ContentsZoomChange(bool zoom_in) override; // WebContentsObserver overrides void RenderFrameCreated(content::RenderFrameHost *render_frame_host) override; @@ -200,7 +201,10 @@ public: base::WeakPtr<WebContentsDelegateQt> AsWeakPtr() { return m_weakPtrFactory.GetWeakPtr(); } private: - QWeakPointer<WebContentsAdapter> createWindow(std::unique_ptr<content::WebContents> new_contents, WindowOpenDisposition disposition, const gfx::Rect& initial_pos, bool user_gesture); + QSharedPointer<WebContentsAdapter> + createWindow(std::unique_ptr<content::WebContents> new_contents, + WindowOpenDisposition disposition, const gfx::Rect &initial_pos, + bool user_gesture); void EmitLoadStarted(const QUrl &url, bool isErrorPage = false); void EmitLoadFinished(bool success, const QUrl &url, bool isErrorPage = false, int errorCode = 0, const QString &errorDescription = QString()); void EmitLoadCommitted(); diff --git a/src/core/web_event_factory.cpp b/src/core/web_event_factory.cpp index f37cce6c7..9824b3f37 100644 --- a/src/core/web_event_factory.cpp +++ b/src/core/web_event_factory.cpp @@ -71,6 +71,8 @@ #include "ui/events/keycodes/dom/keycode_converter.h" #include "ui/events/keycodes/keyboard_code_conversion.h" +#include "render_widget_host_view_qt_delegate.h" + #include <QtGui/private/qtgui-config_p.h> #include <QCoreApplication> @@ -84,6 +86,8 @@ #endif #include <QWheelEvent> +namespace QtWebEngineCore { + using namespace blink; enum class KeyboardDriver { Unknown, Windows, Cocoa, Xkb, Evdev }; @@ -1306,6 +1310,42 @@ static inline WebInputEvent::Modifiers modifiersForEvent(const QInputEvent* even return (WebInputEvent::Modifiers)result; } +static inline Qt::KeyboardModifiers keyboardModifiersForModifier(unsigned int modifier) +{ + Qt::KeyboardModifiers modifiers = {}; + if (modifier & WebInputEvent::kControlKey) + modifiers |= Qt::ControlModifier; + if (modifier & WebInputEvent::kMetaKey) + modifiers |= Qt::MetaModifier; + if (modifier & WebInputEvent::kShiftKey) + modifiers |= Qt::ShiftModifier; + if (modifier & WebInputEvent::kAltKey) + modifiers |= Qt::AltModifier; + if (modifier & WebInputEvent::kIsKeyPad) + modifiers |= Qt::KeypadModifier; + + if (keyboardDriver() == KeyboardDriver::Cocoa && !qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) { + bool controlModifier = modifiers.testFlag(Qt::ControlModifier); + bool metaModifier = modifiers.testFlag(Qt::MetaModifier); + modifiers.setFlag(Qt::ControlModifier, metaModifier); + modifiers.setFlag(Qt::MetaModifier, controlModifier); + } + + return modifiers; +} + +static inline Qt::MouseButtons mouseButtonsForModifier(unsigned int modifier) +{ + Qt::MouseButtons buttons = {}; + if (modifier & WebInputEvent::kLeftButtonDown) + buttons |= Qt::LeftButton; + if (modifier & WebInputEvent::kRightButtonDown) + buttons |= Qt::RightButton; + if (modifier & WebInputEvent::kMiddleButtonDown) + buttons |= Qt::MiddleButton; + return buttons; +} + static WebInputEvent::Type webEventTypeForEvent(const QEvent* event) { switch (event->type()) { @@ -1471,6 +1511,14 @@ static void setBlinkWheelEventDelta(blink::WebMouseWheelEvent &webEvent) webEvent.delta_y = webEvent.wheel_ticks_y * wheelScrollLines * cDefaultQtScrollStep; } +static QPoint getWheelEventDelta(const blink::WebGestureEvent &webEvent) +{ + static const float cDefaultQtScrollStep = 20.f; + static const int wheelScrollLines = QGuiApplication::styleHints()->wheelScrollLines(); + return QPoint(webEvent.data.scroll_update.delta_x * QWheelEvent::DefaultDeltasPerStep / (wheelScrollLines * cDefaultQtScrollStep), + webEvent.data.scroll_update.delta_y * QWheelEvent::DefaultDeltasPerStep / (wheelScrollLines * cDefaultQtScrollStep)); +} + blink::WebMouseWheelEvent::Phase toBlinkPhase(QWheelEvent *ev) { switch (ev->phase()) { @@ -1552,6 +1600,26 @@ bool WebEventFactory::coalesceWebWheelEvent(blink::WebMouseWheelEvent &webEvent, return true; } +static QPointF toQt(blink::WebFloatPoint p) +{ + return QPointF(p.x, p.y); +} + +void WebEventFactory::sendUnhandledWheelEvent(const blink::WebGestureEvent &event, + RenderWidgetHostViewQtDelegate *delegate) +{ + Q_ASSERT(event.GetType() == blink::WebInputEvent::kGestureScrollUpdate); + + QWheelEvent ev(toQt(event.PositionInWidget()), + toQt(event.PositionInScreen()), + QPoint(event.data.scroll_update.delta_x, event.data.scroll_update.delta_y), + getWheelEventDelta(event), + mouseButtonsForModifier(event.GetModifiers()), + keyboardModifiersForModifier(event.GetModifiers()), + Qt::NoScrollPhase, false); + delegate->unhandledWheelEvent(&ev); +} + content::NativeWebKeyboardEvent WebEventFactory::toWebKeyboardEvent(QKeyEvent *ev) { content::NativeWebKeyboardEvent webKitEvent(reinterpret_cast<gfx::NativeEvent>(ev)); @@ -1686,3 +1754,5 @@ bool WebEventFactory::getEditCommand(QKeyEvent *event, std::string *editCommand) return false; } + +} // namespace QtWebEngineCore diff --git a/src/core/web_event_factory.h b/src/core/web_event_factory.h index 526202cfb..390502a9d 100644 --- a/src/core/web_event_factory.h +++ b/src/core/web_event_factory.h @@ -63,6 +63,10 @@ class QNativeGestureEvent; #endif QT_END_NAMESPACE +namespace QtWebEngineCore { + +class RenderWidgetHostViewQtDelegate; + class WebEventFactory { public: @@ -77,9 +81,11 @@ public: #endif static blink::WebMouseWheelEvent toWebWheelEvent(QWheelEvent *); static bool coalesceWebWheelEvent(blink::WebMouseWheelEvent &, QWheelEvent *); + static void sendUnhandledWheelEvent(const blink::WebGestureEvent &, RenderWidgetHostViewQtDelegate *); static content::NativeWebKeyboardEvent toWebKeyboardEvent(QKeyEvent*); static bool getEditCommand(QKeyEvent *event, std::string *editCommand); }; +} // namespace QtWebEngineCore #endif // WEB_EVENT_FACTORY_H diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp index 7052afe42..4096eb7f6 100644 --- a/src/webengine/api/qquickwebengineview.cpp +++ b/src/webengine/api/qquickwebengineview.cpp @@ -547,12 +547,13 @@ void QQuickWebEngineViewPrivate::unhandledKeyEvent(QKeyEvent *event) QCoreApplication::sendEvent(q->parentItem(), event); } -void QQuickWebEngineViewPrivate::adoptNewWindow(QSharedPointer<WebContentsAdapter> newWebContents, WindowOpenDisposition disposition, bool userGesture, const QRect &, const QUrl &targetUrl) +QSharedPointer<WebContentsAdapter> +QQuickWebEngineViewPrivate::adoptNewWindow(QSharedPointer<WebContentsAdapter> newWebContents, + WindowOpenDisposition disposition, bool userGesture, + const QRect &, const QUrl &targetUrl) { Q_Q(QQuickWebEngineView); QQuickWebEngineNewViewRequest request; - // This increases the ref-count of newWebContents and will tell Chromium - // to start loading it and possibly return it to its parent page window.open(). request.m_adapter = newWebContents; request.m_isUserInitiated = userGesture; request.m_requestedUrl = targetUrl; @@ -575,6 +576,8 @@ void QQuickWebEngineViewPrivate::adoptNewWindow(QSharedPointer<WebContentsAdapte } Q_EMIT q->newViewRequested(&request); + + return newWebContents; } bool QQuickWebEngineViewPrivate::isBeingAdopted() diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h index 5c884e36e..e696f6a0c 100644 --- a/src/webengine/api/qquickwebengineview_p_p.h +++ b/src/webengine/api/qquickwebengineview_p_p.h @@ -121,7 +121,10 @@ public: void loadFinished(bool success, const QUrl &url, bool isErrorPage = false, int errorCode = 0, const QString &errorDescription = QString()) override; void focusContainer() override; void unhandledKeyEvent(QKeyEvent *event) override; - void adoptNewWindow(QSharedPointer<QtWebEngineCore::WebContentsAdapter> newWebContents, WindowOpenDisposition disposition, bool userGesture, const QRect &, const QUrl &targetUrl) override; + QSharedPointer<QtWebEngineCore::WebContentsAdapter> + adoptNewWindow(QSharedPointer<QtWebEngineCore::WebContentsAdapter> newWebContents, + WindowOpenDisposition disposition, bool userGesture, const QRect &, + const QUrl &targetUrl) override; bool isBeingAdopted() override; void close() override; void windowCloseRejected() override; diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index af98fda0b..4bbddd740 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -348,7 +348,10 @@ void QWebEnginePagePrivate::unhandledKeyEvent(QKeyEvent *event) QGuiApplication::sendEvent(view->parentWidget(), event); } -void QWebEnginePagePrivate::adoptNewWindow(QSharedPointer<WebContentsAdapter> newWebContents, WindowOpenDisposition disposition, bool userGesture, const QRect &initialGeometry, const QUrl &targetUrl) +QSharedPointer<WebContentsAdapter> +QWebEnginePagePrivate::adoptNewWindow(QSharedPointer<WebContentsAdapter> newWebContents, + WindowOpenDisposition disposition, bool userGesture, + const QRect &initialGeometry, const QUrl &targetUrl) { Q_Q(QWebEnginePage); Q_UNUSED(userGesture); @@ -356,27 +359,11 @@ void QWebEnginePagePrivate::adoptNewWindow(QSharedPointer<WebContentsAdapter> ne QWebEnginePage *newPage = q->createWindow(toWindowType(disposition)); if (!newPage) - return; + return nullptr; - if (newPage->d_func() == this) { - // If createWindow returns /this/ we must delay the adoption. - Q_ASSERT(q == newPage); - // WebContents might be null if we just opened a new page for navigation, in that case - // avoid referencing newWebContents so that it is deleted and WebContentsDelegateQt::OpenURLFromTab - // will fall back to navigating current page. - if (newWebContents->webContents()) { - QTimer::singleShot(0, q, [this, newPage, newWebContents, initialGeometry] () { - adoptNewWindowImpl(newPage, newWebContents, initialGeometry); - }); - } - } else { - adoptNewWindowImpl(newPage, newWebContents, initialGeometry); - } -} + if (!newWebContents->webContents()) + return newPage->d_func()->adapter; // Reuse existing adapter -void QWebEnginePagePrivate::adoptNewWindowImpl(QWebEnginePage *newPage, - const QSharedPointer<WebContentsAdapter> &newWebContents, const QRect &initialGeometry) -{ // Mark the new page as being in the process of being adopted, so that a second mouse move event // sent by newWebContents->initialize() gets filtered in RenderWidgetHostViewQt::forwardEvent. // The first mouse move event is being sent by q->createWindow(). This is necessary because @@ -394,6 +381,8 @@ void QWebEnginePagePrivate::adoptNewWindowImpl(QWebEnginePage *newPage, if (!initialGeometry.isEmpty()) emit newPage->geometryChangeRequested(initialGeometry); + + return newWebContents; } bool QWebEnginePagePrivate::isBeingAdopted() diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h index f37413b8e..b4424ec4b 100644 --- a/src/webenginewidgets/api/qwebenginepage_p.h +++ b/src/webenginewidgets/api/qwebenginepage_p.h @@ -112,10 +112,10 @@ public: void loadFinished(bool success, const QUrl &url, bool isErrorPage = false, int errorCode = 0, const QString &errorDescription = QString()) override; void focusContainer() override; void unhandledKeyEvent(QKeyEvent *event) override; - void adoptNewWindow(QSharedPointer<QtWebEngineCore::WebContentsAdapter> newWebContents, WindowOpenDisposition disposition, bool userGesture, const QRect &initialGeometry, const QUrl &targetUrl) override; - void adoptNewWindowImpl(QWebEnginePage *newPage, - const QSharedPointer<QtWebEngineCore::WebContentsAdapter> &newWebContents, - const QRect &initialGeometry); + QSharedPointer<QtWebEngineCore::WebContentsAdapter> + adoptNewWindow(QSharedPointer<QtWebEngineCore::WebContentsAdapter> newWebContents, + WindowOpenDisposition disposition, bool userGesture, + const QRect &initialGeometry, const QUrl &targetUrl) override; bool isBeingAdopted() override; void close() override; void windowCloseRejected() override; diff --git a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp index fdd6d1c4f..66b4cffea 100644 --- a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp +++ b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp @@ -474,11 +474,16 @@ bool RenderWidgetHostViewQtDelegateWidget::event(QEvent *event) if (!handled) return QQuickWidget::event(event); - // Most events are accepted by default, but tablet events are not: event->accept(); return true; } +void RenderWidgetHostViewQtDelegateWidget::unhandledWheelEvent(QWheelEvent *ev) +{ + if (QWidget *p = parentWidget()) + qApp->sendEvent(p, ev); +} + void RenderWidgetHostViewQtDelegateWidget::onWindowPosChanged() { m_client->visualPropertiesChanged(); diff --git a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h index c7783117a..034fdd65c 100644 --- a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h +++ b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h @@ -91,6 +91,7 @@ public: void setInputMethodHints(Qt::InputMethodHints) override; void setClearColor(const QColor &color) override; bool copySurface(const QRect &, const QSize &, QImage &) override; + void unhandledWheelEvent(QWheelEvent *ev) override; protected: bool event(QEvent *event) override; diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index 39bbc96de..ab19548e8 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -198,6 +198,8 @@ private Q_SLOTS: void dataURLFragment(); void devTools(); void openLinkInDifferentProfile(); + void openLinkInNewPage_data(); + void openLinkInNewPage(); void triggerActionWithoutMenu(); void dynamicFrame(); @@ -3380,6 +3382,162 @@ void tst_QWebEnginePage::openLinkInDifferentProfile() QVERIFY(spy2.takeFirst().value(0).toBool()); } +// What does createWindow do? +enum class OpenLinkInNewPageDecision { + // Returns nullptr, + ReturnNull, + // Returns this, + ReturnSelf, + // Returns page != this + ReturnOther, +}; + +// What causes createWindow to be called? +enum class OpenLinkInNewPageCause { + // User clicks on a link with target=_blank. + TargetBlank, + // User clicks with MiddleButton. + MiddleClick, +}; + +// What happens after createWindow? +enum class OpenLinkInNewPageEffect { + // The navigation request disappears into the ether. + Blocked, + // The navigation request becomes a navigation in the original page. + LoadInSelf, + // The navigation request becomes a navigation in a different page. + LoadInOther, +}; + +Q_DECLARE_METATYPE(OpenLinkInNewPageCause) +Q_DECLARE_METATYPE(OpenLinkInNewPageDecision) +Q_DECLARE_METATYPE(OpenLinkInNewPageEffect) + +void tst_QWebEnginePage::openLinkInNewPage_data() +{ + using Decision = OpenLinkInNewPageDecision; + using Cause = OpenLinkInNewPageCause; + using Effect = OpenLinkInNewPageEffect; + + QTest::addColumn<Decision>("decision"); + QTest::addColumn<Cause>("cause"); + QTest::addColumn<Effect>("effect"); + + // Note that the meaning of returning nullptr from createWindow is not + // consistent between the TargetBlank and MiddleClick scenarios. + // + // With TargetBlank, the open-in-new-page disposition comes from the HTML + // target attribute; something the user is probably not aware of. Returning + // nullptr is interpreted as a decision by the app to block an unwanted + // popup. + // + // With MiddleClick, the open-in-new-page disposition comes from the user's + // explicit intent. Returning nullptr is then interpreted as a failure by + // the app to fulfill this intent, which we try to compensate by ignoring + // the disposition and performing the navigation request normally. + + QTest::newRow("BlockPopup") << Decision::ReturnNull << Cause::TargetBlank << Effect::Blocked; + QTest::newRow("IgnoreIntent") << Decision::ReturnNull << Cause::MiddleClick << Effect::LoadInSelf; + QTest::newRow("OverridePopup") << Decision::ReturnSelf << Cause::TargetBlank << Effect::LoadInSelf; + QTest::newRow("OverrideIntent") << Decision::ReturnSelf << Cause::MiddleClick << Effect::LoadInSelf; + QTest::newRow("AcceptPopup") << Decision::ReturnOther << Cause::TargetBlank << Effect::LoadInOther; + QTest::newRow("AcceptIntent") << Decision::ReturnOther << Cause::MiddleClick << Effect::LoadInOther; +} + +void tst_QWebEnginePage::openLinkInNewPage() +{ + using Decision = OpenLinkInNewPageDecision; + using Cause = OpenLinkInNewPageCause; + using Effect = OpenLinkInNewPageEffect; + + class Page : public QWebEnginePage + { + public: + Page *targetPage = nullptr; + QSignalSpy spy{this, &QWebEnginePage::loadFinished}; + Page(QWebEngineProfile *profile) : QWebEnginePage(profile) {} + private: + QWebEnginePage *createWindow(WebWindowType) override { return targetPage; } + }; + + class View : public QWebEngineView + { + public: + View(Page *page) + { + resize(500, 500); + setPage(page); + } + }; + + QFETCH(Decision, decision); + QFETCH(Cause, cause); + QFETCH(Effect, effect); + + QWebEngineProfile profile; + Page page1(&profile); + Page page2(&profile); + View view1(&page1); + View view2(&page2); + + view1.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view1)); + + page1.setHtml("<html><body>" + "<a id='link' href='data:,hello' target='_blank'>link</a>" + "</body></html>"); + QTRY_COMPARE(page1.spy.count(), 1); + QVERIFY(page1.spy.takeFirst().value(0).toBool()); + + switch (decision) { + case Decision::ReturnNull: + page1.targetPage = nullptr; + break; + case Decision::ReturnSelf: + page1.targetPage = &page1; + break; + case Decision::ReturnOther: + page1.targetPage = &page2; + break; + } + + Qt::MouseButton button; + switch (cause) { + case Cause::TargetBlank: + button = Qt::LeftButton; + break; + case Cause::MiddleClick: + button = Qt::MiddleButton; + break; + } + QTest::mouseClick(view1.focusProxy(), button, {}, elementCenter(&page1, "link")); + + switch (effect) { + case Effect::Blocked: + // Nothing to test + break; + case Effect::LoadInSelf: + QTRY_COMPARE(page1.spy.count(), 1); + QVERIFY(page1.spy.takeFirst().value(0).toBool()); + QCOMPARE(page2.spy.count(), 0); + if (decision == Decision::ReturnSelf && cause == Cause::TargetBlank) + // History was discarded due to AddNewContents + QCOMPARE(page1.history()->count(), 1); + else + QCOMPARE(page1.history()->count(), 2); + QCOMPARE(page2.history()->count(), 0); + break; + case Effect::LoadInOther: + QTRY_COMPARE(page2.spy.count(), 1); + QVERIFY(page2.spy.takeFirst().value(0).toBool()); + QCOMPARE(page1.spy.count(), 0); + QCOMPARE(page1.history()->count(), 1); + QCOMPARE(page2.history()->count(), 1); + break; + } +} + void tst_QWebEnginePage::triggerActionWithoutMenu() { // Calling triggerAction should not crash even when for diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp index 935babe81..00d4bae5a 100644 --- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp +++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp @@ -246,7 +246,7 @@ void tst_QWebEngineProfile::clearDataFromCache() QTest::qWait(1000); QVERIFY(sizeBeforeClear > totalSize(cacheDir)); - QVERIFY(server.stop()); + (void)server.stop(); } void tst_QWebEngineProfile::disableCache() @@ -271,7 +271,7 @@ void tst_QWebEngineProfile::disableCache() QVERIFY(loadSync(&page, server.url("/hedgehog.html"))); QVERIFY(cacheDir.exists("Cache")); - QVERIFY(server.stop()); + (void)server.stop(); } class RedirectingUrlSchemeHandler : public QWebEngineUrlSchemeHandler @@ -864,7 +864,7 @@ void tst_QWebEngineProfile::changePersistentPath() QVERIFY(loadSync(&page, server.url("/hedgehog.html"))); QVERIFY(dataDir2.exists()); - QVERIFY(server.stop()); + (void)server.stop(); } void tst_QWebEngineProfile::changeHttpUserAgent() @@ -937,7 +937,7 @@ void tst_QWebEngineProfile::changeUseForGlobalCertificateVerification() page.reset(new QWebEnginePage(&profile)); QVERIFY(loadSync(page.get(), server.url("/hedgehog.html"))); // Don't check for error: there can be disconnects during GET hedgehog.png. - server.stop(); + (void)server.stop(); } void tst_QWebEngineProfile::changePersistentCookiesPolicy() @@ -961,7 +961,7 @@ void tst_QWebEngineProfile::changePersistentCookiesPolicy() QVERIFY(loadSync(&page, server.url("/hedgehog.html"))); QVERIFY(dataDir.exists("Cookies")); - QVERIFY(server.stop()); + (void)server.stop(); } class InitiatorSpy : public QWebEngineUrlSchemeHandler diff --git a/tools/scripts/version_resolver.py b/tools/scripts/version_resolver.py index 20141e4b2..b3181db10 100644 --- a/tools/scripts/version_resolver.py +++ b/tools/scripts/version_resolver.py @@ -38,7 +38,7 @@ import json import urllib2 import git_submodule as GitSubmodule -chromium_version = '80.0.3987.136' +chromium_version = '80.0.3987.163' chromium_branch = '3987' ninja_version = 'v1.8.2' |