Title: [286346] trunk/Source
Revision
286346
Author
timothy_hor...@apple.com
Date
2021-11-30 18:56:24 -0800 (Tue, 30 Nov 2021)

Log Message

Add a momentum event synthesizer
https://bugs.webkit.org/show_bug.cgi?id=233653
<rdar://problem/85571258>

Reviewed by Simon Fraser.

Source/WebCore/PAL:

* pal/spi/mac/IOKitSPIMac.h:
Add some SPI.

Source/WebKit:

* Platform/Logging.h:
Add ScrollAnimations log channel to WebKit (it already exists in WebCore).

* Shared/ScrollingAccelerationCurve.cpp: Added.
(WebKit::ScrollingAccelerationCurve::ScrollingAccelerationCurve):
(WebKit::ScrollingAccelerationCurve::interpolate):
(WebKit::ScrollingAccelerationCurve::computeIntermediateValuesIfNeeded):
(WebKit::ScrollingAccelerationCurve::evaluateQuartic):
(WebKit::ScrollingAccelerationCurve::accelerationFactor):
(WebKit::ScrollingAccelerationCurve::encode const):
(WebKit::ScrollingAccelerationCurve::decode):
(WebKit::operator<<):
(WebKit::ScrollingAccelerationCurve::fromNativeWheelEvent):
* Shared/ScrollingAccelerationCurve.h: Added.
(WebKit::ScrollingAccelerationCurve::operator== const):
(WebKit::ScrollingAccelerationCurve::operator!= const):
Add a class that represents a quartic scrolling acceleration curve with
a trailing linear tangent region, and allows interpolation between curves
and evaluation of the curve at a point.

* Shared/mac/ScrollingAccelerationCurveMac.mm: Added.
(WebKit::fromFixedPoint):
(WebKit::readFixedPointParameter):
(WebKit::fromIOHIDCurve):
(WebKit::fromIOHIDCurveArrayWithAcceleration):
(WebKit::ScrollingAccelerationCurve::fromNativeWheelEvent):
Given a NativeWebWheelEvent, extract its underlying platform event
and fetch the ScrollingAccelerationCurve for the originating device.

* Sources.txt:
* SourcesCocoa.txt:
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::handleWheelEvent):
Fetch the current ScrollingAccelerationCurve on each momentum begin event,
if the feature is enabled.

(WebKit::WebPageProxy::sendWheelEvent):
Send the ScrollingAccelerationCurve to EventDispatcher just before
we send the momentum begin event along, if it has changed.

(WebKit::WebPageProxy::windowScreenDidChange):
Keep EventDispatcher apprised of changes to the current screen of the page.

(WebKit::WebPageProxy::resetState):
* UIProcess/WebPageProxy.h:
* WebKit.xcodeproj/project.pbxproj:
* WebProcess/WebPage/EventDispatcher.cpp:
(WebKit::EventDispatcher::EventDispatcher):
(WebKit::EventDispatcher::internalWheelEvent):
Factor most of wheelEvent out into internalWheelEvent, so that
MomentumEventDispatcher can call internalWheelEvent(), skipping the code
in wheelEvent() that forwards the event... to MomentumEventDispatcher.

Also, keep track of where the event came from (UI process or MomentumEventDispatcher)
and avoid sending didReceiveEvent replies to the UI process for synthetic
events that it doesn't know about.

(WebKit::EventDispatcher::wheelEvent):
Forward wheel events to MomentumEventDispatcher.

(WebKit::EventDispatcher::setScrollingAccelerationCurve):
Forward scrolling curve changes to MomentumEventDispatcher.

(WebKit::EventDispatcher::displayWasRefreshed):
Forward display refresh callbacks to MomentumEventDispatcher.

(WebKit::EventDispatcher::windowScreenDidChange):
Forward page screen changes to MomentumEventDispatcher.

* WebProcess/WebPage/EventDispatcher.h:
* WebProcess/WebPage/EventDispatcher.messages.in:

* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::wheelEvent):
(WebKit::WebPage::dispatchWheelEventWithoutScrolling):
* WebProcess/WebPage/WebPage.h:
Don't send didReceiveEvent for synthetic events from the main thread either.

* WebProcess/WebPage/MomentumEventDispatcher.cpp: Added.
(WebKit::MomentumEventDispatcher::MomentumEventDispatcher):
(WebKit::MomentumEventDispatcher::~MomentumEventDispatcher):
(WebKit::MomentumEventDispatcher::eventShouldStartSyntheticMomentumPhase const):
If we have everything we need (a scrolling curve - which also implies
that the setting is enabled - and raw platform deltas), and the event
is a momentum begin event, we can use it to start a synthetic momentum phase.

(WebKit::MomentumEventDispatcher::handleWheelEvent):
Start or stop the momentum phase as appropriate.
Record any incoming fingers-down phase events in a rolling event history structure.
Block platform-event-driven events from being further handled by WebKit
if we are inside a synthetic momentum phase, since we'll be replacing them.

(WebKit::appKitScrollMultiplierForEvent):
Determine the AppKit scroll muliplier ("accelerated" vs "unaccelerated"
deltas in NSEvent parlance) from the event via division.

(WebKit::MomentumEventDispatcher::dispatchSyntheticMomentumEvent):
Dispatch a synthetic event with the given momentum phase and delta.
We borrow some properties from the event that initiated the momentum
phase (e.g. the position), and some from whatever event we've most
recently seen (even if we told the rest of WebKit to ignore it), so
that we get the most up-to-date state for e.g. the keyboard modifiers.

(WebKit::MomentumEventDispatcher::didStartMomentumPhase):
Record relevant information about the start event, generate the
momentum curve, and start a full-rate display link. Also, send a momentum
begin event, since we blocked the real one.

(WebKit::MomentumEventDispatcher::didEndMomentumPhase):
Send a momentum ended event, shut down the display link, and reset all
per-gesture state.

(WebKit::MomentumEventDispatcher::setScrollingAccelerationCurve):
Store ScrollingAccelerationCurves on a per-page basis.

(WebKit::MomentumEventDispatcher::displayID const):
Look up the displayID for the current gesture's page.

(WebKit::MomentumEventDispatcher::startDisplayLink):
(WebKit::MomentumEventDispatcher::stopDisplayLink):
Start and stop a display link, which will - in a roundabout manner -
eventually call back into displayWasRefreshed.

(WebKit::MomentumEventDispatcher::windowScreenDidChange):
Store DisplayIDs on a per-page basis.

(WebKit::MomentumEventDispatcher::displayWasRefreshed):
When the display link calls us back, emit a single momentum changed
event of sufficient delta to move us to the desired offset along the curve.

(WebKit::MomentumEventDispatcher::didScrollWithInterval):
(WebKit::MomentumEventDispatcher::didScroll):
Record incoming scrolling events into a rolling history deque.

(WebKit::MomentumEventDispatcher::buildOffsetTableWithInitialDelta):
Compute the offsets for the entire "ideal curve" table (at 60Hz) by
running the animation and accumulating the deltas.

(WebKit::interpolate):
(WebKit::MomentumEventDispatcher::offsetAtTime):
Interpolate along the "ideal curve" to determine the intended offset
at a given time.

(WebKit::momentumDecayRate):
(WebKit::MomentumEventDispatcher::computeNextDelta):
Compute the next delta given a delta (and the event history) by applying
a bit of exponential decay and then passing it through the
ScrollingAccelerationCurve. We return both the unaccelerated
and accelerated deltas, because the input to the next iteration of
computeNextDelta is the unaccelerated delta, but the delta applied
to the actual scroll position is the accelerated delta.

* WebProcess/WebPage/MomentumEventDispatcher.h: Added.

Source/WTF:

* wtf/PlatformEnable.h:
Add an ENABLE().

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WTF/ChangeLog (286345 => 286346)


--- trunk/Source/WTF/ChangeLog	2021-12-01 02:32:45 UTC (rev 286345)
+++ trunk/Source/WTF/ChangeLog	2021-12-01 02:56:24 UTC (rev 286346)
@@ -1,3 +1,14 @@
+2021-11-30  Tim Horton  <timothy_hor...@apple.com>
+
+        Add a momentum event synthesizer
+        https://bugs.webkit.org/show_bug.cgi?id=233653
+        <rdar://problem/85571258>
+
+        Reviewed by Simon Fraser.
+
+        * wtf/PlatformEnable.h:
+        Add an ENABLE().
+
 2021-11-30  Keith Miller  <keith_mil...@apple.com>
 
         Structures should be allocated out of an aligned pool of memory so StructureID->Structure* is fast.

Modified: trunk/Source/WTF/wtf/PlatformEnable.h (286345 => 286346)


--- trunk/Source/WTF/wtf/PlatformEnable.h	2021-12-01 02:32:45 UTC (rev 286345)
+++ trunk/Source/WTF/wtf/PlatformEnable.h	2021-12-01 02:56:24 UTC (rev 286346)
@@ -876,6 +876,10 @@
 #define ENABLE_WHEEL_EVENT_LATCHING 1
 #endif
 
+#if PLATFORM(MAC)
+#define ENABLE_MOMENTUM_EVENT_DISPATCHER 1
+#endif
+
 #if !defined(ENABLE_SCROLLING_THREAD)
 #if USE(NICOSIA)
 #define ENABLE_SCROLLING_THREAD 1

Modified: trunk/Source/WebCore/PAL/ChangeLog (286345 => 286346)


--- trunk/Source/WebCore/PAL/ChangeLog	2021-12-01 02:32:45 UTC (rev 286345)
+++ trunk/Source/WebCore/PAL/ChangeLog	2021-12-01 02:56:24 UTC (rev 286346)
@@ -1,3 +1,14 @@
+2021-11-30  Tim Horton  <timothy_hor...@apple.com>
+
+        Add a momentum event synthesizer
+        https://bugs.webkit.org/show_bug.cgi?id=233653
+        <rdar://problem/85571258>
+
+        Reviewed by Simon Fraser.
+
+        * pal/spi/mac/IOKitSPIMac.h:
+        Add some SPI.
+
 2021-11-30  Commit Queue  <commit-qu...@webkit.org>
 
         Unreviewed, reverting r286227.

Modified: trunk/Source/WebCore/PAL/pal/spi/mac/IOKitSPIMac.h (286345 => 286346)


--- trunk/Source/WebCore/PAL/pal/spi/mac/IOKitSPIMac.h	2021-12-01 02:32:45 UTC (rev 286345)
+++ trunk/Source/WebCore/PAL/pal/spi/mac/IOKitSPIMac.h	2021-12-01 02:56:24 UTC (rev 286346)
@@ -66,6 +66,8 @@
 void IOHIDEventSystemClientRegisterDeviceMatchingBlock(IOHIDEventSystemClientRef, IOHIDServiceClientBlock, void *, void *);
 void IOHIDEventSystemClientUnregisterDeviceMatchingBlock(IOHIDEventSystemClientRef);
 void IOHIDEventSystemClientScheduleWithDispatchQueue(IOHIDEventSystemClientRef, dispatch_queue_t);
+void IOHIDEventSystemClientSetDispatchQueue(IOHIDEventSystemClientRef, dispatch_queue_t);
+void IOHIDEventSystemClientActivate(IOHIDEventSystemClientRef);
 
 CFTypeRef IOHIDServiceClientCopyProperty(IOHIDServiceClientRef service, CFStringRef key);
 
@@ -84,6 +86,7 @@
 typedef uint32_t IOHIDEventType;
 
 typedef uint32_t IOHIDEventField;
+typedef uint64_t IOHIDEventSenderID;
 
 #ifdef __LP64__
 typedef double IOHIDFloat;
