<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[287641] trunk/Source</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/287641">287641</a></dd>
<dt>Author</dt> <dd>timothy_horton@apple.com</dd>
<dt>Date</dt> <dd>2022-01-05 11:29:43 -0800 (Wed, 05 Jan 2022)</dd>
</dl>

<h3>Log Message</h3>
<pre>Momentum Event Dispatcher: Momentum tails may get truncated if the duration runs longer than the system's
https://bugs.webkit.org/show_bug.cgi?id=234535
<rdar://problem/86338105>

Reviewed by Simon Fraser.

Source/WebKit:

Currently, synthetic momentum dispatch is strictly tied to the duration
of the real platform momentum phase, which has two unfortunate implications:

- if our phase runs shorter than the platform phase, we'll keep dispatching
  zero-delta events until the platform momentum ends

- more importantly, if our phase runs longer, it will be abruptly terminated
  when the platform momentum ends

In practice, our synthetic phase is very close in duration to the system one,
so the impact is minimal. But, to be safe, disentagle the two durations,
using a new bit from the platform to determine if the system momentum phase
was interrupted by the user (e.g. by tapping the trackpad) or naturally,
and ignoring the ended event in the natural case, allowing synthetic
events to continue being dispatched.

* Shared/WebWheelEvent.cpp:
(WebKit::WebWheelEvent::WebWheelEvent):
(WebKit::WebWheelEvent::encode const):
(WebKit::WebWheelEvent::decode):
* Shared/WebWheelEvent.h:
(WebKit::WebWheelEvent::momentumEndType const):
* Shared/WebWheelEventCoalescer.cpp:
(WebKit::WebWheelEventCoalescer::coalesce):
Plumb momentumEndType along on WebWheelEvent. Platforms that don't
provide information about the interruption reason will always say Unknown.

* Shared/mac/WebEventFactory.mm:
(WebKit::WebEventFactory::createWebWheelEvent):
Only bother looking up the CGEvent/IOHIDEvent once, and extract all
relevant details in one go.

* WebProcess/WebPage/MomentumEventDispatcher.cpp:
(WebKit::MomentumEventDispatcher::handleWheelEvent):
Don't interrupt the synthetic momentum phase if the momentum-ended event
comes from the natural end of the deceleration instead of an interruption
(or an unknown reason).

Keep track of whether we're in the middle of a platform momentum phase
that we chose (at momentum-begin time) to override. When deciding
whether to eat an incoming event, take *both* this new bit and whether
we are currently in the middle of a synthetic phase into account. It
is important to continue eating incoming events in the case where
the synthetic phase ended early (so `active` became false) but the
platform phase continues.

(WebKit::MomentumEventDispatcher::dispatchSyntheticMomentumEvent):
(WebKit::MomentumEventDispatcher::didEndMomentumPhase):
Adjust some logging wording to be more precise.

(WebKit::MomentumEventDispatcher::setScrollingAccelerationCurve):
Make this log public so that the curve value is visible in logs.

(WebKit::MomentumEventDispatcher::consumeDeltaForCurrentTime):
Make consumeDeltaForCurrentTime inform the client via an optional when
we are at the end of the delta table.

(WebKit::MomentumEventDispatcher::displayWasRefreshed):
Stop the synthetic momentum phase as soon as we run out of deltas.

(WebKit::MomentumEventDispatcher::computeNextDelta):
* WebProcess/WebPage/MomentumEventDispatcher.h:

Source/WTF:

