diff --git a/src/core/desktop_screen_qt.cpp b/src/core/desktop_screen_qt.cpp
index bef15a90283d9876bdd8827773cdab8c2c2add02..e0f2b0dc2e4780fc38e7d34aaf172baafea0b130 100644
--- a/src/core/desktop_screen_qt.cpp
+++ b/src/core/desktop_screen_qt.cpp
@@ -98,7 +98,6 @@ gfx::Display DesktopScreenQt::GetDisplayMatching(const gfx::Rect& match_rect) co
 
 gfx::Display DesktopScreenQt::GetPrimaryDisplay() const
 {
-    Q_UNREACHABLE();
     return gfx::Display();
 }
 
diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp
index d2ad67af680fde25cf8036d1047ef8a8740079c9..eaa63192176f33fde15d3d3fc1b3cab4a37fe634 100644
--- a/src/core/render_widget_host_view_qt.cpp
+++ b/src/core/render_widget_host_view_qt.cpp
@@ -54,8 +54,8 @@
 #include "base/command_line.h"
 #include "cc/output/compositor_frame_ack.h"
 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
+#include "content/browser/renderer_host/input/web_input_event_util.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
-#include "content/browser/renderer_host/ui_events_helper.h"
 #include "content/common/cursors/webcursor.h"
 #include "content/common/gpu/gpu_messages.h"
 #include "content/common/view_messages.h"
@@ -67,7 +67,8 @@
 #include "third_party/WebKit/public/platform/WebCursorInfo.h"
 #include "third_party/WebKit/public/web/WebCompositionUnderline.h"
 #include "ui/base/clipboard/scoped_clipboard_writer.h"
-#include "ui/events/event.h"
+#include "ui/events/gesture_detection/gesture_config_helper.h"
+#include "ui/events/gesture_detection/motion_event.h"
 #include "ui/gfx/size_conversions.h"
 
 #include <QEvent>
@@ -84,21 +85,6 @@
 #include <QWindow>
 #include <QtGui/qaccessible.h>
 