@@ -99,6 +102,7 @@
 
 uint64_t IOHIDEventGetTimeStamp(IOHIDEventRef);
 IOHIDFloat IOHIDEventGetFloatValue(IOHIDEventRef, IOHIDEventField);
+IOHIDEventSenderID IOHIDEventGetSenderID(IOHIDEventRef);
 
 WTF_EXTERN_C_END
 

Modified: trunk/Source/WebKit/ChangeLog (286345 => 286346)


--- trunk/Source/WebKit/ChangeLog	2021-12-01 02:32:45 UTC (rev 286345)
+++ trunk/Source/WebKit/ChangeLog	2021-12-01 02:56:24 UTC (rev 286346)
@@ -1,3 +1,165 @@
+2021-11-30  Tim Horton  <timothy_hor...@apple.com>
+
+        Add a momentum event synthesizer
+        https://bugs.webkit.org/show_bug.cgi?id=233653
+        <rdar://problem/85571258>
+
+        Reviewed by Simon Fraser.
+
+        * Platform/Logging.h:
+        Add ScrollAnimations log channel to WebKit (it already exists in WebCore).
+
+        * Shared/ScrollingAccelerationCurve.cpp: Added.
+        (WebKit::ScrollingAccelerationCurve::ScrollingAccelerationCurve):
+        (WebKit::ScrollingAccelerationCurve::interpolate):
+        (WebKit::ScrollingAccelerationCurve::computeIntermediateValuesIfNeeded):
+        (WebKit::ScrollingAccelerationCurve::evaluateQuartic):
+        (WebKit::ScrollingAccelerationCurve::accelerationFactor):
+        (WebKit::ScrollingAccelerationCurve::encode const):
+        (WebKit::ScrollingAccelerationCurve::decode):
+        (WebKit::operator<<):
+        (WebKit::ScrollingAccelerationCurve::fromNativeWheelEvent):
+        * Shared/ScrollingAccelerationCurve.h: Added.
+        (WebKit::ScrollingAccelerationCurve::operator== const):
+        (WebKit::ScrollingAccelerationCurve::operator!= const):
+        Add a class that represents a quartic scrolling acceleration curve with
+        a trailing linear tangent region, and allows interpolation between curves
+        and evaluation of the curve at a point.
+
+        * Shared/mac/ScrollingAccelerationCurveMac.mm: Added.
+        (WebKit::fromFixedPoint):
+        (WebKit::readFixedPointParameter):
+        (WebKit::fromIOHIDCurve):
+        (WebKit::fromIOHIDCurveArrayWithAcceleration):
+        (WebKit::ScrollingAccelerationCurve::fromNativeWheelEvent):
+        Given a NativeWebWheelEvent, extract its underlying platform event
+        and fetch the ScrollingAccelerationCurve for the originating device.
+
+        * Sources.txt:
+        * SourcesCocoa.txt:
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::handleWheelEvent):
+        Fetch the current ScrollingAccelerationCurve on each momentum begin event,
+        if the feature is enabled.
+
+        (WebKit::WebPageProxy::sendWheelEvent):
+        Send the ScrollingAccelerationCurve to EventDispatcher just before
+        we send the momentum begin event along, if it has changed.
+
+        (WebKit::WebPageProxy::windowScreenDidChange):
+        Keep EventDispatcher apprised of changes to the current screen of the page.
+
+        (WebKit::WebPageProxy::resetState):
+        * UIProcess/WebPageProxy.h:
+        * WebKit.xcodeproj/project.pbxproj:
+        * WebProcess/WebPage/EventDispatcher.cpp:
+        (WebKit::EventDispatcher::EventDispatcher):
+        (WebKit::EventDispatcher::internalWheelEvent):
+        Factor most of wheelEvent out into internalWheelEvent, so that
+        MomentumEventDispatcher can call internalWheelEvent(), skipping the code
+        in wheelEvent() that forwards the event... to MomentumEventDispatcher.
+
+        Also, keep track of where the event came from (UI process or MomentumEventDispatcher)
+        and avoid sending didReceiveEvent replies to the UI process for synthetic
+        events that it doesn't know about.
+
+        (WebKit::EventDispatcher::wheelEvent):
+        Forward wheel events to MomentumEventDispatcher.
+
+        (WebKit::EventDispatcher::setScrollingAccelerationCurve):
+        Forward scrolling curve changes to MomentumEventDispatcher.
+
+        (WebKit::EventDispatcher::displayWasRefreshed):
+        Forward display refresh callbacks to MomentumEventDispatcher.
+
+        (WebKit::EventDispatcher::windowScreenDidChange):
+        Forward page screen changes to MomentumEventDispatcher.
+
+        * WebProcess/WebPage/EventDispatcher.h:
+        * WebProcess/WebPage/EventDispatcher.messages.in:
+
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::wheelEvent):
+        (WebKit::WebPage::dispatchWheelEventWithoutScrolling):
+        * WebProcess/WebPage/WebPage.h:
+        Don't send didReceiveEvent for synthetic events from the main thread either.
+
+        * WebProcess/WebPage/MomentumEventDispatcher.cpp: Added.
+        (WebKit::MomentumEventDispatcher::MomentumEventDispatcher):
+        (WebKit::MomentumEventDispatcher::~MomentumEventDispatcher):
+        (WebKit::MomentumEventDispatcher::eventShouldStartSyntheticMomentumPhase const):
+        If we have everything we need (a scrolling curve - which also implies
+        that the setting is enabled - and raw platform deltas), and the event
+        is a momentum begin event, we can use it to start a synthetic momentum phase. 
+
+        (WebKit::MomentumEventDispatcher::handleWheelEvent):
+        Start or stop the momentum phase as appropriate.
+        Record any incoming fingers-down phase events in a rolling event history structure.
+        Block platform-event-driven events from being further handled by WebKit
+        if we are inside a synthetic momentum phase, since we'll be replacing them.
+
+        (WebKit::appKitScrollMultiplierForEvent):
+        Determine the AppKit scroll muliplier ("accelerated" vs "unaccelerated"
+        deltas in NSEvent parlance) from the event via division.
+
+        (WebKit::MomentumEventDispatcher::dispatchSyntheticMomentumEvent):
+        Dispatch a synthetic event with the given momentum phase and delta.
+        We borrow some properties from the event that initiated the momentum
+        phase (e.g. the position), and some from whatever event we've most
+        recently seen (even if we told the rest of WebKit to ignore it), so
+        that we get the most up-to-date state for e.g. the keyboard modifiers.
+
+        (WebKit::MomentumEventDispatcher::didStartMomentumPhase):
+        Record relevant information about the start event, generate the
+        momentum curve, and start a full-rate display link. Also, send a momentum
+        begin event, since we blocked the real one.
+
+        (WebKit::MomentumEventDispatcher::didEndMomentumPhase):
+        Send a momentum ended event, shut down the display link, and reset all
+        per-gesture state.
+
+        (WebKit::MomentumEventDispatcher::setScrollingAccelerationCurve):
+        Store ScrollingAccelerationCurves on a per-page basis.
+
+        (WebKit::MomentumEventDispatcher::displayID const):
+        Look up the displayID for the current gesture's page.
+
+        (WebKit::MomentumEventDispatcher::startDisplayLink):
+        (WebKit::MomentumEventDispatcher::stopDisplayLink):
+        Start and stop a display link, which will - in a roundabout manner -
+        eventually call back into displayWasRefreshed.
+
+        (WebKit::MomentumEventDispatcher::windowScreenDidChange):
+        Store DisplayIDs on a per-page basis.
+
+        (WebKit::MomentumEventDispatcher::displayWasRefreshed):
+        When the display link calls us back, emit a single momentum changed
+        event of sufficient delta to move us to the desired offset along the curve.
+
+        (WebKit::MomentumEventDispatcher::didScrollWithInterval):
+        (WebKit::MomentumEventDispatcher::didScroll):
+        Record incoming scrolling events into a rolling history deque.
+
+        (WebKit::MomentumEventDispatcher::buildOffsetTableWithInitialDelta):
+        Compute the offsets for the entire "ideal curve" table (at 60Hz) by
+        running the animation and accumulating the deltas.
+
+        (WebKit::interpolate):
+        (WebKit::MomentumEventDispatcher::offsetAtTime):
+        Interpolate along the "ideal curve" to determine the intended offset
+        at a given time.
+
+        (WebKit::momentumDecayRate):
+        (WebKit::MomentumEventDispatcher::computeNextDelta):
+        Compute the next delta given a delta (and the event history) by applying
+        a bit of exponential decay and then passing it through the
+        ScrollingAccelerationCurve. We return both the unaccelerated
+        and accelerated deltas, because the input to the next iteration of
+        computeNextDelta is the unaccelerated delta, but the delta applied
+        to the actual scroll position is the accelerated delta.
+
+        * WebProcess/WebPage/MomentumEventDispatcher.h: Added.
+
 2021-11-30  BJ Burg  <bb...@apple.com>
 
         Web Inspector: add ExtensionTabActivation diagnostic event

Modified: trunk/Source/WebKit/Platform/Logging.h (286345 => 286346)


--- trunk/Source/WebKit/Platform/Logging.h	2021-12-01 02:32:45 UTC (rev 286345)
+++ trunk/Source/WebKit/Platform/Logging.h	2021-12-01 02:56:24 UTC (rev 286346)
@@ -91,6 +91,7 @@
     M(RemoteLayerTree) \
     M(Resize) \
     M(ResourceLoadStatistics) \
+    M(ScrollAnimations) \
     M(Scrolling) \
     M(Selection) \
     M(ServiceWorker) \

Added: trunk/Source/WebKit/Shared/ScrollingAccelerationCurve.cpp (0 => 286346)