* wtf/PlatformHave.h:
Add a HAVE for kIOHIDEventScrollMomentumInterrupted.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWTFChangeLog">trunk/Source/WTF/ChangeLog</a></li>
<li><a href="#trunkSourceWTFwtfPlatformHaveh">trunk/Source/WTF/wtf/PlatformHave.h</a></li>
<li><a href="#trunkSourceWebCorePALpalspimacIOKitSPIMach">trunk/Source/WebCore/PAL/pal/spi/mac/IOKitSPIMac.h</a></li>
<li><a href="#trunkSourceWebKitChangeLog">trunk/Source/WebKit/ChangeLog</a></li>
<li><a href="#trunkSourceWebKitSharedWebWheelEventcpp">trunk/Source/WebKit/Shared/WebWheelEvent.cpp</a></li>
<li><a href="#trunkSourceWebKitSharedWebWheelEventh">trunk/Source/WebKit/Shared/WebWheelEvent.h</a></li>
<li><a href="#trunkSourceWebKitSharedWebWheelEventCoalescercpp">trunk/Source/WebKit/Shared/WebWheelEventCoalescer.cpp</a></li>
<li><a href="#trunkSourceWebKitSharedmacWebEventFactorymm">trunk/Source/WebKit/Shared/mac/WebEventFactory.mm</a></li>
<li><a href="#trunkSourceWebKitWebProcessWebPageMomentumEventDispatchercpp">trunk/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.cpp</a></li>
<li><a href="#trunkSourceWebKitWebProcessWebPageMomentumEventDispatcherh">trunk/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWTFChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/ChangeLog (287640 => 287641)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/ChangeLog       2022-01-05 19:19:35 UTC (rev 287640)
+++ trunk/Source/WTF/ChangeLog  2022-01-05 19:29:43 UTC (rev 287641)
</span><span class="lines">@@ -1,3 +1,14 @@
</span><ins>+2022-01-05  Tim Horton  <timothy_horton@apple.com>
+
+        Momentum Event Dispatcher: Momentum tails may get truncated if the duration runs longer than the system's
+        https://bugs.webkit.org/show_bug.cgi?id=234535
+        <rdar://problem/86338105>
+
+        Reviewed by Simon Fraser.
+
+        * wtf/PlatformHave.h:
+        Add a HAVE for kIOHIDEventScrollMomentumInterrupted.
+
</ins><span class="cx"> 2021-12-31  Yusuke Suzuki  <ysuzuki@apple.com>
</span><span class="cx"> 
</span><span class="cx">         [JSC] Replace UDIS86 with Zydis
</span></span></pre></div>
<a id="trunkSourceWTFwtfPlatformHaveh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/wtf/PlatformHave.h (287640 => 287641)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/wtf/PlatformHave.h      2022-01-05 19:19:35 UTC (rev 287640)
+++ trunk/Source/WTF/wtf/PlatformHave.h 2022-01-05 19:29:43 UTC (rev 287641)
</span><span class="lines">@@ -1140,3 +1140,7 @@
</span><span class="cx"> #if PLATFORM(COCOA)
</span><span class="cx"> #define HAVE_CFAUTORELEASE 1
</span><span class="cx"> #endif
</span><ins>+
+#if PLATFORM(MAC) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 120200
+#define HAVE_PLATFORM_SCROLL_MOMENTUM_INTERRUPTION_REASON 1
+#endif
</ins></span></pre></div>
<a id="trunkSourceWebCorePALpalspimacIOKitSPIMach"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/PAL/pal/spi/mac/IOKitSPIMac.h (287640 => 287641)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/PAL/pal/spi/mac/IOKitSPIMac.h       2022-01-05 19:19:35 UTC (rev 287640)
+++ trunk/Source/WebCore/PAL/pal/spi/mac/IOKitSPIMac.h  2022-01-05 19:29:43 UTC (rev 287641)
</span><span class="lines">@@ -81,7 +81,6 @@
</span><span class="cx">     kIOHIDEventTypeDigitizer = 11,
</span><span class="cx">     kIOHIDEventTypeNavigationSwipe = 16,
</span><span class="cx">     kIOHIDEventTypeForce = 32,
</span><del>-
</del><span class="cx"> };
</span><span class="cx"> typedef uint32_t IOHIDEventType;
</span><span class="cx"> 
</span><span class="lines">@@ -88,6 +87,12 @@
</span><span class="cx"> typedef uint32_t IOHIDEventField;
</span><span class="cx"> typedef uint64_t IOHIDEventSenderID;
</span><span class="cx"> 
</span><ins>+
+enum {
+    kIOHIDEventScrollMomentumInterrupted = (1 << 4),
+};
+typedef uint8_t IOHIDEventScrollMomentumBits;
+
</ins><span class="cx"> #ifdef __LP64__
</span><span class="cx"> typedef double IOHIDFloat;
</span><span class="cx"> #else
</span><span class="lines">@@ -103,6 +108,7 @@
</span><span class="cx"> uint64_t IOHIDEventGetTimeStamp(IOHIDEventRef);
</span><span class="cx"> IOHIDFloat IOHIDEventGetFloatValue(IOHIDEventRef, IOHIDEventField);
</span><span class="cx"> IOHIDEventSenderID IOHIDEventGetSenderID(IOHIDEventRef);
</span><ins>+IOHIDEventScrollMomentumBits IOHIDEventGetScrollMomentum(IOHIDEventRef);
</ins><span class="cx"> 
</span><span class="cx"> WTF_EXTERN_C_END
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKitChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/ChangeLog (287640 => 287641)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/ChangeLog    2022-01-05 19:19:35 UTC (rev 287640)
+++ trunk/Source/WebKit/ChangeLog       2022-01-05 19:29:43 UTC (rev 287641)
</span><span class="lines">@@ -1,3 +1,74 @@
</span><ins>+2022-01-05  Tim Horton  <timothy_horton@apple.com>
+
+        Momentum Event Dispatcher: Momentum tails may get truncated if the duration runs longer than the system's
+        https://bugs.webkit.org/show_bug.cgi?id=234535
+        <rdar://problem/86338105>
+
+        Reviewed by Simon Fraser.
+
+        Currently, synthetic momentum dispatch is strictly tied to the duration
+        of the real platform momentum phase, which has two unfortunate implications:
+
+        - if our phase runs shorter than the platform phase, we'll keep dispatching
+          zero-delta events until the platform momentum ends
+
+        - more importantly, if our phase runs longer, it will be abruptly terminated
+          when the platform momentum ends
+
+        In practice, our synthetic phase is very close in duration to the system one,
+        so the impact is minimal. But, to be safe, disentagle the two durations,
+        using a new bit from the platform to determine if the system momentum phase
+        was interrupted by the user (e.g. by tapping the trackpad) or naturally,
+        and ignoring the ended event in the natural case, allowing synthetic
+        events to continue being dispatched.
+
+        * Shared/WebWheelEvent.cpp:
+        (WebKit::WebWheelEvent::WebWheelEvent):
+        (WebKit::WebWheelEvent::encode const):
+        (WebKit::WebWheelEvent::decode):
+        * Shared/WebWheelEvent.h:
+        (WebKit::WebWheelEvent::momentumEndType const):
+        * Shared/WebWheelEventCoalescer.cpp:
+        (WebKit::WebWheelEventCoalescer::coalesce):
+        Plumb momentumEndType along on WebWheelEvent. Platforms that don't
+        provide information about the interruption reason will always say Unknown.
+
+        * Shared/mac/WebEventFactory.mm:
+        (WebKit::WebEventFactory::createWebWheelEvent):
+        Only bother looking up the CGEvent/IOHIDEvent once, and extract all
+        relevant details in one go.
+
+        * WebProcess/WebPage/MomentumEventDispatcher.cpp:
+        (WebKit::MomentumEventDispatcher::handleWheelEvent):
+        Don't interrupt the synthetic momentum phase if the momentum-ended event
+        comes from the natural end of the deceleration instead of an interruption
+        (or an unknown reason).
+
+        Keep track of whether we're in the middle of a platform momentum phase
+        that we chose (at momentum-begin time) to override. When deciding
+        whether to eat an incoming event, take *both* this new bit and whether
+        we are currently in the middle of a synthetic phase into account. It
+        is important to continue eating incoming events in the case where 
+        the synthetic phase ended early (so `active` became false) but the
+        platform phase continues.
+
+        (WebKit::MomentumEventDispatcher::dispatchSyntheticMomentumEvent):
+        (WebKit::MomentumEventDispatcher::didEndMomentumPhase):
+        Adjust some logging wording to be more precise.
+
+        (WebKit::MomentumEventDispatcher::setScrollingAccelerationCurve):
+        Make this log public so that the curve value is visible in logs.
+
+        (WebKit::MomentumEventDispatcher::consumeDeltaForCurrentTime):
+        Make consumeDeltaForCurrentTime inform the client via an optional when
+        we are at the end of the delta table.
+
+        (WebKit::MomentumEventDispatcher::displayWasRefreshed):
+        Stop the synthetic momentum phase as soon as we run out of deltas.
+
+        (WebKit::MomentumEventDispatcher::computeNextDelta):
+        * WebProcess/WebPage/MomentumEventDispatcher.h:
+
</ins><span class="cx"> 2022-01-05  Per Arne Vollan  <pvollan@apple.com>
</span><span class="cx"> 
</span><span class="cx">         [macCatalyst][GPUP][NetworkProcess] Block WindowServer connections
</span></span></pre></div>
<a id="trunkSourceWebKitSharedWebWheelEventcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/Shared/WebWheelEvent.cpp (287640 => 287641)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/Shared/WebWheelEvent.cpp     2022-01-05 19:19:35 UTC (rev 287640)
+++ trunk/Source/WebKit/Shared/WebWheelEvent.cpp        2022-01-05 19:29:43 UTC (rev 287641)
</span><span class="lines">@@ -44,7 +44,7 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> #if PLATFORM(COCOA)
</span><del>-WebWheelEvent::WebWheelEvent(Type type, const IntPoint& position, const IntPoint& globalPosition, const FloatSize& delta, const FloatSize& wheelTicks, Granularity granularity, bool directionInvertedFromDevice, Phase phase, Phase momentumPhase, bool hasPreciseScrollingDeltas, uint32_t scrollCount, const WebCore::FloatSize& unacceleratedScrollingDelta, OptionSet<Modifier> modifiers, WallTime timestamp, WallTime ioHIDEventTimestamp, std::optional<WebCore::FloatSize> rawPlatformDelta)
</del><ins>+WebWheelEvent::WebWheelEvent(Type type, const IntPoint& position, const IntPoint& globalPosition, const FloatSize& delta, const FloatSize& wheelTicks, Granularity granularity, bool directionInvertedFromDevice, Phase phase, Phase momentumPhase, bool hasPreciseScrollingDeltas, uint32_t scrollCount, const WebCore::FloatSize& unacceleratedScrollingDelta, OptionSet<Modifier> modifiers, WallTime timestamp, WallTime ioHIDEventTimestamp, std::optional<WebCore::FloatSize> rawPlatformDelta, MomentumEndType momentumEndType)
</ins><span class="cx">     : WebEvent(type, modifiers, timestamp)
</span><span class="cx">     , m_position(position)
</span><span class="cx">     , m_globalPosition(globalPosition)
</span><span class="lines">@@ -53,6 +53,7 @@
</span><span class="cx">     , m_granularity(granularity)
</span><span class="cx">     , m_phase(phase)
</span><span class="cx">     , m_momentumPhase(momentumPhase)
</span><ins>+    , m_momentumEndType(momentumEndType)
</ins><span class="cx">     , m_directionInvertedFromDevice(directionInvertedFromDevice)
</span><span class="cx">     , m_hasPreciseScrollingDeltas(hasPreciseScrollingDeltas)
</span><span class="cx">     , m_ioHIDEventTimestamp(ioHIDEventTimestamp)
</span><span class="lines">@@ -87,6 +88,7 @@
</span><span class="cx">     encoder << m_delta;
</span><span class="cx">     encoder << m_wheelTicks;
</span><span class="cx">     encoder << m_granularity;
</span><ins>+    encoder << m_momentumEndType;
</ins><span class="cx">     encoder << m_directionInvertedFromDevice;
</span><span class="cx"> #if PLATFORM(COCOA) || PLATFORM(GTK) || USE(LIBWPE)
</span><span class="cx">     encoder << m_phase;
</span><span class="lines">@@ -115,6 +117,8 @@
</span><span class="cx">         return false;
</span><span class="cx">     if (!decoder.decode(t.m_granularity))
</span><span class="cx">         return false;
</span><ins>+    if (!decoder.decode(t.m_momentumEndType))
+        return false;
</ins><span class="cx">     if (!decoder.decode(t.m_directionInvertedFromDevice))
</span><span class="cx">         return false;
</span><span class="cx"> #if PLATFORM(COCOA) || PLATFORM(GTK) || USE(LIBWPE)
</span></span></pre></div>
<a id="trunkSourceWebKitSharedWebWheelEventh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/Shared/WebWheelEvent.h (287640 => 287641)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/Shared/WebWheelEvent.h       2022-01-05 19:19:35 UTC (rev 287640)
+++ trunk/Source/WebKit/Shared/WebWheelEvent.h  2022-01-05 19:29:43 UTC (rev 287641)
</span><span class="lines">@@ -29,6 +29,7 @@
</span><span class="cx"> #include "WebEvent.h"
</span><span class="cx"> #include <WebCore/FloatSize.h>
</span><span class="cx"> #include <WebCore/IntPoint.h>
</span><ins>+#include <wtf/EnumTraits.h>
</ins><span class="cx"> 
</span><span class="cx"> namespace WebKit {
</span><span class="cx"> 
</span><span class="lines">@@ -49,11 +50,17 @@
</span><span class="cx">         PhaseMayBegin    = 1 << 5,
</span><span class="cx">     };
</span><span class="cx"> 
</span><ins>+    enum class MomentumEndType : uint8_t {
+        Unknown,
+        Interrupted,
+        Natural,
+    };
+
</ins><span class="cx">     WebWheelEvent() = default;
</span><span class="cx"> 
</span><span class="cx">     WebWheelEvent(Type, const WebCore::IntPoint& position, const WebCore::IntPoint& globalPosition, const WebCore::FloatSize& delta, const WebCore::FloatSize& wheelTicks, Granularity, OptionSet<Modifier>, WallTime timestamp);
</span><span class="cx"> #if PLATFORM(COCOA)
</span><del>-    WebWheelEvent(Type, const WebCore::IntPoint& position, const WebCore::IntPoint& globalPosition, const WebCore::FloatSize& delta, const WebCore::FloatSize& wheelTicks, Granularity, bool directionInvertedFromDevice, Phase, Phase momentumPhase, bool hasPreciseScrollingDeltas, uint32_t scrollCount, const WebCore::FloatSize& unacceleratedScrollingDelta, OptionSet<Modifier>, WallTime timestamp, WallTime ioHIDEventTimestamp, std::optional<WebCore::FloatSize> rawPlatformDelta);
</del><ins>+    WebWheelEvent(Type, const WebCore::IntPoint& position, const WebCore::IntPoint& globalPosition, const WebCore::FloatSize& delta, const WebCore::FloatSize& wheelTicks, Granularity, bool directionInvertedFromDevice, Phase, Phase momentumPhase, bool hasPreciseScrollingDeltas, uint32_t scrollCount, const WebCore::FloatSize& unacceleratedScrollingDelta, OptionSet<Modifier>, WallTime timestamp, WallTime ioHIDEventTimestamp, std::optional<WebCore::FloatSize> rawPlatformDelta, MomentumEndType);
</ins><span class="cx"> #elif PLATFORM(GTK) || USE(LIBWPE)
</span><span class="cx">     WebWheelEvent(Type, const WebCore::IntPoint& position, const WebCore::IntPoint& globalPosition, const WebCore::FloatSize& delta, const WebCore::FloatSize& wheelTicks, Phase, Phase momentumPhase, Granularity, bool hasPreciseScrollingDeltas, OptionSet<Modifier>, WallTime timestamp);
</span><span class="cx"> #endif
</span><span class="lines">@@ -66,6 +73,7 @@
</span><span class="cx">     bool directionInvertedFromDevice() const { return m_directionInvertedFromDevice; }
</span><span class="cx">     Phase phase() const { return static_cast<Phase>(m_phase); }
</span><span class="cx">     Phase momentumPhase() const { return static_cast<Phase>(m_momentumPhase); }
</span><ins>+    MomentumEndType momentumEndType() const { return m_momentumEndType; }
</ins><span class="cx"> #if PLATFORM(COCOA) || PLATFORM(GTK) || USE(LIBWPE)
</span><span class="cx">     bool hasPreciseScrollingDeltas() const { return m_hasPreciseScrollingDeltas; }
</span><span class="cx"> #endif
</span><span class="lines">@@ -90,6 +98,7 @@
</span><span class="cx">     uint32_t m_phase { Phase::PhaseNone };
</span><span class="cx">     uint32_t m_momentumPhase { Phase::PhaseNone };
</span><span class="cx"> 
</span><ins>+    MomentumEndType m_momentumEndType { MomentumEndType::Unknown };
</ins><span class="cx">     bool m_directionInvertedFromDevice { false };
</span><span class="cx"> #if PLATFORM(COCOA) || PLATFORM(GTK) || USE(LIBWPE)
</span><span class="cx">     bool m_hasPreciseScrollingDeltas { false };
</span><span class="lines">@@ -103,3 +112,16 @@
</span><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebKit
</span><ins>+
+namespace WTF {
+
+template<> struct EnumTraits<WebKit::WebWheelEvent::MomentumEndType> {
+    using values = EnumValues<
+    WebKit::WebWheelEvent::MomentumEndType,
+    WebKit::WebWheelEvent::MomentumEndType::Unknown,
+    WebKit::WebWheelEvent::MomentumEndType::Interrupted,
+    WebKit::WebWheelEvent::MomentumEndType::Natural
+    >;
+};
+
+} // namespace WTF
</ins></span></pre></div>
<a id="trunkSourceWebKitSharedWebWheelEventCoalescercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/Shared/WebWheelEventCoalescer.cpp (287640 => 287641)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/Shared/WebWheelEventCoalescer.cpp    2022-01-05 19:19:35 UTC (rev 287640)
+++ trunk/Source/WebKit/Shared/WebWheelEventCoalescer.cpp       2022-01-05 19:29:43 UTC (rev 287641)
</span><span class="lines">@@ -81,7 +81,7 @@
</span><span class="cx">     if (a.rawPlatformDelta() && b.rawPlatformDelta())
</span><span class="cx">         mergedRawPlatformScrollingDelta = a.rawPlatformDelta().value() + b.rawPlatformDelta().value();
</span><span class="cx"> 
</span><del>-    return WebWheelEvent(WebEvent::Wheel, b.position(), b.globalPosition(), mergedDelta, mergedWheelTicks, b.granularity(), b.directionInvertedFromDevice(), b.phase(), b.momentumPhase(), b.hasPreciseScrollingDeltas(), b.scrollCount(), mergedUnacceleratedScrollingDelta, b.modifiers(), b.timestamp(), b.ioHIDEventTimestamp(), mergedRawPlatformScrollingDelta);
</del><ins>+    return WebWheelEvent(WebEvent::Wheel, b.position(), b.globalPosition(), mergedDelta, mergedWheelTicks, b.granularity(), b.directionInvertedFromDevice(), b.phase(), b.momentumPhase(), b.hasPreciseScrollingDeltas(), b.scrollCount(), mergedUnacceleratedScrollingDelta, b.modifiers(), b.timestamp(), b.ioHIDEventTimestamp(), mergedRawPlatformScrollingDelta, b.momentumEndType());
</ins><span class="cx"> #elif PLATFORM(GTK) || USE(LIBWPE)
</span><span class="cx">     return WebWheelEvent(WebEvent::Wheel, b.position(), b.globalPosition(), mergedDelta, mergedWheelTicks, b.phase(), b.momentumPhase(), b.granularity(), b.hasPreciseScrollingDeltas(), b.modifiers(), b.timestamp());
</span><span class="cx"> #else
</span></span></pre></div>
<a id="trunkSourceWebKitSharedmacWebEventFactorymm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/Shared/mac/WebEventFactory.mm (287640 => 287641)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/Shared/mac/WebEventFactory.mm        2022-01-05 19:19:35 UTC (rev 287640)
+++ trunk/Source/WebKit/Shared/mac/WebEventFactory.mm   2022-01-05 19:29:43 UTC (rev 287641)
</span><span class="lines">@@ -410,33 +410,31 @@
</span><span class="cx"> 
</span><span class="cx">     auto modifiers = modifiersForEvent(event);
</span><span class="cx">     auto timestamp = WebCore::eventTimeStampSince1970(event.timestamp);
</span><del>-
-    auto ioHIDEventTimestamp = [&]() {
</del><ins>+    
+    auto ioHIDEventWallTime = timestamp;
+    std::optional<WebCore::FloatSize> rawPlatformDelta;
+    auto momentumEndType = WebWheelEvent::MomentumEndType::Unknown;
+    
+    ([&] {
</ins><span class="cx">         auto cgEvent = event.CGEvent;
</span><span class="cx">         if (!cgEvent)
</span><del>-            return event.timestamp;
</del><ins>+            return;
</ins><span class="cx"> 
</span><span class="cx">         auto ioHIDEvent = adoptCF(CGEventCopyIOHIDEvent(cgEvent));
</span><span class="cx">         if (!ioHIDEvent)
</span><del>-            return event.timestamp;
</del><ins>+            return;
</ins><span class="cx"> 
</span><span class="cx">         auto ioHIDEventTimestamp = IOHIDEventGetTimeStamp(ioHIDEvent.get()); // IOEventRef timestamp is mach_absolute_time units.
</span><del>-        return MonotonicTime::fromMachAbsoluteTime(ioHIDEventTimestamp).secondsSinceEpoch().seconds();
-    }();
-
-    auto rawPlatformDelta = [&]() -> std::optional<WebCore::FloatSize> {
-        auto cgEvent = event.CGEvent;
-        if (!cgEvent)
-            return std::nullopt;
-
-        auto ioHIDEvent = adoptCF(CGEventCopyIOHIDEvent(cgEvent));
-        if (!ioHIDEvent)
-            return std::nullopt;
</del><ins>+        auto monotonicIOHIDEventTimestamp = MonotonicTime::fromMachAbsoluteTime(ioHIDEventTimestamp).secondsSinceEpoch().seconds();
+        ioHIDEventWallTime = WebCore::eventTimeStampSince1970(monotonicIOHIDEventTimestamp);
</ins><span class="cx">         
</span><del>-        return { WebCore::FloatSize(-IOHIDEventGetFloatValue(ioHIDEvent.get(), kIOHIDEventFieldScrollX), -IOHIDEventGetFloatValue(ioHIDEvent.get(), kIOHIDEventFieldScrollY)) };
-    }();
</del><ins>+        rawPlatformDelta = { WebCore::FloatSize(-IOHIDEventGetFloatValue(ioHIDEvent.get(), kIOHIDEventFieldScrollX), -IOHIDEventGetFloatValue(ioHIDEvent.get(), kIOHIDEventFieldScrollY)) };
</ins><span class="cx"> 
</span><del>-    auto ioHIDEventWallTime = WebCore::eventTimeStampSince1970(ioHIDEventTimestamp);
</del><ins>+#if HAVE(PLATFORM_SCROLL_MOMENTUM_INTERRUPTION_REASON)
+        bool momentumWasInterrupted = IOHIDEventGetScrollMomentum(ioHIDEvent.get()) & kIOHIDEventScrollMomentumInterrupted;
+        momentumEndType = momentumWasInterrupted ? WebWheelEvent::MomentumEndType::Interrupted : WebWheelEvent::MomentumEndType::Natural;
+#endif
+    })();
</ins><span class="cx"> 
</span><span class="cx">     if (phase == WebWheelEvent::PhaseCancelled) {
</span><span class="cx">         deltaX = 0;
</span><span class="lines">@@ -449,7 +447,7 @@
</span><span class="cx"> 
</span><span class="cx">     return WebWheelEvent(WebEvent::Wheel, WebCore::IntPoint(position), WebCore::IntPoint(globalPosition), WebCore::FloatSize(deltaX, deltaY), WebCore::FloatSize(wheelTicksX, wheelTicksY),
</span><span class="cx">         granularity, directionInvertedFromDevice, phase, momentumPhase, hasPreciseScrollingDeltas,
</span><del>-        scrollCount, unacceleratedScrollingDelta, modifiers, timestamp, ioHIDEventWallTime, rawPlatformDelta);
</del><ins>+        scrollCount, unacceleratedScrollingDelta, modifiers, timestamp, ioHIDEventWallTime, rawPlatformDelta, momentumEndType);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> WebKeyboardEvent WebEventFactory::createWebKeyboardEvent(NSEvent *event, bool handledByInputMethod, bool replacesSoftSpace, const Vector<WebCore::KeypressCommand>& commands)
</span></span></pre></div>
<a id="trunkSourceWebKitWebProcessWebPageMomentumEventDispatchercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.cpp (287640 => 287641)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.cpp       2022-01-05 19:19:35 UTC (rev 287640)
+++ trunk/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.cpp  2022-01-05 19:29:43 UTC (rev 287641)
</span><span class="lines">@@ -87,8 +87,23 @@
</span><span class="cx">         // momentumPhase == PhaseEnded that interrupts.
</span><span class="cx">         bool eventShouldInterruptGesture = !isMomentumEvent || event.momentumPhase() != WebWheelEvent::PhaseChanged;
</span><span class="cx"> 
</span><del>-        if (pageIdentifierChanged || eventShouldInterruptGesture)
</del><ins>+        if (event.momentumPhase() == WebWheelEvent::PhaseEnded) {
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING)
+            RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher saw momentum ended phase, interrupted=%d", static_cast<int>(event.momentumEndType()));
+#endif
+
+            // Ignore momentumPhase == PhaseEnded if it was due to the natural
+            // end of the animation (as opposed to interruption by placing fingers
+            // on the trackpad), so that our momentum is not cut short if the
+            // deceleration runs longer than the system curve.
+            if (event.momentumEndType() == WebWheelEvent::MomentumEndType::Natural)
+                eventShouldInterruptGesture = false;
+        }
+
+        if (pageIdentifierChanged || eventShouldInterruptGesture) {
+            RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher interrupting synthetic momentum phase");
</ins><span class="cx">             didEndMomentumPhase();
</span><ins>+        }
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (event.phase() == WebWheelEvent::PhaseBegan || event.phase() == WebWheelEvent::PhaseChanged) {
</span><span class="lines">@@ -100,18 +115,26 @@
</span><span class="cx"> #endif
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (eventShouldStartSyntheticMomentumPhase(pageIdentifier, event))
</del><ins>+    if (eventShouldStartSyntheticMomentumPhase(pageIdentifier, event)) {
</ins><span class="cx">         didStartMomentumPhase(pageIdentifier, event);
</span><ins>+        m_isInOverriddenPlatformMomentumGesture = true;
+    }
</ins><span class="cx"> 
</span><del>-    bool isMomentumEventDuringSyntheticGesture = isMomentumEvent && m_currentGesture.active;
</del><ins>+    // Consume any incoming momentum events while we're generating a synthetic
+    // momentum gesture *or* a platform momentum phase that was overridden
+    // is still running after we finished the synthetic gesture.
+    bool shouldIgnoreIncomingPlatformEvent = isMomentumEvent && (m_isInOverriddenPlatformMomentumGesture || m_currentGesture.active);
</ins><span class="cx"> 
</span><ins>+    if (event.momentumPhase() == WebWheelEvent::PhaseEnded)
+        m_isInOverriddenPlatformMomentumGesture = false;
+
</ins><span class="cx"> #if ENABLE(MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING)
</span><del>-    if (isMomentumEventDuringSyntheticGesture)
</del><ins>+    if (shouldIgnoreIncomingPlatformEvent)
</ins><span class="cx">         m_currentGesture.accumulatedEventOffset += event.delta();
</span><span class="cx"> 
</span><span class="cx">     auto combinedPhase = (event.phase() << 8) | (event.momentumPhase());
</span><span class="cx">     m_currentLogState.totalEventOffset += event.delta().height();
</span><del>-    if (!isMomentumEventDuringSyntheticGesture) {
</del><ins>+    if (!shouldIgnoreIncomingPlatformEvent) {
</ins><span class="cx">         // Log events that we don't block to the generated offsets log as well,
</span><span class="cx">         // even though we didn't technically generate them, just passed them through.
</span><span class="cx">         m_currentLogState.totalGeneratedOffset += event.delta().height();
</span><span class="lines">@@ -121,8 +144,7 @@
</span><span class="cx">     
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-    // Consume any normal momentum events while we're inside a synthetic momentum gesture.
-    return isMomentumEventDuringSyntheticGesture;
</del><ins>+    return shouldIgnoreIncomingPlatformEvent;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static float appKitScrollMultiplierForEvent(const WebWheelEvent& event)
</span><span class="lines">@@ -170,7 +192,8 @@
</span><span class="cx">         m_lastIncomingEvent->modifiers(),
</span><span class="cx">         time,
</span><span class="cx">         time,
</span><del>-        { });
</del><ins>+        { },
+        WebWheelEvent::MomentumEndType::Unknown);
</ins><span class="cx">     m_dispatcher.internalWheelEvent(m_currentGesture.pageIdentifier, syntheticEvent, m_lastRubberBandableEdges, EventDispatcher::WheelEventOrigin::MomentumEventDispatcher);
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING)
</span><span class="lines">@@ -223,7 +246,7 @@
</span><span class="cx">     dispatchSyntheticMomentumEvent(WebWheelEvent::PhaseEnded, { });
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING)
</span><del>-    RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher saw momentum end phase with total offset %.1f %.1f, duration %f (event offset would have been %.1f %.1f) (tail index %d of %zu)", m_currentGesture.currentOffset.width(), m_currentGesture.currentOffset.height(), (MonotonicTime::now() - m_currentGesture.startTime).seconds(), m_currentGesture.accumulatedEventOffset.width(), m_currentGesture.accumulatedEventOffset.height(), m_currentGesture.currentTailDeltaIndex, m_currentGesture.tailDeltaTable.size());
</del><ins>+    RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher ending synthetic momentum phase with total offset %.1f %.1f, duration %f (event offset would have been %.1f %.1f) (tail index %d of %zu)", m_currentGesture.currentOffset.width(), m_currentGesture.currentOffset.height(), (MonotonicTime::now() - m_currentGesture.startTime).seconds(), m_currentGesture.accumulatedEventOffset.width(), m_currentGesture.accumulatedEventOffset.height(), m_currentGesture.currentTailDeltaIndex, m_currentGesture.tailDeltaTable.size());
</ins><span class="cx">     m_dispatcher.queue().dispatchAfter(1_s, [this] {
</span><span class="cx">         flushLog();
</span><span class="cx">     });
</span><span class="lines">@@ -242,7 +265,7 @@
</span><span class="cx"> #if ENABLE(MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING)
</span><span class="cx">     WTF::TextStream stream(WTF::TextStream::LineMode::SingleLine);
</span><span class="cx">     stream << curve;
</span><del>-    RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher set curve %s", stream.release().utf8().data());
</del><ins>+    RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher set curve %{public}s", stream.release().utf8().data());
</ins><span class="cx"> #endif
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -299,7 +322,7 @@
</span><span class="cx">         startDisplayLink();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-WebCore::FloatSize MomentumEventDispatcher::consumeDeltaForCurrentTime()
</del><ins>+std::optional<WebCore::FloatSize> MomentumEventDispatcher::consumeDeltaForCurrentTime()
</ins><span class="cx"> {
</span><span class="cx">     WebCore::FloatSize delta;
</span><span class="cx"> 
</span><span class="lines">@@ -311,7 +334,7 @@
</span><span class="cx">         if (m_currentGesture.currentTailDeltaIndex < m_currentGesture.tailDeltaTable.size())
</span><span class="cx">             delta = -m_currentGesture.tailDeltaTable[m_currentGesture.currentTailDeltaIndex++];
</span><span class="cx">         else
</span><del>-            delta = { };
</del><ins>+            return std::nullopt;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     m_currentGesture.currentOffset += delta;
</span><span class="lines">@@ -331,7 +354,16 @@
</span><span class="cx">     if (!displayProperties || displayID != displayProperties->displayID)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    dispatchSyntheticMomentumEvent(WebWheelEvent::PhaseChanged, consumeDeltaForCurrentTime());
</del><ins>+    auto delta = consumeDeltaForCurrentTime();
+    if (!delta) {
+#if ENABLE(MOMENTUM_EVENT_DISPATCHER_TEMPORARY_LOGGING)
+        RELEASE_LOG(ScrollAnimations, "MomentumEventDispatcher completed synthetic momentum phase");
+#endif
+        didEndMomentumPhase();
+        return;
+    }
+
+    dispatchSyntheticMomentumEvent(WebWheelEvent::PhaseChanged, *delta);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void MomentumEventDispatcher::didReceiveScrollEventWithInterval(WebCore::FloatSize size, Seconds frameInterval)
</span></span></pre></div>
<a id="trunkSourceWebKitWebProcessWebPageMomentumEventDispatcherh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.h (287640 => 287641)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.h 2022-01-05 19:19:35 UTC (rev 287640)
+++ trunk/Source/WebKit/WebProcess/WebPage/MomentumEventDispatcher.h    2022-01-05 19:29:43 UTC (rev 287641)
</span><span class="lines">@@ -86,7 +86,7 @@
</span><span class="cx">     void equalizeTailGaps();
</span><span class="cx"> 
</span><span class="cx">     // Once consumed, this delta *must* be dispatched in an event.
</span><del>-    WebCore::FloatSize consumeDeltaForCurrentTime();
</del><ins>+    std::optional<WebCore::FloatSize> consumeDeltaForCurrentTime();
</ins><span class="cx"> 
</span><span class="cx">     WebCore::FloatSize offsetAtTime(Seconds);
</span><span class="cx">     std::pair<WebCore::FloatSize, WebCore::FloatSize> computeNextDelta(WebCore::FloatSize currentUnacceleratedDelta);
</span><span class="lines">@@ -125,6 +125,7 @@
</span><span class="cx">     std::optional<WallTime> m_lastScrollTimestamp;
</span><span class="cx">     std::optional<WebWheelEvent> m_lastIncomingEvent;
</span><span class="cx">     WebCore::RectEdges<bool> m_lastRubberBandableEdges;
</span><ins>+    bool m_isInOverriddenPlatformMomentumGesture { false };
</ins><span class="cx"> 
</span><span class="cx">     struct {
</span><span class="cx">         bool active { false };
</span></span></pre>
</div>
</div>

</body>
</html>