diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp
index d1fc7d17114e229ad1653f2bb99233b469c1ef18..a45383946f22ecf06a46c1e06f8cd1d0b7c5d4da 100644
--- a/src/core/render_widget_host_view_qt.cpp
+++ b/src/core/render_widget_host_view_qt.cpp
@@ -282,6 +282,7 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost* widget
     , m_cursorPositionWithinSelection(-1)
     , m_cursorPosition(0)
     , m_emptyPreviousSelection(true)
+    , m_wheelAckPending(false)
 {
     m_host->SetView(this);
 #ifndef QT_NO_ACCESSIBILITY
@@ -1350,7 +1351,28 @@ void RenderWidgetHostViewQt::accessibilityActiveChanged(bool active)
 
 void RenderWidgetHostViewQt::handleWheelEvent(QWheelEvent *ev)
 {
-    m_host->ForwardWheelEvent(WebEventFactory::toWebWheelEvent(ev, dpiScale()));
+    if (!m_wheelAckPending) {
+        Q_ASSERT(m_pendingWheelEvents.isEmpty());
+        m_wheelAckPending = true;
+        m_host->ForwardWheelEvent(WebEventFactory::toWebWheelEvent(ev, dpiScale()));
+        return;
+    }
+    if (!m_pendingWheelEvents.isEmpty()) {
+        // Try to combine with this wheel event with the last pending one.
+        if (WebEventFactory::coalesceWebWheelEvent(m_pendingWheelEvents.last(), ev, dpiScale()))
+            return;
+    }
+    m_pendingWheelEvents.append(WebEventFactory::toWebWheelEvent(ev, dpiScale()));
+}
+
+void RenderWidgetHostViewQt::WheelEventAck(const blink::WebMouseWheelEvent &/*event*/, content::InputEventAckState /*ack_result*/)
+{
+    m_wheelAckPending = false;
+    if (!m_pendingWheelEvents.isEmpty()) {
+        m_wheelAckPending = true;
+        m_host->ForwardWheelEvent(m_pendingWheelEvents.takeFirst());
+    }
+    // TODO: We could forward unhandled wheelevents to our parent.
 }
 
 void RenderWidgetHostViewQt::clearPreviousTouchMotionState()
diff --git a/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h
index 59e21b853932d372c97a90d4170e9ea4ee6de3a6..4b7f9094e44b14417890fcd2a3f830b29fbc3f7d 100644
--- a/src/core/render_widget_host_view_qt.h
+++ b/src/core/render_widget_host_view_qt.h
@@ -152,6 +152,7 @@ public:
     bool HasAcceleratedSurface(const gfx::Size&) override;
     void DidCreateNewRendererCompositorFrameSink(cc::mojom::CompositorFrameSinkClient* renderer_compositor_frame_sink) override;
     void SubmitCompositorFrame(const viz::LocalSurfaceId&, cc::CompositorFrame) override;
+    void WheelEventAck(const blink::WebMouseWheelEvent &event, content::InputEventAckState ack_result) override;
 
     void GetScreenInfo(content::ScreenInfo* results);
     gfx::Rect GetBoundsInRootWindow() override;
@@ -270,6 +271,9 @@ private:
     QString m_surroundingText;
 
     bool m_imeHasHiddenTextCapability;
+
+    bool m_wheelAckPending;
+    QList<blink::WebMouseWheelEvent> m_pendingWheelEvents;
 };
 
 } // namespace QtWebEngineCore
diff --git a/src/core/web_event_factory.cpp b/src/core/web_event_factory.cpp
index 18f8d393f0d76d057f65ce3610fdd8898b28558d..c31de19a287cbd686c93251480349f35a4b008c5 100644
--- a/src/core/web_event_factory.cpp
+++ b/src/core/web_event_factory.cpp
@@ -1247,38 +1247,56 @@ WebGestureEvent WebEventFactory::toWebGestureEvent(QNativeGestureEvent *ev, doub
 }
 #endif
 