--- trunk/Source/WebKit/Shared/ScrollingAccelerationCurve.cpp	                        (rev 0)
+++ trunk/Source/WebKit/Shared/ScrollingAccelerationCurve.cpp	2021-12-01 02:56:24 UTC (rev 286346)
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ScrollingAccelerationCurve.h"
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+
+#include "ArgumentCoders.h"
+#include "WebCoreArgumentCoders.h"
+#include <wtf/text/TextStream.h>
+
+namespace WebKit {
+
+ScrollingAccelerationCurve::ScrollingAccelerationCurve(float gainLinear, float gainParabolic, float gainCubic, float gainQuartic, float tangentSpeedLinear, float tangentSpeedParabolicRoot, float resolution)
+    : m_parameters { gainLinear, gainParabolic, gainCubic, gainQuartic, tangentSpeedLinear, tangentSpeedParabolicRoot, resolution }
+{
+}
+
+ScrollingAccelerationCurve ScrollingAccelerationCurve::interpolate(const ScrollingAccelerationCurve& from, const ScrollingAccelerationCurve& to, float amount)
+{
+    auto interpolate = [&] (float a, float b) -> float {
+        return a + amount * (b - a);
+    };
+
+    auto gainLinear = interpolate(from.m_parameters.gainLinear, to.m_parameters.gainLinear);
+    auto gainParabolic = interpolate(from.m_parameters.gainParabolic, to.m_parameters.gainParabolic);
+    auto gainCubic = interpolate(from.m_parameters.gainCubic, to.m_parameters.gainCubic);
+    auto gainQuartic = interpolate(from.m_parameters.gainQuartic, to.m_parameters.gainQuartic);
+
+    auto tangentSpeedLinear = interpolate(from.m_parameters.tangentSpeedLinear, to.m_parameters.tangentSpeedLinear);
+    auto tangentSpeedParabolicRoot = interpolate(from.m_parameters.tangentSpeedParabolicRoot, to.m_parameters.tangentSpeedParabolicRoot);
+
+    return { gainLinear, gainParabolic, gainCubic, gainQuartic, tangentSpeedLinear, tangentSpeedParabolicRoot, from.m_parameters.resolution };
+}
+
+void ScrollingAccelerationCurve::computeIntermediateValuesIfNeeded()
+{
+    if (m_intermediates)
+        return;
+    m_intermediates = ComputedIntermediateValues { };
+
+    float tangentInitialY;
+    m_intermediates->tangentStartX = std::numeric_limits<float>::max();
+    m_intermediates->falloffStartX = std::numeric_limits<float>::max();
+
+    auto quarticSlope = [&](float x) {
+        return (4 * std::pow(x, 3) * std::pow(m_parameters.gainQuartic, 4)) + (3 * std::pow(x, 2) * std::pow(m_parameters.gainCubic, 3)) + (2 * x * std::pow(m_parameters.gainParabolic, 2)) + m_parameters.gainLinear;
+    };
+
+    if (m_parameters.tangentSpeedLinear) {
+        tangentInitialY = evaluateQuartic(m_parameters.tangentSpeedLinear);
+        m_intermediates->tangentSlope = quarticSlope(m_parameters.tangentSpeedLinear);
+        m_intermediates->tangentIntercept = tangentInitialY - m_intermediates->tangentSlope * m_parameters.tangentSpeedLinear;
+        m_intermediates->tangentStartX = m_parameters.tangentSpeedLinear;
+
+        if (m_parameters.tangentSpeedParabolicRoot) {
+            float y1 = m_intermediates->tangentSlope * m_parameters.tangentSpeedParabolicRoot + m_intermediates->tangentIntercept;
+            m_intermediates->falloffSlope = 2 * y1 * m_intermediates->tangentSlope;
+            m_intermediates->falloffIntercept = std::pow(y1, 2) - m_intermediates->falloffSlope * m_parameters.tangentSpeedParabolicRoot;
+            m_intermediates->falloffStartX = m_parameters.tangentSpeedParabolicRoot;
+        }
+    } else if (m_parameters.tangentSpeedParabolicRoot) {
+        tangentInitialY = evaluateQuartic(m_parameters.tangentSpeedParabolicRoot);
+        m_intermediates->falloffSlope = quarticSlope(m_parameters.tangentSpeedParabolicRoot);
+        m_intermediates->falloffIntercept = std::pow(tangentInitialY, 2) - m_intermediates->falloffSlope *  m_parameters.tangentSpeedParabolicRoot;
+        m_intermediates->tangentStartX = m_parameters.tangentSpeedParabolicRoot;
+    }
+}
+
+float ScrollingAccelerationCurve::evaluateQuartic(float x) const
+{
+    return std::pow(m_parameters.gainQuartic * x, 4) + std::pow(m_parameters.gainCubic * x, 3) + std::pow(m_parameters.gainParabolic * x, 2) + m_parameters.gainLinear * x;
+}
+
+float ScrollingAccelerationCurve::accelerationFactor(float value)
+{
+    constexpr float frameRate = 67.f; // Why is this 67 and not 60?
+    constexpr float cursorScale = 96.f / frameRate;
+
+    computeIntermediateValuesIfNeeded();
+
+    float multiplier;
+    float deviceScale = m_parameters.resolution / frameRate;
+    value /= deviceScale;
+
+    if (value <= m_intermediates->tangentStartX)
+        multiplier = evaluateQuartic(value);
+    else if (value <= m_intermediates->falloffStartX && m_intermediates->tangentStartX == m_parameters.tangentSpeedLinear)
+        multiplier = m_intermediates->tangentSlope * value + m_intermediates->tangentIntercept;
+    else
+        multiplier = std::sqrt(m_intermediates->falloffSlope * value + m_intermediates->falloffIntercept);
+
+    return multiplier * cursorScale;
+}
+
+void ScrollingAccelerationCurve::encode(IPC::Encoder& encoder) const
+{
+    encoder << m_parameters.gainLinear;
+    encoder << m_parameters.gainParabolic;
+    encoder << m_parameters.gainCubic;
+    encoder << m_parameters.gainQuartic;
+
+    encoder << m_parameters.tangentSpeedLinear;
+    encoder << m_parameters.tangentSpeedParabolicRoot;
+
+    encoder << m_parameters.resolution;
+}
+
+std::optional<ScrollingAccelerationCurve> ScrollingAccelerationCurve::decode(IPC::Decoder& decoder)
+{
+    float gainLinear;
+    if (!decoder.decode(gainLinear))
+        return std::nullopt;
+    float gainParabolic;
+    if (!decoder.decode(gainParabolic))
+        return std::nullopt;
+    float gainCubic;
+    if (!decoder.decode(gainCubic))
+        return std::nullopt;
+    float gainQuartic;
+    if (!decoder.decode(gainQuartic))
+        return std::nullopt;
+
+    float tangentSpeedLinear;
+    if (!decoder.decode(tangentSpeedLinear))
+        return std::nullopt;
+    float tangentSpeedParabolicRoot;
+    if (!decoder.decode(tangentSpeedParabolicRoot))
+        return std::nullopt;
+
+    float resolution;
+    if (!decoder.decode(resolution))
+        return std::nullopt;
+
+    return { { gainLinear, gainParabolic, gainCubic, gainQuartic, tangentSpeedLinear, tangentSpeedParabolicRoot, resolution } };
+}
+
+TextStream& operator<<(TextStream& ts, const ScrollingAccelerationCurve& curve)
+{
+    TextStream::GroupScope group(ts);
+
+    ts << "ScrollingAccelerationCurve";
+
+    ts.dumpProperty("gainLinear", curve.m_parameters.gainLinear);
+    ts.dumpProperty("gainParabolic", curve.m_parameters.gainParabolic);
+    ts.dumpProperty("gainCubic", curve.m_parameters.gainCubic);
+    ts.dumpProperty("gainQuartic", curve.m_parameters.gainQuartic);
+    ts.dumpProperty("tangentSpeedLinear", curve.m_parameters.tangentSpeedLinear);
+    ts.dumpProperty("tangentSpeedParabolicRoot", curve.m_parameters.tangentSpeedParabolicRoot);
+    ts.dumpProperty("resolution", curve.m_parameters.resolution);
+
+    return ts;
+}
+
+#if !PLATFORM(MAC)
+
+std::optional<ScrollingAccelerationCurve> ScrollingAccelerationCurve::fromNativeWheelEvent(const NativeWebWheelEvent&)
+{
+    return std::nullopt;
+}
+
+#endif
+
+} // namespace WebKit
+
+#endif // ENABLE(MOMENTUM_EVENT_DISPATCHER)

Added: trunk/Source/WebKit/Shared/ScrollingAccelerationCurve.h (0 => 286346)


--- trunk/Source/WebKit/Shared/ScrollingAccelerationCurve.h	                        (rev 0)
+++ trunk/Source/WebKit/Shared/ScrollingAccelerationCurve.h	2021-12-01 02:56:24 UTC (rev 286346)
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+
+#include <WebCore/FloatSize.h>
+#include <wtf/text/WTFString.h>
+
+namespace IPC {
+class Decoder;
+class Encoder;
+}
+
+namespace WebKit {
+
+class NativeWebWheelEvent;
+
+class ScrollingAccelerationCurve {
+public:
+    ScrollingAccelerationCurve(float gainLinear, float gainParabolic, float gainCubic, float gainQuartic, float tangentSpeedLinear, float tangentSpeedParabolicRoot, float resolution);
+
+    static std::optional<ScrollingAccelerationCurve> fromNativeWheelEvent(const NativeWebWheelEvent&);
+
+    static ScrollingAccelerationCurve interpolate(const ScrollingAccelerationCurve& from, const ScrollingAccelerationCurve& to, float amount);
+
+    float accelerationFactor(float);
+
+    void encode(IPC::Encoder&) const;
+    static std::optional<ScrollingAccelerationCurve> decode(IPC::Decoder&);
+
+    bool operator==(const ScrollingAccelerationCurve& other) const
+    {
+        return m_parameters.gainLinear == other.m_parameters.gainLinear
+            && m_parameters.gainParabolic == other.m_parameters.gainParabolic
+            && m_parameters.gainCubic == other.m_parameters.gainCubic
+            && m_parameters.gainQuartic == other.m_parameters.gainQuartic
+            && m_parameters.tangentSpeedLinear == other.m_parameters.tangentSpeedLinear
+            && m_parameters.tangentSpeedParabolicRoot == other.m_parameters.tangentSpeedParabolicRoot
+            && m_parameters.resolution == other.m_parameters.resolution;
+    }
+
+    bool operator!=(const ScrollingAccelerationCurve& other) const { return !(*this == other); }
+    
+private:
+    friend TextStream& operator<<(TextStream&, const ScrollingAccelerationCurve&);
+
+    void computeIntermediateValuesIfNeeded();
+
+    float evaluateQuartic(float) const;
+
+    struct {
+        float gainLinear { 0 };
+        float gainParabolic { 0 };
+        float gainCubic { 0 };
+        float gainQuartic { 0 };
+        float tangentSpeedLinear { 0 };
+        float tangentSpeedParabolicRoot { 0 };
+        float resolution { 0 };
+    } m_parameters;
+
+    struct ComputedIntermediateValues {
+        float tangentStartX { 0 };
+        float tangentSlope { 0 };
+        float tangentIntercept { 0 };
+        float falloffStartX { 0 };
+        float falloffSlope { 0 };
+        float falloffIntercept { 0 };
+    };
+
+    std::optional<ComputedIntermediateValues> m_intermediates;
+};
+
+TextStream& operator<<(TextStream&, const ScrollingAccelerationCurve&);
+
+} // namespace WebKit
+
+#endif // ENABLE(MOMENTUM_EVENT_DISPATCHER)

Added: trunk/Source/WebKit/Shared/mac/ScrollingAccelerationCurveMac.mm (0 => 286346)