-static inline ui::EventType toUIEventType(Qt::TouchPointState state)
-{
-    switch (state) {
-    case Qt::TouchPointPressed:
-        return ui::ET_TOUCH_PRESSED;
-    case Qt::TouchPointMoved:
-        return ui::ET_TOUCH_MOVED;
-    case Qt::TouchPointReleased:
-        return ui::ET_TOUCH_RELEASED;
-    default:
-        Q_ASSERT(false);
-        return ui::ET_UNKNOWN;
-    }
-}
-
 static inline Qt::InputMethodHints toQtInputMethodHints(ui::TextInputType inputType)
 {
     switch (inputType) {
@@ -134,30 +120,61 @@ static inline Qt::InputMethodHints toQtInputMethodHints(ui::TextInputType inputT
     }
 }
 
-static inline gfx::Point toGfxPoint(const QPoint& point)
-{
-    return gfx::Point(point.x(), point.y());
+static inline ui::GestureProvider::Config QtGestureProviderConfig() {
+    ui::GestureProvider::Config config = ui::DefaultGestureProviderConfig();
+    // Causes an assert in CreateWebGestureEventFromGestureEventData and we don't need them in Qt.
+    config.gesture_begin_end_types_enabled = false;
+    return config;
 }
 
-static void UpdateWebTouchEventAfterDispatch(blink::WebTouchEvent* event, blink::WebTouchPoint* point) {
-    if (point->state != blink::WebTouchPoint::StateReleased &&
-        point->state != blink::WebTouchPoint::StateCancelled)
-        return;
-    --event->touchesLength;
-    for (unsigned i = point - event->touches; i < event->touchesLength; ++i) {
-        event->touches[i] = event->touches[i + 1];
+class MotionEventQt : public ui::MotionEvent {
+public:
+    MotionEventQt(QTouchEvent *ev, const base::TimeTicks &eventTime, Action action, int index = -1)
+        : touchPoints(ev->touchPoints())
+        , eventTime(eventTime)
+        , action(action)
+        , index(index)
+    {
+        // ACTION_DOWN and ACTION_UP must be accesssed through pointer_index 0
+        Q_ASSERT((action != ACTION_DOWN && action != ACTION_UP) || index == 0);
     }
-}
 
-static bool shouldSendPinchGesture()
-{
-    static bool pinchAllowed = CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnablePinch);
-    return pinchAllowed;
-}
+    virtual int GetId() const Q_DECL_OVERRIDE { return 0; }
+    virtual Action GetAction() const Q_DECL_OVERRIDE { return action; }
+    virtual int GetActionIndex() const Q_DECL_OVERRIDE { return index; }
+    virtual size_t GetPointerCount() const Q_DECL_OVERRIDE { return touchPoints.size(); }
+    virtual int GetPointerId(size_t pointer_index) const Q_DECL_OVERRIDE { return touchPoints.at(pointer_index).id(); }
+    virtual float GetX(size_t pointer_index) const Q_DECL_OVERRIDE { return touchPoints.at(pointer_index).pos().x(); }
+    virtual float GetY(size_t pointer_index) const Q_DECL_OVERRIDE { return touchPoints.at(pointer_index).pos().y(); }
+    virtual float GetRawX(size_t pointer_index) const Q_DECL_OVERRIDE { return touchPoints.at(pointer_index).screenPos().x(); }
+    virtual float GetRawY(size_t pointer_index) const Q_DECL_OVERRIDE { return touchPoints.at(pointer_index).screenPos().y(); }
+    virtual float GetTouchMajor(size_t pointer_index) const Q_DECL_OVERRIDE { return touchPoints.at(pointer_index).rect().height(); }
+    virtual float GetPressure(size_t pointer_index) const Q_DECL_OVERRIDE { return touchPoints.at(pointer_index).pressure(); }
+    virtual base::TimeTicks GetEventTime() const Q_DECL_OVERRIDE { return eventTime; }
+
+    virtual size_t GetHistorySize() const Q_DECL_OVERRIDE { return 0; }
+    virtual base::TimeTicks GetHistoricalEventTime(size_t historical_index) const Q_DECL_OVERRIDE { return base::TimeTicks(); }
+    virtual float GetHistoricalTouchMajor(size_t pointer_index, size_t historical_index) const Q_DECL_OVERRIDE { return 0; }
+    virtual float GetHistoricalX(size_t pointer_index, size_t historical_index) const Q_DECL_OVERRIDE { return 0; }
+    virtual float GetHistoricalY(size_t pointer_index, size_t historical_index) const Q_DECL_OVERRIDE { return 0; }
+
+    virtual scoped_ptr<MotionEvent> Cancel() const Q_DECL_OVERRIDE { Q_UNREACHABLE(); return scoped_ptr<MotionEvent>(); }
+
+    virtual scoped_ptr<MotionEvent> Clone() const Q_DECL_OVERRIDE {
+        return scoped_ptr<MotionEvent>(new MotionEventQt(*this));
+    }
+
+private:
+    QList<QTouchEvent::TouchPoint> touchPoints;
+    base::TimeTicks eventTime;
+    Action action;
+    int index;
+};
 
 RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost* widget)
     : m_host(content::RenderWidgetHostImpl::From(widget))
-    , m_gestureRecognizer(ui::GestureRecognizer::Create())
+    , m_gestureProvider(QtGestureProviderConfig(), this)
+    , m_sendMotionActionDown(false)
     , m_frameNodeData(new DelegatedFrameNodeData)
     , m_needsDelegatedFrameAck(false)
     , m_didFirstVisuallyNonEmptyLayout(false)
@@ -167,12 +184,10 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost* widget
     , m_initPending(false)
 {
     m_host->SetView(this);
-    m_gestureRecognizer->AddGestureEventHelper(this);
 }
 
 RenderWidgetHostViewQt::~RenderWidgetHostViewQt()
 {
-    m_gestureRecognizer->RemoveGestureEventHelper(this);
 }
 
 void RenderWidgetHostViewQt::setDelegate(RenderWidgetHostViewQtDelegate* delegate)
@@ -618,26 +633,9 @@ void RenderWidgetHostViewQt::SelectionChanged(const base::string16 &text, size_t
 #endif
 }
 
-bool RenderWidgetHostViewQt::CanDispatchToConsumer(ui::GestureConsumer *consumer)
-{
-    Q_ASSERT(static_cast<RenderWidgetHostViewQt*>(consumer) == this);
-    return true;
-}
-
-void RenderWidgetHostViewQt::DispatchGestureEvent(ui::GestureEvent* event)
+void RenderWidgetHostViewQt::OnGestureEvent(const ui::GestureEventData& gesture)
 {
-    ForwardGestureEventToRenderer(event);
-}
-
-void RenderWidgetHostViewQt::DispatchCancelTouchEvent(ui::TouchEvent *event)
-{
-    if (!m_host)
-        return;
-
-    blink::WebTouchEvent cancelEvent;
-    cancelEvent.type = blink::WebInputEvent::TouchCancel;
-    cancelEvent.timeStampSeconds = event->time_stamp().InSecondsF();
-    m_host->ForwardTouchEventWithLatencyInfo(cancelEvent, *event->latency());
+    m_host->ForwardGestureEvent(content::CreateWebGestureEventFromGestureEventData(gesture));
 }
 
 QSGNode *RenderWidgetHostViewQt::updatePaintNode(QSGNode *oldNode)
@@ -684,6 +682,7 @@ bool RenderWidgetHostViewQt::forwardEvent(QEvent *event)
         Focus(); // Fall through.
     case QEvent::TouchUpdate:
     case QEvent::TouchEnd:
+    case QEvent::TouchCancel:
         handleTouchEvent(static_cast<QTouchEvent*>(event));
         break;
     case QEvent::HoverEnter:
@@ -737,16 +736,9 @@ void RenderWidgetHostViewQt::windowChanged()
 }
 
 void RenderWidgetHostViewQt::ProcessAckedTouchEvent(const content::TouchEventWithLatencyInfo &touch, content::InputEventAckState ack_result) {
-    ScopedVector<ui::TouchEvent> events;
-    if (!content::MakeUITouchEventsFromWebTouchEvents(touch, &events, content::LOCAL_COORDINATES))
-        return;
-
-    ui::EventResult result = (ack_result == content::INPUT_EVENT_ACK_STATE_CONSUMED) ? ui::ER_HANDLED : ui::ER_UNHANDLED;
-    for (ScopedVector<ui::TouchEvent>::iterator iter = events.begin(), end = events.end(); iter != end; ++iter)  {
-        scoped_ptr<ui::GestureRecognizer::Gestures> gestures;
-        gestures.reset(m_gestureRecognizer->ProcessTouchEventForGesture(*(*iter), result, this));
-        ProcessGestures(gestures.get());
-    }
+    Q_UNUSED(touch);
+    const bool eventConsumed = ack_result == content::INPUT_EVENT_ACK_STATE_CONSUMED;
+    m_gestureProvider.OnTouchEventAck(eventConsumed);
 }
 
 void RenderWidgetHostViewQt::sendDelegatedFrameAck()