-blink::WebMouseWheelEvent WebEventFactory::toWebWheelEvent(QWheelEvent *ev, double dpiScale)
+static void setBlinkWheelEventDelta(blink::WebMouseWheelEvent &webEvent)
 {
-    WebMouseWheelEvent webEvent;
-    webEvent.delta_x = 0;
-    webEvent.delta_y = 0;
-    webEvent.wheel_ticks_x = 0;
-    webEvent.wheel_ticks_y = 0;
-    webEvent.SetType(webEventTypeForEvent(ev));
-    webEvent.SetModifiers(modifiersForEvent(ev));
-    webEvent.SetTimeStampSeconds(currentTimeForEvent(ev));
-
-    webEvent.wheel_ticks_x = static_cast<float>(ev->angleDelta().x()) / QWheelEvent::DefaultDeltasPerStep;
-    webEvent.wheel_ticks_y = static_cast<float>(ev->angleDelta().y()) / QWheelEvent::DefaultDeltasPerStep;
-
     // We can't use the device specific QWheelEvent::pixelDelta(), so we calculate
     // a pixel delta based on ticks and scroll per line.
     static const float cDefaultQtScrollStep = 20.f;
 
 #if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
-    const int wheelScrollLines = QGuiApplication::styleHints()->wheelScrollLines();
+    static const int wheelScrollLines = QGuiApplication::styleHints()->wheelScrollLines();
 #else
-    const int wheelScrollLines = 3;
+    static const int wheelScrollLines = 3;
 #endif
     webEvent.delta_x = webEvent.wheel_ticks_x * wheelScrollLines * cDefaultQtScrollStep;
     webEvent.delta_y = webEvent.wheel_ticks_y * wheelScrollLines * cDefaultQtScrollStep;
+}
 
+
+blink::WebMouseWheelEvent WebEventFactory::toWebWheelEvent(QWheelEvent *ev, double dpiScale)
+{
+    WebMouseWheelEvent webEvent;
+    webEvent.SetType(webEventTypeForEvent(ev));
+    webEvent.SetModifiers(modifiersForEvent(ev));
+    webEvent.SetTimeStampSeconds(currentTimeForEvent(ev));
     webEvent.SetPositionInWidget(ev->x() / dpiScale, ev->y() / dpiScale);
     webEvent.SetPositionInScreen(ev->globalX(), ev->globalY());
 
+    webEvent.wheel_ticks_x = static_cast<float>(ev->angleDelta().x()) / QWheelEvent::DefaultDeltasPerStep;
+    webEvent.wheel_ticks_y = static_cast<float>(ev->angleDelta().y()) / QWheelEvent::DefaultDeltasPerStep;
+    setBlinkWheelEventDelta(webEvent);
+
     return webEvent;
 }
 
+bool WebEventFactory::coalesceWebWheelEvent(blink::WebMouseWheelEvent &webEvent, QWheelEvent *ev, double dpiScale)
+{
+    if (webEventTypeForEvent(ev) != webEvent.GetType())
+        return false;
+    if (modifiersForEvent(ev) != webEvent.GetModifiers())
+        return false;
+
+    webEvent.SetTimeStampSeconds(currentTimeForEvent(ev));
+    webEvent.SetPositionInWidget(ev->x() / dpiScale, ev->y() / dpiScale);
+    webEvent.SetPositionInScreen(ev->globalX(), ev->globalY());
+
+    webEvent.wheel_ticks_x += static_cast<float>(ev->angleDelta().x()) / QWheelEvent::DefaultDeltasPerStep;
+    webEvent.wheel_ticks_y += static_cast<float>(ev->angleDelta().y()) / QWheelEvent::DefaultDeltasPerStep;
+    setBlinkWheelEventDelta(webEvent);
+
+    return true;
+}
+
 content::NativeWebKeyboardEvent WebEventFactory::toWebKeyboardEvent(QKeyEvent *ev)
 {
     content::NativeWebKeyboardEvent webKitEvent(reinterpret_cast<gfx::NativeEvent>(ev));
diff --git a/src/core/web_event_factory.h b/src/core/web_event_factory.h
index 259795c1f5d4abb8aea847a61f9c6f826fe1c1b1..5758af848109469bd6ed1400bfa26fec91b79da5 100644
--- a/src/core/web_event_factory.h
+++ b/src/core/web_event_factory.h
@@ -68,6 +68,7 @@ public:
     static blink::WebGestureEvent toWebGestureEvent(QNativeGestureEvent *, double dpiScale);
 #endif
     static blink::WebMouseWheelEvent toWebWheelEvent(QWheelEvent*, double dpiScale);
+    static bool coalesceWebWheelEvent(blink::WebMouseWheelEvent &, QWheelEvent*, double dpiScale);
     static content::NativeWebKeyboardEvent toWebKeyboardEvent(QKeyEvent*);
 };