--- trunk/Source/WebKit/Shared/mac/ScrollingAccelerationCurveMac.mm	                        (rev 0)
+++ trunk/Source/WebKit/Shared/mac/ScrollingAccelerationCurveMac.mm	2021-12-01 02:56:24 UTC (rev 286346)
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "ScrollingAccelerationCurve.h"
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER) && PLATFORM(MAC)
+
+#import "Logging.h"
+#import <pal/spi/cg/CoreGraphicsSPI.h>
+#import <pal/spi/cocoa/IOKitSPI.h>
+
+namespace WebKit {
+
+static float fromFixedPoint(float value)
+{
+    return value / 65536.0f;
+}
+
+static float readFixedPointParameter(NSDictionary *parameters, const char *key)
+{
+    return fromFixedPoint([[parameters objectForKey:@(key)] floatValue]);
+}
+
+static ScrollingAccelerationCurve fromIOHIDCurve(NSDictionary *parameters, float resolution)
+{
+    auto gainLinear = readFixedPointParameter(parameters, kHIDAccelGainLinearKey);
+    auto gainParabolic = readFixedPointParameter(parameters, kHIDAccelGainParabolicKey);
+    auto gainCubic = readFixedPointParameter(parameters, kHIDAccelGainCubicKey);
+    auto gainQuartic = readFixedPointParameter(parameters, kHIDAccelGainQuarticKey);
+
+    auto tangentSpeedLinear = readFixedPointParameter(parameters, kHIDAccelTangentSpeedLinearKey);
+    auto tangentSpeedParabolicRoot = readFixedPointParameter(parameters, kHIDAccelTangentSpeedParabolicRootKey);
+
+    return { gainLinear, gainParabolic, gainCubic, gainQuartic, tangentSpeedLinear, tangentSpeedParabolicRoot, resolution };
+}
+
+static ScrollingAccelerationCurve fromIOHIDCurveArrayWithAcceleration(NSArray<NSDictionary *> *ioHIDCurves, float desiredAcceleration, float resolution)
+{
+    __block size_t currentIndex = 0;
+    __block Vector<std::pair<float, ScrollingAccelerationCurve>> curves;
+
+    [ioHIDCurves enumerateObjectsUsingBlock:^(NSDictionary *parameters, NSUInteger i, BOOL *) {
+        auto curveAcceleration = readFixedPointParameter(parameters, kHIDAccelIndexKey);
+        auto curve = fromIOHIDCurve(parameters, resolution);
+
+        if (desiredAcceleration > curveAcceleration)
+            currentIndex = i;
+
+        curves.append({ curveAcceleration, curve });
+    }];
+
+    // Interpolation if desiredAcceleration is in between two curves.
+    if (curves[currentIndex].first < desiredAcceleration && (currentIndex + 1) < curves.size()) {
+        const auto& lowCurve = curves[currentIndex];
+        const auto& highCurve = curves[currentIndex + 1];
+        float ratio = (desiredAcceleration - lowCurve.first) / (highCurve.first - lowCurve.first);
+        return ScrollingAccelerationCurve::interpolate(lowCurve.second, highCurve.second, ratio);
+    }
+
+    return curves[currentIndex].second;
+}
+
+static RetainPtr<IOHIDEventSystemClientRef> createHIDClient()
+{
+    auto client = adoptCF(IOHIDEventSystemClientCreateWithType(nil, kIOHIDEventSystemClientTypePassive, nil));
+    IOHIDEventSystemClientSetDispatchQueue(client.get(), dispatch_get_main_queue());
+    IOHIDEventSystemClientActivate(client.get());
+    return client;
+}
+
+static std::optional<ScrollingAccelerationCurve> fromIOHIDDevice(IOHIDEventSenderID senderID)
+{
+    static NeverDestroyed<RetainPtr<IOHIDEventSystemClientRef>> client;
+    if (!client.get())
+        client.get() = createHIDClient();
+
+    RetainPtr<IOHIDServiceClientRef> ioHIDService = adoptCF(IOHIDEventSystemClientCopyServiceForRegistryID(client.get().get(), senderID));
+    if (!ioHIDService) {
+        RELEASE_LOG(ScrollAnimations, "ScrollingAccelerationCurve::fromIOHIDDevice did not find matching HID service");
+        return std::nullopt;
+    }
+
+    auto curves = adoptCF(dynamic_cf_cast<CFArrayRef>(IOHIDServiceClientCopyProperty(ioHIDService.get(), CFSTR(kHIDScrollAccelParametricCurvesKey))));
+    if (!curves) {
+        RELEASE_LOG(ScrollAnimations, "ScrollingAccelerationCurve::fromIOHIDDevice failed to look up curves");
+        return std::nullopt;
+    }
+
+    // FIXME: There is some additional fallback to implement here, though this seems usually sufficient.
+    auto scrollAccelerationType = adoptCF(dynamic_cf_cast<CFStringRef>(IOHIDServiceClientCopyProperty(ioHIDService.get(), CFSTR("HIDScrollAccelerationType"))));
+    if (!scrollAccelerationType) {
+        RELEASE_LOG(ScrollAnimations, "ScrollingAccelerationCurve::fromIOHIDDevice failed to look up acceleration type");
+        return std::nullopt;
+    }
+
+    auto scrollAcceleration = adoptCF(dynamic_cf_cast<CFNumberRef>(IOHIDServiceClientCopyProperty(ioHIDService.get(), scrollAccelerationType.get())));
+    if (!scrollAcceleration) {
+        RELEASE_LOG(ScrollAnimations, "ScrollingAccelerationCurve::fromIOHIDDevice failed to look up acceleration value");
+        return std::nullopt;
+    }
+
+    auto resolution = adoptCF(dynamic_cf_cast<CFNumberRef>(IOHIDServiceClientCopyProperty(ioHIDService.get(), CFSTR(kIOHIDScrollResolutionKey))));
+    if (!resolution) {
+        RELEASE_LOG(ScrollAnimations, "ScrollingAccelerationCurve::fromIOHIDDevice failed to look up resolution");
+        return std::nullopt;
+    }
+
+    return fromIOHIDCurveArrayWithAcceleration((NSArray *)curves.get(), fromFixedPoint([(NSNumber *)scrollAcceleration.get() floatValue]), fromFixedPoint([(NSNumber *)resolution.get() floatValue]));
+}
+
+std::optional<ScrollingAccelerationCurve> ScrollingAccelerationCurve::fromNativeWheelEvent(const NativeWebWheelEvent& nativeWebWheelEvent)
+{
+    NSEvent *event = nativeWebWheelEvent.nativeEvent();
+
+    auto cgEvent = event.CGEvent;
+    if (!cgEvent) {
+        RELEASE_LOG(ScrollAnimations, "ScrollingAccelerationCurve::fromNativeWheelEvent did not find CG event");
+        return std::nullopt;
+    }
+
+    auto hidEvent = adoptCF(CGEventCopyIOHIDEvent(cgEvent));
+    if (!hidEvent) {
+        RELEASE_LOG(ScrollAnimations, "ScrollingAccelerationCurve::fromNativeWheelEvent did not find HID event");
+        return std::nullopt;
+    }
+
+    return fromIOHIDDevice(IOHIDEventGetSenderID(hidEvent.get()));
+}
+
+} // namespace WebKit
+
+#endif // ENABLE(MOMENTUM_EVENT_DISPATCHER) && PLATFORM(MAC)

Modified: trunk/Source/WebKit/Sources.txt (286345 => 286346)


--- trunk/Source/WebKit/Sources.txt	2021-12-01 02:32:45 UTC (rev 286345)
+++ trunk/Source/WebKit/Sources.txt	2021-12-01 02:56:24 UTC (rev 286346)
@@ -225,6 +225,7 @@
 Shared/PrintInfo.cpp
 Shared/RTCNetwork.cpp
 Shared/RTCPacketOptions.cpp
+Shared/ScrollingAccelerationCurve.cpp
 Shared/ServiceWorkerInitializationData.cpp
 Shared/SessionState.cpp
 Shared/ShareableBitmap.cpp @no-unify
@@ -813,6 +814,7 @@
 WebProcess/WebPage/EventDispatcher.cpp
 WebProcess/WebPage/FindController.cpp
 WebProcess/WebPage/IPCTestingAPI.cpp
+WebProcess/WebPage/MomentumEventDispatcher.cpp
 WebProcess/WebPage/PageBanner.cpp
 WebProcess/WebPage/VisitedLinkTableController.cpp
 WebProcess/WebPage/WebBackForwardListProxy.cpp

Modified: trunk/Source/WebKit/SourcesCocoa.txt (286345 => 286346)


--- trunk/Source/WebKit/SourcesCocoa.txt	2021-12-01 02:32:45 UTC (rev 286345)
+++ trunk/Source/WebKit/SourcesCocoa.txt	2021-12-01 02:56:24 UTC (rev 286346)
@@ -220,6 +220,7 @@
 Shared/mac/ObjCObjectGraph.mm
 Shared/mac/PasteboardTypes.mm
 Shared/mac/PrintInfoMac.mm
+Shared/mac/ScrollingAccelerationCurveMac.mm
 Shared/mac/SecItemRequestData.cpp
 Shared/mac/SecItemResponseData.cpp
 Shared/mac/SecItemShim.cpp

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.cpp (286345 => 286346)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.cpp	2021-12-01 02:32:45 UTC (rev 286345)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.cpp	2021-12-01 02:56:24 UTC (rev 286346)
@@ -2894,6 +2894,12 @@
 
     closeOverlayedViews();
 
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+    // FIXME: We should not have to look this up repeatedly, but it can also change occasionally.
+    if (event.momentumPhase() == WebWheelEvent::PhaseBegan && preferences().momentumScrollingAnimatorEnabled())
+        m_scrollingAccelerationCurve = ScrollingAccelerationCurve::fromNativeWheelEvent(event);
+#endif
+
     if (wheelEventCoalescer().shouldDispatchEvent(event)) {
         auto event = wheelEventCoalescer().nextEventToDispatch();
         sendWheelEvent(*event);
@@ -2941,6 +2947,14 @@
         rubberBandableEdges.setLeft(!m_backForwardList->backItem());
         rubberBandableEdges.setRight(!m_backForwardList->forwardItem());
     }
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+    if (event.momentumPhase() == WebWheelEvent::PhaseBegan && m_scrollingAccelerationCurve != m_lastSentScrollingAccelerationCurve) {
+        connection->send(Messages::EventDispatcher::SetScrollingAccelerationCurve(m_webPageID, *m_scrollingAccelerationCurve), 0, { }, Thread::QOS::UserInteractive);
+        m_lastSentScrollingAccelerationCurve = m_scrollingAccelerationCurve;
+    }
+#endif
+
     connection->send(Messages::EventDispatcher::WheelEvent(m_webPageID, event, rubberBandableEdges), 0, { }, Thread::QOS::UserInteractive);
 
     // Manually ping the web process to check for responsiveness since our wheel
@@ -3941,6 +3955,7 @@
     if (!hasRunningProcess())
         return;
 
+    send(Messages::EventDispatcher::PageScreenDidChange(m_webPageID, displayID));
     send(Messages::WebPage::WindowScreenDidChange(displayID, nominalFramesPerSecond));
 }
 
@@ -7899,6 +7914,10 @@
         m_xrSystem = nullptr;
     }
 #endif
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+    m_lastSentScrollingAccelerationCurve = std::nullopt;
+#endif
 }
 
 void WebPageProxy::resetStateAfterProcessExited(ProcessTerminationReason terminationReason)

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.h (286345 => 286346)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.h	2021-12-01 02:32:45 UTC (rev 286345)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.h	2021-12-01 02:56:24 UTC (rev 286346)
@@ -51,6 +51,7 @@
 #include "ProcessTerminationReason.h"
 #include "ProcessThrottler.h"
 #include "SandboxExtension.h"
+#include "ScrollingAccelerationCurve.h"
 #include "ShareableBitmap.h"
 #include "ShareableResource.h"
 #include "SpeechRecognitionPermissionRequest.h"
@@ -3153,6 +3154,11 @@
     WindowKind m_windowKind { WindowKind::Unparented };
 
     WebNotificationManagerMessageHandler m_notificationManagerMessageHandler;
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+    std::optional<ScrollingAccelerationCurve> m_scrollingAccelerationCurve;
+    std::optional<ScrollingAccelerationCurve> m_lastSentScrollingAccelerationCurve;
+#endif
 };
 
 #ifdef __OBJC__

Modified: trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj (286345 => 286346)


--- trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2021-12-01 02:32:45 UTC (rev 286345)
+++ trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2021-12-01 02:56:24 UTC (rev 286346)
@@ -3500,6 +3500,9 @@
 		2D5C9D0419C81D8F00B3C5C1 /* WebPageOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebPageOverlay.h; sourceTree = "<group>"; };
 		2D6482492644B1AB00030335 /* RemoteLayerTreeLayers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RemoteLayerTreeLayers.h; sourceTree = "<group>"; };
 		2D64824A2644B1AB00030335 /* RemoteLayerTreeLayers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RemoteLayerTreeLayers.mm; sourceTree = "<group>"; };
+		2D65D194274B8E73009C4101 /* ScrollingAccelerationCurveMac.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ScrollingAccelerationCurveMac.mm; sourceTree = "<group>"; };
+		2D65D195274B8E84009C4101 /* ScrollingAccelerationCurve.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScrollingAccelerationCurve.h; sourceTree = "<group>"; };
+		2D65D196274B8E84009C4101 /* ScrollingAccelerationCurve.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScrollingAccelerationCurve.cpp; sourceTree = "<group>"; };
 		2D6AB53F192B1C4A003A9FD1 /* WKPDFPageNumberIndicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WKPDFPageNumberIndicator.h; path = ios/WKPDFPageNumberIndicator.h; sourceTree = "<group>"; };
 		2D6AB540192B1C4A003A9FD1 /* WKPDFPageNumberIndicator.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = WKPDFPageNumberIndicator.mm; path = ios/WKPDFPageNumberIndicator.mm; sourceTree = "<group>"; };
 		2D6B371918A967AD0042AE80 /* _WKThumbnailView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _WKThumbnailView.h; sourceTree = "<group>"; };
@@ -3783,6 +3786,8 @@
 		2DE9B1382231F61C005287B7 /* _WKTextInputContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _WKTextInputContext.h; sourceTree = "<group>"; };
 		2DE9B13B2231F77C005287B7 /* _WKTextInputContextInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _WKTextInputContextInternal.h; sourceTree = "<group>"; };
 		2DEAC5CE1AC368BB00A195D8 /* _WKFindOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _WKFindOptions.h; sourceTree = "<group>"; };
+		2DEE709D2755A46800FBF864 /* MomentumEventDispatcher.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MomentumEventDispatcher.h; sourceTree = "<group>"; };
+		2DEE709E2755A46800FBF864 /* MomentumEventDispatcher.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MomentumEventDispatcher.cpp; sourceTree = "<group>"; };
 		2DF9593418A42412009785A1 /* ViewGestureControllerIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ViewGestureControllerIOS.mm; path = ios/ViewGestureControllerIOS.mm; sourceTree = "<group>"; };
 		2DF9EEE31A781FB400B6CFBE /* APIFrameInfo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = APIFrameInfo.cpp; sourceTree = "<group>"; };
 		2DF9EEE41A781FB400B6CFBE /* APIFrameInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APIFrameInfo.h; sourceTree = "<group>"; };
@@ -7089,6 +7094,8 @@
 				BC2D021612AC41CB00E732A3 /* SameDocumentNavigationType.h */,
 				1AAB4A8C1296F0A20023952F /* SandboxExtension.h */,
 				E1E552C316AE065E004ED653 /* SandboxInitializationParameters.h */,
+				2D65D196274B8E84009C4101 /* ScrollingAccelerationCurve.cpp */,
+				2D65D195274B8E84009C4101 /* ScrollingAccelerationCurve.h */,
 				5C80B3DD23690F100086E6DE /* ServiceWorkerInitializationData.cpp */,
 				5C80B3DB23690D8D0086E6DE /* ServiceWorkerInitializationData.h */,
 				1AFDE6571954A42B00C48FFA /* SessionState.cpp */,
@@ -9880,9 +9887,9 @@
 		5C157A0A2717C9F100ED5280 /* webpushd */ = {
 			isa = PBXGroup;
 			children = (
-				5CBB6D4D271F67CC00FD1A5D /* com.apple.webkit.webpushd.plist */,
 				5160E954274B887100567388 /* AppBundleRequest.h */,
 				5160E953274B887100567388 /* AppBundleRequest.mm */,
+				5CBB6D4D271F67CC00FD1A5D /* com.apple.webkit.webpushd.plist */,
 				51F7BB75274498BB00C45A72 /* MockAppBundleForTesting.h */,
 				51F7BB74274498BA00C45A72 /* MockAppBundleForTesting.mm */,
 				5160E95C274C2A0300567388 /* MockAppBundleRegistry.h */,
@@ -10621,6 +10628,8 @@
 				1A90C1F21264FD71003E44D4 /* FindController.h */,
 				9B033E72252580F300501071 /* IPCTestingAPI.cpp */,
 				9B033E71252580F300501071 /* IPCTestingAPI.h */,
+				2DEE709E2755A46800FBF864 /* MomentumEventDispatcher.cpp */,
+				2DEE709D2755A46800FBF864 /* MomentumEventDispatcher.h */,
 				7C387433172F5615001BD88A /* PageBanner.cpp */,
 				7CF47FF917275C57008ACB91 /* PageBanner.h */,
 				2D819B99186275B3001F03D1 /* ViewGestureGeometryCollector.cpp */,
@@ -11187,6 +11196,7 @@
 				C574A58012E66681002DFE98 /* PasteboardTypes.mm */,
 				C1E123B920A11572002646F4 /* PDFContextMenu.h */,
 				E1CC1B8F12D7EADF00625838 /* PrintInfoMac.mm */,
+				2D65D194274B8E73009C4101 /* ScrollingAccelerationCurveMac.mm */,
 				51D1304F1382EAC000351EDD /* SecItemRequestData.cpp */,
 				51D130501382EAC000351EDD /* SecItemRequestData.h */,
 				51D130511382EAC000351EDD /* SecItemResponseData.cpp */,
@@ -12435,13 +12445,13 @@
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				5160E956274B887200567388 /* AppBundleRequest.h in Headers */,
+				5C1579FC2717AF5000ED5280 /* DaemonUtilities.h in Headers */,
+				51F7BB77274498BB00C45A72 /* MockAppBundleForTesting.h in Headers */,
+				5160E95E274C2A0300567388 /* MockAppBundleRegistry.h in Headers */,
 				5160E959274C0D8900567388 /* PushAppBundle.h in Headers */,
 				51F7BB7B2744C50700C45A72 /* PushClientConnection.h in Headers */,
-				5160E95E274C2A0300567388 /* MockAppBundleRegistry.h in Headers */,
-				5C1579FC2717AF5000ED5280 /* DaemonUtilities.h in Headers */,
-				5160E956274B887200567388 /* AppBundleRequest.h in Headers */,
 				512CD69F2723393A00F7F8EC /* WebPushDaemon.h in Headers */,
-				51F7BB77274498BB00C45A72 /* MockAppBundleForTesting.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -14975,16 +14985,16 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				5160E955274B887200567388 /* AppBundleRequest.mm in Sources */,
 				5C157A012717B7FB00ED5280 /* ArgumentCoders.cpp in Sources */,
 				51F7BB7D2745640400C45A72 /* CodeSigning.mm in Sources */,
 				5C1579FF2717B6D200ED5280 /* DaemonDecoder.cpp in Sources */,
+				5C1579FE2717B6C100ED5280 /* DaemonEncoder.cpp in Sources */,
+				5C1579FB2717AF5000ED5280 /* DaemonUtilities.mm in Sources */,
 				51F7BB76274498BB00C45A72 /* MockAppBundleForTesting.mm in Sources */,
-				5160E955274B887200567388 /* AppBundleRequest.mm in Sources */,
+				5160E960274C2A4000567388 /* MockAppBundleRegistry.mm in Sources */,
 				5160E95A274C0D8900567388 /* PushAppBundle.mm in Sources */,
-				5160E960274C2A4000567388 /* MockAppBundleRegistry.mm in Sources */,
 				51F7BB7C2744C50700C45A72 /* PushClientConnection.mm in Sources */,
-				5C1579FE2717B6C100ED5280 /* DaemonEncoder.cpp in Sources */,
-				5C1579FB2717AF5000ED5280 /* DaemonUtilities.mm in Sources */,
 				512CD6A02723393A00F7F8EC /* WebPushDaemon.mm in Sources */,
 				5C157A0C2717CA1D00ED5280 /* WebPushDaemonMain.mm in Sources */,
 			);

Modified: trunk/Source/WebKit/WebProcess/WebPage/EventDispatcher.cpp (286345 => 286346)


--- trunk/Source/WebKit/WebProcess/WebPage/EventDispatcher.cpp	2021-12-01 02:32:45 UTC (rev 286345)
+++ trunk/Source/WebKit/WebProcess/WebPage/EventDispatcher.cpp	2021-12-01 02:56:24 UTC (rev 286346)
@@ -27,6 +27,7 @@
 #include "EventDispatcher.h"
 
 #include "EventDispatcherMessages.h"
+#include "MomentumEventDispatcher.h"
 #include "WebEventConversion.h"
 #include "WebPage.h"
 #include "WebPageProxyMessages.h"
@@ -63,6 +64,9 @@
 EventDispatcher::EventDispatcher()
     : m_queue(WorkQueue::create("com.apple.WebKit.EventDispatcher", WorkQueue::QOS::UserInteractive))
     , m_recentWheelEventDeltaFilter(WheelEventDeltaFilter::create())
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+    , m_momentumEventDispatcher(WTF::makeUnique<MomentumEventDispatcher>(*this))
+#endif
 {
 }
 
@@ -97,7 +101,7 @@
     connection->addWorkQueueMessageReceiver(Messages::EventDispatcher::messageReceiverName(), m_queue.get(), this);
 }
 
