diff options
author | Alexandru Croitor <alexandru.croitor@theqtcompany.com> | 2016-03-01 14:16:05 +0100 |
---|---|---|
committer | Alexandru Croitor <alexandru.croitor@theqtcompany.com> | 2016-03-24 11:17:46 +0000 |
commit | 9cdd03b44a09b6b40656d4a494f58384007d8aac (patch) | |
tree | ee3226bf28a850975907a1e69b45a6f85afbc224 | |
parent | fab5cff1171de54f43d9ab57ed16f8d0f85b433e (diff) |
Fix crash when link opens a modal QDialog, using the trackpad.
When a QWebEngine link is clicked on, and as a result a modal dialog is
opened, a QEvent::TouchCancel without any touch points is forwarded
to Chromium, which tries to access the first touch point
position, and causes a QList assertion.
Fix consists of two parts:
1) Make sure that no TouchCancel is forwarded, in case if no TouchBegin
or TouchUpdate was issued beforehand.
2) Because QEvent::TouchCancel events might contain an empty touch
point list, and Chromium expects at least one point, make sure
to forward the last saved touch points (saved in previous TouchUpdate)
together with the TouchCancel.
Task-number: QTBUG-48661
Change-Id: I1eeb2980417b1b04e8387dc9f82f935ef2bd8f00
Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
-rw-r--r-- | src/core/render_widget_host_view_qt.cpp | 53 | ||||
-rw-r--r-- | src/core/render_widget_host_view_qt.h | 3 |
2 files changed, 48 insertions, 8 deletions
diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp index b15aa94ef..500a94659 100644 --- a/src/core/render_widget_host_view_qt.cpp +++ b/src/core/render_widget_host_view_qt.cpp @@ -249,6 +249,7 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost* widget : m_host(content::RenderWidgetHostImpl::From(widget)) , m_gestureProvider(QtGestureProviderConfig(), this) , m_sendMotionActionDown(false) + , m_touchMotionStarted(false) , m_chromiumCompositorData(new ChromiumCompositorData) , m_needsDelegatedFrameAck(false) , m_didFirstVisuallyNonEmptyLayout(false) @@ -1009,6 +1010,12 @@ void RenderWidgetHostViewQt::handleWheelEvent(QWheelEvent *ev) m_host->ForwardWheelEvent(WebEventFactory::toWebWheelEvent(ev, dpiScale())); } +void RenderWidgetHostViewQt::clearPreviousTouchMotionState() +{ + m_previousTouchPoints.clear(); + m_touchMotionStarted = false; +} + void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev) { // Chromium expects the touch event timestamps to be comparable to base::TimeTicks::Now(). @@ -1023,19 +1030,46 @@ void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev) QList<QTouchEvent::TouchPoint> touchPoints = mapTouchPointIds(ev->touchPoints()); - if (ev->type() == QEvent::TouchCancel) { - MotionEventQt cancelEvent(touchPoints, eventTimestamp, ui::MotionEvent::ACTION_CANCEL, ev->modifiers(), dpiScale()); + switch (ev->type()) { + case QEvent::TouchBegin: + m_sendMotionActionDown = true; + m_touchMotionStarted = true; + break; + case QEvent::TouchUpdate: + m_touchMotionStarted = true; + break; + case QEvent::TouchCancel: + { + // Don't process a TouchCancel event if no motion was started beforehand, or if there are + // no touch points in the current event or in the previously processed event. + if (!m_touchMotionStarted || (touchPoints.isEmpty() && m_previousTouchPoints.isEmpty())) { + clearPreviousTouchMotionState(); + return; + } + + // Use last saved touch points for the cancel event, to get rid of a QList assert, + // because Chromium expects a MotionEvent::ACTION_CANCEL instance to contain at least + // one touch point, whereas a QTouchCancel may not contain any touch points at all. + if (touchPoints.isEmpty()) + touchPoints = m_previousTouchPoints; + clearPreviousTouchMotionState(); + MotionEventQt cancelEvent(touchPoints, eventTimestamp, ui::MotionEvent::ACTION_CANCEL, + ev->modifiers(), dpiScale()); processMotionEvent(cancelEvent); return; } - - if (ev->type() == QEvent::TouchBegin) - m_sendMotionActionDown = true; + case QEvent::TouchEnd: + clearPreviousTouchMotionState(); + break; + default: + break; + } // Make sure that ACTION_POINTER_DOWN is delivered before ACTION_MOVE, // and ACTION_MOVE before ACTION_POINTER_UP. std::sort(touchPoints.begin(), touchPoints.end(), compareTouchPoints); + m_previousTouchPoints = touchPoints; for (int i = 0; i < touchPoints.size(); ++i) { ui::MotionEvent::Action action; switch (touchPoints[i].state()) { @@ -1043,21 +1077,24 @@ void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev) if (m_sendMotionActionDown) { action = ui::MotionEvent::ACTION_DOWN; m_sendMotionActionDown = false; - } else + } else { action = ui::MotionEvent::ACTION_POINTER_DOWN; + } break; case Qt::TouchPointMoved: action = ui::MotionEvent::ACTION_MOVE; break; case Qt::TouchPointReleased: - action = touchPoints.size() > 1 ? ui::MotionEvent::ACTION_POINTER_UP : ui::MotionEvent::ACTION_UP; + action = touchPoints.size() > 1 ? ui::MotionEvent::ACTION_POINTER_UP : + ui::MotionEvent::ACTION_UP; break; default: // Ignore Qt::TouchPointStationary continue; } - MotionEventQt motionEvent(touchPoints, eventTimestamp, action, ev->modifiers(), dpiScale(), i); + MotionEventQt motionEvent(touchPoints, eventTimestamp, action, ev->modifiers(), dpiScale(), + i); processMotionEvent(motionEvent); } } diff --git a/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h index 7c4723f0e..2a56f61a4 100644 --- a/src/core/render_widget_host_view_qt.h +++ b/src/core/render_widget_host_view_qt.h @@ -205,6 +205,7 @@ public: private: void sendDelegatedFrameAck(); void processMotionEvent(const ui::MotionEvent &motionEvent); + void clearPreviousTouchMotionState(); QList<QTouchEvent::TouchPoint> mapTouchPointIds(const QList<QTouchEvent::TouchPoint> &inputPoints); float dpiScale() const; @@ -214,7 +215,9 @@ private: ui::FilteredGestureProvider m_gestureProvider; base::TimeDelta m_eventsToNowDelta; bool m_sendMotionActionDown; + bool m_touchMotionStarted; QMap<int, int> m_touchIdMapping; + QList<QTouchEvent::TouchPoint> m_previousTouchPoints; scoped_ptr<RenderWidgetHostViewQtDelegate> m_delegate; QExplicitlySharedDataPointer<ChromiumCompositorData> m_chromiumCompositorData; |