@@ -758,70 +750,19 @@ void RenderWidgetHostViewQt::sendDelegatedFrameAck()
         m_host->GetProcess()->GetID(), ack);
 }
 
-void RenderWidgetHostViewQt::ForwardGestureEventToRenderer(ui::GestureEvent* gesture)
+void RenderWidgetHostViewQt::processMotionEvent(const ui::MotionEvent &motionEvent)
 {
-    if ((gesture->type() == ui::ET_GESTURE_PINCH_BEGIN
-       || gesture->type() == ui::ET_GESTURE_PINCH_UPDATE
-       || gesture->type() == ui::ET_GESTURE_PINCH_END)
-       && !shouldSendPinchGesture()
-       ) {
+    if (!m_gestureProvider.OnTouchEvent(motionEvent))
         return;
-    }
 
-    blink::WebGestureEvent webGestureEvent = content::MakeWebGestureEventFromUIEvent(*gesture);
-
-    if (webGestureEvent.type == blink::WebInputEvent::Undefined)
+    // Short-circuit touch forwarding if no touch handlers exist.
+    if (!m_host->ShouldForwardTouchEvent()) {
+        const bool eventConsumed = false;
+        m_gestureProvider.OnTouchEventAck(eventConsumed);
         return;
-
-    if (webGestureEvent.type == blink::WebGestureEvent::GestureTapDown) {
-        // Chromium does not stop a fling-scroll on tap-down.
-        // So explicitly send an event to stop any in-progress flings.
-        blink::WebGestureEvent flingCancel = webGestureEvent;
-        flingCancel.type = blink::WebInputEvent::GestureFlingCancel;
-        flingCancel.sourceDevice = blink::WebGestureDeviceTouchscreen;
-        m_host->ForwardGestureEvent(flingCancel);
     }
 
-    webGestureEvent.x = gesture->x();
-    webGestureEvent.y = gesture->y();
-    m_host->ForwardGestureEventWithLatencyInfo(webGestureEvent, *gesture->latency());
-}
-
-void RenderWidgetHostViewQt::ProcessGestures(ui::GestureRecognizer::Gestures *gestures)
-{
-    if (!gestures || gestures->empty())
-        return;
-    for (ui::GestureRecognizer::Gestures::iterator g_it = gestures->begin(); g_it != gestures->end(); ++g_it) {
-        ForwardGestureEventToRenderer(*g_it);
-    }
-}
-
-// Find (or create) a mapping to a 0-based ID.
-int RenderWidgetHostViewQt::GetMappedTouch(int qtTouchId)
-{
-    QMap<int, int>::const_iterator it = m_touchIdMapping.find(qtTouchId);
-    if (it != m_touchIdMapping.end())
-        return it.value();
-    int nextValue = 0;
-    for (it = m_touchIdMapping.begin(); it != m_touchIdMapping.end(); ++it)
-        nextValue = std::max(nextValue, it.value() + 1);
-    m_touchIdMapping[qtTouchId] = nextValue;
-    return nextValue;
-}
-
-void RenderWidgetHostViewQt::RemoveExpiredMappings(QTouchEvent *ev)
-{
-    QMap<int, int> newMap;
-    for (QMap<int, int>::const_iterator it = m_touchIdMapping.begin(); it != m_touchIdMapping.end(); ++it) {
-        Q_FOREACH (const QTouchEvent::TouchPoint& touchPoint, ev->touchPoints()) {
-            if ((touchPoint.id() == it.key()) &&
-                (touchPoint.state() != Qt::TouchPointReleased)) {
-                newMap.insert(it.key(), it.value());
-                break;
-            }
-        }
-    }
-    m_touchIdMapping.swap(newMap);
+    m_host->ForwardTouchEvent(content::CreateWebTouchEventFromMotionEvent(motionEvent));
 }
 
 float RenderWidgetHostViewQt::dpiScale() const
@@ -976,45 +917,44 @@ void RenderWidgetHostViewQt::handleTouchEvent(QTouchEvent *ev)
     // Calculate a delta between event timestamps and Now() on the first received event, and
     // apply this delta to all successive events. This delta is most likely smaller than it
     // should by calculating it here but this will hopefully cause less than one frame of delay.
-    base::TimeDelta eventTimestamp = base::TimeDelta::FromMilliseconds(ev->timestamp());
+    base::TimeTicks eventTimestamp = base::TimeTicks() + base::TimeDelta::FromMilliseconds(ev->timestamp());
     if (m_eventsToNowDelta == base::TimeDelta())
-        m_eventsToNowDelta = base::TimeTicks::Now() - base::TimeTicks() - eventTimestamp;
+        m_eventsToNowDelta = base::TimeTicks::Now() - eventTimestamp;
     eventTimestamp += m_eventsToNowDelta;
 
-    // Convert each of our QTouchEvent::TouchPoint to the simpler ui::TouchEvent to
-    // be able to use the same code path for both gesture recognition and WebTouchEvents.
-    // It's a waste to do a double QTouchEvent -> ui::TouchEvent -> blink::WebTouchEvent
-    // conversion but this should hopefully avoid a few bugs in the future.
-    // FIXME: Carry Qt::TouchCancel from the event to each TouchPoint.
-    Q_FOREACH (const QTouchEvent::TouchPoint& touchPoint, ev->touchPoints()) {
-        // Stationary touch points are already in our accumulator.
-        if (touchPoint.state() == Qt::TouchPointStationary)
-            continue;
+    if (ev->type() == QEvent::TouchCancel) {
+        MotionEventQt cancelEvent(ev, eventTimestamp, ui::MotionEvent::ACTION_CANCEL);
+        processMotionEvent(cancelEvent);
+        return;
+    }
 
-        ui::TouchEvent uiEvent(
-            toUIEventType(touchPoint.state()),
-            toGfxPoint((touchPoint.pos() / dpiScale()).toPoint()),
-            0, // flags
-            GetMappedTouch(touchPoint.id()),
-            eventTimestamp,
-            0, 0, // radius
-            0, // angle
-            touchPoint.pressure());
-
-        blink::WebTouchPoint *point = content::UpdateWebTouchEventFromUIEvent(uiEvent, &m_accumTouchEvent);
-        if (point) {
-            if (m_host->ShouldForwardTouchEvent())
-                // This will come back through ProcessAckedTouchEvent if the page didn't want it.
-                m_host->ForwardTouchEventWithLatencyInfo(m_accumTouchEvent, ui::LatencyInfo());
-            else {
-                scoped_ptr<ui::GestureRecognizer::Gestures> gestures;
-                gestures.reset(m_gestureRecognizer->ProcessTouchEventForGesture(uiEvent, ui::ER_UNHANDLED, this));
-                ProcessGestures(gestures.get());
-            }
-            UpdateWebTouchEventAfterDispatch(&m_accumTouchEvent, point);
+    if (ev->type() == QEvent::TouchBegin)
+        m_sendMotionActionDown = true;
+
+    for (int i = 0; i < ev->touchPoints().size(); ++i) {
+        ui::MotionEvent::Action action;
+        switch (ev->touchPoints()[i].state()) {
+        case Qt::TouchPointPressed:
+            if (m_sendMotionActionDown) {
+                action = ui::MotionEvent::ACTION_DOWN;
+                m_sendMotionActionDown = false;
+            } else
+                action = ui::MotionEvent::ACTION_POINTER_DOWN;
+            break;
+        case Qt::TouchPointMoved:
+            action = ui::MotionEvent::ACTION_MOVE;
+            break;
+        case Qt::TouchPointReleased:
+            action = ev->touchPoints().size() > 1 ? ui::MotionEvent::ACTION_POINTER_UP : ui::MotionEvent::ACTION_UP;
+            break;
+        default:
+            // Ignore Qt::TouchPointStationary
+            continue;
         }
+
+        MotionEventQt motionEvent(ev, eventTimestamp, action, i);
+        processMotionEvent(motionEvent);
     }
-    RemoveExpiredMappings(ev);
 }
 
 void RenderWidgetHostViewQt::handleHoverEvent(QHoverEvent *ev)
diff --git a/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h
index bb0478787b5a3215e47afde3110a43e0ef8b41b0..cbcd3a36ef8137171163d3ecf7220c449b50abb2 100644
--- a/src/core/render_widget_host_view_qt.h
+++ b/src/core/render_widget_host_view_qt.h
@@ -49,14 +49,13 @@
 #include "cc/resources/transferable_resource.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "content/browser/renderer_host/render_widget_host_view_base.h"
-#include "delegated_frame_node.h"
-#include "ui/events/gestures/gesture_recognizer.h"
-#include "ui/events/gestures/gesture_types.h"
-#include <QMap>
+#include "ui/events/gesture_detection/filtered_gesture_provider.h"
 #include <QPoint>
 #include <QRect>
 #include <QtGlobal>
 
+#include "delegated_frame_node.h"
+
 QT_BEGIN_NAMESPACE
 class QEvent;
 class QFocusEvent;
@@ -93,8 +92,7 @@ struct MultipleMouseClickHelper
 
 class RenderWidgetHostViewQt
     : public content::RenderWidgetHostViewBase
-    , public ui::GestureConsumer
-    , public ui::GestureEventHelper
+    , public ui::GestureProviderClient
     , public RenderWidgetHostViewQtDelegateClient
     , public content::BrowserAccessibilityDelegate
     , public base::SupportsWeakPtr<RenderWidgetHostViewQt>
@@ -157,10 +155,8 @@ public:
     // Overridden from RenderWidgetHostViewBase.
     virtual void SelectionChanged(const base::string16 &text, size_t offset, const gfx::Range &range) Q_DECL_OVERRIDE;
 
-    // Overridden from ui::GestureEventHelper.
-    virtual bool CanDispatchToConsumer(ui::GestureConsumer*) Q_DECL_OVERRIDE;
-    virtual void DispatchGestureEvent(ui::GestureEvent*) Q_DECL_OVERRIDE;
-    virtual void DispatchCancelTouchEvent(ui::TouchEvent*) Q_DECL_OVERRIDE;
+    // Overridden from ui::GestureProviderClient.
+    virtual void OnGestureEvent(const ui::GestureEventData& gesture) Q_DECL_OVERRIDE;
 
     // Overridden from RenderWidgetHostViewQtDelegateClient.
     virtual QSGNode *updatePaintNode(QSGNode *) Q_DECL_OVERRIDE;
@@ -224,20 +220,16 @@ public:
 
 private:
     void sendDelegatedFrameAck();
-    void ProcessGestures(ui::GestureRecognizer::Gestures *gestures);
-    void ForwardGestureEventToRenderer(ui::GestureEvent* gesture);
-    int GetMappedTouch(int qtTouchId);
-    void RemoveExpiredMappings(QTouchEvent *ev);
+    void processMotionEvent(const ui::MotionEvent &motionEvent);
     float dpiScale() const;
 
     bool IsPopup() const;
     void CreateBrowserAccessibilityManagerIfNeeded();
 
     content::RenderWidgetHostImpl *m_host;
-    scoped_ptr<ui::GestureRecognizer> m_gestureRecognizer;
+    ui::FilteredGestureProvider m_gestureProvider;
     base::TimeDelta m_eventsToNowDelta;
-    QMap<int, int> m_touchIdMapping;
-    blink::WebTouchEvent m_accumTouchEvent;
+    bool m_sendMotionActionDown;
     scoped_ptr<RenderWidgetHostViewQtDelegate> m_delegate;
 
     QExplicitlySharedDataPointer<DelegatedFrameNodeData> m_frameNodeData;