-void EventDispatcher::wheelEvent(PageIdentifier pageID, const WebWheelEvent& wheelEvent, RectEdges<bool> rubberBandableEdges)
+void EventDispatcher::internalWheelEvent(PageIdentifier pageID, const WebWheelEvent& wheelEvent, RectEdges<bool> rubberBandableEdges, WheelEventOrigin wheelEventOrigin)
 {
     auto processingSteps = OptionSet<WebCore::WheelEventProcessingSteps> { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch };
 #if ENABLE(SCROLLING_THREAD)
@@ -114,7 +118,7 @@
         Locker locker { m_scrollingTreesLock };
         auto scrollingTree = m_scrollingTrees.get(pageID);
         if (!scrollingTree) {
-            dispatchWheelEventViaMainThread(pageID, wheelEvent, processingSteps);
+            dispatchWheelEventViaMainThread(pageID, wheelEvent, processingSteps, wheelEventOrigin);
             break;
         }
         
@@ -136,10 +140,10 @@
 
         scrollingTree->willProcessWheelEvent();
 
-        ScrollingThread::dispatch([scrollingTree, wheelEvent, platformWheelEvent, processingSteps, useMainThreadForScrolling, pageID, protectedThis = Ref { *this }] {
+        ScrollingThread::dispatch([scrollingTree, wheelEvent, platformWheelEvent, processingSteps, useMainThreadForScrolling, pageID, wheelEventOrigin, protectedThis = Ref { *this }] {
             if (useMainThreadForScrolling) {
                 scrollingTree->willSendEventToMainThread(platformWheelEvent);
-                protectedThis->dispatchWheelEventViaMainThread(pageID, wheelEvent, processingSteps);
+                protectedThis->dispatchWheelEventViaMainThread(pageID, wheelEvent, processingSteps, wheelEventOrigin);
                 scrollingTree->waitForEventToBeProcessedByMainThread(platformWheelEvent);
                 return;
             }
@@ -147,7 +151,7 @@
             auto result = scrollingTree->handleWheelEvent(platformWheelEvent, processingSteps);
 
             if (result.needsMainThreadProcessing()) {
-                protectedThis->dispatchWheelEventViaMainThread(pageID, wheelEvent, result.steps);
+                protectedThis->dispatchWheelEventViaMainThread(pageID, wheelEvent, result.steps, wheelEventOrigin);
                 if (result.steps.contains(WheelEventProcessingSteps::MainThreadForScrolling))
                     return;
             }
@@ -154,16 +158,35 @@
 
             // If we scrolled on the scrolling thread (even if we send the event to the main thread for passive event handlers)
             // respond to the UI process that the event was handled.
-            protectedThis->sendDidReceiveEvent(pageID, wheelEvent.type(), result.wasHandled);
+            if (wheelEventOrigin == WheelEventOrigin::UIProcess)
+                protectedThis->sendDidReceiveEvent(pageID, wheelEvent.type(), result.wasHandled);
         });
     } while (false);
 #else
     UNUSED_PARAM(rubberBandableEdges);
 
-    dispatchWheelEventViaMainThread(pageID, wheelEvent, processingSteps);
+    dispatchWheelEventViaMainThread(pageID, wheelEvent, processingSteps, wheelEventOrigin);
 #endif
 }
 
+void EventDispatcher::wheelEvent(PageIdentifier pageID, const WebWheelEvent& wheelEvent, RectEdges<bool> rubberBandableEdges)
+{
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+    if (m_momentumEventDispatcher->handleWheelEvent(pageID, wheelEvent, rubberBandableEdges)) {
+        sendDidReceiveEvent(pageID, wheelEvent.type(), true);
+        return;
+    }
+#endif
+    internalWheelEvent(pageID, wheelEvent, rubberBandableEdges, WheelEventOrigin::UIProcess);
+}
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+void EventDispatcher::setScrollingAccelerationCurve(PageIdentifier pageID, std::optional<ScrollingAccelerationCurve> curve)
+{
+    m_momentumEventDispatcher->setScrollingAccelerationCurve(pageID, curve);
+}
+#endif
+
 #if ENABLE(MAC_GESTURE_EVENTS)
 void EventDispatcher::gestureEvent(PageIdentifier pageID, const WebKit::WebGestureEvent& gestureEvent)
 {
@@ -230,15 +253,15 @@
 }
 #endif
 
-void EventDispatcher::dispatchWheelEventViaMainThread(WebCore::PageIdentifier pageID, const WebWheelEvent& wheelEvent, OptionSet<WheelEventProcessingSteps> processingSteps)
+void EventDispatcher::dispatchWheelEventViaMainThread(WebCore::PageIdentifier pageID, const WebWheelEvent& wheelEvent, OptionSet<WheelEventProcessingSteps> processingSteps, WheelEventOrigin wheelEventOrigin)
 {
     ASSERT(!RunLoop::isMain());
-    RunLoop::main().dispatch([protectedThis = Ref { *this }, pageID, wheelEvent, steps = processingSteps - WheelEventProcessingSteps::ScrollingThread]() mutable {
-        protectedThis->dispatchWheelEvent(pageID, wheelEvent, steps);
+    RunLoop::main().dispatch([protectedThis = Ref { *this }, pageID, wheelEvent, wheelEventOrigin, steps = processingSteps - WheelEventProcessingSteps::ScrollingThread]() mutable {
+        protectedThis->dispatchWheelEvent(pageID, wheelEvent, steps, wheelEventOrigin);
     });
 }
 
-void EventDispatcher::dispatchWheelEvent(PageIdentifier pageID, const WebWheelEvent& wheelEvent, OptionSet<WheelEventProcessingSteps> processingSteps)
+void EventDispatcher::dispatchWheelEvent(PageIdentifier pageID, const WebWheelEvent& wheelEvent, OptionSet<WheelEventProcessingSteps> processingSteps, WheelEventOrigin wheelEventOrigin)
 {
     ASSERT(RunLoop::isMain());
 
@@ -246,7 +269,7 @@
     if (!webPage)
         return;
 
-    webPage->wheelEvent(wheelEvent, processingSteps);
+    webPage->wheelEvent(wheelEvent, processingSteps, wheelEventOrigin);
 }
 
 #if ENABLE(MAC_GESTURE_EVENTS)
@@ -284,6 +307,11 @@
     tracePoint(DisplayRefreshDispatchingToMainThread, displayID, sendToMainThread);
 
     ASSERT(!RunLoop::isMain());
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+    m_momentumEventDispatcher->displayWasRefreshed(displayID, displayUpdate);
+#endif
+
     notifyScrollingTreesDisplayWasRefreshed(displayID);
 
     if (!sendToMainThread)
@@ -295,4 +323,14 @@
 }
 #endif
 
+void EventDispatcher::pageScreenDidChange(PageIdentifier pageID, PlatformDisplayID displayID)
+{
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+    m_momentumEventDispatcher->pageScreenDidChange(pageID, displayID);
+#else
+    UNUSED_PARAM(pageID);
+    UNUSED_PARAM(displayID);
+#endif
+}
+
 } // namespace WebKit

Modified: trunk/Source/WebKit/WebProcess/WebPage/EventDispatcher.h (286345 => 286346)


--- trunk/Source/WebKit/WebProcess/WebPage/EventDispatcher.h	2021-12-01 02:32:45 UTC (rev 286345)
+++ trunk/Source/WebKit/WebProcess/WebPage/EventDispatcher.h	2021-12-01 02:56:24 UTC (rev 286346)
@@ -50,6 +50,8 @@
 
 namespace WebKit {
 
+class MomentumEventDispatcher;
+class ScrollingAccelerationCurve;
 class WebPage;
 class WebWheelEvent;
 
@@ -76,6 +78,9 @@
 
     void notifyScrollingTreesDisplayWasRefreshed(WebCore::PlatformDisplayID);
 
+    enum class WheelEventOrigin : bool { UIProcess, MomentumEventDispatcher };
+    void internalWheelEvent(WebCore::PageIdentifier, const WebWheelEvent&, WebCore::RectEdges<bool> rubberBandableEdges, WheelEventOrigin);
+
 private:
     EventDispatcher();
 
@@ -84,6 +89,9 @@
 
     // Message handlers
     void wheelEvent(WebCore::PageIdentifier, const WebWheelEvent&, WebCore::RectEdges<bool> rubberBandableEdges);
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+    void setScrollingAccelerationCurve(WebCore::PageIdentifier, std::optional<ScrollingAccelerationCurve>);
+#endif
 #if ENABLE(IOS_TOUCH_EVENTS)
     void touchEvent(WebCore::PageIdentifier, const WebTouchEvent&, CompletionHandler<void(bool)>&&);
     void touchEventWithoutCallback(WebCore::PageIdentifier, const WebTouchEvent&);
@@ -93,8 +101,8 @@
 #endif
 
     // This is called on the main thread.
-    void dispatchWheelEvent(WebCore::PageIdentifier, const WebWheelEvent&, OptionSet<WebCore::WheelEventProcessingSteps>);
-    void dispatchWheelEventViaMainThread(WebCore::PageIdentifier, const WebWheelEvent&, OptionSet<WebCore::WheelEventProcessingSteps>);
+    void dispatchWheelEvent(WebCore::PageIdentifier, const WebWheelEvent&, OptionSet<WebCore::WheelEventProcessingSteps>, WheelEventOrigin);
+    void dispatchWheelEventViaMainThread(WebCore::PageIdentifier, const WebWheelEvent&, OptionSet<WebCore::WheelEventProcessingSteps>, WheelEventOrigin);
 
 #if ENABLE(IOS_TOUCH_EVENTS)
     void dispatchTouchEvents();
@@ -115,6 +123,8 @@
     void displayDidRefreshOnScrollingThread(WebCore::PlatformDisplayID);
 #endif
 
+    void pageScreenDidChange(WebCore::PageIdentifier, WebCore::PlatformDisplayID);
+
     Ref<WorkQueue> m_queue;
 
 #if ENABLE(SCROLLING_THREAD)
@@ -126,6 +136,10 @@
     Lock m_touchEventsLock;
     HashMap<WebCore::PageIdentifier, TouchEventQueue> m_touchEvents WTF_GUARDED_BY_LOCK(m_touchEventsLock);
 #endif
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+    std::unique_ptr<MomentumEventDispatcher> m_momentumEventDispatcher;
+#endif
 };
 
 } // namespace WebKit

Modified: trunk/Source/WebKit/WebProcess/WebPage/EventDispatcher.messages.in (286345 => 286346)


--- trunk/Source/WebKit/WebProcess/WebPage/EventDispatcher.messages.in	2021-12-01 02:32:45 UTC (rev 286345)
+++ trunk/Source/WebKit/WebProcess/WebPage/EventDispatcher.messages.in	2021-12-01 02:56:24 UTC (rev 286346)
@@ -32,4 +32,9 @@
 #if HAVE(CVDISPLAYLINK)
     DisplayWasRefreshed(uint32_t displayID, struct WebCore::DisplayUpdate update, bool sendToMainThread)
 #endif
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+    SetScrollingAccelerationCurve(WebCore::PageIdentifier pageID, std::optional<WebKit::ScrollingAccelerationCurve> curve)
+#endif
+    PageScreenDidChange(WebCore::PageIdentifier pageID, uint32_t displayID)
 }

Added: trunk/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.cpp (0 => 286346)


--- trunk/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.cpp	                        (rev 0)
+++ trunk/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.cpp	2021-12-01 02:56:24 UTC (rev 286346)
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "MomentumEventDispatcher.h"
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+
+#include "EventDispatcher.h"
+#include "Logging.h"
+#include "WebProcessProxyMessages.h"
+#include <WebCore/DisplayRefreshMonitor.h>
+
+namespace WebKit {
+
+static constexpr Seconds deltaHistoryMaximumAge = 500_ms;
+static constexpr Seconds deltaHistoryMaximumInterval = 150_ms;
+static constexpr Seconds idealCurveFrameRate = 1_s / 60;
+
+MomentumEventDispatcher::MomentumEventDispatcher(EventDispatcher& dispatcher)
+    : m_observerID(DisplayLinkObserverID::generate())
+    , m_dispatcher(dispatcher)
+{
+}
+
+MomentumEventDispatcher::~MomentumEventDispatcher()
+{
+    stopDisplayLink();
+}
+
+bool MomentumEventDispatcher::eventShouldStartSyntheticMomentumPhase(WebCore::PageIdentifier pageIdentifier, const WebWheelEvent& event) const
+{
+    if (event.momentumPhase() != WebWheelEvent::PhaseBegan)
+        return false;
+
+    if (!m_accelerationCurves.contains(pageIdentifier)) {
+        RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher not using synthetic momentum phase: no acceleration curve");
+        return false;
+    }
+
+    if (!event.rawPlatformDelta()) {
+        RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher not using synthetic momentum phase: no raw platform delta on start event");
+        return false;
+    }
+
+    return true;
+}
+
+bool MomentumEventDispatcher::handleWheelEvent(WebCore::PageIdentifier pageIdentifier, const WebWheelEvent& event, WebCore::RectEdges<bool> rubberBandableEdges)
+{
+    m_lastRubberBandableEdges = rubberBandableEdges;
+    m_lastIncomingEvent = event;
+
+    bool isMomentumEvent = event.momentumPhase() != WebWheelEvent::PhaseNone;
+
+    if (m_currentGesture.active) {
+        bool pageIdentifierChanged = pageIdentifier != m_currentGesture.pageIdentifier;
+
+        // Any incoming wheel event other than a changed event for the current gesture
+        // should interrupt the animation; in the usual case, it will be
+        // momentumPhase == PhaseEnded that interrupts.
+        bool eventShouldInterruptGesture = !isMomentumEvent || event.momentumPhase() != WebWheelEvent::PhaseChanged;
+
+        if (pageIdentifierChanged || eventShouldInterruptGesture)
+            didEndMomentumPhase();
+    }
+
+    if (event.phase() == WebWheelEvent::PhaseBegan || event.phase() == WebWheelEvent::PhaseChanged) {
+        didReceiveScrollEvent(event);
+
+        if (auto lastActivePhaseDelta = event.rawPlatformDelta())
+            m_lastActivePhaseDelta = *lastActivePhaseDelta;
+    }
+
+    if (eventShouldStartSyntheticMomentumPhase(pageIdentifier, event))
+        didStartMomentumPhase(pageIdentifier, event);
+
+    bool isMomentumEventDuringSyntheticGesture = isMomentumEvent && m_currentGesture.active;
+
+#if !RELEASE_LOG_DISABLED
+    if (isMomentumEventDuringSyntheticGesture)
+        m_currentGesture.accumulatedEventOffset += event.delta();
+#endif
+
+    // Consume any normal momentum events while we're inside a synthetic momentum gesture.
+    return isMomentumEventDuringSyntheticGesture;
+}
+
+static float appKitScrollMultiplierForEvent(const WebWheelEvent& event)
+{
+    auto delta = event.delta();
+    auto unacceleratedDelta = event.unacceleratedScrollingDelta();
+    float multiplier = 1;
+    
+    // FIXME: Can the AppKit multiplier ever differ by axis?
+    if (delta.width() && unacceleratedDelta.width())
+        multiplier = std::max(multiplier, delta.width() / unacceleratedDelta.width());
+    if (delta.height() && unacceleratedDelta.height())
+        multiplier = std::max(multiplier, delta.height() / unacceleratedDelta.height());
+
+    return multiplier;
+}
+
+void MomentumEventDispatcher::dispatchSyntheticMomentumEvent(WebWheelEvent::Phase phase, WebCore::FloatSize delta)
+{
+    ASSERT(m_currentGesture.active);
+    ASSERT(m_currentGesture.initiatingEvent);
+
+    auto appKitScrollMultiplier = appKitScrollMultiplierForEvent(*m_currentGesture.initiatingEvent);
+    auto appKitAcceleratedDelta = delta * appKitScrollMultiplier;
+    auto wheelTicks = appKitAcceleratedDelta / WebCore::Scrollbar::pixelsPerLineStep();
+    auto time = WallTime::now();
+
+    // FIXME: Ideally we would stick legitimate rawPlatformDeltas on the event,
+    // but currently nothing will consume them, and we'd have to keep track of them separately.
+    WebWheelEvent syntheticEvent(
+        WebEvent::Wheel,
+        m_currentGesture.initiatingEvent->position(),
+        m_currentGesture.initiatingEvent->globalPosition(),
+        appKitAcceleratedDelta,
+        wheelTicks,
+        WebWheelEvent::ScrollByPixelWheelEvent,
+        m_currentGesture.initiatingEvent->directionInvertedFromDevice(),
+        WebWheelEvent::PhaseNone,
+        phase,
+        true,
+        m_currentGesture.initiatingEvent->scrollCount(),
+        delta,
+        m_lastIncomingEvent->modifiers(),
+        time,
+        time,
+        { });
+    m_dispatcher.internalWheelEvent(m_currentGesture.pageIdentifier, syntheticEvent, m_lastRubberBandableEdges, EventDispatcher::WheelEventOrigin::MomentumEventDispatcher);
+}
+
+void MomentumEventDispatcher::didStartMomentumPhase(WebCore::PageIdentifier pageIdentifier, const WebWheelEvent& event)
+{
+    m_currentGesture.active = true;
+    m_currentGesture.pageIdentifier = pageIdentifier;
+    m_currentGesture.initiatingEvent = event;
+    m_currentGesture.currentOffset = { };
+    m_currentGesture.startTime = WallTime::now();
+    m_currentGesture.accelerationCurve = [&] () -> std::optional<ScrollingAccelerationCurve> {
+        auto curveIterator = m_accelerationCurves.find(m_currentGesture.pageIdentifier);
+        if (curveIterator == m_accelerationCurves.end())
+            return { };
+        return curveIterator->value;
+    }();
+
+    startDisplayLink();
+
+    // FIXME: The system falls back from the table to just generating deltas
+    // directly when the frame interval is within 20fps of idealCurveFrameRate;
+    // we should perhaps do the same.
+    buildOffsetTableWithInitialDelta(*event.rawPlatformDelta());
+
+    dispatchSyntheticMomentumEvent(WebWheelEvent::PhaseBegan, { });
+}
+
+void MomentumEventDispatcher::didEndMomentumPhase()
+{
+    ASSERT(m_currentGesture.active);
+
+    dispatchSyntheticMomentumEvent(WebWheelEvent::PhaseEnded, { });
+
+    RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher saw momentum end phase with total offset %.1f %.1f, duration %f (event offset would have been %.1f %.1f)", m_currentGesture.currentOffset.width(), m_currentGesture.currentOffset.height(), (WallTime::now() - m_currentGesture.startTime).seconds(), m_currentGesture.accumulatedEventOffset.width(), m_currentGesture.accumulatedEventOffset.height());
+
+    stopDisplayLink();
+
+    m_currentGesture = { };
+}
+
+void MomentumEventDispatcher::setScrollingAccelerationCurve(WebCore::PageIdentifier pageIdentifier, std::optional<ScrollingAccelerationCurve> curve)
+{
+    m_accelerationCurves.set(pageIdentifier, curve);
+
+#if USE_MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING
+    WTF::TextStream stream(WTF::TextStream::LineMode::SingleLine);
+    stream << curve;
+    RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher set curve %s", stream.release().utf8().data());
+#endif
+}
+
+WebCore::PlatformDisplayID MomentumEventDispatcher::displayID() const
+{
+    ASSERT(m_currentGesture.pageIdentifier);
+    auto displayIDIterator = m_displayIDs.find(m_currentGesture.pageIdentifier);
+    if (displayIDIterator == m_displayIDs.end())
+        return { };
+    return displayIDIterator->value;
+}
+
+void MomentumEventDispatcher::startDisplayLink()
+{
+    auto displayID = this->displayID();
+    if (!displayID) {
+        RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher failed to start display link");
+        return;
+    }
+
+    // FIXME: Switch down to lower-than-full-speed frame rates for the tail end of the curve.
+    WebProcess::singleton().parentProcessConnection()->send(Messages::WebProcessProxy::StartDisplayLink(m_observerID, displayID, FullSpeedFramesPerSecond), 0);
+    RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher starting display link for display %d", displayID);
+}
+
+void MomentumEventDispatcher::stopDisplayLink()
+{
+    auto displayID = this->displayID();
+    if (!displayID) {
+        RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher failed to stop display link");
+        return;
+    }
+
+    WebProcess::singleton().parentProcessConnection()->send(Messages::WebProcessProxy::StopDisplayLink(m_observerID, displayID), 0);
+    RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher stopping display link for display %d", displayID);
+}
+
+void MomentumEventDispatcher::pageScreenDidChange(WebCore::PageIdentifier pageID, WebCore::PlatformDisplayID displayID)
+{
+    bool affectsCurrentGesture = (pageID == m_currentGesture.pageIdentifier);
+    if (affectsCurrentGesture)
+        stopDisplayLink();
+
+    m_displayIDs.set(pageID, displayID);
+
+    if (affectsCurrentGesture)
+        startDisplayLink();
+}
+
+void MomentumEventDispatcher::displayWasRefreshed(WebCore::PlatformDisplayID displayID, const WebCore::DisplayUpdate&)
+{
+    if (!m_currentGesture.active)
+        return;
+
+    if (displayID != this->displayID())
+        return;
+
+    auto animationTime = WallTime::now() - m_currentGesture.startTime;
+    auto desiredOffset = offsetAtTime(animationTime);
+
+#if !USE(MOMENTUM_EVENT_DISPATCHER_PREMATURE_ROUNDING)
+    // Intentional delta rounding (but at the end!).
+    FloatSize delta = roundedIntSize(desiredOffset - m_currentGesture.currentOffset);
+#else
+    FloatSize delta = desiredOffset - m_currentGesture.currentOffset;
+#endif
+
+    dispatchSyntheticMomentumEvent(WebWheelEvent::PhaseChanged, -delta);
+
+    m_currentGesture.currentOffset += delta;
+}
+
+void MomentumEventDispatcher::didReceiveScrollEventWithInterval(FloatSize size, Seconds frameInterval)
+{
+    auto push = [](HistoricalDeltas& deltas, Delta newDelta) {
+        bool directionChanged = deltas.size() && (deltas.first().rawPlatformDelta > 0) != (newDelta.rawPlatformDelta > 0);
+        if (directionChanged || newDelta.frameInterval > deltaHistoryMaximumAge)
+            deltas.clear();
+
+        deltas.prepend(newDelta);
+        if (deltas.size() > deltaHistoryQueueSize)
+            deltas.removeLast();
+    };
+
+    push(m_deltaHistoryX, { size.width(), frameInterval });
+    push(m_deltaHistoryY, { size.height(), frameInterval });
+}
+
+void MomentumEventDispatcher::didReceiveScrollEvent(const WebWheelEvent& event)
+{
+    ASSERT(!m_currentGesture.active);
+
+    if (!event.rawPlatformDelta())
+        return;
+
+    auto delta = *event.rawPlatformDelta();
+    auto time = event.ioHIDEventTimestamp();
+
+    if (!m_lastScrollTimestamp) {
+        m_lastScrollTimestamp = time;
+        // FIXME: Check that this matches the system (they may go with 10ms for the first frame).
+        return;
+    }
+
+    auto frameInterval = time - *m_lastScrollTimestamp;
+    m_lastScrollTimestamp = time;
+
+    didReceiveScrollEventWithInterval(delta, frameInterval);
+}
+
+void MomentumEventDispatcher::buildOffsetTableWithInitialDelta(FloatSize initialUnacceleratedDelta)
+{
+#if USE(MOMENTUM_EVENT_DISPATCHER_PREMATURE_ROUNDING)
+    m_currentGesture.carryOffset = { };
+#endif
+    m_currentGesture.offsetTable.clear();
+
+    FloatSize accumulatedOffset;
+    FloatSize unacceleratedDelta = initialUnacceleratedDelta;
+
+    do {
+        FloatSize acceleratedDelta;
+        std::tie(unacceleratedDelta, acceleratedDelta) = computeNextDelta(unacceleratedDelta);
+
+        accumulatedOffset += acceleratedDelta;
+        m_currentGesture.offsetTable.append(accumulatedOffset);
+    } while (std::abs(unacceleratedDelta.width()) > 0.5 || std::abs(unacceleratedDelta.height()) > 0.5);
+
+    RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher built table with %ld frames, initial delta %f %f, distance %f %f (initial delta from last changed event %f %f)", m_currentGesture.offsetTable.size(), initialUnacceleratedDelta.width(), initialUnacceleratedDelta.height(), accumulatedOffset.width(), accumulatedOffset.height(), m_lastActivePhaseDelta.width(), m_lastActivePhaseDelta.height());
+}
+
+static float interpolate(float a, float b, float t)
+{
+    return a + t * (b - a);
+}
+
+FloatSize MomentumEventDispatcher::offsetAtTime(Seconds time)
+{
+    if (!m_currentGesture.offsetTable.size())
+        return { };
+
+    float fractionalFrameNumber = time.seconds() / idealCurveFrameRate.seconds();
+    unsigned long lowerFrameNumber = std::min<unsigned long>(m_currentGesture.offsetTable.size() - 1, floor(fractionalFrameNumber));
+    unsigned long upperFrameNumber = std::min<unsigned long>(m_currentGesture.offsetTable.size() - 1, lowerFrameNumber + 1);
+    float amount = fractionalFrameNumber - lowerFrameNumber;
+    
+    return {
+        -interpolate(m_currentGesture.offsetTable[lowerFrameNumber].width(), m_currentGesture.offsetTable[upperFrameNumber].width(), amount),
+        -interpolate(m_currentGesture.offsetTable[lowerFrameNumber].height(), m_currentGesture.offsetTable[upperFrameNumber].height(), amount)
+    };
+}
+
+static float momentumDecayRate(FloatSize delta, Seconds frameInterval)
+{
+    constexpr float defaultDecay = 0.975f;
+    constexpr float tailVelocity = 250.f;
+    constexpr float tailDecay = 0.91f;
+    auto alpha = defaultDecay;
+
+    if (frameInterval) {
+        float initialVelocity = delta.diagonalLength() / frameInterval.seconds();
+        float weakMomentum = std::max<float>(0.f, (tailVelocity - initialVelocity) / tailVelocity);
+        alpha = defaultDecay - (defaultDecay - tailDecay) * weakMomentum;
+    }
+
+    return std::pow(alpha, (frameInterval.seconds() / 0.008f));
+}
+
+static constexpr float fromFixedPoint(float value)
+{
+    return value / 65536.0f;
+}
+
+std::pair<FloatSize, FloatSize> MomentumEventDispatcher::computeNextDelta(FloatSize currentUnacceleratedDelta)
+{
+    FloatSize unacceleratedDelta = currentUnacceleratedDelta;
+
+    float decayRate = momentumDecayRate(unacceleratedDelta, idealCurveFrameRate);
+    unacceleratedDelta.scale(decayRate);
+
+    auto quantizedUnacceleratedDelta = unacceleratedDelta;
+
+#if USE(MOMENTUM_EVENT_DISPATCHER_PREMATURE_ROUNDING)
+    // Round and carry.
+    int32_t quantizedX = std::round(quantizedUnacceleratedDelta.width());
+    int32_t quantizedY = std::round(quantizedUnacceleratedDelta.height());
+
+    if (std::abs(quantizedUnacceleratedDelta.width()) < 1 && std::abs(quantizedUnacceleratedDelta.height()) < 1) {
+        float deltaXIncludingCarry = quantizedUnacceleratedDelta.width() + m_currentGesture.carryOffset.width();
+        float deltaYIncludingCarry = quantizedUnacceleratedDelta.height() + m_currentGesture.carryOffset.height();
+
+        // Intentional truncation.
+        quantizedX = deltaXIncludingCarry;
+        quantizedY = deltaYIncludingCarry;
+        m_currentGesture.carryOffset = { deltaXIncludingCarry - quantizedX, deltaYIncludingCarry - quantizedY };
+    }
+
+    quantizedUnacceleratedDelta = { static_cast<float>(quantizedX), static_cast<float>(quantizedY) };
+#endif
+
+    // The delta queue operates on pre-acceleration deltas, so insert the new event *before* accelerating.
+    didReceiveScrollEventWithInterval(quantizedUnacceleratedDelta, idealCurveFrameRate);
+
+    auto accelerateAxis = [&] (HistoricalDeltas& deltas, float value) {
+        float totalDelta = 0;
+        Seconds totalTime;
+        unsigned count = 0;
+
+        for (const auto& delta : deltas) {
+            totalDelta += std::abs(delta.rawPlatformDelta);
+            count++;
+
+            if (delta.frameInterval > deltaHistoryMaximumInterval) {
+                totalTime += deltaHistoryMaximumInterval;
+                break;
+            }
+
+            totalTime += delta.frameInterval;
+
+            if (totalTime >= deltaHistoryMaximumAge)
+                break;
+        }
+
+        auto averageFrameInterval = std::clamp(totalTime / count, 1_ms, deltaHistoryMaximumInterval);
+        float averageFrameIntervalMS = averageFrameInterval.milliseconds();
+        float averageDelta = totalDelta / count;
+
+#if !RELEASE_LOG_DISABLED
+        if (!m_currentGesture.didLogInitialQueueState)
+            RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher initial historical deltas: average delta %f, average time %fms, event count %d", averageDelta, averageFrameIntervalMS, count);
+#endif
+
+        constexpr float velocityGainA = fromFixedPoint(2.f);
+        constexpr float velocityGainB = fromFixedPoint(955.f);
+        constexpr float velocityConstant = fromFixedPoint(98369.f);
+        constexpr float minimumVelocity = fromFixedPoint(1.f);
+
+        float velocity = (velocityGainA * averageFrameIntervalMS * averageFrameIntervalMS - velocityGainB * averageFrameIntervalMS + velocityConstant) * averageDelta;
+        velocity = std::max<float>(velocity, minimumVelocity);
+
+        // FIXME: This math is incomplete if we need to support acceleration tables as well.
+        float multiplier = m_currentGesture.accelerationCurve->accelerationFactor(velocity) / velocity;
+        return value * multiplier;
+    };
+
+    FloatSize acceleratedDelta(
+        accelerateAxis(m_deltaHistoryX, quantizedUnacceleratedDelta.width()),
+        accelerateAxis(m_deltaHistoryY, quantizedUnacceleratedDelta.height())
+    );
+
+    m_currentGesture.didLogInitialQueueState = true;
+
+    return { unacceleratedDelta, acceleratedDelta };
+}
+
+} // namespace WebKit
+
+#endif

Added: trunk/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.h (0 => 286346)


--- trunk/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.h	                        (rev 0)
+++ trunk/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.h	2021-12-01 02:56:24 UTC (rev 286346)
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER)
+
+// FIXME: Remove this once we decide which version we want.
+#define USE_MOMENTUM_EVENT_DISPATCHER_PREMATURE_ROUNDING 0
+#define USE_MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING 1
+
+#include "ScrollingAccelerationCurve.h"
+#include "WebWheelEvent.h"
+#include <WebCore/FloatSize.h>
+#include <WebCore/PageIdentifier.h>
+#include <WebCore/RectEdges.h>
+#include <memory>
+#include <wtf/Noncopyable.h>
+
+namespace WebCore {
+struct DisplayUpdate;
+using PlatformDisplayID = uint32_t;
+}
+
+namespace WebKit {
+
+class EventDispatcher;
+
+class MomentumEventDispatcher {
+    WTF_MAKE_NONCOPYABLE(MomentumEventDispatcher);
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    MomentumEventDispatcher(EventDispatcher&);
+    ~MomentumEventDispatcher();
+
+    bool handleWheelEvent(WebCore::PageIdentifier, const WebWheelEvent&, WebCore::RectEdges<bool> rubberBandableEdges);
+
+    void setScrollingAccelerationCurve(WebCore::PageIdentifier, std::optional<ScrollingAccelerationCurve>);
+
+    void displayWasRefreshed(WebCore::PlatformDisplayID, const WebCore::DisplayUpdate&);
+
+    void pageScreenDidChange(WebCore::PageIdentifier, WebCore::PlatformDisplayID);
+
+private:
+    void didStartMomentumPhase(WebCore::PageIdentifier, const WebWheelEvent&);
+    void didEndMomentumPhase();
+
+    bool eventShouldStartSyntheticMomentumPhase(WebCore::PageIdentifier, const WebWheelEvent&) const;
+
+    void startDisplayLink();
+    void stopDisplayLink();
+
+    WebCore::PlatformDisplayID displayID() const;
+
+    void dispatchSyntheticMomentumEvent(WebWheelEvent::Phase, WebCore::FloatSize delta);
+
+    void buildOffsetTableWithInitialDelta(FloatSize);
+
+    FloatSize offsetAtTime(Seconds);
+    std::pair<FloatSize, FloatSize> computeNextDelta(FloatSize currentUnacceleratedDelta);
+
+    void didReceiveScrollEventWithInterval(FloatSize, Seconds);
+    void didReceiveScrollEvent(const WebWheelEvent&);
+
+    struct Delta {
+        float rawPlatformDelta;
+        Seconds frameInterval;
+    };
+    static constexpr unsigned deltaHistoryQueueSize = 9;
+    typedef Deque<Delta, deltaHistoryQueueSize> HistoricalDeltas;
+    HistoricalDeltas m_deltaHistoryX;
+    HistoricalDeltas m_deltaHistoryY;
+
+    std::optional<WallTime> m_lastScrollTimestamp;
+    std::optional<WebWheelEvent> m_lastIncomingEvent;
+    WebCore::RectEdges<bool> m_lastRubberBandableEdges;
+#if !RELEASE_LOG_DISABLED
+    FloatSize m_lastActivePhaseDelta;
+#endif
+
+    struct {
+        bool active { false };
+
+        WebCore::PageIdentifier pageIdentifier;
+        std::optional<ScrollingAccelerationCurve> accelerationCurve;
+        std::optional<WebWheelEvent> initiatingEvent;
+
+        FloatSize currentOffset;
+        WallTime startTime;
+
+        Vector<FloatSize> offsetTable;
+
+#if !RELEASE_LOG_DISABLED
+        FloatSize accumulatedEventOffset;
+        bool didLogInitialQueueState { false };
+#endif
+
+#if USE(MOMENTUM_EVENT_DISPATCHER_PREMATURE_ROUNDING)
+        FloatSize carryOffset;
+#endif
+    } m_currentGesture;
+
+    DisplayLinkObserverID m_observerID;
+    HashMap<WebCore::PageIdentifier, WebCore::PlatformDisplayID> m_displayIDs;
+    HashMap<WebCore::PageIdentifier, std::optional<ScrollingAccelerationCurve>> m_accelerationCurves;
+    EventDispatcher& m_dispatcher;
+};
+
+} // namespace WebKit
+
+#endif

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp (286345 => 286346)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2021-12-01 02:32:45 UTC (rev 286345)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2021-12-01 02:56:24 UTC (rev 286346)
@@ -3031,7 +3031,7 @@
     return page->userInputBridge().handleWheelEvent(platformWheelEvent, processingSteps);
 }
 
-bool WebPage::wheelEvent(const WebWheelEvent& wheelEvent, OptionSet<WheelEventProcessingSteps> processingSteps)
+bool WebPage::wheelEvent(const WebWheelEvent& wheelEvent, OptionSet<WheelEventProcessingSteps> processingSteps, EventDispatcher::WheelEventOrigin wheelEventOrigin)
 {
     m_userActivity.impulse();
 
@@ -3039,7 +3039,7 @@
 
     bool handled = handleWheelEvent(wheelEvent, m_page.get(), processingSteps);
 
-    if (processingSteps.contains(WheelEventProcessingSteps::MainThreadForScrolling))
+    if (processingSteps.contains(WheelEventProcessingSteps::MainThreadForScrolling) && wheelEventOrigin == EventDispatcher::WheelEventOrigin::UIProcess)
         send(Messages::WebPageProxy::DidReceiveEvent(static_cast<uint32_t>(wheelEvent.type()), handled));
 
     return handled;
@@ -3053,7 +3053,7 @@
 #else
     bool isCancelable = true;
 #endif
-    bool handled = this->wheelEvent(wheelEvent, { isCancelable ? WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch : WheelEventProcessingSteps::MainThreadForNonBlockingDOMEventDispatch });
+    bool handled = this->wheelEvent(wheelEvent, { isCancelable ? WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch : WheelEventProcessingSteps::MainThreadForNonBlockingDOMEventDispatch }, EventDispatcher::WheelEventOrigin::UIProcess);
     completionHandler(handled);
 }
 

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.h (286345 => 286346)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.h	2021-12-01 02:32:45 UTC (rev 286345)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.h	2021-12-01 02:56:24 UTC (rev 286346)
@@ -38,6 +38,7 @@
 #include "DownloadID.h"
 #include "DrawingAreaInfo.h"
 #include "EditingRange.h"
+#include "EventDispatcher.h"
 #include "FocusedElementInformation.h"
 #include "GeolocationIdentifier.h"
 #include "IdentifierTypes.h"
@@ -1065,7 +1066,7 @@
     void startWaitingForContextMenuToShow() { m_waitingForContextMenuToShow = true; }
 #endif
 
-    bool wheelEvent(const WebWheelEvent&, OptionSet<WebCore::WheelEventProcessingSteps>);
+    bool wheelEvent(const WebWheelEvent&, OptionSet<WebCore::WheelEventProcessingSteps>, EventDispatcher::WheelEventOrigin);
 
     void wheelEventHandlersChanged(bool);
     void recomputeShortCircuitHorizontalWheelEventsState();
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to